From 440ed864a460743e3320fa200ef81da4619d85f0 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Wed, 18 May 2022 10:25:12 +0200 Subject: [PATCH] F #5422: Improve the split pane component (#2048) --- src/fireedge/package-lock.json | 11 + src/fireedge/package.json | 1 + .../src/client/components/SplitPane/index.js | 205 ++++++++---------- .../src/client/containers/Clusters/index.js | 97 ++++++--- .../src/client/containers/Datastores/index.js | 97 ++++++--- .../src/client/containers/Groups/index.js | 97 ++++++--- .../src/client/containers/Hosts/index.js | 103 ++++++--- .../src/client/containers/Images/index.js | 97 ++++++--- .../containers/MarketplaceApps/index.js | 101 ++++++--- .../client/containers/Marketplaces/index.js | 97 ++++++--- .../src/client/containers/Users/index.js | 97 ++++++--- .../containers/VNetworkTemplates/index.js | 97 ++++++--- .../containers/VirtualMachines/index.js | 103 ++++++--- .../containers/VirtualNetworks/index.js | 97 ++++++--- .../client/containers/VmTemplates/index.js | 103 ++++++--- .../src/client/containers/Zones/index.js | 97 ++++++--- 16 files changed, 1023 insertions(+), 477 deletions(-) diff --git a/src/fireedge/package-lock.json b/src/fireedge/package-lock.json index 78750f9153..a5d33760b5 100644 --- a/src/fireedge/package-lock.json +++ b/src/fireedge/package-lock.json @@ -92,6 +92,7 @@ "socket.io": "4.4.1", "socket.io-client": "4.4.1", "speakeasy": "2.0.0", + "split-grid": "1.0.11", "sprintf-js": "1.1.2", "style-loader": "3.2.1", "terser-webpack-plugin": "5.1.4", @@ -11544,6 +11545,11 @@ "node": ">= 0.10.0" } }, + "node_modules/split-grid": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz", + "integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==" + }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -21752,6 +21758,11 @@ "base32.js": "0.0.1" } }, + "split-grid": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz", + "integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==" + }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", diff --git a/src/fireedge/package.json b/src/fireedge/package.json index f4b0c126dd..7222e40033 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -126,6 +126,7 @@ "socket.io": "4.4.1", "socket.io-client": "4.4.1", "speakeasy": "2.0.0", + "split-grid": "1.0.11", "sprintf-js": "1.1.2", "style-loader": "3.2.1", "terser-webpack-plugin": "5.1.4", diff --git a/src/fireedge/src/client/components/SplitPane/index.js b/src/fireedge/src/client/components/SplitPane/index.js index 0ded71ed06..dcb27bf7a7 100644 --- a/src/fireedge/src/client/components/SplitPane/index.js +++ b/src/fireedge/src/client/components/SplitPane/index.js @@ -13,130 +13,117 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState, createRef, useRef, useEffect } from 'react' +import { useRef, useEffect, useCallback, Children, ReactElement } from 'react' import PropTypes from 'prop-types' -import { Divider } from '@mui/material' -import makeStyles from '@mui/styles/makeStyles' +import Split, { SplitOptions } from 'split-grid' +import { styled, Divider } from '@mui/material' -const useStyles = makeStyles((theme) => ({ - splitPane: { - display: 'flex', - flexDirection: 'column', - height: '100%', - }, - topPane: { - flex: 1, - }, - separator: { - position: 'relative', - cursor: 'row-resize', +const Gutter = styled(Divider)(({ theme, direction = 'row' }) => ({ + position: 'relative', + cursor: `${direction}-resize`, + height: 8, + marginBlock: '1em', + background: `linear-gradient( + to right, + transparent, + ${theme.palette.divider}, + transparent + )`, + '&:after': { + content: "''", + position: 'absolute', + zIndex: 1, + left: '50%', + width: 8, height: 8, - marginBlock: '1em', - background: `linear-gradient( - to right, - transparent, - ${theme.palette.divider}, - transparent - )`, - '&:after': { - content: "''", - position: 'absolute', - zIndex: 1, - left: '50%', - width: 8, - height: 8, - transform: 'rotate(45deg)', - backgroundColor: theme.palette.action.active, - }, + transform: 'rotate(45deg)', + backgroundColor: theme.palette.action.active, }, })) -const SplitPane = ({ children, containerProps }) => { - const classes = useStyles() - const [topHeight, setTopHeight] = useState(null) +/** + * @typedef SplitGridHook + * @property {function():object} getGridProps - Function to get grid props + * @property {ReactElement} GutterComponent - Gutter component + */ - const splitPaneRef = createRef() - const topRef = createRef() - const separatorYPosition = useRef(null) +/** + * Hook to create a split pane with a divider between the two panes. + * + * @param {SplitOptions} options - Options to configure the split pane + * @returns {SplitGridHook} Hook functions to element grid and gutter + */ +const useSplitGrid = (options) => { + const { + columnMinSizes, + rowMinSizes, + columnMaxSizes, + rowMaxSizes, + gridTemplateColumns, + gridTemplateRows, + } = options - const onMouseDown = (event) => { - separatorYPosition.current = event?.touches?.[0]?.clientY ?? event.clientY - } - - const onMouseMove = (event) => { - if (!separatorYPosition.current) return - - const clientY = event?.touches?.[0]?.clientY || event.clientY - - const newTopHeight = topHeight + clientY - separatorYPosition.current - separatorYPosition.current = clientY - - if (newTopHeight <= 0) { - return topHeight !== 0 && setTopHeight(0) - } - - const splitPaneHeight = splitPaneRef.current?.clientHeight - - if (newTopHeight >= splitPaneHeight) { - return topHeight !== splitPaneHeight && setTopHeight(splitPaneHeight) - } - - setTopHeight(newTopHeight) - } - - const onMouseUp = () => { - separatorYPosition.current = null - } + const split = useRef({}) useEffect(() => { - document.addEventListener('mousemove', onMouseMove) - document.addEventListener('mouseup', onMouseUp) - // on mobile device - document.addEventListener('touchmove', onMouseMove) - document.addEventListener('touchend', onMouseUp) + split.current = Split(options) - return () => { - document.removeEventListener('mousemove', onMouseMove) - document.removeEventListener('mouseup', onMouseUp) - // on mobile device - document.removeEventListener('touchmove', onMouseMove) - document.removeEventListener('touchend', onMouseUp) - } - }) + return () => split.current.destroy() + }, [columnMinSizes, rowMinSizes, columnMaxSizes, rowMaxSizes]) - useEffect(() => { - if (!topHeight && children[1]) { - setTopHeight(document.body.clientHeight / 2) - topRef.current.style.flex = 'none' - } - - topRef.current.style.height = children[1] - ? `${topHeight}px` - : `${splitPaneRef.current?.clientHeight}px` - }, [topHeight, children[1]]) - - return ( -
-
- {children[0]} -
- {!!children[1] && ( - - )} - {children[1]} -
+ const getGridProps = useCallback( + () => ({ + display: 'grid', + height: 1, // 100% + gridTemplateColumns, + gridTemplateRows, + }), + [gridTemplateColumns, gridTemplateRows] ) + + const GutterComponent = useCallback( + ({ direction, track }) => { + const handleDragStart = (e) => { + split.current?.handleDragStart(e, direction, track) + } + + return ( + + ) + }, + [split.current?.handleDragStart] + ) + + return { getGridProps, GutterComponent } } -SplitPane.propTypes = { - children: PropTypes.array.isRequired, - containerProps: PropTypes.object, +/** + * @param {SplitOptions} props - Component props + * @param {Function|ReactElement} [props.children] - Child components + * @returns {ReactElement|function(SplitGridHook):ReactElement} Split pane component + */ +const SplitGrid = ({ children, ...options }) => { + const hook = useSplitGrid(options) + + if (!children) return null + if (typeof children === 'function') return children(hook) + + return !(Children.count(children) === 0) ? Children.only(children) : null } -export default SplitPane +SplitGrid.propTypes = { + children: PropTypes.oneOfType([PropTypes.func, PropTypes.element]), + options: PropTypes.object, +} + +export { Gutter, useSplitGrid } + +export default SplitGrid diff --git a/src/fireedge/src/client/containers/Clusters/index.js b/src/fireedge/src/client/containers/Clusters/index.js index cb6852ef38..72efb9467f 100644 --- a/src/fireedge/src/client/containers/Clusters/index.js +++ b/src/fireedge/src/client/containers/Clusters/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { ClustersTable } from 'client/components/Tables' import ClusterTabs from 'client/components/Tabs/Cluster' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Clusters with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Clusters list and selected row(s) + */ function Clusters() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Cluster. + * + * @param {object} cluster - Cluster to display + * @returns {ReactElement} Cluster details + */ +const InfoTabs = memo(({ cluster }) => ( + + + {`#${cluster.ID} | ${cluster.NAME}`} + + + +)) + +InfoTabs.propTypes = { cluster: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Clusters diff --git a/src/fireedge/src/client/containers/Datastores/index.js b/src/fireedge/src/client/containers/Datastores/index.js index 03905da472..8001b64aba 100644 --- a/src/fireedge/src/client/containers/Datastores/index.js +++ b/src/fireedge/src/client/containers/Datastores/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { DatastoresTable } from 'client/components/Tables' import DatastoreTabs from 'client/components/Tabs/Datastore' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Datastores with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Datastores list and selected row(s) + */ function Datastores() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Datastore. + * + * @param {object} datastore - Datastore to display + * @returns {ReactElement} Datastore details + */ +const InfoTabs = memo(({ datastore }) => ( + + + {`#${datastore.ID} | ${datastore.NAME}`} + + + +)) + +InfoTabs.propTypes = { datastore: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Datastores diff --git a/src/fireedge/src/client/containers/Groups/index.js b/src/fireedge/src/client/containers/Groups/index.js index 43127c3c41..d5348f0577 100644 --- a/src/fireedge/src/client/containers/Groups/index.js +++ b/src/fireedge/src/client/containers/Groups/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { GroupsTable } from 'client/components/Tables' import GroupTabs from 'client/components/Tabs/Group' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Groups with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Groups list and selected row(s) + */ function Groups() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Group. + * + * @param {object} group - Group to display + * @returns {ReactElement} Group details + */ +const InfoTabs = memo(({ group }) => ( + + + {`#${group.ID} | ${group.NAME}`} + + + +)) + +InfoTabs.propTypes = { group: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Groups diff --git a/src/fireedge/src/client/containers/Hosts/index.js b/src/fireedge/src/client/containers/Hosts/index.js index 9c59fe111c..d356e4eccc 100644 --- a/src/fireedge/src/client/containers/Hosts/index.js +++ b/src/fireedge/src/client/containers/Hosts/index.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { HostsTable } from 'client/components/Tables' import HostTabs from 'client/components/Tabs/Host' @@ -23,42 +24,84 @@ import HostActions from 'client/components/Tables/Hosts/actions' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Hosts with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Hosts list and selected row(s) + */ function Hosts() { const [selectedRows, onSelectedRowsChange] = useState(() => []) const actions = HostActions() - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Host. + * + * @param {object} host - Host to display + * @returns {ReactElement} Host details + */ +const InfoTabs = memo(({ host }) => ( + + + {`#${host.ID} | ${host.NAME}`} + + + +)) + +InfoTabs.propTypes = { host: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Hosts diff --git a/src/fireedge/src/client/containers/Images/index.js b/src/fireedge/src/client/containers/Images/index.js index 36e481dc03..543d3c70c8 100644 --- a/src/fireedge/src/client/containers/Images/index.js +++ b/src/fireedge/src/client/containers/Images/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { ImagesTable } from 'client/components/Tables' import ImageTabs from 'client/components/Tabs/Image' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Images with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Images list and selected row(s) + */ function Images() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of an Image. + * + * @param {object} image - Image to display + * @returns {ReactElement} Image details + */ +const InfoTabs = memo(({ image }) => ( + + + {`#${image.ID} | ${image.NAME}`} + + + +)) + +InfoTabs.propTypes = { image: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Images diff --git a/src/fireedge/src/client/containers/MarketplaceApps/index.js b/src/fireedge/src/client/containers/MarketplaceApps/index.js index e511c387e4..8e23c2eb11 100644 --- a/src/fireedge/src/client/containers/MarketplaceApps/index.js +++ b/src/fireedge/src/client/containers/MarketplaceApps/index.js @@ -13,8 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -import { useState, JSXElementConstructor } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { MarketplaceAppsTable } from 'client/components/Tables' import MarketplaceAppActions from 'client/components/Tables/MarketplaceApps/actions' @@ -23,46 +25,83 @@ import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' /** - * Displays a Marketplace Apps list. + * Displays a list of Marketplace Apps with a split pane between the list and selected row(s). * - * @returns {JSXElementConstructor} List of Marketplace Apps + * @returns {ReactElement} Marketplace Apps list and selected row(s) */ function MarketplaceApps() { const [selectedRows, onSelectedRowsChange] = useState(() => []) const actions = MarketplaceAppActions() - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Marketplace App. + * + * @param {object} app - Marketplace App to display + * @returns {ReactElement} Marketplace App details + */ +const InfoTabs = memo(({ app }) => ( + + + {`#${app.ID} | ${app.NAME}`} + + + +)) + +InfoTabs.propTypes = { app: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default MarketplaceApps diff --git a/src/fireedge/src/client/containers/Marketplaces/index.js b/src/fireedge/src/client/containers/Marketplaces/index.js index b08b76a0ec..b7d2f65c45 100644 --- a/src/fireedge/src/client/containers/Marketplaces/index.js +++ b/src/fireedge/src/client/containers/Marketplaces/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { MarketplacesTable } from 'client/components/Tables' import MarketplaceTabs from 'client/components/Tabs/Marketplace' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Marketplaces with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Marketplaces list and selected row(s) + */ function Marketplaces() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Marketplace. + * + * @param {object} market - Marketplace to display + * @returns {ReactElement} Marketplace details + */ +const InfoTabs = memo(({ market }) => ( + + + {`#${market.ID} | ${market.NAME}`} + + + +)) + +InfoTabs.propTypes = { market: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Marketplaces diff --git a/src/fireedge/src/client/containers/Users/index.js b/src/fireedge/src/client/containers/Users/index.js index f2d4b782ae..78cd6c3e4b 100644 --- a/src/fireedge/src/client/containers/Users/index.js +++ b/src/fireedge/src/client/containers/Users/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { UsersTable } from 'client/components/Tables' import UserTabs from 'client/components/Tabs/User' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Users with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Users list and selected row(s) + */ function Users() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of an User. + * + * @param {object} user - User to display + * @returns {ReactElement} User details + */ +const InfoTabs = memo(({ user }) => ( + + + {`#${user.ID} | ${user.NAME}`} + + + +)) + +InfoTabs.propTypes = { user: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Users diff --git a/src/fireedge/src/client/containers/VNetworkTemplates/index.js b/src/fireedge/src/client/containers/VNetworkTemplates/index.js index 04d9230cc6..e210dab5c1 100644 --- a/src/fireedge/src/client/containers/VNetworkTemplates/index.js +++ b/src/fireedge/src/client/containers/VNetworkTemplates/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { VNetworkTemplatesTable } from 'client/components/Tables' import VNetworkTemplateTabs from 'client/components/Tabs/VNetworkTemplate' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of VNet Templates with a split pane between the list and selected row(s). + * + * @returns {ReactElement} VNet Templates list and selected row(s) + */ function VNetworkTemplates() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a VNet Template. + * + * @param {object} vnTemplate - VNet Template to display + * @returns {ReactElement} VNet Template details + */ +const InfoTabs = memo(({ vnTemplate }) => ( + + + {`#${vnTemplate.ID} | ${vnTemplate.NAME}`} + + + +)) + +InfoTabs.propTypes = { vnTemplate: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default VNetworkTemplates diff --git a/src/fireedge/src/client/containers/VirtualMachines/index.js b/src/fireedge/src/client/containers/VirtualMachines/index.js index f6b171d847..8ce078f1bf 100644 --- a/src/fireedge/src/client/containers/VirtualMachines/index.js +++ b/src/fireedge/src/client/containers/VirtualMachines/index.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Typography, Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { VmsTable } from 'client/components/Tables' import VmActions from 'client/components/Tables/Vms/actions' @@ -23,46 +24,84 @@ import VmTabs from 'client/components/Tabs/Vm' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of VMs with a split pane between the list and selected row(s). + * + * @returns {ReactElement} VMs list and selected row(s) + */ function VirtualMachines() { const [selectedRows, onSelectedRowsChange] = useState(() => []) const actions = VmActions() + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' + return ( - - - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( <> - - {`#${selectedRows[0]?.original.ID} | ${selectedRows[0]?.original.NAME}`} - - + + {moreThanOneSelected ? ( + + ) : ( + + )} - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - )} - + )} ) } +/** + * Displays details of a VM. + * + * @param {object} vm - VM to display + * @returns {ReactElement} VM details + */ +const InfoTabs = memo(({ vm }) => ( + + + {`#${vm.ID} | ${vm.NAME}`} + + + +)) + +InfoTabs.propTypes = { vm: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default VirtualMachines diff --git a/src/fireedge/src/client/containers/VirtualNetworks/index.js b/src/fireedge/src/client/containers/VirtualNetworks/index.js index 631417113f..8db0e9448c 100644 --- a/src/fireedge/src/client/containers/VirtualNetworks/index.js +++ b/src/fireedge/src/client/containers/VirtualNetworks/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { VNetworksTable } from 'client/components/Tables' import VNetworkTabs from 'client/components/Tabs/VNetwork' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Virtual Networks with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Virtual Networks list and selected row(s) + */ function VirtualNetworks() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Virtual Network. + * + * @param {object} vnet - Virtual Network to display + * @returns {ReactElement} Virtual Network details + */ +const InfoTabs = memo(({ vnet }) => ( + + + {`#${vnet.ID} | ${vnet.NAME}`} + + + +)) + +InfoTabs.propTypes = { vnet: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default VirtualNetworks diff --git a/src/fireedge/src/client/containers/VmTemplates/index.js b/src/fireedge/src/client/containers/VmTemplates/index.js index 609863b6d2..9918fdfe34 100644 --- a/src/fireedge/src/client/containers/VmTemplates/index.js +++ b/src/fireedge/src/client/containers/VmTemplates/index.js @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Typography, Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { VmTemplatesTable } from 'client/components/Tables' import VmTemplateActions from 'client/components/Tables/VmTemplates/actions' @@ -23,46 +24,84 @@ import VmTemplateTabs from 'client/components/Tabs/VmTemplate' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of VM Templates with a split pane between the list and selected row(s). + * + * @returns {ReactElement} VM Templates list and selected row(s) + */ function VmTemplates() { const [selectedRows, onSelectedRowsChange] = useState(() => []) const actions = VmTemplateActions() + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' + return ( - - - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( <> - - {`#${selectedRows[0]?.original.ID} | ${selectedRows[0]?.original.NAME}`} - - + + {moreThanOneSelected ? ( + + ) : ( + + )} - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - )} - + )} ) } +/** + * Displays details of a VM Template. + * + * @param {object} vmTemplate - VM Template to display + * @returns {ReactElement} VM Template details + */ +const InfoTabs = memo(({ vmTemplate }) => ( + + + {`#${vmTemplate.ID} | ${vmTemplate.NAME}`} + + + +)) + +InfoTabs.propTypes = { vmTemplate: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default VmTemplates diff --git a/src/fireedge/src/client/containers/Zones/index.js b/src/fireedge/src/client/containers/Zones/index.js index f32079ef01..9080c2af74 100644 --- a/src/fireedge/src/client/containers/Zones/index.js +++ b/src/fireedge/src/client/containers/Zones/index.js @@ -13,47 +13,90 @@ * See the License for the specific language governing permissions and * * limitations under the License. * * ------------------------------------------------------------------------- */ -/* eslint-disable jsdoc/require-jsdoc */ -import { useState } from 'react' -import { Stack, Chip } from '@mui/material' +import { ReactElement, useState, memo } from 'react' +import PropTypes from 'prop-types' +import { Typography, Box, Stack, Chip } from '@mui/material' +import { Row } from 'react-table' import { ZonesTable } from 'client/components/Tables' import ZoneTabs from 'client/components/Tabs/Zone' import SplitPane from 'client/components/SplitPane' import MultipleTags from 'client/components/MultipleTags' +/** + * Displays a list of Zones with a split pane between the list and selected row(s). + * + * @returns {ReactElement} Zones list and selected row(s) + */ function Zones() { const [selectedRows, onSelectedRowsChange] = useState(() => []) - return ( - - + const hasSelectedRows = selectedRows?.length > 0 + const moreThanOneSelected = selectedRows?.length > 1 + const gridTemplateRows = hasSelectedRows ? '1fr auto 1fr' : '1fr' - {selectedRows?.length > 0 && ( - - {selectedRows?.length === 1 ? ( - - ) : ( - - ( - toggleRowSelected(false)} - /> - ) - )} - /> - + return ( + + {({ getGridProps, GutterComponent }) => ( + + + + {hasSelectedRows && ( + <> + + {moreThanOneSelected ? ( + + ) : ( + + )} + )} - + )} ) } +/** + * Displays details of a Zone. + * + * @param {object} zone - Zone to display + * @returns {ReactElement} Zone details + */ +const InfoTabs = memo(({ zone }) => ( + + + {`#${zone.ID} | ${zone.NAME}`} + + + +)) + +InfoTabs.propTypes = { zone: PropTypes.object.isRequired } +InfoTabs.displayName = 'InfoTabs' + +/** + * Displays a list of tags that represent the selected rows. + * + * @param {Row[]} tags - Row(s) to display as tags + * @returns {ReactElement} List of tags + */ +const GroupedTags = memo(({ tags = [] }) => ( + + ( + toggleRowSelected(false)} + /> + ))} + /> + +)) + +GroupedTags.propTypes = { tags: PropTypes.array } +GroupedTags.displayName = 'GroupedTags' + export default Zones