Skip to content

Commit

Permalink
polish :)
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Mar 22, 2020
1 parent 1a00008 commit 4c6e446
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 77 deletions.
3 changes: 2 additions & 1 deletion docs/pages/api-docs/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,19 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
| <span class="prop-name">disabled</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the input will be disabled. |
| <span class="prop-name">disableListWrap</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the list box in the popup will not wrap focus. |
| <span class="prop-name">disablePortal</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | Disable the portal behavior. The children stay within it's parent DOM hierarchy. |
| <span class="prop-name">filterMaxTags</span> | <span class="prop-type">number</span> | | The number of tags that will be visible. Set `-1` to display them all. |
| <span class="prop-name">filterOptions</span> | <span class="prop-type">func</span> | | A filter function that determines the options that are eligible.<br><br>**Signature:**<br>`function(options: T[], state: object) => undefined`<br>*options:* The options to render.<br>*state:* The state of the component. |
| <span class="prop-name">filterSelectedOptions</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, hide the selected options from the list box. |
| <span class="prop-name">forcePopupIcon</span> | <span class="prop-type">'auto'<br>&#124;&nbsp;bool</span> | <span class="prop-default">'auto'</span> | Force the visibility display of the popup icon. |
| <span class="prop-name">freeSolo</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. |
| <span class="prop-name">getLimitTagsText</span> | <span class="prop-type">func</span> | <span class="prop-default">(more) => `+${more}`</span> | The label to display when the tags are truncated (`limitTags`).<br><br>**Signature:**<br>`function(more: any) => ReactNode`<br>*more:* The number of truncated tags. |
| <span class="prop-name">getOptionDisabled</span> | <span class="prop-type">func</span> | | Used to determine the disabled state for a given option. |
| <span class="prop-name">getOptionLabel</span> | <span class="prop-type">func</span> | <span class="prop-default">(x) => x</span> | Used to determine the string value for a given option. It's used to fill the input (and the list box options if `renderOption` is not provided). |
| <span class="prop-name">getOptionSelected</span> | <span class="prop-type">func</span> | | Used to determine if an option is selected. Uses strict equality by default. |
| <span class="prop-name">groupBy</span> | <span class="prop-type">func</span> | | If provided, the options will be grouped under the returned string. The groupBy value is also used as the text for group headings when `renderGroup` is not provided.<br><br>**Signature:**<br>`function(options: T) => string`<br>*options:* The option to group. |
| <span class="prop-name">id</span> | <span class="prop-type">string</span> | | This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id. |
| <span class="prop-name">includeInputInList</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the highlight can move to the input. |
| <span class="prop-name">inputValue</span> | <span class="prop-type">string</span> | | The input value. |
| <span class="prop-name">limitTags</span> | <span class="prop-type">number</span> | <span class="prop-default">-1</span> | The maximum number of tags that will be visible when not focused. Set `-1` to disable the limit. |
| <span class="prop-name">ListboxComponent</span> | <span class="prop-type">elementType</span> | <span class="prop-default">'ul'</span> | The component used to render the listbox. |
| <span class="prop-name">ListboxProps</span> | <span class="prop-type">object</span> | | Props applied to the Listbox element. |
| <span class="prop-name">loading</span> | <span class="prop-type">bool</span> | <span class="prop-default">false</span> | If `true`, the component is in a loading state. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,32 @@ import Autocomplete from '@material-ui/lab/Autocomplete';
import { makeStyles } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';

const useStyles = makeStyles((theme) => ({
root: {
width: 500,
'& > * + *': {
marginTop: theme.spacing(3),
const useStyles = makeStyles(
(theme) => ({
root: {
width: 500,
'& > * + *': {
marginTop: theme.spacing(3),
},
},
},
}));
}),
Ò,
);

export default function FilterMaxTags() {
export default function LimitTags() {
const classes = useStyles();

return (
<div className={classes.root}>
<Autocomplete
multiple
filterMaxTags={2}
limitTags={2}
id="tags-standard"
options={top100Films}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="filterMaxTags" placeholder="Favorites" />
<TextField {...params} variant="outlined" label="limitTags" placeholder="Favorites" />
)}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ const useStyles = makeStyles((theme: Theme) =>
marginTop: theme.spacing(3),
},
},
}),
}),Ò
);

