Skip to content

Commit

Permalink
Autocomplete creatable: improved API to cover more use cases (#881)
Browse files Browse the repository at this point in the history
  • Loading branch information
vmilan authored Jun 21, 2024
1 parent 6a3bcf1 commit 3327465
Show file tree
Hide file tree
Showing 6 changed files with 255 additions and 42 deletions.
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)
- 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

0 comments on commit 3327465

Please sign in to comment.