diff --git a/frontend/package.json b/frontend/package.json index 4d519b14be..f8ac855c38 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,7 +35,8 @@ "fmt": "prettier src --write --loglevel warn", "fmt:check": "prettier src --check", "e2e": "yarn run cypress open --config baseUrl='http://localhost:3000' --env AUTH_USER=admin,AUTH_PASSWORD=unleash4all", - "e2e:heroku": "yarn run cypress open --config baseUrl='http://localhost:3000' --env AUTH_USER=example@example.com" + "e2e:heroku": "yarn run cypress open --config baseUrl='http://localhost:3000' --env AUTH_USER=example@example.com", + "isready": "yarn lint && yarn fmt && yarn prepare" }, "devDependencies": { "@emotion/react": "11.9.3", diff --git a/frontend/src/assets/icons/24_Negator off.svg b/frontend/src/assets/icons/24_Negator off.svg new file mode 100644 index 0000000000..a3dababecf --- /dev/null +++ b/frontend/src/assets/icons/24_Negator off.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/icons/24_Negator.svg b/frontend/src/assets/icons/24_Negator.svg new file mode 100644 index 0000000000..84e1638591 --- /dev/null +++ b/frontend/src/assets/icons/24_Negator.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/icons/24_Text format off.svg b/frontend/src/assets/icons/24_Text format off.svg new file mode 100644 index 0000000000..c4e0102f8a --- /dev/null +++ b/frontend/src/assets/icons/24_Text format off.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/assets/icons/24_Text format.svg b/frontend/src/assets/icons/24_Text format.svg new file mode 100644 index 0000000000..ea8ae72501 --- /dev/null +++ b/frontend/src/assets/icons/24_Text format.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/component/addons/AddonForm/AddonForm.tsx b/frontend/src/component/addons/AddonForm/AddonForm.tsx index 0909ae38d6..f7bf2a49e2 100644 --- a/frontend/src/component/addons/AddonForm/AddonForm.tsx +++ b/frontend/src/component/addons/AddonForm/AddonForm.tsx @@ -6,13 +6,7 @@ import React, { useState, VFC, } from 'react'; -import { - Button, - Divider, - FormControlLabel, - Switch, - TextField, -} from '@mui/material'; +import { Button, Divider, FormControlLabel, Switch } from '@mui/material'; import produce from 'immer'; import { trim } from 'component/common/util'; import { IAddon, IAddonProvider } from 'interfaces/addons'; diff --git a/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx b/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx index 0d657c2b7a..6019204c23 100644 --- a/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx +++ b/frontend/src/component/addons/AddonForm/AddonMultiSelector/AddonMultiSelector.tsx @@ -7,14 +7,7 @@ import { AutocompleteRenderOptionState, } from '@mui/material/Autocomplete'; import { styled } from '@mui/system'; -import { - Autocomplete, - Box, - capitalize, - Checkbox, - Paper, - TextField, -} from '@mui/material'; +import { capitalize, Checkbox, Paper, TextField } from '@mui/material'; import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank'; import CheckBoxIcon from '@mui/icons-material/CheckBox'; import { ConditionallyRender } from '../../../common/ConditionallyRender/ConditionallyRender'; diff --git a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx index dcd4f2d6b8..f032f3e930 100644 --- a/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx +++ b/frontend/src/component/admin/groups/GroupsList/GroupCard/GroupCardAvatars/GroupPopover/GroupPopover.tsx @@ -1,14 +1,9 @@ -import { Popover, Badge, styled, Tooltip } from '@mui/material'; -import { IGroup, IGroupUser, Role } from 'interfaces/group'; -import { Link } from 'react-router-dom'; +import { Badge, Popover, styled } from '@mui/material'; +import { IGroupUser, Role } from 'interfaces/group'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { Badge as StyledBadge } from 'component/common/Badge/Badge'; - -import { RemoveGroup } from 'component/admin/groups/RemoveGroup/RemoveGroup'; -import { useState } from 'react'; import StarIcon from '@mui/icons-material/Star'; -import { IUser } from '../../../../../../../interfaces/user'; const StyledPopover = styled(Popover)(({ theme }) => ({ pointerEvents: 'none', diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts index 61e799332e..45fc481d0b 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts @@ -17,8 +17,8 @@ export const useStyles = makeStyles()(theme => ({ fill: '#fff', }, accordion: { - border: `1px solid ${theme.palette.grey[300]}`, - borderRadius: '5px', + border: `1px solid ${theme.palette.grey[400]}`, + borderRadius: '8px', backgroundColor: '#fff', boxShadow: 'none', margin: 0, @@ -46,6 +46,11 @@ export const useStyles = makeStyles()(theme => ({ position: 'relative', }, }, + headerValuesContainerWrapper: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + }, headerValuesContainer: { display: 'flex', flexDirection: 'column', @@ -81,12 +86,6 @@ export const useStyles = makeStyles()(theme => ({ top: '-10px', }, }, - help: { - fill: theme.palette.grey[600], - [theme.breakpoints.down(860)]: { - display: 'none', - }, - }, headerText: { maxWidth: '400px', fontSize: theme.fontSizes.smallBody, @@ -105,8 +104,9 @@ export const useStyles = makeStyles()(theme => ({ [theme.breakpoints.down(770)]: { marginTop: '1rem', }, + display: 'inline-flex', }, - headerSelect: { marginRight: '2rem', width: '200px' }, + headerSelect: { marginRight: '1rem', width: '200px' }, chip: { margin: '0 0.5rem 0.5rem 0', }, diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx index c6abd4e958..ffb54cc4a1 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.tsx @@ -33,6 +33,7 @@ export const ConstraintAccordion = ({ constraint={constraint} onCancel={onCancel} onSave={onSave!} + onDelete={onDelete} compact={compact} /> } @@ -41,7 +42,6 @@ export const ConstraintAccordion = ({ constraint={constraint} onEdit={onEdit} onDelete={onDelete} - compact={compact} /> } /> diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEdit.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEdit.tsx index 0f7051ae2f..73b30eb261 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEdit.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEdit.tsx @@ -19,6 +19,7 @@ interface IConstraintAccordionEditProps { onCancel: () => void; onSave: (constraint: IConstraint) => void; compact: boolean; + onDelete?: () => void; } export const CANCEL = 'cancel'; @@ -48,6 +49,7 @@ export const ConstraintAccordionEdit = ({ compact, onCancel, onSave, + onDelete, }: IConstraintAccordionEditProps) => { const [localConstraint, setLocalConstraint] = useState( cleanConstraint(constraint) @@ -203,6 +205,9 @@ export const ConstraintAccordionEdit = ({ setOperator={setOperator} action={action} compact={compact} + setInvertedOperator={setInvertedOperator} + setCaseInsensitive={setCaseInsensitive} + onDelete={onDelete} /> @@ -214,10 +219,8 @@ export const ConstraintAccordionEdit = ({ localConstraint={localConstraint} setValues={setValues} setValue={setValue} - setCaseInsensitive={setCaseInsensitive} triggerTransition={triggerTransition} setAction={setAction} - setInvertedOperator={setInvertedOperator} onSubmit={onSubmit} > diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/CaseInsensitive/CaseInsensitive.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/CaseInsensitive/CaseInsensitive.tsx deleted file mode 100644 index f0c2a71711..0000000000 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/CaseInsensitive/CaseInsensitive.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { FormControlLabel, Switch } from '@mui/material'; -import { ConstraintFormHeader } from '../ConstraintFormHeader/ConstraintFormHeader'; - -interface ICaseInsensitiveProps { - caseInsensitive: boolean; - setCaseInsensitive: (caseInsensitive: boolean) => void; -} - -export const CaseInsensitive = ({ - caseInsensitive, - setCaseInsensitive, -}: ICaseInsensitiveProps) => { - return ( - <> - - Should the constraint be case insensitive? - - setCaseInsensitive(!caseInsensitive)} - color="primary" - /> - } - label="Case insensitive" - /> - - ); -}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.styles.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.styles.ts index 017450106d..82c2efa82a 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.styles.ts +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.styles.ts @@ -3,6 +3,7 @@ import { makeStyles } from 'tss-react/mui'; export const useStyles = makeStyles()(theme => ({ inputContainer: { padding: '1rem', + backgroundColor: theme.palette.neutral.light, }, buttonContainer: { display: 'flex', diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx index 57efd2da2b..ebfcb4d3a9 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx @@ -1,8 +1,7 @@ -import { Button, FormControlLabel, Switch } from '@mui/material'; +import { Button } from '@mui/material'; import { IConstraint } from 'interfaces/strategy'; import { CANCEL } from '../ConstraintAccordionEdit'; -import { ConstraintFormHeader } from './ConstraintFormHeader/ConstraintFormHeader'; import { useStyles } from './ConstraintAccordionEditBody.styles'; import React from 'react'; import { newOperators } from 'constants/operators'; @@ -16,21 +15,12 @@ interface IConstraintAccordionBody { triggerTransition: () => void; setValue: (value: string) => void; setAction: React.Dispatch>; - setCaseInsensitive: () => void; - setInvertedOperator: () => void; onSubmit: () => void; } export const ConstraintAccordionEditBody: React.FC< IConstraintAccordionBody -> = ({ - localConstraint, - children, - triggerTransition, - setInvertedOperator, - setAction, - onSubmit, -}) => { +> = ({ localConstraint, children, triggerTransition, setAction, onSubmit }) => { const { classes: styles } = useStyles(); return ( @@ -40,10 +30,6 @@ export const ConstraintAccordionEditBody: React.FC< condition={oneOf(newOperators, localConstraint.operator)} show={} /> - {children}
@@ -71,33 +57,3 @@ export const ConstraintAccordionEditBody: React.FC< ); }; - -interface IInvertedOperatorProps { - inverted: boolean; - setInvertedOperator: () => void; -} - -const InvertedOperator = ({ - inverted, - setInvertedOperator, -}: IInvertedOperatorProps) => { - return ( - <> - - Should the operator be negated? (this will make the operator do - the opposite) - - setInvertedOperator()} - color="primary" - /> - } - label="Negated" - /> - - ); -}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx index 406c14a249..91c9e2b190 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/FreeTextInput/FreeTextInput.tsx @@ -110,8 +110,8 @@ export const FreeTextInput = ({
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx index a56da12348..033f5b6c18 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx @@ -1,5 +1,4 @@ import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material'; -import { ExpandMore } from '@mui/icons-material'; import { IConstraint } from 'interfaces/strategy'; import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody'; @@ -12,41 +11,52 @@ import { } from 'constants/operators'; import { useStyles } from '../ConstraintAccordion.styles'; +import { useState } from 'react'; interface IConstraintAccordionViewProps { constraint: IConstraint; onDelete?: () => void; onEdit?: () => void; - compact: boolean; } export const ConstraintAccordionView = ({ - compact, constraint, onEdit, onDelete, }: IConstraintAccordionViewProps) => { const { classes: styles } = useStyles(); + const [expandable, setExpandable] = useState(true); + const [expanded, setExpanded] = useState(false); const singleValue = oneOf( [...semVerOperators, ...numOperators, ...dateOperators], constraint.operator ); + const handleClick = () => { + if (expandable) { + setExpanded(!expanded); + } + }; + return ( } + expandIcon={null} + onClick={handleClick} > diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx index 8521bdadf4..e9eddc7f85 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx @@ -1,149 +1,43 @@ -import { Chip, IconButton, Tooltip, styled } from '@mui/material'; import { ConstraintIcon } from 'component/common/ConstraintAccordion/ConstraintIcon'; -import { Delete, Edit } from '@mui/icons-material'; import { IConstraint } from 'interfaces/strategy'; import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import React from 'react'; -import { formatConstraintValue } from 'utils/formatConstraintValue'; -import { useLocationSettings } from 'hooks/useLocationSettings'; -import { ConstraintOperator } from 'component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator'; -import classnames from 'classnames'; - -const StyledHeaderText = styled('span')(({ theme }) => ({ - display: '-webkit-box', - WebkitLineClamp: 3, - WebkitBoxOrient: 'vertical', - overflow: 'hidden', - maxWidth: '100px', - minWidth: '100px', - marginRight: '10px', - wordBreak: 'break-word', - fontSize: theme.fontSizes.smallBody, - [theme.breakpoints.down(710)]: { - textAlign: 'center', - padding: theme.spacing(1, 0), - marginRight: 'inherit', - maxWidth: 'inherit', - }, -})); - -const StyledValuesSpan = styled('span')(({ theme }) => ({ - display: '-webkit-box', - WebkitLineClamp: 2, - WebkitBoxOrient: 'vertical', - overflow: 'hidden', - wordBreak: 'break-word', - fontSize: theme.fontSizes.smallBody, - [theme.breakpoints.down(710)]: { - margin: theme.spacing(1, 0), - textAlign: 'center', - }, -})); - -const StyledSingleValueChip = styled(Chip)(({ theme }) => ({ - [theme.breakpoints.down(710)]: { - margin: theme.spacing(1, 0), - }, -})); +import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo'; +import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions'; interface IConstraintAccordionViewHeaderProps { - compact: boolean; constraint: IConstraint; onDelete?: () => void; onEdit?: () => void; singleValue: boolean; + expanded: boolean; + allowExpand: (shouldExpand: boolean) => void; } export const ConstraintAccordionViewHeader = ({ - compact, constraint, onEdit, onDelete, singleValue, + allowExpand, + expanded, }: IConstraintAccordionViewHeaderProps) => { const { classes: styles } = useStyles(); - const { locationSettings } = useLocationSettings(); - - const onEditClick = - onEdit && - ((event: React.SyntheticEvent) => { - event.stopPropagation(); - onEdit(); - }); - - const onDeleteClick = - onDelete && - ((event: React.SyntheticEvent) => { - event.stopPropagation(); - onDelete(); - }); return (
-
- - - {constraint.contextName} - - -
- -
- - } - elseShow={ -
- - {constraint?.values - ?.map(value => value) - .join(', ')} - -

- Expand to view all ({constraint?.values?.length} - ) -

-
- } - /> -
-
- ( - - - - - - )} - /> - ( - - - - - - )} - /> -
+ +
); }; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx new file mode 100644 index 0000000000..3e2fc4e282 --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx @@ -0,0 +1,65 @@ +import { styled, Tooltip } from '@mui/material'; +import { ConstraintViewHeaderOperator } from '../ConstraintViewHeaderOperator/ConstraintViewHeaderOperator'; +import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender'; +import { ContraintAccordionViewHeaderSingleValue } from '../ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue'; +import { ConstraintAccordionViewHeaderMultipleValues } from '../ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues'; +import React from 'react'; +import { IConstraint } from '../../../../../../interfaces/strategy'; +import { useStyles } from '../../../ConstraintAccordion.styles'; + +const StyledHeaderText = styled('span')(({ theme }) => ({ + display: '-webkit-box', + WebkitLineClamp: 3, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + maxWidth: '100px', + minWidth: '100px', + marginRight: '10px', + wordBreak: 'break-word', + fontSize: theme.fontSizes.smallBody, + [theme.breakpoints.down(710)]: { + textAlign: 'center', + padding: theme.spacing(1, 0), + marginRight: 'inherit', + maxWidth: 'inherit', + }, +})); + +interface ConstraintAccordionViewHeaderMetaInfoProps { + constraint: IConstraint; + singleValue: boolean; + expanded: boolean; + allowExpand: (shouldExpand: boolean) => void; +} + +export const ConstraintAccordionViewHeaderInfo = ({ + constraint, + singleValue, + allowExpand, + expanded, +}: ConstraintAccordionViewHeaderMetaInfoProps) => { + const { classes: styles } = useStyles(); + return ( +
+ + {constraint.contextName} + + + + } + elseShow={ + + } + /> +
+ ); +}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx new file mode 100644 index 0000000000..9cd51b9269 --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx @@ -0,0 +1,36 @@ +import { IConstraint } from '../../../../../../interfaces/strategy'; +import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender'; +import { Tooltip } from '@mui/material'; +import { ReactComponent as NegatedIcon } from '../../../../../../assets/icons/24_Negator.svg'; +import { ConstraintOperator } from '../../../ConstraintOperator/ConstraintOperator'; +import React from 'react'; +import { useStyles } from '../../../ConstraintAccordion.styles'; +import { StyledIconWrapper } from '../StyledIconWrapper/StyledIconWrapper'; + +interface ConstraintViewHeaderOperatorProps { + constraint: IConstraint; +} + +export const ConstraintViewHeaderOperator = ({ + constraint, +}: ConstraintViewHeaderOperatorProps) => { + const { classes: styles } = useStyles(); + + return ( +
+ + + + + + } + /> +
+ +
+
+ ); +}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx new file mode 100644 index 0000000000..2ddb041d2b --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx @@ -0,0 +1,102 @@ +import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender'; +import { oneOf } from '../../../../../../utils/oneOf'; +import { stringOperators } from '../../../../../../constants/operators'; +import { styled, Tooltip } from '@mui/material'; +import { ReactComponent as CaseSensitive } from '../../../../../../assets/icons/24_Text format.svg'; +import React, { useEffect, useRef, useState } from 'react'; +import classnames from 'classnames'; +import { StyledIconWrapper } from '../StyledIconWrapper/StyledIconWrapper'; +import { IConstraint } from '../../../../../../interfaces/strategy'; +import { useStyles } from '../../../ConstraintAccordion.styles'; +import { getTextWidth } from '../../../helpers'; + +const StyledValuesSpan = styled('span')(({ theme }) => ({ + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + overflow: 'hidden', + wordBreak: 'break-word', + fontSize: theme.fontSizes.smallBody, + [theme.breakpoints.down(710)]: { + margin: theme.spacing(1, 0), + textAlign: 'center', + }, +})); + +interface ConstraintSingleValueProps { + constraint: IConstraint; + expanded: boolean; + allowExpand: (shouldExpand: boolean) => void; +} + +export const ConstraintAccordionViewHeaderMultipleValues = ({ + constraint, + expanded, + allowExpand, +}: ConstraintSingleValueProps) => { + const { classes: styles } = useStyles(); + + const elementRef = useRef(null); + + const [width, setWidth] = useState(0); + const [textWidth, setTextWidth] = useState(0); + const [expandable, setExpandable] = useState(false); + + useEffect(() => { + if (elementRef && elementRef.current != null) { + setTextWidth( + Math.round(getTextWidth(elementRef.current.innerText) / 2) // 2 lines + ); + setWidth(elementRef.current.clientWidth); + } + }, []); + + useEffect(() => { + if (textWidth && width) { + setExpandable(textWidth > width); + } + }, [textWidth, width]); + + useEffect(() => { + allowExpand(expandable); + }, [expandable, allowExpand]); + + return ( +
+ + + + + + } + /> +
+ + {constraint?.values?.map(value => value).join(', ')} + + + {!expanded + ? `View all ( + ${constraint?.values?.length})` + : 'View less'} +

