Skip to content

Commit

Permalink
F #5862: Add label filter component (#2142)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio Betanzos authored Jun 10, 2022
1 parent 782660a commit 205cafb
Show file tree
Hide file tree
Showing 18 changed files with 637 additions and 227 deletions.
40 changes: 37 additions & 3 deletions src/fireedge/src/client/components/Cards/MarketplaceAppCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,44 @@ import PropTypes from 'prop-types'
import { Lock, User, Group, Cart } from 'iconoir-react'
import { Typography } from '@mui/material'

import { useViews } from 'client/features/Auth'
import MultipleTags from 'client/components/MultipleTags'
import Timer from 'client/components/Timer'
import { StatusCircle, StatusChip } from 'client/components/Status'
import { Tr } from 'client/components/HOC'
import { rowStyles } from 'client/components/Tables/styles'

import { getState, getType } from 'client/models/MarketplaceApp'
import { timeFromMilliseconds } from 'client/models/Helper'
import {
timeFromMilliseconds,
getUniqueLabels,
getColorFromString,
} from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, MarketplaceApp } from 'client/constants'
import {
T,
MarketplaceApp,
MARKETPLACE_APP_ACTIONS,
RESOURCE_NAMES,
} from 'client/constants'

const MarketplaceAppCard = memo(
/**
* @param {object} props - Props
* @param {MarketplaceApp} props.app - Marketplace App resource
* @param {object} props.rootProps - Props to root component
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
* @returns {ReactElement} - Card
*/
({ app, rootProps }) => {
({ app, rootProps, onClickLabel, onDeleteLabel }) => {
const classes = rowStyles()
const { [RESOURCE_NAMES.VM]: vmView } = useViews()

const enableEditLabels =
vmView?.actions?.[MARKETPLACE_APP_ACTIONS.EDIT_LABELS] === true &&
!!onDeleteLabel

const {
ID,
NAME,
Expand All @@ -48,6 +67,7 @@ const MarketplaceAppCard = memo(
MARKETPLACE,
ZONE_ID,
SIZE,
TEMPLATE: { LABELS } = {},
} = app

const state = useMemo(() => getState(app), [app?.STATE])
Expand All @@ -56,6 +76,17 @@ const MarketplaceAppCard = memo(
const time = useMemo(() => timeFromMilliseconds(+REGTIME), [REGTIME])
const type = useMemo(() => getType(app), [app?.TYPE])

const labels = useMemo(
() =>
getUniqueLabels(LABELS).map((label) => ({
text: label,
stateColor: getColorFromString(label),
onClick: onClickLabel,
onDelete: enableEditLabels && onDeleteLabel,
})),
[LABELS, enableEditLabels, onClickLabel, onDeleteLabel]
)

return (
<div {...rootProps} data-cy={`app-${ID}`}>
<div className={classes.main}>
Expand All @@ -67,6 +98,7 @@ const MarketplaceAppCard = memo(
{LOCK && <Lock />}
<span className={classes.labels}>
<StatusChip text={type} />
<MultipleTags tags={labels} />
</span>
</div>
<div className={classes.caption}>
Expand Down Expand Up @@ -104,6 +136,8 @@ MarketplaceAppCard.propTypes = {
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
onClickLabel: PropTypes.func,
onDeleteLabel: PropTypes.func,
actions: PropTypes.any,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ const VirtualMachineCard = memo(
* @param {object} props - Props
* @param {VM} props.vm - Virtual machine resource
* @param {object} props.rootProps - Props to root component
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
* @param {ReactElement} [props.actions] - Actions
* @returns {ReactElement} - Card
*/
({ vm, rootProps, actions, onDeleteLabel }) => {
({ vm, rootProps, actions, onClickLabel, onDeleteLabel }) => {
const classes = rowStyles()
const { [RESOURCE_NAMES.VM]: vmView } = useViews()

Expand Down Expand Up @@ -90,9 +91,10 @@ const VirtualMachineCard = memo(
getUniqueLabels(LABELS).map((label) => ({
text: label,
stateColor: getColorFromString(label),
onClick: onClickLabel,
onDelete: enableEditLabels && onDeleteLabel,
})),
[LABELS, enableEditLabels, onDeleteLabel]
[LABELS, enableEditLabels, onClickLabel, onDeleteLabel]
)

return (
Expand Down Expand Up @@ -159,6 +161,7 @@ VirtualMachineCard.propTypes = {
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
onClickLabel: PropTypes.func,
onDeleteLabel: PropTypes.func,
actions: PropTypes.any,
}
Expand Down
7 changes: 5 additions & 2 deletions src/fireedge/src/client/components/Cards/VmTemplateCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@ const VmTemplateCard = memo(
* @param {object} props - Props
* @param {VM} props.template - Virtual machine resource
* @param {object} props.rootProps - Props to root component
* @param {function(string):Promise} [props.onClickLabel] - Callback to click label
* @param {function(string):Promise} [props.onDeleteLabel] - Callback to delete label
* @returns {ReactElement} - Card
*/
({ template, rootProps, onDeleteLabel }) => {
({ template, rootProps, onClickLabel, onDeleteLabel }) => {
const classes = rowStyles()
const { [RESOURCE_NAMES.VM_TEMPLATE]: templateView } = useViews()

Expand Down Expand Up @@ -83,9 +84,10 @@ const VmTemplateCard = memo(
getUniqueLabels(LABELS).map((label) => ({
text: label,
stateColor: getColorFromString(label),
onClick: onClickLabel,
onDelete: enableEditLabels && onDeleteLabel,
})),
[LABELS, enableEditLabels, onDeleteLabel]
[LABELS, enableEditLabels, onClickLabel, onDeleteLabel]
)

return (
Expand Down Expand Up @@ -134,6 +136,7 @@ VmTemplateCard.propTypes = {
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
onClickLabel: PropTypes.func,
onDeleteLabel: PropTypes.func,
}

Expand Down
108 changes: 48 additions & 60 deletions src/fireedge/src/client/components/Header/Popover.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,49 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import { memo, useState, useMemo, useEffect } from 'react'
import { memo, useState, useEffect } from 'react'
import PropTypes from 'prop-types'
import { Cancel as CloseIcon, NavArrowDown as CaretIcon } from 'iconoir-react'

import {
Paper,
styled,
useMediaQuery,
Paper,
Popper,
Typography,
useTheme,
IconButton,
Button,
Fade,
Box,
buttonClasses,
ClickAwayListener,
} from '@mui/material'

const callAll =
(...fns) =>
(...args) =>
fns.forEach((fn) => fn && fn?.(...args))

const StyledPopper = styled(Popper)(({ theme }) => ({
boxShadow: theme.shadows[1],
zIndex: theme.zIndex.modal + 1,
[theme.breakpoints.down('xs')]: { width: '100%', height: '100%' },
}))

const StyledPaper = styled(Paper)(({ theme }) => ({
[theme.breakpoints.down('xs')]: { width: '100%', height: '100%' },
}))

const HeaderPopover = memo(
({
id,
icon,
buttonLabel,
buttonProps,
onMouseHover,
buttonProps: { onClick, ...buttonProps } = {},
headerTitle,
popperProps,
onClickAway,
children,
}) => {
const { zIndex } = useTheme()
const isMobile = useMediaQuery((theme) => theme.breakpoints.only('xs'))

const [open, setOpen] = useState(false)
Expand All @@ -54,18 +67,7 @@ const HeaderPopover = memo(
setOpen((previousOpen) => !previousOpen)
}

const handleClose = () => setOpen(false)

const mobileStyles = useMemo(
() => ({
...(isMobile && {
width: '100%',
height: '100%',
}),
}),
[isMobile]
)

const handleClose = callAll(onClickAway, () => setOpen(false))
const canBeOpen = open && Boolean(anchorEl)
const hasId = canBeOpen ? id : undefined

Expand All @@ -79,9 +81,7 @@ const HeaderPopover = memo(
aria-haspopup
aria-describedby={hasId}
aria-expanded={open ? 'true' : 'false'}
{...(onMouseHover
? { onMouseEnter: handleClick, onMouseLeave: handleClose }
: { onClick: handleClick })}
onClick={callAll(handleClick, onClick)}
size="small"
endIcon={<CaretIcon />}
startIcon={icon}
Expand All @@ -94,50 +94,38 @@ const HeaderPopover = memo(
>
{!isMobile && buttonLabel}
</Button>
<Popper
<StyledPopper
id={hasId}
open={open}
anchorEl={anchorEl}
transition
placement="bottom-end"
keepMounted={false}
style={{
zIndex: zIndex.modal + 1,
...mobileStyles,
}}
{...popperProps}
>
{({ TransitionProps }) => (
<ClickAwayListener onClickAway={handleClose}>
<Fade {...TransitionProps} timeout={300}>
<Paper
variant="outlined"
sx={{ p: headerTitle ? 2 : 0, ...mobileStyles }}
<ClickAwayListener onClickAway={handleClose}>
<StyledPaper variant="outlined" sx={{ p: headerTitle ? 2 : 0 }}>
{(headerTitle || isMobile) && (
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
borderBottom="1px solid"
borderColor="divider"
>
{(headerTitle || isMobile) && (
<Box
display="flex"
alignItems="center"
justifyContent="space-between"
borderBottom="1px solid"
borderColor="divider"
>
{headerTitle && (
<Typography variant="body1">{headerTitle}</Typography>
)}
{isMobile && (
<IconButton onClick={handleClose} size="large">
<CloseIcon />
</IconButton>
)}
</Box>
{headerTitle && (
<Typography variant="body1">{headerTitle}</Typography>
)}
{isMobile && (
<IconButton onClick={handleClose} size="large">
<CloseIcon />
</IconButton>
)}
{children({ handleClose: handleClose })}
</Paper>
</Fade>
</ClickAwayListener>
)}
</Popper>
</Box>
)}
{children({ handleClose })}
</StyledPaper>
</ClickAwayListener>
</StyledPopper>
</>
)
}
Expand All @@ -146,13 +134,13 @@ const HeaderPopover = memo(
HeaderPopover.propTypes = {
id: PropTypes.string,
icon: PropTypes.node,
buttonLabel: PropTypes.string,
buttonLabel: PropTypes.any,
buttonProps: PropTypes.object,
tooltip: PropTypes.any,
headerTitle: PropTypes.any,
onMouseHover: PropTypes.bool,
disablePadding: PropTypes.bool,
popperProps: PropTypes.object,
onClickAway: PropTypes.func,
children: PropTypes.func,
}

Expand All @@ -164,8 +152,8 @@ HeaderPopover.defaultProps = {
buttonProps: {},
headerTitle: undefined,
disablePadding: false,
onMouseHover: false,
popperProps: {},
onClickAway: undefined,
children: () => undefined,
}

Expand Down
Loading

0 comments on commit 205cafb

Please sign in to comment.