Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Autocomplete creatable: improved API to cover more use cases #881

Merged
merged 16 commits into from
Jun 21, 2024
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Not released

- Autocomplete creatable: improved API to cover more use cases [#881](https://github.com/CartoDB/carto-react/pull/881)
- Fix LegendProportion radius scale [#877](https://github.com/CartoDB/carto-react/pull/877)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing entry from previous PR

- Fix time zone handling in week counts, separate getMonday and getUTCMonday utilities [#879](https://github.com/CartoDB/carto-react/pull/879)

## 3.0.0
Expand All @@ -12,7 +14,7 @@

### 3.0.0-alpha.11 (2024-06-12)

- Add creatable functionality to Autocomplete & MenuItem fixed [#828](https://github.com/CartoDB/carto-react/pull/828)
- Add creatable functionality to Autocomplete & MenuItem fixed [#873](https://github.com/CartoDB/carto-react/pull/873)
- Table component: Added selected row and with checkbox example in Storybook [#876](https://github.com/CartoDB/carto-react/pull/876)

### 3.0.0-alpha.10 (2024-06-03)
Expand Down
3 changes: 2 additions & 1 deletion packages/react-ui/src/components/molecules/Autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type AutocompleteProps<
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']
> = MuiAutocompleteProps<Value, Multiple, DisableClearable, FreeSolo, ChipComponent> & {
creatable?: boolean;
newItemTitle?: React.ReactNode | string;
newItemLabel?: string | ((value: string) => React.ReactNode | string);
newItemIcon?: React.ReactNode;
};

declare const Autocomplete: <
Expand Down
62 changes: 47 additions & 15 deletions packages/react-ui/src/components/molecules/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@ import {
import { AddCircleOutlineOutlined } from '@mui/icons-material';
import MenuItem from './MenuItem';
import useImperativeIntl from '../../hooks/useImperativeIntl';
import Typography from '../atoms/Typography';

const filter = createFilterOptions();

const Autocomplete = forwardRef(
(
{
creatable,
newItemTitle,
newItemLabel = 'c4r.form.add',
newItemIcon,
freeSolo,
renderOption,
forcePopupIcon,
Expand All @@ -33,7 +35,7 @@ const Autocomplete = forwardRef(
const intl = useIntl();
const intlConfig = useImperativeIntl(intl);

const creatableOptions = (options, params) => {
const creatableFilterOptions = (options, params) => {
const filtered = filter(options, params);
const { inputValue } = params;

Expand All @@ -43,8 +45,9 @@ const Autocomplete = forwardRef(
filtered.push({
inputValue,
title:
newItemTitle ||
`${intlConfig.formatMessage({ id: 'c4r.form.add' })} "${inputValue}"`
typeof newItemLabel === 'function'
? newItemLabel(inputValue)
: `${intlConfig.formatMessage({ id: newItemLabel })} "${inputValue}"`
});
}

Expand All @@ -66,22 +69,46 @@ const Autocomplete = forwardRef(

const creatableRenderOption = (props, option) => (
<React.Fragment key={option.inputValue || option.title}>
{option.inputValue && <Divider />}
<MenuItem {...props}>
{option.inputValue && (
<ListItemIcon>
<AddCircleOutlineOutlined />
</ListItemIcon>
)}
<ListItemText>{option.title}</ListItemText>
</MenuItem>
{option.divider ? (
<Divider />
) : (
<>
{option.inputValue && <Divider />}
<MenuItem
{...props}
fixed={option.fixed}
extended={option.extended}
dense={option.dense}
destructive={option.destructive}
disabled={option.disabled}
subtitle={option.subtitle}
iconColor={option.iconColor}
>
{option.inputValue && (
<ListItemIcon>{newItemIcon || <AddCircleOutlineOutlined />}</ListItemIcon>
)}
{option.startAdornment && !option.inputValue && (
<ListItemIcon>{option.startAdornment}</ListItemIcon>
)}
<ListItemText>
{option.alternativeTitle || option.title}
{option.secondaryText && (
<Typography component='p' variant='caption' color='text.secondary'>
{option.secondaryText}
</Typography>
)}
</ListItemText>
{option.endAdornment}
</MenuItem>
</>
)}
</React.Fragment>
);

return (
<MuiAutocomplete
{...otherProps}
filterOptions={creatable ? creatableOptions : filterOptions}
filterOptions={creatable ? creatableFilterOptions : filterOptions}
getOptionLabel={creatable ? creatableOptionLabel : getOptionLabel}
renderOption={creatable ? creatableRenderOption : renderOption}
freeSolo={creatable || freeSolo}
Expand All @@ -93,7 +120,12 @@ const Autocomplete = forwardRef(

Autocomplete.propTypes = {
creatable: PropTypes.bool,
newItemTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.element])
newItemLabel: PropTypes.oneOfType([
PropTypes.func,
PropTypes.string,
PropTypes.element
]),
newItemIcon: PropTypes.element
};

export default Autocomplete;
29 changes: 26 additions & 3 deletions packages/react-ui/src/components/molecules/MenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,28 @@ import { MenuItem as MuiMenuItem, styled } from '@mui/material';
const StyledMenuItem = styled(MuiMenuItem, {
shouldForwardProp: (prop) =>
!['subtitle', 'destructive', 'extended', 'iconColor', 'fixed'].includes(prop)
})(({ subtitle, destructive, extended, iconColor, fixed, theme }) => ({
})(({ dense, subtitle, destructive, extended, iconColor, fixed, theme }) => ({
...(subtitle && {
pointerEvents: 'none',
columnGap: 0,
...theme.typography.caption,
fontWeight: 500,
color: theme.palette.text.secondary,

'.MuiListItemText-root .MuiTypography-root': {
...theme.typography.caption,
fontWeight: 500,
color: theme.palette.text.secondary
},

'&.MuiMenuItem-root': {
minHeight: theme.spacing(3),
paddingTop: 0,
paddingBottom: 0,
marginTop: theme.spacing(1),

'&:not(:first-of-type)': {
minHeight: theme.spacing(5),
marginTop: theme.spacing(1),
paddingTop: theme.spacing(1),
borderTop: `1px solid ${theme.palette.divider}`
}
Expand All @@ -33,6 +39,9 @@ const StyledMenuItem = styled(MuiMenuItem, {
},
'&.Mui-selected .MuiListItemIcon-root svg path': {
fill: theme.palette.primary.main
},
'.MuiAutocomplete-listbox &[aria-selected="true"] svg path': {
fill: theme.palette.primary.main
}
}),
...(destructive && {
Expand Down Expand Up @@ -77,7 +86,7 @@ const StyledMenuItem = styled(MuiMenuItem, {
}
}),
...(extended && {
'&.MuiMenuItem-root': {
'&.MuiButtonBase-root.MuiMenuItem-root': {
minHeight: theme.spacing(6)
}
}),
Expand All @@ -94,12 +103,26 @@ const StyledMenuItem = styled(MuiMenuItem, {
padding: theme.spacing(0.5, 1.5),
backgroundColor: theme.palette.background.paper,
borderBottom: `1px solid ${theme.palette.divider}`
},
'.MuiAutocomplete-listbox &.MuiAutocomplete-option:first-of-type': {
minHeight: theme.spacing(6),
marginTop: 0,

'&:hover': {
backgroundColor: theme.palette.background.paper
}
}
}),
...(!fixed && {
'.MuiList-root &:first-of-type': {
marginTop: theme.spacing(1)
}
}),
...(dense && {
'&.MuiButtonBase-root.MuiMenuItem-root': {
minHeight: theme.spacing(3),
padding: theme.spacing(0.25, 1.5)
}
})
}));

Expand Down
14 changes: 14 additions & 0 deletions packages/react-ui/src/theme/sections/components/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -715,10 +715,19 @@ export const formsOverrides = {
color: theme.palette.primary.main,
backgroundColor: theme.palette.primary.background,

'.MuiTypography-root': {
color: theme.palette.primary.main
},
'.MuiTypography-caption': {
color: theme.palette.text.secondary
},
'&.Mui-focused:hover': {
backgroundColor: theme.palette.action.hover
}
},
'&:first-of-type': {
marginTop: theme.spacing(1)
},

...(ownerState.size === 'small' && {
padding: theme.spacing(0.5, 1.5)
Expand All @@ -737,11 +746,16 @@ export const formsOverrides = {
}),

listbox: ({ ownerState, theme }) => ({
paddingTop: 0,

'.MuiDivider-root': {
display: 'none'
},
'.MuiButtonBase-root + .MuiDivider-root': {
display: 'block'
},
'.MuiMenuItem-root:first-of-type': {
marginTop: theme.spacing(1)
}
})
}
Expand Down
Loading
Loading