+ } + /> +
+
+ ); +}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx new file mode 100644 index 0000000000..00818af583 --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx @@ -0,0 +1,49 @@ +import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender'; +import { oneOf } from '../../../../../../utils/oneOf'; +import { stringOperators } from '../../../../../../constants/operators'; +import { Chip, styled, Tooltip } from '@mui/material'; +import { ReactComponent as CaseSensitive } from '../../../../../../assets/icons/24_Text format.svg'; +import { formatConstraintValue } from '../../../../../../utils/formatConstraintValue'; +import React from 'react'; +import { useStyles } from '../../../ConstraintAccordion.styles'; +import { StyledIconWrapper } from '../StyledIconWrapper/StyledIconWrapper'; +import { IConstraint } from '../../../../../../interfaces/strategy'; +import { useLocationSettings } from '../../../../../../hooks/useLocationSettings'; + +const StyledSingleValueChip = styled(Chip)(({ theme }) => ({ + [theme.breakpoints.down(710)]: { + margin: theme.spacing(1, 0), + }, +})); + +interface ConstraintSingleValueProps { + constraint: IConstraint; +} + +export const ContraintAccordionViewHeaderSingleValue = ({ + constraint, +}: ConstraintSingleValueProps) => { + const { locationSettings } = useLocationSettings(); + const { classes: styles } = useStyles(); + + return ( +
+ + + {' '} + + + } + /> + +
+ ); +}; diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx new file mode 100644 index 0000000000..dd6568c427 --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx @@ -0,0 +1,16 @@ +import { styled } from '@mui/material'; + +export const StyledIconWrapper = styled('div')<{ + marginright?: string; +}>(({ theme, marginright }) => ({ + backgroundColor: theme.palette.grey[200], + width: 28, + height: 48, + display: 'inline-flex', + justifyContent: 'center', + padding: '10px 0', + color: theme.palette.primary.main, + marginRight: marginright ? marginright : '1rem', + marginTop: 'auto', + marginBottom: 'auto', +})); diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx index 9cd3a5cf61..b21c6159a1 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx @@ -1,6 +1,7 @@ import { IConstraint } from 'interfaces/strategy'; import { formatOperatorDescription } from 'component/common/ConstraintAccordion/ConstraintOperator/formatOperatorDescription'; import { useStyles } from 'component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.styles'; +import React from 'react'; interface IConstraintOperatorProps { constraint: IConstraint; @@ -14,15 +15,8 @@ export const ConstraintOperator = ({ const operatorName = constraint.operator; const operatorText = formatOperatorDescription(constraint.operator); - const notLabel = constraint.inverted && ( -
- NOT -
- ); - return (
- {notLabel}
{operatorName}
{operatorText}
diff --git a/frontend/src/component/common/ConstraintAccordion/helpers.ts b/frontend/src/component/common/ConstraintAccordion/helpers.ts new file mode 100644 index 0000000000..aa7da24837 --- /dev/null +++ b/frontend/src/component/common/ConstraintAccordion/helpers.ts @@ -0,0 +1,13 @@ +export function getTextWidth(text: string | null) { + if (text != null) { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + + if (context != null) { + context.font = getComputedStyle(document.body).font; + + return context.measureText(text).width; + } + } + return 0; +} diff --git a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx index a066c7219d..c364bfecb4 100644 --- a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundConnectionFieldset/PlaygroundConnectionFieldset.tsx @@ -6,7 +6,6 @@ import { Typography, useTheme, } from '@mui/material'; -import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; import { GuidanceIndicator } from 'component/common/GuidanceIndicator/GuidanceIndicator'; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx index 5f7137c5fb..2e695ce979 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx @@ -22,7 +22,6 @@ import { FeatureStatusCell } from './FeatureStatusCell/FeatureStatusCell'; import { PlaygroundFeatureSchema } from 'hooks/api/actions/usePlayground/playground.model'; import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; import useLoading from 'hooks/useLoading'; -import { GuidanceIndicator } from 'component/common/GuidanceIndicator/GuidanceIndicator'; import { VariantCell } from './VariantCell/VariantCell'; const defaultSort: SortingRule = { id: 'name' }; diff --git a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx index fd75a7224a..6fc76c2828 100644 --- a/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx +++ b/frontend/src/component/project/ProjectAccess/ProjectAccessTable/ProjectAccessTable.tsx @@ -32,7 +32,6 @@ import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; import { IUser } from 'interfaces/user'; import { IGroup } from 'interfaces/group'; import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; -import { mapGroupUsers } from '../../../../hooks/api/getters/useGroup/useGroup'; const StyledAvatar = styled(Avatar)(({ theme }) => ({ width: theme.spacing(4), diff --git a/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts b/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts index 9d67f266eb..445d74201f 100644 --- a/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts +++ b/frontend/src/hooks/api/getters/useProjectAccess/useProjectAccess.ts @@ -1,11 +1,10 @@ import useSWR, { mutate, SWRConfiguration } from 'swr'; -import { useState, useEffect, useMemo } from 'react'; +import { useState, useEffect } from 'react'; import { formatApiPath } from 'utils/formatPath'; import handleErrorResponses from '../httpErrorResponseHandler'; import { IProjectRole } from 'interfaces/role'; import { IGroup } from 'interfaces/group'; import { IUser } from 'interfaces/user'; -import { useGroups } from '../useGroups/useGroups'; import { mapGroupUsers } from '../useGroup/useGroup'; export enum ENTITY_TYPE { diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts index 0e9b54d8ba..8f23875c5e 100644 --- a/frontend/src/themes/theme.ts +++ b/frontend/src/themes/theme.ts @@ -95,6 +95,8 @@ export default createTheme({ light: colors.grey[200], main: colors.grey[400], dark: colors.grey[600], + background: 'white', + contrast: colors.grey[300], }, divider: colors.grey[300], dividerAlternative: colors.grey[400], diff --git a/frontend/src/themes/themeTypes.ts b/frontend/src/themes/themeTypes.ts index cb59be8a6e..9e61141b28 100644 --- a/frontend/src/themes/themeTypes.ts +++ b/frontend/src/themes/themeTypes.ts @@ -88,6 +88,8 @@ declare module '@mui/material/styles' { main: string; light: string; dark: string; + background: string; + contrast: string; }; }