Skip to content

Commit

Permalink
fix types and add example for renderOption
Browse files Browse the repository at this point in the history
  • Loading branch information
vmilan committed Jun 6, 2024
1 parent 6c6ade1 commit ad206ab
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 25 deletions.
20 changes: 18 additions & 2 deletions packages/react-ui/src/components/molecules/Autocomplete.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { ChipTypeMap } from '@mui/material';
import { AutocompleteProps as MuiAutocompleteProps } from '@mui/material/Autocomplete';

export type AutocompleteProps = MuiAutocompleteProps & {
// Boilerplate to avoid Typescript error with generic types: we must repeat all original typings in declaration, so variants like multiple/freesolo, etc. have proper typings
export type AutocompleteProps<
Value,
Multiple extends boolean | undefined,
DisableClearable extends boolean | undefined,
FreeSolo extends boolean | undefined,
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']
> = MuiAutocompleteProps<Value, Multiple, DisableClearable, FreeSolo, ChipComponent> & {
creatable?: boolean;
};

declare const Autocomplete: (props: AutocompleteProps) => JSX.Element;
declare const Autocomplete: <
Value,
Multiple extends boolean | undefined,
DisableClearable extends boolean | undefined,
FreeSolo extends boolean | undefined,
ChipComponent extends React.ElementType = ChipTypeMap['defaultComponent']
>(
props: AutocompleteProps<Value, Multiple, DisableClearable, FreeSolo, ChipComponent>
) => JSX.Element;
export default Autocomplete;
22 changes: 16 additions & 6 deletions packages/react-ui/src/components/molecules/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import {

const filter = createFilterOptions();

const Autocomplete = ({ creatable, freeSolo, ...otherProps }) => {
console.log('creatable', creatable);
const Autocomplete = ({
creatable,
newItemTitle,
freeSolo,
renderOption,
...otherProps
}) => {
return (
<MuiAutocomplete
{...otherProps}
Expand All @@ -21,7 +26,7 @@ const Autocomplete = ({ creatable, freeSolo, ...otherProps }) => {
if (inputValue.length > 1 && inputValue !== '' && !isExisting) {
filtered.push({
inputValue,
title: `Add "${inputValue}"`
title: newItemTitle || `Add "${inputValue}"`
});
}

Expand All @@ -32,14 +37,18 @@ const Autocomplete = ({ creatable, freeSolo, ...otherProps }) => {
if (typeof option === 'string') {
return option;
}
// Add "xxx" option created dynamically
// Add option created dynamically
if (option.inputValue) {
return option.inputValue;
}
// Regular option
return option.title;
}}
renderOption={(props, option) => <MenuItem {...props}>{option.title}</MenuItem>}
renderOption={
creatable
? (props, option) => <MenuItem {...props}>{option.title}</MenuItem>
: renderOption
}
freeSolo={creatable || freeSolo}
forcePopupIcon
creatable={creatable}
Expand All @@ -48,7 +57,8 @@ const Autocomplete = ({ creatable, freeSolo, ...otherProps }) => {
};

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

export default Autocomplete;
138 changes: 121 additions & 17 deletions packages/react-ui/storybook/stories/molecules/Autocomplete.stories.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import React from 'react';
import { Grid, TextField } from '@mui/material';
import React, { useState } from 'react';
import {
Grid,
InputAdornment,
ListItemIcon,
ListItemText,
TextField
} from '@mui/material';
import {
AccessAlarmOutlined,
AccountTreeOutlined,
AddCircleOutlined,
AddTaskOutlined,
AnalyticsOutlined,
AnimationOutlined,
AutorenewOutlined,
BlockOutlined,
BookmarkAddOutlined,
BuildOutlined,
CheckCircleOutlined,
EditOutlined,
MovieOutlined
} from '@mui/icons-material';
import Typography from '../../../src/components/atoms/Typography';
import Autocomplete from '../../../src/components/molecules/Autocomplete';
import MenuItem from '../../../src/components/molecules/MenuItem';
import {
Container,
DocContainer,
Expand Down Expand Up @@ -77,6 +99,11 @@ const options = {
control: {
type: 'text'
}
},
newItemTitle: {
control: {
type: 'text'
}
}
},
parameters: {
Expand All @@ -92,21 +119,37 @@ const options = {
export default options;

const top100Films = [
{ title: 'The Shawshank Redemption', year: 1994 },
{ title: 'The Godfather', year: 1972 },
{ title: 'The Godfather: Part II', year: 1974 },
{ title: 'The Dark Knight', year: 2008 },
{ title: '12 Angry Men', year: 1957 },
{ title: "Schindler's List", year: 1993 },
{ title: 'Pulp Fiction', year: 1994 },
{ title: 'The Lord of the Rings: The Return of the King', year: 2003 },
{ title: 'The Good, the Bad and the Ugly', year: 1966 },
{ title: 'Fight Club', year: 1999 },
{ title: 'The Lord of the Rings: The Fellowship of the Ring', year: 2001 },
{ title: 'Star Wars: Episode V - The Empire Strikes Back', year: 1980 },
{ title: 'Forrest Gump', year: 1994 },
{ title: 'Inception', year: 2010 },
{ title: 'The Lord of the Rings: The Two Towers', year: 2002 }
{ title: 'The Shawshank Redemption', year: 1994, icon: <MovieOutlined /> },
{ title: 'The Godfather', year: 1972, icon: <EditOutlined /> },
{ title: 'The Godfather: Part II', year: 1974, icon: <AccessAlarmOutlined /> },
{ title: 'The Dark Knight', year: 2008, icon: <AccountTreeOutlined /> },
{ title: '12 Angry Men', year: 1957, icon: <AddCircleOutlined /> },
{ title: "Schindler's List", year: 1993, icon: <AddTaskOutlined /> },
{ title: 'Pulp Fiction', year: 1994, icon: <AnalyticsOutlined /> },
{
title: 'The Lord of the Rings: The Return of the King',
year: 2003,
icon: <AnimationOutlined />
},
{ title: 'The Good, the Bad and the Ugly', year: 1966, icon: <AutorenewOutlined /> },
{ title: 'Fight Club', year: 1999, icon: <MovieOutlined /> },
{
title: 'The Lord of the Rings: The Fellowship of the Ring',
year: 2001,
icon: <BlockOutlined />
},
{
title: 'Star Wars: Episode V - The Empire Strikes Back',
year: 1980,
icon: <BookmarkAddOutlined />
},
{ title: 'Forrest Gump', year: 1994, icon: <BuildOutlined /> },
{ title: 'Inception', year: 2010, icon: <MovieOutlined /> },
{
title: 'The Lord of the Rings: The Two Towers',
year: 2002,
icon: <CheckCircleOutlined />
}
];

const PlaygroundTemplate = ({
Expand Down Expand Up @@ -786,6 +829,64 @@ const SizeTemplate = ({
);
};

const RenderOptionTemplate = ({
label,
variant,
placeholder,
helperText,
error,
size,
required,
...args
}) => {
const [selectedOption, setSelectedOption] = useState(null);

return (
<>
<DocContainer severity='info'>
Uses `startAdornment` for the icon in the input and `renderOption` to override
default list items
</DocContainer>

<Autocomplete
{...args}
options={top100Films}
getOptionLabel={(option) => option.title}
onChange={(event, newValue) => {
setSelectedOption(newValue);
}}
renderInput={(params) => {
if (selectedOption) {
params.InputProps.startAdornment = (
<InputAdornment position='start'>{selectedOption.icon}</InputAdornment>
);
}
return (
<TextField
{...params}
label={label}
placeholder={placeholder}
helperText={helperText}
variant={variant}
error={error}
size={size}
required={required}
InputLabelProps={{ shrink: true }}
/>
);
}}
size={size}
renderOption={(props, option) => (
<MenuItem {...props}>
<ListItemIcon>{option.icon}</ListItemIcon>
<ListItemText>{option.title}</ListItemText>
</MenuItem>
)}
/>
</>
);
};

const DocTemplate = () => (
<DocContainer severity='warning'>
We have our own
Expand Down Expand Up @@ -859,5 +960,8 @@ export const Small = SizeTemplate.bind({});
Small.args = { ...commonArgs, ...sizeArgs, size: 'small' };
Small.argTypes = disabledControlsSizeArgTypes;

export const CustomRenderOption = RenderOptionTemplate.bind({});
CustomRenderOption.args = { ...commonArgs };

export const Creatable = PlaygroundTemplate.bind({});
Creatable.args = { ...commonArgs, creatable: true };

0 comments on commit ad206ab

Please sign in to comment.