-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[material-ui][ListItem] Removing deprecated props #41566
base: next
Are you sure you want to change the base?
Conversation
Netlify deploy previewListItem: parsed: -22.93% 😍, gzip: -21.11% 😍 Bundle size reportDetails of bundle changes (Toolpad) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @thathva, thanks for working on this, here's my initial review.
Besides the comments, we should add these removals as breaking changes in https://github.com/DiegoAndai/material-ui/blob/next/docs/data/material/migration/migration-v5/migration-v5.md. You might have to rebase/merge next
into your branch.
I have made the changes as commented. Let me know if there are any more changes! |
Nicely done @thathva! Regarding the migration guide: We need to add these removals to the guide in this PR. But the guide was added after the
Which will bring the latest changes of the |
Thank you for the references and the detailed steps @DiegoAndai! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @thathva, thanks for incorporating the suggested changes 🙌🏼
I left a suggestion on the migration guide structure and text.
Besides that, I think we should add a codemod for this breaking change in this same PR. The codemod will replace all occurrences of <ListItem button />
to <ListItemButton />
. Would you be willing to work on it? If so, I can set it up and guide you through it.
Hi @DiegoAndai |
I'll set it up today/tomorrow and reach out 😊 |
Hey @thathva, I set up the codemods for v6 🎉 First you'll have to merge
Then, read the codemods contributing guide. The codemod we will add should be named
The codemod should be added to the For further guidance, I recommend going through:
Please reach out if you're stuck and need help 😊 I will be on vacation starting tomorrow (March 28th) until April 8th, so I won't attend to notifications during that time. @siriwatknp may I ask you to provide guidance in the meantime? |
Awesome, thanks for the guide @DiegoAndai! |
Hey @siriwatknp
import findComponentJSX from '../../util/findComponentJSX';
/**
* @param {import('jscodeshift').FileInfo} file
* @param {import('jscodeshift').API} api
*/
export default function transformer(file, api, options) {
const j = api.jscodeshift;
const root = j(file.source);
const printOptions = options.printOptions;
//Rename components that have ListItem button to ListItemButton
findComponentJSX(j, { root, componentName: 'ListItem' }, (elementPath) => {
const index = elementPath.node.openingElement.attributes.findIndex(
(attr) => attr.type === 'JSXAttribute' && attr.name.name === 'button',
);
if (index !== -1) {
elementPath.node.openingElement.name.name = 'ListItemButton';
elementPath.node.openingElement.attributes.splice(index, 1);
}
});
//Find if there are ListItem imports/named imports.
let containsListItemImport = root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material');
let containsListItemNamedImport = containsListItemImport.find(j.ImportSpecifier).filter(path => path.node.imported.name === 'ListItem');
let containsListItem = false;
//Find components that use ListItem. If they do, we shouldn't remove it
findComponentJSX(j, { root, componentName: 'ListItem' }, (elementPath) => {
if(elementPath.node.openingElement.name.name === 'ListItem') {
containsListItem = true;
}
});
//Remove ListItem import if there is no usage
if(containsListItemNamedImport.length === 0 || !containsListItem) {
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItem').remove();
}
//If ListItemButton does not already exist, add it at the end
let imports = root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItemButton');
if(imports.length === 0) {
let lastImport = root.find(j.ImportDeclaration).at(-1);
// Insert the import for 'ListItemButton' after the last import declaration
lastImport.insertAfter(
j.importDeclaration(
[j.importDefaultSpecifier(j.identifier('ListItemButton'))],
j.stringLiteral('@mui/material/ListItemButton')
)
);
}
return root.toSource(printOptions);
}
import ListItem from '@mui/material/ListItem';
import {ListItem as MyListItem} from '@mui/material';
<ListItem button/>;
<MyListItem button/>;
import ListItemButton from '@mui/material/ListItem';
import {ListItemButton as MyListItemButton} from '@mui/material';
<ListItemButton />;
<MyListItemButton />; The test case to transform the props is failing with these results: +expected
-actual
-import {ListItem as MyListItem} from '@mui/material';
-
-import ListItemButton from "@mui/material/ListItemButton";
-
-
-
-<ListItemButton />;
-
-
-
-<ListItemButton />;
+import {ListItemButton as MyListItemButton} from '@mui/material';
+import ListItemButton from '@mui/material/ListItem';
+
+<ListItemButton />;
+<MyListItemButton />; |
Hey @thathva, I'm back from vacation so I can help with this.
Could you commit this first version? That way it will be easier to review. In the following code: //Remove ListItem import if there is no usage
if(containsListItemNamedImport.length === 0 || !containsListItem) {
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItem').remove();
} We're only removing the
is not removed. We have to cover that case as well. |
Hey @DiegoAndai |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @thathva! Here are some suggestions to move forward
//Find if there are ListItem imports/named imports. | ||
let containsListItemImport = root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material'); | ||
let containsListItemNamedImport = containsListItemImport.find(j.ImportSpecifier).filter(path => path.node.imported.name === 'ListItem'); | ||
let containsListItem = false; | ||
|
||
//Find components that use ListItem. If they do, we shouldn't remove it | ||
findComponentJSX(j, { root, componentName: 'ListItem' }, (elementPath) => { | ||
if(elementPath.node.openingElement.name.name === 'ListItem') { | ||
containsListItem = true; | ||
} | ||
}); | ||
|
||
//Remove ListItem import if there is no usage | ||
if(containsListItemNamedImport.length === 0 || !containsListItem) { | ||
// root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material').find(j.ImportSpecifier) | ||
// .filter(path => path.node.imported.name === 'ListItem').remove(); | ||
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItem').remove(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's try this (I haven't tried it so it might need some fixing):
//Find if there are ListItem imports/named imports. | |
let containsListItemImport = root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material'); | |
let containsListItemNamedImport = containsListItemImport.find(j.ImportSpecifier).filter(path => path.node.imported.name === 'ListItem'); | |
let containsListItem = false; | |
//Find components that use ListItem. If they do, we shouldn't remove it | |
findComponentJSX(j, { root, componentName: 'ListItem' }, (elementPath) => { | |
if(elementPath.node.openingElement.name.name === 'ListItem') { | |
containsListItem = true; | |
} | |
}); | |
//Remove ListItem import if there is no usage | |
if(containsListItemNamedImport.length === 0 || !containsListItem) { | |
// root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material').find(j.ImportSpecifier) | |
// .filter(path => path.node.imported.name === 'ListItem').remove(); | |
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItem').remove(); | |
} | |
//Find components that use ListItem. If they do, we shouldn't remove it | |
findComponentJSX(j, { root, componentName: 'ListItem' }, (elementPath) => { | |
if(elementPath.node.openingElement.name.name === 'ListItem') { | |
containsListItem = true; | |
} | |
}); | |
//Find if there are ListItem imports/named imports. | |
let containsListItemImport = root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material'); | |
let containsListItemNamedImport = containsListItemImport.find(j.ImportSpecifier).filter(path => path.node.imported.name === 'ListItem'); | |
let containsListItem = false; | |
// Remove ListItem imports if there is no usage | |
if(!containsListItem) { | |
// Remove named imports | |
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material').find(j.ImportSpecifier).filter(path => path.node.imported.name === 'ListItem').remove(); | |
// Remove default imports | |
root.find(j.ImportDeclaration).filter(path => path.node.source.value === '@mui/material/ListItem').remove(); | |
} |
* @param {import('jscodeshift').API} api | ||
*/ | ||
export default function deprecationsAll(file, api, options) { | ||
file.source = transformerListItemButtonProps(file, api, options); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be added to the v6-all
one, as it's not a deprecation. And we should remove this file.
Hey @DiegoAndai!
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey! Here's my thoughts about the test cases
|
||
<ListItem button/>; | ||
|
||
<MyListItem button/>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add another component with the button
prop which shouldn't be removed, to test that case:
// ...
import AnotherComponent from "ui";
// ...
<AnotherComponent button />
fn({ | ||
MuiListItem: { | ||
defaultProps: { | ||
button | ||
} | ||
} | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the transform should remove the MuiListItem
completely, but:
- Remove the
button
and any other of the removed props - If the
button
props was found, copy the values to a newMuiListItemButton
entry
So in this example the actual should be
fn({
MuiListItem: {
defaultProps: {
anotherProp: 'value',
button,
}
}
});
And the expected:
fn({
MuiListItem: {
defaultProps: {
anotherProp: 'value',
}
},
MuiListItemButton: {
defaultProps: {
anotherProp: 'value',
autoFocus: true,
}
}
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the transform should remove the
MuiListItem
completely
I meant:
I think the transform shouldn't remove the MuiListItem
completely
The fn({
MuiListItem: {
defaultProps: {
anotherProp: 'value'
- },
-
- MuiListItemButton: {
- defaultProps: {
- anotherProp: 'value',
- autoFocus: true
- }
}
+ },
+ MuiListItemButton: {
+ defaultProps: {
+ anotherProp: 'value',
+ autoFocus: true
+ }
}
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @thathva, sorry for the delay.
// Copy properties from MuiListItem's defaultProps except 'button' | ||
const newButtonProps = defaultPropsNode.value.properties.filter(prop => prop.key.name !== 'button'); | ||
|
||
// Add autoFocus:true to newButtonProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By the testcase, I assumed that we would need to move all of MuiListItem
props to MuiListItemButton
prop. So we would only need to remove button
, and copy over the remaining props to MuiListItemButton
correct? Or would it just be removing button
and MuiListItemButton
would by default have the anotherProp
key?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we would need to move all of MuiListItem props to MuiListItemButton prop
Yes, we should move MuiListItem
props into MuiListItemButton
if those props apply to MuiListItemButton
.
So we would only need to remove button, and copy over the remaining props to MuiListItemButton correct?
Correct
My question is, why always adding autoFocus: true
? Shouldn't we add it only if it's present in MuiListItem
's defaultProps
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay got it! I made a mistake in assuming autoFocus: true
would be a default property. I will make the changes accordingly.
}); | ||
|
||
let hasButton = false; | ||
root.find(j.ObjectProperty).forEach((path) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May I ask you to refactor to use the findComponentDefaultProps
util?
That returns a collection of defaultProps
paths, given a component name. You can follow this example as a guideline: https://github.com/mui/material-ui/blob/next/packages/mui-codemod/src/deprecations/utils/movePropIntoSlots.js
This will make it easier to guide and debug 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I will take a look at that. That is helpful!
Hi @DiegoAndai, I wanted to apologize for the long delay in addressing this issue. I got caught up with some things so I wasn't able to focus on it. Given the complexity and the time it has taken, would it be possible to consider closing the deprecations part of this issue for now? I was wondering if the codemod transformations be moved to a separate issue. Let me know what you think, thanks! |
Removed the deprecated props from Material-UI ListItem API component (Closes Issue #41296). Modified test cases, documentation and proptypes. Removed props:
autoFocus
button
disabled
selected