Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Migrating from deprecated APIs

<p class="description">Learn how to migrate away from recently deprecated APIs before they become breaking changes.</p>

## Why you should migrate

Features become deprecated over time as maintainers make improvements to the APIs.
Migrating to these improved APIs results in a better developer experience, so it's in your best interest to stay up to date.
Deprecated APIs often become breaking changes in subsequent major versions, so the sooner you migrate, the smoother the next major update will be.

Check warning on line 9 in docs/data/material/migration/upgrade-to-v7/migrating-from-deprecated-apis.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Google.Will] Avoid using 'will'. Raw Output: {"message": "[Google.Will] Avoid using 'will'.", "location": {"path": "docs/data/material/migration/upgrade-to-v7/migrating-from-deprecated-apis.md", "range": {"start": {"line": 9, "column": 139}}}, "severity": "WARNING"}

## Autocomplete

Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#autocomplete-props) below to migrate the code as described in the following sections:

```bash
npx @mui/codemod@latest deprecations/autocomplete-props <path>
```

### renderTags prop

The `renderTags` prop is deprecated, use `renderValue` instead.

```diff
<Autocomplete
multiple
options={options}
- renderTags={(value, getTagProps, ownerState) =>
- value.map((option, index) => (
- <Chip label={option.label} {...getTagProps({ index })} />
- ))
- }
+ renderValue={(value, getItemProps, ownerState) =>
+ value.map((option, index) => (
+ <Chip label={option.label} {...getItemProps({ index })} />
+ ))
+ }
/>
```

---

### useAutocomplete deprecated fields

The following return value fields are deprecated from the `useAutocomplete` hook:

- `getTagProps` → use `getItemProps`
- `focusedTag` → use `focusedItem`

#### getTagProps

```diff
const {
- getTagProps,
+ getItemProps,
} = useAutocomplete(props);

// ...
-<Chip {...getTagProps({ index })} />
+<Chip {...getItemProps({ index })} />
```

#### focusedTag

```diff
const {
- focusedTag,
+ focusedItem,
} = useAutocomplete(props);
```
7 changes: 7 additions & 0 deletions docs/data/material/migration/upgrade-to-v7/upgrade-to-v7.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@
If you're on React 18 or below, mismatched versions of `react-is` can cause runtime errors in prop type checks.
Forcing `react-is` to match your React version prevents these errors.

## Deprecations

It is not required to immediately go through the deprecations in order to use Material UI v7.

You can do it at your own pace by checking out the [deprecations page](/material-ui/migration/v7/migrating-from-deprecated-apis/).
Those deprecations will be removed in the next major version.

Check warning on line 147 in docs/data/material/migration/upgrade-to-v7/upgrade-to-v7.md

View workflow job for this annotation

GitHub Actions / runner / vale

[vale] reported by reviewdog 🐶 [Google.Will] Avoid using 'will'. Raw Output: {"message": "[Google.Will] Avoid using 'will'.", "location": {"path": "docs/data/material/migration/upgrade-to-v7/upgrade-to-v7.md", "range": {"start": {"line": 147, "column": 20}}}, "severity": "WARNING"}

## Breaking changes