export default function FilterMaxTags() {
export default function LimitTags() {
const classes = useStyles();

return (
<div className={classes.root}>
<Autocomplete
multiple
filterMaxTags={2}
limitTags={2}
id="tags-standard"
options={top100Films}
getOptionLabel={(option) => option.title}
defaultValue={[top100Films[13], top100Films[12], top100Films[11]]}
renderInput={(params) => (
<TextField {...params} variant="outlined" label="filterMaxTags" placeholder="Favorites" />
<TextField {...params} variant="outlined" label="limitTags" placeholder="Favorites" />
)}
/>
</div>
Expand Down
34 changes: 17 additions & 17 deletions docs/src/pages/components/autocomplete/autocomplete.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,6 @@ Also known as tags, the user is allowed to enter more than one value.

{{"demo": "pages/components/autocomplete/Tags.js"}}

## Filter Max Tags

Do you want to show only a few tags? Use the `filterMaxTags` prop.

{{"demo": "pages/components/autocomplete/FilterMaxTags.js"}}

### Fixed options

In the event that you need to lock certain tag so that they can't be removed in the interface, you can set the chips disabled.
Expand All @@ -116,6 +110,12 @@ In the event that you need to lock certain tag so that they can't be removed in

{{"demo": "pages/components/autocomplete/CheckboxesTags.js"}}

### Limit max tags

You can use the `limitMaxTags` prop to avoid displaying a very long list of options.

{{"demo": "pages/components/autocomplete/LimitMaxTags.js"}}

## Sizes

Fancy smaller inputs? Use the `size` prop.
Expand Down Expand Up @@ -149,14 +149,13 @@ import { createFilterOptions } from '@material-ui/lab/Autocomplete';

#### Arguments

1. `config` (_Object_ [optional]):

- `config.ignoreAccents` (_Boolean_ [optional]): Defaults to `true`. Remove diacritics.
- `config.ignoreCase` (_Boolean_ [optional]): Defaults to `true`. Lowercase everything.
- `config.matchFrom` (_'any' | 'start'_ [optional]): Defaults to `'any'`.
- `config.stringify` (_Func_ [optional]): Controls how an option is converted into a string so that it can be matched against the input text fragment.
- `config.trim` (_Boolean_ [optional]): Defaults to `false`. Remove trailing spaces.
- `config.limit` (_Number_ [optional]): Default to null. Limit the number of suggested options to be shown. For example, if `config.limit` is `100`, only the first `100` matching options are shown. It can be useful if a lot of options match and virtualization wasn't set up.
1. `config` (*Object* [optional]):
- `config.ignoreAccents` (*Boolean* [optional]): Defaults to `true`. Remove diacritics.
- `config.ignoreCase` (*Boolean* [optional]): Defaults to `true`. Lowercase everything.
- `config.matchFrom` (*'any' | 'start'* [optional]): Defaults to `'any'`.
- `config.stringify` (*Func* [optional]): Controls how an option is converted into a string so that it can be matched against the input text fragment.
- `config.trim` (*Boolean* [optional]): Defaults to `false`. Remove trailing spaces.
- `config.limit` (*Number* [optional]): Default to null. Limit the number of suggested options to be shown. For example, if `config.limit` is `100`, only the first `100` matching options are shown. It can be useful if a lot of options match and virtualization wasn't set up.

#### Returns

Expand All @@ -170,7 +169,7 @@ const filterOptions = createFilterOptions({
stringify: option => option.title,
});

<Autocomplete filterOptions={filterOptions} />;
<Autocomplete filterOptions={filterOptions} />
```

{{"demo": "pages/components/autocomplete/Filter.js", "defaultCodeOpen": false}}
Expand All @@ -182,9 +181,10 @@ For richer filtering mechanisms, like fuzzy matching, it's recommended to look a
```jsx
import matchSorter from 'match-sorter';

const filterOptions = (options, { inputValue }) => matchSorter(options, inputValue);
const filterOptions = (options, { inputValue }) =>
matchSorter(options, inputValue);

<Autocomplete filterOptions={filterOptions} />;
<Autocomplete filterOptions={filterOptions} />
```

## Virtualization
Expand Down
12 changes: 10 additions & 2 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@ export interface AutocompleteProps<T>
*/
forcePopupIcon?: true | false | 'auto';
/**
* The number of tags that will be visible. Set `-1` to display them all.
* The label to display when the tags are truncated (`limitTags`).
*
* @param {any} more The number of truncated tags.
* @returns {ReactNode}
*/
filterMaxTags?: number;
getLimitTagsText?: (more: number) => React.ReactNode;
/**
* The component used to render the listbox.
*/
Expand All @@ -105,6 +108,11 @@ export interface AutocompleteProps<T>
* For localization purposes, you can use the provided [translations](/guides/localization/).
*/
loadingText?: React.ReactNode;
/**
* The maximum number of tags that will be visible when not focused.
* Set `-1` to disable the limit.
*/
limitTags?: number;
/**
* Text to display when there are no options.
*
Expand Down
33 changes: 23 additions & 10 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,19 +257,20 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
filterSelectedOptions = false,
forcePopupIcon = 'auto',
freeSolo = false,
getLimitTagsText = (more) => `+${more}`,
getOptionDisabled,
getOptionLabel = (x) => x,
getOptionSelected,
groupBy,
id: idProp,
includeInputInList = false,
inputValue: inputValueProp,
limitTags = -1,
ListboxComponent = 'ul',
ListboxProps,
loading = false,
loadingText = 'Loading…',
multiple = false,
filterMaxTags,
noOptionsText = 'No options',
onChange,
onClose,
Expand Down Expand Up @@ -341,11 +342,15 @@ const Autocomplete = React.forwardRef(function Autocomplete(props, ref) {
}
}

if (filterMaxTags && Array.isArray(startAdornment)) {
const more = startAdornment.length - filterMaxTags;
if (filterMaxTags && !focused && more > 0) {
startAdornment = startAdornment.splice(0, filterMaxTags);
startAdornment.push(<span key={filterMaxTags} data-testid="more">{` + ${more} more`}</span>);
if (limitTags && Array.isArray(startAdornment)) {
const more = startAdornment.length - limitTags;
if (limitTags && !focused && more > 0) {
startAdornment = startAdornment.splice(0, limitTags);
startAdornment.push(
<span className={classes.tag} key={startAdornment.length}>
{getLimitTagsText(more)}
</span>,
);
}
}

Expand Down Expand Up @@ -581,10 +586,6 @@ Autocomplete.propTypes = {
* The children stay within it's parent DOM hierarchy.
*/
disablePortal: PropTypes.bool,
/**
* The number of tags that will be visible. Set `-1` to display them all.
*/
filterMaxTags: PropTypes.number,
/**
* A filter function that determines the options that are eligible.
*
Expand All @@ -605,6 +606,13 @@ Autocomplete.propTypes = {
* If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options.
*/
freeSolo: PropTypes.bool,
/**
* The label to display when the tags are truncated (`limitTags`).
*
* @param {any} more The number of truncated tags.
* @returns {ReactNode}
*/
getLimitTagsText: PropTypes.func,
/**
* Used to determine the disabled state for a given option.
*/
Expand Down Expand Up @@ -640,6 +648,11 @@ Autocomplete.propTypes = {
* The input value.
*/
inputValue: PropTypes.string,
/**
* The maximum number of tags that will be visible when not focused.
* Set `-1` to disable the limit.
*/
limitTags: PropTypes.number,
/**
* The component used to render the listbox.
*/
Expand Down
46 changes: 13 additions & 33 deletions packages/material-ui-lab/src/Autocomplete/Autocomplete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,48 +97,28 @@ describe('<Autocomplete />', () => {
});
});

describe('prop: filterMaxTags', () => {
it('show only the max number of items', () => {
const { queryByTestId, getAllByRole } = render(
<Autocomplete
multiple
filterMaxTags={2}
{...defaultProps}
options={['one', 'two', 'three', 'four']}
defaultValue={['one', 'two', 'three']}
renderInput={params => <TextField {...params} />}
/>,
);

const tags = getAllByRole('button');
expect(queryByTestId('more')).to.be.exist;
expect(tags[0].textContent).to.be.equal('one');
expect(tags[1].textContent).to.be.equal('two');
expect(tags[2].textContent).to.not.be.equal('three');
});

describe('prop: limitTags', () => {
it('show all items on focus', () => {
const { getAllByRole, queryByTestId, getByRole } = render(
const { container, getAllByRole, getByRole } = render(
<Autocomplete
multiple
filterMaxTags={2}
limitTags={2}
{...defaultProps}
options={['one', 'two', 'three', 'four']}
options={['one', 'two', 'three']}
defaultValue={['one', 'two', 'three']}
renderInput={params => <TextField {...params} />}
renderInput={(params) => <TextField {...params} />}
/>,
);

const input = getByRole('textbox');
expect(getAllByRole('button')[2].textContent).to.not.be.equal('three');
expect(queryByTestId('more')).to.exist;
let tags;
tags = getAllByRole('button');
expect(container.textContent).to.equal('onetwo+1');
expect(tags.length).to.be.equal(4);

input.focus();
const tags = getAllByRole('button');
expect(queryByTestId('more')).to.not.exist;
expect(tags[0].textContent).to.be.equal('one');
expect(tags[1].textContent).to.be.equal('two');
expect(tags[2].textContent).to.be.equal('three');
getByRole('textbox').focus();
tags = getAllByRole('button');
expect(container.textContent).to.equal('onetwothree');
expect(tags.length).to.be.equal(5);
});
});

Expand Down

0 comments on commit 4c6e446

Please sign in to comment.