Skip to content

Commit

Permalink
[Autocomplete] Enable global customization of different options (#36971)
Browse files Browse the repository at this point in the history
Co-authored-by: ZeeshanTamboli <[email protected]>
  • Loading branch information
nicolas-ot and ZeeshanTamboli committed Jul 10, 2023
1 parent be6b6e2 commit e0e5d28
Show file tree
Hide file tree
Showing 8 changed files with 1,327 additions and 8 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<ThemeProvider theme={customTheme(outerTheme)}>
<Stack spacing={5} sx={{ width: 300 }}>
<MovieSelect />
<CountrySelect />
</Stack>
</ThemeProvider>
10 changes: 10 additions & 0 deletions docs/data/material/components/autocomplete/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ Pay specific attention to the `ref` and `inputProps` keys.

{{"demo": "CustomInputAutocomplete.js"}}

### Globally Customized Options

To globally customize the Autocomplete options for all components in your app,
you can use the [theme default props](/material-ui/customization/theme-components/#theme-default-props) and set the `renderOption` property in the `defaultProps` key.
The `renderOption` property takes the `ownerState` as the fourth parameter, which includes props and internal component state.
To display the label, you can use the `getOptionLabel` prop from the `ownerState`.
This approach enables different options for each Autocomplete component while keeping the options styling consistent.

{{"demo": "GloballyCustomizedOptions.js"}}

### GitHub's picker

This demo reproduces GitHub's label picker:
Expand Down
2 changes: 1 addition & 1 deletion docs/translations/api-docs/autocomplete/autocomplete.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"readOnly": "If <code>true</code>, the component becomes readonly. It is also supported for multiple tags where the tag cannot be deleted.",
"renderGroup": "Render the group.<br><br><strong>Signature:</strong><br><code>function(params: AutocompleteRenderGroupParams) =&gt; ReactNode</code><br><em>params:</em> The group to render.",
"renderInput": "Render the input.<br><br><strong>Signature:</strong><br><code>function(params: object) =&gt; ReactNode</code><br>",
"renderOption": "Render the option, use <code>getOptionLabel</code> by default.<br><br><strong>Signature:</strong><br><code>function(props: object, option: T, state: object) =&gt; ReactNode</code><br><em>props:</em> The props to apply on the li element.<br><em>option:</em> The option to render.<br><em>state:</em> The state of the component.",
"renderOption": "Render the option, use <code>getOptionLabel</code> by default.<br><br><strong>Signature:</strong><br><code>function(props: object, option: T, state: object, ownerState: object) =&gt; ReactNode</code><br><em>props:</em> The props to apply on the li element.<br><em>option:</em> The option to render.<br><em>state:</em> The state of each option.<br><em>ownerState:</em> The state of the Autocomplete component.",
"renderTags": "Render the selected value.<br><br><strong>Signature:</strong><br><code>function(value: Array&lt;T&gt;, getTagProps: function, ownerState: object) =&gt; ReactNode</code><br><em>value:</em> The <code>value</code> provided to the component.<br><em>getTagProps:</em> A tag props getter.<br><em>ownerState:</em> The state of the Autocomplete component.",
"selectOnFocus": "If <code>true</code>, the input&#39;s text is selected on focus. It helps the user clear the selected value.",
"size": "The size of the component.",
Expand Down
4 changes: 3 additions & 1 deletion packages/mui-material/src/Autocomplete/Autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,15 @@ export interface AutocompleteProps<
*
* @param {object} props The props to apply on the li element.
* @param {T} option The option to render.
* @param {object} state The state of the component.
* @param {object} state The state of each option.
* @param {object} ownerState The state of the Autocomplete component.
* @returns {ReactNode}
*/
renderOption?: (
props: React.HTMLAttributes<HTMLLIElement>,
option: T,
state: AutocompleteRenderOptionState,
ownerState: AutocompleteOwnerState<T, Multiple, DisableClearable, FreeSolo, ChipComponent>,
) => React.ReactNode;
/**
* Render the selected value.
Expand Down
18 changes: 12 additions & 6 deletions packages/mui-material/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -552,11 +552,16 @@ const Autocomplete = React.forwardRef(function Autocomplete(inProps, ref) {
const renderListOption = (option, index) => {
const optionProps = getOptionProps({ option, index });

return renderOption({ ...optionProps, className: classes.option }, option, {
selected: optionProps['aria-selected'],
index,
inputValue,
});
return renderOption(
{ ...optionProps, className: classes.option },
option,
{
selected: optionProps['aria-selected'],
index,
inputValue,
},
ownerState,
);
};

const clearIndicatorSlotProps = slotProps.clearIndicator ?? componentsProps.clearIndicator;
Expand Down Expand Up @@ -1064,7 +1069,8 @@ Autocomplete.propTypes /* remove-proptypes */ = {
*
* @param {object} props The props to apply on the li element.
* @param {T} option The option to render.
* @param {object} state The state of the component.
* @param {object} state The state of each option.
* @param {object} ownerState The state of the Autocomplete component.
* @returns {ReactNode}
*/
renderOption: PropTypes.func,
Expand Down
21 changes: 21 additions & 0 deletions packages/mui-material/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3026,6 +3026,27 @@ describe('<Autocomplete />', () => {
});
});

describe('prop: renderOption', () => {
it('should pass getOptionLabel through ownerState in renderOption callback', () => {
render(
<Autocomplete
open
options={[{ name: 'Max' }]}
getOptionLabel={(option) => option.name}
renderInput={(params) => <TextField {...params} autoFocus />}
renderOption={(props, option, optionState, ownerState) => (
<li key={option.name} data-testid="optionLi">
{ownerState.getOptionLabel(option)}
</li>
)}
/>,
);

const renderedOption = screen.getByTestId('optionLi');
expect(renderedOption).to.have.text('Max');
});
});

// https://github.com/mui/material-ui/issues/36212
it('should preserve scrollTop position of the listbox when adding new options on mobile', function test() {
if (/jsdom/.test(window.navigator.userAgent)) {
Expand Down

0 comments on commit e0e5d28

Please sign in to comment.