Since v7 is a new major release, it contains some changes that affect the public API.
Expand Down
4 changes: 4 additions & 0 deletions docs/data/material/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ const pages: MuiPage[] = [
pathname: '/material-ui/migration/upgrade-to-v7',
title: 'Upgrade to v7: getting started',
},
{
pathname: '/material-ui/migration/v7/migrating-from-deprecated-apis',
title: 'Migrating from deprecated APIs',
},
{
pathname: '/material-ui/migration/upgrade-to-native-color',
title: 'Native color',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs';
import * as pageProps from 'docs/data/material/migration/upgrade-to-v7/migrating-from-deprecated-apis.md?muiMarkdown';

export default function Page() {
return <MarkdownDocs {...pageProps} />;
}
1 change: 1 addition & 0 deletions docs/translations/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@
"/material-ui/migration/pickers-migration": "Migration from @material-ui/pickers",
"Upgrade to v7": "Upgrade to v7",
"/material-ui/migration/upgrade-to-v7": "Upgrade to v7: getting started",
"/material-ui/migration/v7/migrating-from-deprecated-apis": "Migrating from deprecated APIs",
"/material-ui/migration/upgrade-to-native-color": "Native color",
"Upgrade to v6": "Upgrade to v6",
"/material-ui/migration/upgrade-to-v6": "Upgrade to v6: getting started",
Expand Down
27 changes: 27 additions & 0 deletions packages/mui-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,11 @@ npx @mui/codemod@latest deprecations/alert-props <path>
- PopperComponent={CustomPopper}
- ListboxComponent={CustomListbox}
- ListboxProps={{ height: 12 }}
- renderTags={(value, getTagProps, ownerState) =>
- value.map((option, index) => (
- <Chip label={option.label} {...getTagProps({ index })} />
- ))
- }
- componentsProps={{
- clearIndicator: { width: 10 },
- paper: { width: 12 },
Expand All @@ -299,6 +304,11 @@ npx @mui/codemod@latest deprecations/alert-props <path>
+ popper: { width: 14 },
+ popupIndicator: { width: 16 },
+ }}
+ renderValue={(value, getItemProps, ownerState) =>
+ value.map((option, index) => (
+ <Chip label={option.label} {...getItemProps({ index })} />
+ ))
+ }
/>
```

Expand All @@ -310,6 +320,10 @@ npx @mui/codemod@latest deprecations/alert-props <path>
- PopperComponent: CustomPopper,
- ListboxComponent: CustomListbox,
- ListboxProps: { height: 12 },
- renderTags: (value, getTagProps, ownerState) =>
- value.map((option, index) => (
- <Chip label={option.label} {...getTagProps({ index })} />
- )),
- componentsProps: {
- clearIndicator: { width: 10 },
- paper: { width: 12 },
Expand All @@ -331,10 +345,23 @@ npx @mui/codemod@latest deprecations/alert-props <path>
+ popper: { width: 14 },
+ popupIndicator: { width: 16 },
+ },
+ renderValue: (value, getItemProps, ownerState) =>
+ value.map((option, index) => (
+ <Chip label={option.label} {...getItemProps({ index })} />
+ )),
},
},
```

```diff
const {
- getTagProps,
- focusedTag,
+ getItemProps,
+ focusedItem,
} = useAutocomplete(props);
```

```bash
npx @mui/codemod@latest deprecations/autocomplete-props <path>
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,99 @@ import findComponentDefaultProps from '../../util/findComponentDefaultProps';
import assignObject from '../../util/assignObject';
import appendAttribute from '../../util/appendAttribute';

function isNonComputedKey(j, path) {
const parent = path.parent.node;

return (
(j.ObjectProperty.check(parent) || j.Property.check(parent)) &&
parent.key === path.node &&
!parent.computed
);
}

function renameIdentifiersInScope(j, scopePath, oldName, newName) {
const bindingScope = scopePath.scope.lookup(oldName);

if (!bindingScope) {
return;
}

j(bindingScope.path)
.find(j.Identifier, { name: oldName })
.filter((path) => {
if (isNonComputedKey(j, path)) {
return false;
}

return path.scope.lookup(oldName) === bindingScope;
})
.replaceWith(() => j.identifier(newName));
}

function renameRenderTagsCallback(j, callbackPath) {
const getTagPropsParam = callbackPath.node.params[1];

if (getTagPropsParam?.type === 'Identifier' && getTagPropsParam.name === 'getTagProps') {
renameIdentifiersInScope(j, callbackPath, 'getTagProps', 'getItemProps');
}
}

function renameRenderTagsProp(j, propertyPath) {
if (propertyPath.node.key.type === 'Identifier') {
propertyPath.node.key.name = 'renderValue';
}

if (
propertyPath.node.value.type === 'ArrowFunctionExpression' ||
propertyPath.node.value.type === 'FunctionExpression'
) {
renameRenderTagsCallback(j, propertyPath.get('value'));
}
}

function renameUseAutocompleteReturnMembers(j, root) {
const renamedMembers = new Map([
['getTagProps', 'getItemProps'],
['focusedTag', 'focusedItem'],
]);

root
.find(j.VariableDeclarator)
.filter((path) => {
const { id, init } = path.node;

return (
id.type === 'ObjectPattern' &&
init?.type === 'CallExpression' &&
init.callee.type === 'Identifier' &&
init.callee.name === 'useAutocomplete'
);
})
.forEach((path) => {
path.node.id.properties.forEach((property) => {
if (property.type !== 'ObjectProperty' || property.key.type !== 'Identifier') {
return;
}

const nextName = renamedMembers.get(property.key.name);

if (!nextName) {
return;
}

const isShorthand = property.shorthand === true;
const localName = property.value.type === 'Identifier' ? property.value.name : null;

property.key.name = nextName;

if (isShorthand && localName) {
renameIdentifiersInScope(j, path, localName, nextName);
property.shorthand = true;
}
});
});
}

/**
* @param {import('jscodeshift').FileInfo} file
* @param {import('jscodeshift').API} api
Expand Down Expand Up @@ -59,6 +152,26 @@ export default function transformer(file, api, options) {
{ root, packageName: options.packageName, componentName: 'Autocomplete' },
(elementPath) => {
const element = elementPath.node;

element.openingElement.attributes.forEach((attribute, index) => {
if (attribute.type !== 'JSXAttribute' || attribute.name.name !== 'renderTags') {
return;
}

attribute.name.name = 'renderValue';

if (
attribute.value?.type === 'JSXExpressionContainer' &&
(attribute.value.expression.type === 'ArrowFunctionExpression' ||
attribute.value.expression.type === 'FunctionExpression')
) {
renameRenderTagsCallback(
j,
elementPath.get('openingElement', 'attributes', index, 'value', 'expression'),
);
}
});

const propIndex = element.openingElement.attributes.findIndex(
(attr) => attr.type === 'JSXAttribute' && attr.name.name === 'ListboxComponent',
);
Expand Down Expand Up @@ -169,5 +282,13 @@ export default function transformer(file, api, options) {
path.prune();
});

defaultPropsPathCollection
.find(j.ObjectProperty, { key: { name: 'renderTags' } })
.forEach((path) => {
renameRenderTagsProp(j, path);
});

renameUseAutocompleteReturnMembers(j, root);

return root.toSource(printOptions);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Autocomplete from '@mui/material/Autocomplete';
import {Autocomplete as MyAutocomplete} from '@mui/material';
import Chip from '@mui/material/Chip';
import useAutocomplete from '@mui/material/useAutocomplete';

<Autocomplete
ChipProps={{ height: 10 }}
Expand Down Expand Up @@ -61,4 +63,23 @@ import {Autocomplete as MyAutocomplete} from '@mui/material';
PopperComponent={CustomPopper}
ListboxComponent={CustomListbox}
ListboxProps={{ height: 12 }}
/>
/>;

<Autocomplete
multiple
options={options}
renderTags={(value, getTagProps, ownerState) =>
value.map((option, index) => (
<Chip label={option.label} data-focused={ownerState.focused} {...getTagProps({ index })} />
))
}
/>;

const { getTagProps, focusedTag } = useAutocomplete(props);

<Chip {...getTagProps({ index: focusedTag })} />;

const { getTagProps: getAutocompleteTagProps, focusedTag: focusedAutocompleteTag } =
useAutocomplete(props);

<Chip {...getAutocompleteTagProps({ index: focusedAutocompleteTag })} />;
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Autocomplete from '@mui/material/Autocomplete';
import {Autocomplete as MyAutocomplete} from '@mui/material';
import Chip from '@mui/material/Chip';
import useAutocomplete from '@mui/material/useAutocomplete';

<Autocomplete
slots={{
Expand Down Expand Up @@ -74,4 +76,23 @@ import {Autocomplete as MyAutocomplete} from '@mui/material';
PopperComponent={CustomPopper}
ListboxComponent={CustomListbox}
ListboxProps={{ height: 12 }}
/>
/>;

<Autocomplete
multiple
options={options}
renderValue={(value, getItemProps, ownerState) =>
value.map((option, index) => (
<Chip label={option.label} data-focused={ownerState.focused} {...getItemProps({ index })} />
))
}
/>;

const { getItemProps, focusedItem } = useAutocomplete(props);

<Chip {...getItemProps({ index: focusedItem })} />;

const { getItemProps: getAutocompleteTagProps, focusedItem: focusedAutocompleteTag } =
useAutocomplete(props);

<Chip {...getAutocompleteTagProps({ index: focusedAutocompleteTag })} />;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Autocomplete from '@org/ui/material/Autocomplete';
import {Autocomplete as MyAutocomplete} from '@org/ui/material';
import Chip from '@org/ui/material/Chip';

<Autocomplete
ChipProps={{ height: 10 }}
Expand Down Expand Up @@ -61,4 +62,12 @@ import {Autocomplete as MyAutocomplete} from '@org/ui/material';
PopperComponent={CustomPopper}
ListboxComponent={CustomListbox}
ListboxProps={{ height: 12 }}
/>;

<MyAutocomplete
multiple
options={options}
renderTags={(value, getTagProps) =>
value.map((option, index) => <Chip label={option.label} {...getTagProps({ index })} />)
}
/>
Loading
Loading