-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
423 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* eslint-disable jsx-a11y/no-static-element-interactions */ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
import React, { useEffect, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Modal, Button, ModalBody } from 'carbon-components-react'; | ||
import MiqTree from '../MiqTreeView'; | ||
|
||
/** Component to render a tree and to select an embedded method. */ | ||
const AeInlineMethod = ({ type }) => { | ||
const [data, setData] = useState({ | ||
isModalOpen: false, | ||
selectedNode: undefined, | ||
list: [], | ||
}); | ||
|
||
/** Function to show/hide the modal. */ | ||
const showModal = (status) => { | ||
setData({ | ||
...data, | ||
isModalOpen: status, | ||
}); | ||
}; | ||
|
||
/** Function to render the Add method button. */ | ||
const renderAddButton = () => ( | ||
<Button | ||
id="add-method" | ||
kind="primary" | ||
title={__('Add Method')} | ||
onClick={() => showModal(true)} | ||
size="sm" | ||
> | ||
{__('Add method')} | ||
</Button> | ||
); | ||
|
||
console.log(data); | ||
|
||
const renderList = () => (data.list.map((item) => ( | ||
<div key={item.key}> | ||
<div>{item.fqname}</div> | ||
</div> | ||
))); | ||
|
||
return ( | ||
<div> | ||
{renderAddButton()} | ||
{renderList()} | ||
<Modal | ||
primaryButtonDisabled={data.selectedNode === undefined} | ||
size="lg" | ||
modalHeading={__('Select item')} | ||
open={data.isModalOpen} | ||
primaryButtonText={__('OK')} | ||
secondaryButtonText={__('Cancel')} | ||
onRequestClose={() => showModal(false)} | ||
onRequestSubmit={() => { | ||
console.log('on onRequestSubmit'); | ||
setData({ | ||
...data, | ||
list: data.list.push(data.selectedNode), | ||
}); | ||
showModal(false); | ||
}} | ||
onSecondarySubmit={() => { | ||
console.log('on onSecondarySubmit'); | ||
showModal(false); | ||
}} | ||
> | ||
<ModalBody> | ||
{ | ||
data.isModalOpen | ||
&& ( | ||
<MiqTree | ||
type={type} | ||
onNodeSelect={(item) => { | ||
setData({ | ||
...data, | ||
selectedNode: item, | ||
}); | ||
}} | ||
/> | ||
) | ||
} | ||
</ModalBody> | ||
</Modal> | ||
|
||
</div> | ||
); | ||
}; | ||
|
||
export default AeInlineMethod; | ||
|
||
AeInlineMethod.propTypes = { | ||
type: PropTypes.string.isRequired, | ||
}; |
46 changes: 46 additions & 0 deletions
46
app/javascript/components/MiqTreeView/MiqTreeChildNode.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* eslint-disable jsx-a11y/no-static-element-interactions */ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
/* eslint-disable import/no-cycle */ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import MiqTreeNode from './MiqTreeNode'; | ||
|
||
/** A component to render the parent node of the tree. */ | ||
const MiqTreeChildNode = ({ | ||
node, onSelect, selectedNode, selectKey, | ||
}) => ( | ||
<div className="tree-row child-tree intend-right" key={node.key}> | ||
{ | ||
node.nodes.map((item) => ( | ||
<MiqTreeNode | ||
key={item.key} | ||
node={item} | ||
selectedNode={selectedNode} | ||
selectKey={selectKey} | ||
onSelect={(childItem) => onSelect(childItem)} | ||
/> | ||
)) | ||
} | ||
</div> | ||
); | ||
|
||
export default MiqTreeChildNode; | ||
|
||
MiqTreeChildNode.propTypes = { | ||
node: PropTypes.shape({ | ||
key: PropTypes.string, | ||
nodes: PropTypes.arrayOf(PropTypes.any), | ||
state: PropTypes.shape({ | ||
expanded: PropTypes.bool, | ||
}), | ||
}).isRequired, | ||
selectKey: PropTypes.string.isRequired, | ||
onSelect: PropTypes.func.isRequired, | ||
selectedNode: PropTypes.shape({ | ||
key: PropTypes.string, | ||
}), | ||
}; | ||
|
||
MiqTreeChildNode.defaultProps = { | ||
selectedNode: undefined, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
/* eslint-disable import/no-cycle */ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import MiqTreeParentNode from './MiqTreeParentNode'; | ||
import MiqTreeChildNode from './MiqTreeChildNode'; | ||
|
||
/** A Recursive Functional component to render the Tree and its child nodes. */ | ||
const MiqTreeNode = ({ | ||
node, selectedNode, selectKey, onSelect, | ||
}) => { | ||
const isSelected = (selectedNode && selectedNode.key === node.key) || false; | ||
|
||
return ( | ||
<div key={node.key}> | ||
<MiqTreeParentNode | ||
node={node} | ||
isSelected={isSelected} | ||
selectKey={selectKey} | ||
onSelect={(parentItem) => onSelect(parentItem)} | ||
/> | ||
{ | ||
node.state.expanded && node.nodes && node.nodes.length > 0 && ( | ||
<MiqTreeChildNode | ||
node={node} | ||
onSelect={(childItem) => onSelect(childItem)} | ||
selectedNode={selectedNode} | ||
selectKey={selectKey} | ||
/> | ||
) | ||
} | ||
</div> | ||
); | ||
}; | ||
|
||
export default MiqTreeNode; | ||
|
||
MiqTreeNode.propTypes = { | ||
node: PropTypes.shape({ | ||
key: PropTypes.string, | ||
nodes: PropTypes.arrayOf(PropTypes.any), | ||
state: PropTypes.shape({ | ||
expanded: PropTypes.bool, | ||
}), | ||
}).isRequired, | ||
selectKey: PropTypes.string.isRequired, | ||
onSelect: PropTypes.func.isRequired, | ||
selectedNode: PropTypes.shape({ | ||
key: PropTypes.string, | ||
}), | ||
}; | ||
|
||
MiqTreeNode.defaultProps = { | ||
selectedNode: undefined, | ||
}; |
49 changes: 49 additions & 0 deletions
49
app/javascript/components/MiqTreeView/MiqTreeParentNode.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* eslint-disable jsx-a11y/no-static-element-interactions */ | ||
/* eslint-disable jsx-a11y/click-events-have-key-events */ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import classNames from 'classnames'; | ||
import { CaretRight16, CaretDown16, CheckmarkFilled16 } from '@carbon/icons-react'; | ||
import { selectableItem } from './helper'; | ||
|
||
/** A component to render the parent node of the tree. */ | ||
const MiqTreeParentNode = ({ | ||
node, selectKey, isSelected, onSelect, | ||
}) => { | ||
/** Function to render the down and right caret. */ | ||
const renderCaret = (item) => { | ||
if (!item) { | ||
return undefined; | ||
} | ||
if (selectableItem(item, selectKey) || !item.lazyLoad) { | ||
return undefined; | ||
} | ||
return item.state.expanded ? <CaretDown16 className="tree-caret" /> : <CaretRight16 className="tree-caret" />; | ||
}; | ||
|
||
return ( | ||
<div className={classNames('tree-row parent-tree', isSelected && 'selected-node')} onClick={() => onSelect(node)}> | ||
{renderCaret(node)} | ||
<div className="tree-icon"><i className={node.icon} /></div> | ||
<div className="tree-text">{node.text}</div> | ||
{isSelected && <CheckmarkFilled16 className="selected-node-check" />} | ||
</div> | ||
); | ||
}; | ||
|
||
export default MiqTreeParentNode; | ||
|
||
MiqTreeParentNode.propTypes = { | ||
node: PropTypes.shape({ | ||
icon: PropTypes.string, | ||
text: PropTypes.string, | ||
key: PropTypes.string, | ||
nodes: PropTypes.arrayOf(PropTypes.any), | ||
state: PropTypes.shape({ | ||
expanded: PropTypes.bool, | ||
}), | ||
}).isRequired, | ||
selectKey: PropTypes.string.isRequired, | ||
isSelected: PropTypes.bool.isRequired, | ||
onSelect: PropTypes.func.isRequired, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
export const TREE_CONFIG = { | ||
aeInlineMethod: { | ||
url: '/tree/automate_inline_methods', | ||
selectKey: 'aem', | ||
}, | ||
}; | ||
|
||
/** Function to find the selected item from the tree data. */ | ||
export const findNodeByKey = (array, keyToFind) => { | ||
const flattenedArray = array.flatMap((item) => [item, ...(item.nodes || [])]); | ||
const foundNode = flattenedArray.find((item) => item.key === keyToFind); | ||
|
||
return foundNode || flattenedArray | ||
.filter((item) => item.nodes && item.nodes.length > 0) | ||
.map((item) => findNodeByKey(item.nodes, keyToFind)) | ||
.find(Boolean); | ||
}; | ||
|
||
export const selectableItem = (child, selectKey) => child.key.split('-')[0] === selectKey; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Loading } from 'carbon-components-react'; | ||
import MiqTreeNode from './MiqTreeNode'; | ||
import { TREE_CONFIG, selectableItem, findNodeByKey } from './helper'; | ||
import './style.scss'; | ||
|
||
const MiqTree = ({ type, onNodeSelect }) => { | ||
const treeType = TREE_CONFIG[type]; | ||
|
||
const [data, setData] = useState({ | ||
list: undefined, | ||
isLoading: true, | ||
selectedNode: undefined, | ||
}); | ||
|
||
/** Function to update the data. */ | ||
const updateData = (others) => { | ||
setData({ | ||
...data, | ||
...others, | ||
}); | ||
}; | ||
|
||
/** A request is made to fetch the initial data during component load. */ | ||
useEffect(() => { | ||
http.get(treeType.url) | ||
.then((response) => updateData({ list: response, isLoading: false })); | ||
}, []); | ||
|
||
useEffect(() => { | ||
onNodeSelect(data.selectedNode); | ||
}, [data.selectedNode]); | ||
|
||
/** Function to select a node from tree. This triggers the useEffect. */ | ||
const selectNode = (node) => { | ||
const selectedNode = (data.selectedNode && data.selectedNode.key === node.key) ? undefined : node; | ||
updateData({ selectedNode }); | ||
}; | ||
|
||
/** Function to handle show the children of a node | ||
* if child nodes are available, just expand the tree. | ||
* else, request an API to fetch the child nodes and update the results. | ||
*/ | ||
const expandTree = (item, node) => { | ||
if (item.nodes && item.nodes.length > 0) { | ||
item.state.expanded = true; | ||
updateData({ list: [...data.list] }); | ||
} else { | ||
http.get(`${treeType.url}?id=${node.key}`) | ||
.then((response) => { | ||
item.nodes = response; | ||
item.state.expanded = true; | ||
updateData({ list: [...data.list] }); | ||
}); | ||
} | ||
}; | ||
|
||
/** Function to collapse the tree to hide the children */ | ||
const collapseTree = (item) => { | ||
item.state.expanded = false; | ||
updateData({ list: [...data.list] }); | ||
}; | ||
|
||
/** Function to expand/collapse the tree. */ | ||
const toggleTree = (node) => { | ||
const item = findNodeByKey(data.list, node.key); | ||
if (item) { | ||
if (node.state.expanded) { | ||
collapseTree(item); | ||
} else { | ||
expandTree(item, node); | ||
} | ||
} | ||
}; | ||
|
||
/** Function to handle the click events of tree node. */ | ||
const loadSelectedNode = (node) => (selectableItem(node, treeType.selectKey) | ||
? selectNode(node) | ||
: toggleTree(node)); | ||
|
||
/** Function to render the tree contents. */ | ||
const renderTree = (list) => (list && list.map((child) => ( | ||
<MiqTreeNode | ||
key={child.key} | ||
node={child} | ||
selectedNode={data.selectedNode} | ||
selectKey={treeType.selectKey} | ||
onSelect={(node) => loadSelectedNode(node)} | ||
/> | ||
))); | ||
|
||
/** Function to render the modal contents. */ | ||
const renderTreeContent = () => ((data.list && data.list.length > 0) ? renderTree(data.list) : undefined); | ||
|
||
return ( | ||
<div> | ||
{ | ||
data.isLoading | ||
? <Loading active small withOverlay={false} className="loading" /> | ||
: renderTreeContent() | ||
} | ||
</div> | ||
); | ||
}; | ||
|
||
export default MiqTree; | ||
|
||
MiqTree.propTypes = { | ||
type: PropTypes.string.isRequired, | ||
onNodeSelect: PropTypes.func.isRequired, | ||
}; |
Oops, something went wrong.