From 44082b24a1c932578a5c24341bccd0d8b481b9d2 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Fri, 25 Apr 2025 12:37:04 +0200 Subject: [PATCH] chore: second design pass for editable constraints (#9843) Fix a number of visual issues with the main editable constraint component. I've introduced a few more layers of container nesting to make the layout break the right ways: - Put everything on the same line when on wide. - At 700px place selected values on the row below - From 700px down, when necessary, also wrap operator options Support super long context names without breaking layout image Wrap values at 700px width container: image Wrap operator options when necessary image Absolutely position delete button to allow to not push it out of the container on narrow screens: image Remove extra focus styling from MUI (darken select background): Before: image image After: image image --- .../ConstraintOperatorSelect.tsx | 3 + .../EditableConstraint.tsx | 144 ++++++++++++------ .../FeatureStrategyConstraints/ValueList.tsx | 7 + 3 files changed, 105 insertions(+), 49 deletions(-) diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ConstraintOperatorSelect.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ConstraintOperatorSelect.tsx index 0dbe8f134a..3f54d319ed 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ConstraintOperatorSelect.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ConstraintOperatorSelect.tsx @@ -47,6 +47,9 @@ const StyledSelect = styled(Select)(({ theme }) => ({ '.MuiInput-input': { paddingBlock: theme.spacing(0.25), }, + ':focus-within .MuiSelect-select': { + background: 'none', + }, })); const StyledMenuItem = styled(MenuItem, { diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx index 5b3564726f..e3264fa76a 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/EditableConstraint.tsx @@ -32,22 +32,44 @@ const Container = styled('article')(({ theme }) => ({ backgroundColor: theme.palette.background.paper, borderRadius: theme.shape.borderRadiusLarge, border: `1px solid ${theme.palette.divider}`, + containerType: 'inline-size', })); +const onNarrowContainer = '@container (max-width: 700px)'; + const TopRow = styled('div')(({ theme }) => ({ + '--gap': theme.spacing(1), padding: 'var(--padding)', display: 'flex', flexFlow: 'row nowrap', alignItems: 'flex-start', justifyItems: 'space-between', + gap: 'var(--gap)', +})); + +const ConstraintOptions = styled('div')(({ theme }) => ({ + display: 'flex', + flexFlow: 'row nowrap', + gap: 'var(--gap)', + alignSelf: 'flex-start', + [onNarrowContainer]: { + flexFlow: 'row wrap', + }, +})); + +const OperatorOptions = styled(ConstraintOptions)(({ theme }) => ({ + flexFlow: 'row nowrap', })); const ConstraintDetails = styled('div')(({ theme }) => ({ display: 'flex', - gap: theme.spacing(1), + gap: 'var(--gap)', flexFlow: 'row nowrap', width: '100%', height: 'min-content', + [onNarrowContainer]: { + flexDirection: 'column', + }, })); const InputContainer = styled('div')(({ theme }) => ({ @@ -57,6 +79,10 @@ const InputContainer = styled('div')(({ theme }) => ({ const StyledSelect = styled(GeneralSelect)(({ theme }) => ({ fieldset: { border: 'none', borderRadius: 0 }, + maxWidth: '25ch', + ':focus-within .MuiSelect-select': { + background: 'none', + }, ':focus-within fieldset': { borderBottomStyle: 'solid' }, 'label + &': { // mui adds a margin top to 'standard' selects with labels @@ -67,10 +93,19 @@ const StyledSelect = styled(GeneralSelect)(({ theme }) => ({ }, })); +const StyledIconButton = styled(IconButton)(({ theme }) => ({ + position: 'absolute', + right: theme.spacing(1), +})); + const StyledButton = styled('button')(({ theme }) => ({ + // todo (`addEditStrategy`): this is pretty rough, but it needs to be the + // same height as the input fields, which are 27.25 px at the moment. + // Consider editing this when we get new icons for the buttons. There may be + // a better solution. + height: `calc(${theme.typography.body1.fontSize} + ${theme.spacing(1.5)})`, width: '5ch', borderRadius: theme.shape.borderRadius, - padding: theme.spacing(0.25, 0), fontSize: theme.fontSizes.smallerBody, background: theme.palette.secondary.light, border: `1px solid ${theme.palette.secondary.border}`, @@ -82,6 +117,14 @@ const StyledButton = styled('button')(({ theme }) => ({ }, })); +const ButtonPlaceholder = styled('div')(({ theme }) => ({ + // this is a trick that lets us use absolute positioning for the button so + // that it can go over the operator context fields when necessary (narrow + // screens), but still retain necessary space for the button when it's all + // on one line. + width: theme.spacing(2), +})); + const StyledCaseInsensitiveIcon = styled(CaseInsensitiveIcon)(({ theme }) => ({ path: { fill: theme.palette.text.disabled, @@ -218,52 +261,55 @@ export const EditableConstraint: FC = ({ - + + - - {localConstraint.inverted ? 'aint' : 'is'} - + + + {localConstraint.inverted ? 'aint' : 'is'} + - - - {showCaseSensitiveButton ? ( - - {localConstraint.caseInsensitive ? ( - - ) : ( - - )} - - Make match - {localConstraint.caseInsensitive - ? ' ' - : ' not '} - case sensitive - - - ) : null} + + {showCaseSensitiveButton ? ( + + {localConstraint.caseInsensitive ? ( + + ) : ( + + )} + + Make match + {localConstraint.caseInsensitive + ? ' ' + : ' not '} + case sensitive + + + ) : null} + + = ({ ) : null} - + - - - + + {showInputField ? ( diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ValueList.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ValueList.tsx index 883c00bc9e..10bb1c9d08 100644 --- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ValueList.tsx +++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyConstraints/ValueList.tsx @@ -94,6 +94,13 @@ export const ValueList: FC> = ({ ref={(el) => { constraintElementRefs.current[index] = el; }} + sx={{ + height: 'auto', + '& .MuiChip-label': { + display: 'block', + whiteSpace: 'normal', + }, + }} deleteIcon={} label={value} onDelete={() => {