Add new component to support Tree view
Add treeview component to show subset of policies by prefix (using .)
Issue-ID: POLICY-3165
Signed-off-by: sebdet <sebastien.determe@intl.att.com>
Change-Id: I1365695f03086beda36a6bafddd9ad0f52944b6d
diff --git a/ui-react/package.json b/ui-react/package.json
index 8d11044..a62cf67 100644
--- a/ui-react/package.json
+++ b/ui-react/package.json
@@ -36,6 +36,7 @@
"react-router-dom": "5.2.0",
"@material-ui/core": "4.11.3",
"@material-ui/icons": "4.11.2",
+ "@material-ui/lab": "4.0.0-alpha.57",
"material-table": "1.68.1",
"react-select": "4.2.1",
"react-uuid": "1.0.2"
diff --git a/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js b/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js
new file mode 100644
index 0000000..9c2f102
--- /dev/null
+++ b/ui-react/src/components/dialogs/Policy/PoliciesTreeViewer.js
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2021 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+import React, { forwardRef } from 'react'
+import TreeView from '@material-ui/lab/TreeView';
+import TreeItem from '@material-ui/lab/TreeItem';
+import FolderIcon from '@material-ui/icons/Folder';
+import FolderOpenIcon from '@material-ui/icons/FolderOpen';
+import DescriptionIcon from '@material-ui/icons/Description';
+
+
+export default class PoliciesTreeViewer extends React.Component {
+
+ separator = ".";
+
+ nodesList = new Map();
+
+ constructor(props, context) {
+ super(props, context);
+ this.createPoliciesTree = this.createPoliciesTree.bind(this);
+ this.handleTreeItemClick = this.handleTreeItemClick.bind(this);
+ this.buildNameWithParent = this.buildNameWithParent.bind(this);
+
+ }
+
+ state = {
+ policiesTreeData: this.createPoliciesTree(this.props.policiesData),
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.policiesData !== this.props.policiesData) {
+ this.setState({policiesTreeData: this.createPoliciesTree(this.props.policiesData)})
+ }
+ }
+
+ createPoliciesTree(policiesArray) {
+ // put my policies array in a Json
+ let nodeId = 1;
+ let root = {id:nodeId, policyCount:0, name:"ROOT", children:[], parent: undefined};
+ this.nodesList.set(nodeId++, root);
+
+ policiesArray.forEach(policy => {
+ let currentTreeNode = root;
+ policy[this.props.valueForTreeCreation].split(this.separator).forEach((policyNamePart, index, policyNamePartsArray) => {
+ let node = currentTreeNode["children"].find(element => element.name === policyNamePart);
+ if (typeof(node) === "undefined") {
+ node = {id:nodeId, policyCount:0, children:[], name:policyNamePart, parent:currentTreeNode};
+ this.nodesList.set(nodeId++, node);
+ currentTreeNode["children"].push(node);
+ }
+ if ((index+1) === policyNamePartsArray.length) {
+ ++currentTreeNode["policyCount"];
+ }
+ currentTreeNode = node;
+ })
+ })
+ return root;
+ }
+
+ buildNameWithParent(node) {
+ let nameToBuild = node.name;
+ if (node.parent !== undefined) {
+ nameToBuild = this.buildNameWithParent(node.parent) + this.separator + node.name;
+ }
+ return nameToBuild;
+ }
+
+ handleTreeItemClick(event, value) {
+ let fullName = this.buildNameWithParent(this.nodesList.get(value[0])).substring(5);
+ this.props.policiesFilterFunction(fullName);
+ }
+
+ renderTreeItems(nodes) {
+ return (<TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name + "("+ nodes.policyCount + ")"} onNodeSelect={this.handleTreeItemClick}>
+ {
+ Array.isArray(nodes.children) ? nodes.children.map((node) => this.renderTreeItems(node)) : null
+ }
+ </TreeItem>);
+ };
+
+ render() {
+ return (
+ <TreeView defaultExpanded={['root']} defaultCollapseIcon={<FolderOpenIcon />}
+ defaultExpandIcon={<FolderIcon />} defaultEndIcon={<DescriptionIcon />} onNodeSelect={this.handleTreeItemClick} multiSelect>
+ {this.renderTreeItems(this.state.policiesTreeData)}
+ </TreeView>
+ );
+ }
+}
\ No newline at end of file
diff --git a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
index f986dff..0fd0d13 100644
--- a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
+++ b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
@@ -44,7 +44,6 @@
import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
import AddIcon from '@material-ui/icons/Add';
import PublishIcon from '@material-ui/icons/Publish';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import MaterialTable from "material-table";
import PolicyService from '../../../api/PolicyService';
@@ -56,6 +55,7 @@
import PolicyEditor from './PolicyEditor';
import ToscaViewer from './ToscaViewer';
import PolicyDeploymentEditor from './PolicyDeploymentEditor';
+import PoliciesTreeViewer from './PoliciesTreeViewer';
const DivWhiteSpaceStyled = styled.div`
white-space: pre;
@@ -79,6 +79,18 @@
margin-top: 20px;
`
+const PoliciesTreeViewerDiv = styled.div`
+ width: 20%;
+ float: left;
+ left: 0;
+ overflow: auto;
+`
+
+const MaterialTableDiv = styled.div`
+ float: right;
+ width: 80%;
+ left: 20%;
+`
const standardCellStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black' };
const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' };
@@ -87,13 +99,11 @@
export default class ViewAllPolicies extends React.Component {
state = {
show: true,
- showPolicyDeploymentDialog: false,
- content: 'Please select a policy to display it',
- selectedRowId: -1,
policiesListData: [],
+ policiesListDataFiltered: [],
toscaModelsListData: [],
+ toscaModelsListDataFiltered: [],
jsonEditorForPolicy: new Map(),
- prefixGrouping: false,
showSuccessAlert: false,
showFailAlert: false,
policyColumnsDefinition: [
@@ -179,12 +189,13 @@
constructor(props, context) {
super(props, context);
this.handleClose = this.handleClose.bind(this);
- this.handlePrefixGrouping = this.handlePrefixGrouping.bind(this);
this.handleDeletePolicy = this.handleDeletePolicy.bind(this);
this.disableAlert = this.disableAlert.bind(this);
this.getAllPolicies = this.getAllPolicies.bind(this);
this.getAllToscaModels = this.getAllToscaModels.bind(this);
this.generateAdditionalPolicyColumns = this.generateAdditionalPolicyColumns.bind(this);
+ this.filterPolicies = this.filterPolicies.bind(this);
+ this.filterTosca = this.filterTosca.bind(this);
this.getAllPolicies();
this.getAllToscaModels();
}
@@ -217,14 +228,18 @@
getAllToscaModels() {
PolicyToscaService.getToscaPolicyModels().then(toscaModelsList => {
- this.setState({ toscaModelsListData: toscaModelsList });
+ this.setState({ toscaModelsListData: toscaModelsList,
+ toscaModelsListDataFiltered: toscaModelsList
+ });
});
}
getAllPolicies() {
PolicyService.getPoliciesList().then(allPolicies => {
this.generateAdditionalPolicyColumns(allPolicies["policies"])
- this.setState({ policiesListData: allPolicies["policies"] })
+ this.setState({ policiesListData: allPolicies["policies"],
+ policiesListDataFiltered: allPolicies["policies"],
+ })
});
}
@@ -234,10 +249,6 @@
this.props.history.push('/')
}
- handlePrefixGrouping(event) {
- this.setState({prefixGrouping: event.target.checked});
- }
-
handleDeletePolicy(event, rowData) {
PolicyService.deletePolicy(rowData["type"], rowData["type_version"], rowData["name"],rowData["version"]).then(
respPolicyDeletion => {
@@ -262,74 +273,84 @@
this.setState ({ showSuccessAlert: false, showFailAlert: false });
}
+ filterPolicies(prefixForFiltering) {
+ this.setState({policiesListDataFiltered: this.state.policiesListData.filter(element => element.name.startsWith(prefixForFiltering))});
+ }
+
+ filterTosca(prefixForFiltering) {
+ this.setState({toscaModelsListDataFiltered: this.state.toscaModelsListData.filter(element => element.policyModelType.startsWith(prefixForFiltering))});
+ }
+
renderPoliciesTab() {
return (
<Tab eventKey="policies" title="Policies in Policy Framework">
<Modal.Body>
- <FormControlLabel
- control={<Switch checked={this.state.prefixGrouping} onChange={this.handlePrefixGrouping} />}
- label="Group by prefix"
+ <div>
+ <PoliciesTreeViewerDiv>
+ <PoliciesTreeViewer policiesData={this.state.policiesListData} valueForTreeCreation="name" policiesFilterFunction={this.filterPolicies} />
+ </PoliciesTreeViewerDiv>
+ <MaterialTableDiv>
+ <MaterialTable
+ title={"Policies"}
+ data={this.state.policiesListDataFiltered}
+ columns={this.state.policyColumnsDefinition}
+ icons={this.state.tableIcons}
+ onRowClick={(event, rowData, togglePanel) => togglePanel()}
+ options={{
+ grouping: true,
+ exportButton: true,
+ headerStyle:rowHeaderStyle,
+ actionsColumnIndex: -1
+ }}
+ detailPanel={[
+ {
+ icon: ArrowForwardIosIcon,
+ tooltip: 'Show Configuration',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <PolicyEditor policyModelType={rowData["type"]} policyModelTypeVersion={rowData["type_version"]}
+ policyName={rowData["name"]} policyVersion={rowData["version"]} policyProperties={rowData["properties"]}
+ policiesTableUpdateFunction={this.getAllPolicies} />
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: DehazeIcon,
+ openIcon: DehazeIcon,
+ tooltip: 'Show Raw Data',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <pre>{JSON.stringify(rowData, null, 2)}</pre>
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: PublishIcon,
+ openIcon: PublishIcon,
+ tooltip: 'PDP Group Deployment',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <PolicyDeploymentEditor policyData={rowData} policiesTableUpdateFunction={this.getAllPolicies} />
+ </DetailedRow>
+ )
+ },
+ }
+ ]}
+ actions={[
+ {
+ icon: forwardRef((props, ref) => <DeleteRoundedIcon {...props} ref={ref} />),
+ tooltip: 'Delete Policy',
+ onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
+ }
+ ]}
/>
- <MaterialTable
- title={"Policies"}
- data={this.state.policiesListData}
- columns={this.state.policyColumnsDefinition}
- icons={this.state.tableIcons}
- onRowClick={(event, rowData, togglePanel) => togglePanel()}
- options={{
- grouping: true,
- exportButton: true,
- headerStyle:rowHeaderStyle,
- rowStyle: rowData => ({
- backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF'
- }),
- actionsColumnIndex: -1
- }}
- detailPanel={[
- {
- icon: ArrowForwardIosIcon,
- tooltip: 'Show Configuration',
- render: rowData => {
- return (
- <DetailedRow>
- <PolicyEditor policyModelType={rowData["type"]} policyModelTypeVersion={rowData["type_version"]} policyName={rowData["name"]} policyVersion={rowData["version"]} policyProperties={rowData["properties"]} policiesTableUpdateFunction={this.getAllPolicies} />
- </DetailedRow>
- )
- },
- },
- {
- icon: DehazeIcon,
- openIcon: DehazeIcon,
- tooltip: 'Show Raw Data',
- render: rowData => {
- return (
- <DetailedRow>
- <pre>{JSON.stringify(rowData, null, 2)}</pre>
- </DetailedRow>
- )
- },
- },
- {
- icon: PublishIcon,
- openIcon: PublishIcon,
- tooltip: 'PDP Group Deployment',
- render: rowData => {
- return (
- <DetailedRow>
- <PolicyDeploymentEditor policyData={rowData} policiesTableUpdateFunction={this.getAllPolicies} />
- </DetailedRow>
- )
- },
- }
- ]}
- actions={[
- {
- icon: forwardRef((props, ref) => <DeleteRoundedIcon {...props} ref={ref} />),
- tooltip: 'Delete Policy',
- onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
- }
- ]}
- />
+ </MaterialTableDiv>
+ </div>
</Modal.Body>
</Tab>
);
@@ -339,63 +360,63 @@
return (
<Tab eventKey="tosca models" title="Tosca Models in Policy Framework">
<Modal.Body>
- <FormControlLabel
- control={<Switch checked={this.state.prefixGrouping} onChange={this.handlePrefixGrouping} />}
- label="Group by prefix"
- />
- <MaterialTable
- title={"Tosca Models"}
- data={this.state.toscaModelsListData}
- columns={this.state.toscaColumnsDefinition}
- icons={this.state.tableIcons}
- onRowClick={(event, rowData, togglePanel) => togglePanel()}
- options={{
- grouping: true,
- exportButton: true,
- headerStyle:rowHeaderStyle,
- rowStyle: rowData => ({
- backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF'
- }),
- actionsColumnIndex: -1
- }}
- detailPanel={[
- {
- icon: ArrowForwardIosIcon,
- tooltip: 'Show Tosca',
- render: rowData => {
- return (
- <DetailedRow>
- <ToscaViewer toscaData={rowData} />
- </DetailedRow>
- )
- },
- },
- {
- icon: DehazeIcon,
- openIcon: DehazeIcon,
- tooltip: 'Show Raw Data',
- render: rowData => {
- return (
- <DetailedRow>
- <pre>{JSON.stringify(rowData, null, 2)}</pre>
- </DetailedRow>
- )
- },
- },
- {
- icon: AddIcon,
- openIcon: AddIcon,
- tooltip: 'Create a policy from this model',
- render: rowData => {
- return (
- <DetailedRow>
- <PolicyEditor policyModelType={rowData["policyModelType"]} policyModelTypeVersion={rowData["version"]} policyProperties={{}} policiesTableUpdateFunction={this.getAllPolicies} />
- </DetailedRow>
- )
- },
- },
- ]}
- />
+ <div>
+ <PoliciesTreeViewerDiv>
+ <PoliciesTreeViewer policiesData={this.state.toscaModelsListData} valueForTreeCreation="policyModelType" policiesFilterFunction={this.filterTosca} />
+ </PoliciesTreeViewerDiv>
+ <MaterialTableDiv>
+ <MaterialTable
+ title={"Tosca Models"}
+ data={this.state.toscaModelsListDataFiltered}
+ columns={this.state.toscaColumnsDefinition}
+ icons={this.state.tableIcons}
+ onRowClick={(event, rowData, togglePanel) => togglePanel()}
+ options={{
+ grouping: true,
+ exportButton: true,
+ headerStyle:rowHeaderStyle,
+ actionsColumnIndex: -1
+ }}
+ detailPanel={[
+ {
+ icon: ArrowForwardIosIcon,
+ tooltip: 'Show Tosca',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <ToscaViewer toscaData={rowData} />
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: DehazeIcon,
+ openIcon: DehazeIcon,
+ tooltip: 'Show Raw Data',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <pre>{JSON.stringify(rowData, null, 2)}</pre>
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: AddIcon,
+ openIcon: AddIcon,
+ tooltip: 'Create a policy from this model',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <PolicyEditor policyModelType={rowData["policyModelType"]} policyModelTypeVersion={rowData["version"]} policyProperties={{}} policiesTableUpdateFunction={this.getAllPolicies} />
+ </DetailedRow>
+ )
+ },
+ },
+ ]}
+ />
+ </MaterialTableDiv>
+ </div>
</Modal.Body>
</Tab>
);