Skip to content
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

Open
wants to merge 13 commits into
base: next
Choose a base branch
from

Conversation

thathva
Copy link

@thathva thathva commented Mar 20, 2024

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

@mui-bot
Copy link

mui-bot commented Mar 20, 2024

Netlify deploy preview

ListItem: parsed: -22.93% 😍, gzip: -21.11% 😍
@material-ui/core: parsed: -0.08% 😍, gzip: -0.08% 😍

Bundle size report

Details of bundle changes (Toolpad)
Details of bundle changes

Generated by 🚫 dangerJS against c1179d4

@thathva thathva changed the title [material-ui][ListItem] Removing deprecated props from material-ui ListItem [material-ui][ListItem] Removing deprecated props Mar 20, 2024
@danilo-leal danilo-leal added component: list This is the name of the generic UI component, not the React module! package: material-ui Specific to @mui/material labels Mar 20, 2024
@DiegoAndai DiegoAndai self-requested a review March 20, 2024 15:17
Copy link
Member

@DiegoAndai DiegoAndai left a 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.

packages/mui-material/src/ListItem/ListItem.d.ts Outdated Show resolved Hide resolved
test/integration/material-ui/components.spec.tsx Outdated Show resolved Hide resolved
@thathva
Copy link
Author

thathva commented Mar 20, 2024

I have made the changes as commented. Let me know if there are any more changes!

@DiegoAndai
Copy link
Member

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 thathva:material-ui-listitem-remove-props branch was created, so the files do not exist. That is why you'll have to do:

  1. git checkout next
  2. git pull upstream next
  3. git checkout material-ui-listitem-remove-props
  4. git merge next

Which will bring the latest changes of the next branch into the material-ui-listitem-remove-props branch. Then you'll be able to find the /docs/data/material/migration/migration-v5/migration-v5.md file and add the removals to it. You can follow this similar migration guide to follow: https://github.com/mui/material-ui/blob/next/docs/data/system/migration/migration-v5/migration-v5.md

@thathva
Copy link
Author

thathva commented Mar 22, 2024

Thank you for the references and the detailed steps @DiegoAndai!
I have updated the migration document.

Copy link
Member

@DiegoAndai DiegoAndai left a 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.

docs/data/material/migration/migration-v5/migration-v5.md Outdated Show resolved Hide resolved
@thathva
Copy link
Author

thathva commented Mar 25, 2024

Hi @DiegoAndai
Sure, I will rephrase it as you suggested.
And the codemod, yes I would love to add that! If there is a guide, I can refer that and try it out.

@DiegoAndai
Copy link
Member

And the codemod, yes I would love to add that! If there is a guide, I can refer that and try it out.

I'll set it up today/tomorrow and reach out 😊

@DiegoAndai
Copy link
Member

Hey @thathva, I set up the codemods for v6 🎉

First you'll have to merge next into your branch:

  1. git checkout next
  2. git pull upstream
  3. git checkout material-ui-listitem-remove-props
  4. git merge next

Then, read the codemods contributing guide.

The codemod we will add should be named list-item-button-prop. It should do the following:

  1. Find all ListItem components with the button prop
  2. Replace them with ListItemButton, removing the button prop but retaining all other props
  3. Add the ListItemButton import to the file if it doesn't exist
  4. If there are no other ListItem components in the file, remove the ListItem import

The codemod should be added to the v6.0.0 codemods folder.

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?

@thathva
Copy link
Author

thathva commented Mar 27, 2024

Awesome, thanks for the guide @DiegoAndai!
I will take a look and reach out if I have any questions. Have a great vacation!

@thathva
Copy link
Author

thathva commented Apr 5, 2024

Hey @siriwatknp
I apologize for the delay in the PR, I have been occupied with few things. I am facing a bit of issues with the codemod, so would appreciate some help. The code is the first version that I was hoping to get it to work before I could refactor.
I have the code below for the codemod, and the test cases, which seem to fail. I was wondering if you could help me out if I am going in the right direction?

list-item-button-prop.js

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);
}

actual.js

import ListItem from '@mui/material/ListItem';
import {ListItem as MyListItem} from '@mui/material';

<ListItem button/>;

<MyListItem button/>;

expected.js

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 />;

@DiegoAndai
Copy link
Member

Hey @thathva, I'm back from vacation so I can help with this.

The code is the first version that I was hoping to get it to work before I could refactor.

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 @mui/material/ListItem import, that's why the

import {ListItem as MyListItem} from '@mui/material';

is not removed. We have to cover that case as well.

@thathva
Copy link
Author

thathva commented Apr 10, 2024

Hey @DiegoAndai
Thank you for your guidance again! I have pushed the changes. I have used similar test cases as other codemods, and I am failing 2 of the 6 test cases, which one of them is because of the named import. Would appreciate your help!

Copy link
Member

@DiegoAndai DiegoAndai left a 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

Comment on lines 23 to 40
//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();
}
Copy link
Member

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):

Suggested change
//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);
Copy link
Member

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.

@thathva
Copy link
Author

thathva commented Apr 11, 2024

Hey @DiegoAndai!
Thank you for your inputs! I used the code you mentioned and was able to fix that test case. I made the following assumptions:

  1. Renaming the imported name from ListItem to ListItemButton since we don't need to worry about the alias of that component.
  2. Using the same assumption, not changing the alias in the code, but just removing the button prop from it.
    Let me know if this sounds good.
    Apart from this, there is a test case failing for the theme, which I am not familiar with. Can you direct me to any other codemod that modifies the theme? To my understanding, we would need to remove the defaultProps from MuiListItem and replace it with MuiListItemButton. The test case:
    I was able to fix all the test cases. But, I still have some doubt over the correctness of the actual.js and expected.js files for themes and props. Can you let me know if this looks good, so that I could perform the codemod for the entire codebase?

Copy link
Member

@DiegoAndai DiegoAndai left a 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/>;
Copy link
Member

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 />

Comment on lines 1 to 8
fn({
MuiListItem: {
defaultProps: {
button
}
}
});

Copy link
Member

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:

  1. Remove the button and any other of the removed props
  2. If the button props was found, copy the values to a new MuiListItemButton 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,
      }
    }
  });

Copy link
Member

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

@thathva
Copy link
Author

thathva commented Apr 17, 2024

The props test cases works as expected, but I am having trouble with the theme test cases. I am able to add the MuiListItemButton object, but it is being adding as a child to MuiListItem. Do you have any suggestions for this?

fn({
MuiListItem: {
defaultProps: {
anotherProp: 'value'
-    },
-
-    MuiListItemButton: {
-      defaultProps: {
-        anotherProp: 'value',
-        autoFocus: true
-      }
}
+  },
+  MuiListItemButton: {
+    defaultProps: {
+      anotherProp: 'value',
+      autoFocus: true
+    }
}
});

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Apr 22, 2024
Copy link
Member

@DiegoAndai DiegoAndai left a 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Author

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?

Copy link
Member

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?

Copy link
Author

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) => {
Copy link
Member

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 😊

Copy link
Author

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!

@github-actions github-actions bot added PR: out-of-date The pull request has merge conflicts and can't be merged and removed PR: out-of-date The pull request has merge conflicts and can't be merged labels Jun 13, 2024
@github-actions github-actions bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged label Jun 16, 2024
@thathva
Copy link
Author

thathva commented Jun 17, 2024

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!

@github-actions github-actions bot added the PR: out-of-date The pull request has merge conflicts and can't be merged label Jun 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: list This is the name of the generic UI component, not the React module! package: material-ui Specific to @mui/material PR: out-of-date The pull request has merge conflicts and can't be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants