From 8d012d11c83d7142571759023b4d71ad7bcc2f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ver=C3=B3nica=20Mil=C3=A1n?= Date: Thu, 4 Jan 2024 14:16:00 +0100 Subject: [PATCH] SelectField: extend to accept more data structures as children and to fix placeholder (#796) --- CHANGELOG.md | 2 + .../components/atoms/LabelWithIndicator.js | 13 +- .../src/components/atoms/SelectField.d.ts | 25 ++-- .../src/components/atoms/SelectField.js | 138 ++++++++++++------ .../atoms/MiltipleSelectField.stories.js | 2 +- .../stories/atoms/SelectField.stories.js | 111 ++++++++------ 6 files changed, 182 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c36ef987..6e2b0acb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Not released +- SelectField: extend to accept more data structures as children and to fix placeholder [#796](https://github.com/CartoDB/carto-react/pull/796) + ## 2.3 ### 2.3.5 (2024-01-02) diff --git a/packages/react-ui/src/components/atoms/LabelWithIndicator.js b/packages/react-ui/src/components/atoms/LabelWithIndicator.js index 70e62b23b..d3406208b 100644 --- a/packages/react-ui/src/components/atoms/LabelWithIndicator.js +++ b/packages/react-ui/src/components/atoms/LabelWithIndicator.js @@ -1,11 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { styled } from '@mui/material/styles'; +import { Box, styled } from '@mui/material'; import Typography from './Typography'; +const Root = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + gap: theme.spacing(0.5) +})); + const LabelIndicator = styled(Typography)(({ theme }) => ({ - marginLeft: theme.spacing(0.5), '.Mui-disabled &': { color: theme.palette.text.disabled } @@ -15,7 +20,7 @@ const LabelWithIndicator = ({ label, type }) => { const isRequired = type === 'required'; return ( - <> + {label} { > {isRequired ? '(required)' : '(optional)'} - + ); }; diff --git a/packages/react-ui/src/components/atoms/SelectField.d.ts b/packages/react-ui/src/components/atoms/SelectField.d.ts index 13f0e2c69..156ef0a7e 100644 --- a/packages/react-ui/src/components/atoms/SelectField.d.ts +++ b/packages/react-ui/src/components/atoms/SelectField.d.ts @@ -1,16 +1,17 @@ -import { MenuProps } from '@mui/material'; -import { SelectProps } from '@mui/material/Select'; -import { TextFieldProps } from '@mui/material/TextField'; import React from 'react'; +import { InputProps, MenuProps } from '@mui/material'; +import { SelectProps } from '@mui/material/Select'; -export type SelectFieldProps = Omit & - Omit & { - placeholder?: React.ReactNode; - size?: 'small' | 'medium'; - selectProps?: Partial>; - renderValue?: (value: string[]) => React.ReactNode; - menuProps?: Partial; - }; +export type SelectFieldProps = Omit< + SelectProps, + 'placeholder' +> & { + placeholder?: React.ReactNode | string; + size?: 'small' | 'medium'; + menuProps?: Partial; + inputProps?: Partial; + helperText?: React.ReactNode | string; +}; -declare const SelectField: (props: SelectFieldProps) => JSX.Element; +declare const SelectField: (props: SelectFieldProps) => JSX.Element; export default SelectField; diff --git a/packages/react-ui/src/components/atoms/SelectField.js b/packages/react-ui/src/components/atoms/SelectField.js index 39e53c22b..b0340fb54 100644 --- a/packages/react-ui/src/components/atoms/SelectField.js +++ b/packages/react-ui/src/components/atoms/SelectField.js @@ -1,20 +1,55 @@ -import React, { forwardRef } from 'react'; +import React, { forwardRef, useState } from 'react'; import PropTypes from 'prop-types'; -import { TextField } from '@mui/material'; +import { + FormControl, + FormHelperText, + InputLabel, + MenuItem, + Select, + styled +} from '@mui/material'; import Typography from './Typography'; +import uniqueId from 'lodash/uniqueId'; + +const StyledSelect = styled(Select)(({ theme }) => ({ + '& .MuiInputAdornment-positionStart': { + paddingLeft: theme.spacing(2), + + '&.MuiInputAdornment-sizeSmall': { + paddingLeft: theme.spacing(1.5) + } + }, + '& .MuiInputBase-inputAdornedStart': { + paddingLeft: '0px !important' + }, + '& .MuiSelect-select .MuiMenuItem-root:hover': { + backgroundColor: 'transparent' + } +})); + +const PlaceholderItem = styled(MenuItem)(() => ({ + display: 'none' +})); const SelectField = forwardRef( ( { children, - onChange, placeholder, size, - multiple, displayEmpty, - selectProps, - renderValue, menuProps, + inputProps, + labelId, + label, + helperText, + name, + error, + focused, + disabled, + fullWidth, + required, + 'aria-label': ariaLabel, ...otherProps }, ref @@ -24,40 +59,34 @@ const SelectField = forwardRef( const isSmall = size === 'small'; - const defaultRenderValue = React.useCallback( - (selected) => { - if (selected.length === 0) { - return ( - - {placeholder} - - ); - } - return selected.join(', '); - }, - [isSmall, placeholder] - ); + // Accessibility attributes + const [defaultId] = useState(uniqueId('select-label-')); + const ariaLabelledBy = label ? labelId || defaultId : undefined; return ( - + {label && {label}} + + - {children} - + }} + > + {placeholder && ( + + + {placeholder} + + + )} + + {children} + + + {helperText && ( + {helperText} + )} + ); } ); SelectField.defaultProps = { - multiple: false, size: 'small' }; SelectField.propTypes = { - placeholder: PropTypes.string, + placeholder: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), size: PropTypes.oneOf(['small', 'medium']), - selectProps: PropTypes.object, - renderValue: PropTypes.func, - menuProps: PropTypes.object + menuProps: PropTypes.object, + inputProps: PropTypes.object, + helperText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]) }; export default SelectField; diff --git a/packages/react-ui/storybook/stories/atoms/MiltipleSelectField.stories.js b/packages/react-ui/storybook/stories/atoms/MiltipleSelectField.stories.js index 62aa3749e..a6a9924c9 100644 --- a/packages/react-ui/storybook/stories/atoms/MiltipleSelectField.stories.js +++ b/packages/react-ui/storybook/stories/atoms/MiltipleSelectField.stories.js @@ -1,6 +1,6 @@ import React, { useState } from 'react'; import MultipleSelectField from '../../../src/components/atoms/MultipleSelectField'; -import { DocContainer, DocHighlight, DocLink } from '../../utils/storyStyles'; +import { DocContainer, DocHighlight } from '../../utils/storyStyles'; import Typography from '../../../src/components/atoms/Typography'; const options = { diff --git a/packages/react-ui/storybook/stories/atoms/SelectField.stories.js b/packages/react-ui/storybook/stories/atoms/SelectField.stories.js index 2c4145df3..96422487b 100644 --- a/packages/react-ui/storybook/stories/atoms/SelectField.stories.js +++ b/packages/react-ui/storybook/stories/atoms/SelectField.stories.js @@ -1,7 +1,16 @@ import React, { useState } from 'react'; -import { FormControl, Grid, InputLabel, MenuItem, TextField } from '@mui/material'; +import { + FormControl, + Grid, + InputAdornment, + InputLabel, + MenuItem, + TextField +} from '@mui/material'; +import { MapOutlined } from '@mui/icons-material'; import Typography from '../../../src/components/atoms/Typography'; import SelectField from '../../../src/components/atoms/SelectField'; +import Button from '../../../src/components/atoms/Button'; import { Container, DocContainer, @@ -9,7 +18,6 @@ import { DocLink, Label } from '../../utils/storyStyles'; -import Button from '../../../src/components/atoms/Button'; const options = { title: 'Atoms/Select Field', @@ -69,18 +77,9 @@ const options = { export default options; const menuItems = [ - { - label: 'Ten: super large text with overflow', - value: '10' - }, - { - label: 'Twenty', - value: '20' - }, - { - label: 'Thirty', - value: '30' - } + { label: 'Ten: super large text with overflow', id: '10' }, + { label: 'Twenty', id: '20' }, + { label: 'Thirty', id: '30' } ]; const SelectFieldItem = ({ @@ -114,33 +113,27 @@ const SelectFieldItem = ({ variant={variant} placeholder={placeholder} helperText={helperText} - onChange={handleChange} value={content} focused={focused} disabled={disabled} + onChange={handleChange} error={error} size={size} fullWidth={rest.fullWidth} + required={required} > - {[...Array(20)].map((item, index) => { - const itemText = - index === 1 - ? `Very long item text with overflow ${index + 1}` - : `Item ${index + 1}`; - - return ( - - {itemText} - - ); - })} + {menuItems.map((o, i) => ( + + {o.label} + + ))} ); }; const PlaygroundTemplate = (args) => ; -const VariantsTemplate = ({ label, required, placeholder, ...rest }) => { +const VariantsTemplate = ({ label, placeholder, ...rest }) => { return ( @@ -216,14 +209,46 @@ const LabelAndHelperTextTemplate = ({ label, placeholder, helperText, ...rest }) ); }; -const SizeTemplate = ({ - label, - placeholder, - defaultValue, - helperText, - size, - ...rest -}) => { +const PrefixTemplate = ({ label, placeholder, ...rest }) => { + return ( + + + + + Kg} + /> + + + + + + + + + } + /> + + + + + + + + + + ); +}; + +const SizeTemplate = ({ label, placeholder, helperText, size, ...rest }) => { const [fixedValue, setFixedValue] = useState('Twenty'); const [fixedValue2, setFixedValue2] = useState('Twenty'); const [fixedValue3, setFixedValue3] = useState('Thirty'); @@ -474,14 +499,7 @@ const SizeTemplate = ({ ); }; -const MultipleTemplate = ({ - label, - placeholder, - defaultValue, - helperText, - size, - ...rest -}) => { +const MultipleTemplate = ({ label, placeholder, helperText, size, ...rest }) => { return ( { +const BehaviorTemplate = ({ label, placeholder, helperText, ...rest }) => { return ( @@ -609,6 +627,9 @@ export const LabelAndHelperText = LabelAndHelperTextTemplate.bind({}); LabelAndHelperText.args = { ...commonArgs }; LabelAndHelperText.argTypes = disabledControlsArgTypes; +export const Prefix = PrefixTemplate.bind({}); +Prefix.args = { ...commonArgs }; + export const Medium = SizeTemplate.bind({}); Medium.args = { ...commonArgs, ...sizeArgs, size: 'medium' }; Medium.argTypes = disabledControlsSizeArgTypes;