Skip to content

Commit

Permalink
[Avatar][joy] Remove imgProps prop and add Codemod script for migra…
Browse files Browse the repository at this point in the history
…tion (#35859)
  • Loading branch information
hbjORbj authored Jan 23, 2023
1 parent 7c1c73f commit 07d6161
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 16 deletions.
18 changes: 18 additions & 0 deletions packages/mui-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,24 @@ npx @mui/codemod <transform> <path> --jscodeshift="--printOptions='{\"quote\":\"

### v5.0.0

#### `joy-avatar-remove-imgProps`

Remove `imgProps` prop by transferring its value into `slotProps.img`

This change only affects Joy UI Avatar component.

```diff
<Avatar
- imgProps={{ ['data-id']: 'imageId' }}
- slotProps={{ root: { ['data-id']: 'rootId' }}}
+ slotProps={{ root: { ['data-id']: 'rootId' }, img: { ['data-id']: 'imageId' } }}
/>;
```

```sh
npx @mui/codemod v5.0.0/joy-avatar-remove-imgProps <path>
```

#### `joy-text-field-to-input`

Replace `<TextField>` with composition of `Input`
Expand Down
92 changes: 92 additions & 0 deletions packages/mui-codemod/src/v5.0.0/joy-avatar-remove-imgProps.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @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;

root
.find(j.ImportDeclaration)
.filter(({ node }) => {
const sourceVal = node.source.value;

return [
'@mui/joy', // Process only Joy UI components
'@mui/joy/Avatar', // Filter default imports of components other than `Avatar`
].includes(sourceVal);
})
.forEach((path) => {
path.node.specifiers.forEach((elementNode) => {
if (
(elementNode.type === 'ImportSpecifier' && elementNode.imported?.name === 'Avatar') ||
elementNode.type === 'ImportDefaultSpecifier'
) {
// Process only Joy `Avatar` component
root.findJSXElements(elementNode.local.name).forEach((elementPath) => {
if (elementPath.node.type !== 'JSXElement') {
return;
}

const slotPropsAttributeNode = elementPath.node.openingElement.attributes.find(
(attributeNode) =>
attributeNode.type === 'JSXAttribute' &&
attributeNode.name.name === 'slotProps' &&
attributeNode.value.expression?.type === 'ObjectExpression',
);
const newAttributeNodes = [];
elementPath.node.openingElement.attributes.forEach((attributeNode) => {
if (attributeNode.type !== 'JSXAttribute') {
return;
}

if (attributeNode.name.name !== 'imgProps') {
newAttributeNodes.push(attributeNode);
return;
}

const val = attributeNode.value;
if (!val?.expression) {
return;
}

if (slotPropsAttributeNode) {
const imgObjInSlotProps = slotPropsAttributeNode.value.expression.properties.find(
(propNode) =>
propNode.key.name === 'img' && propNode.value.type === 'ObjectExpression',
);
if (imgObjInSlotProps) {
const newProperties = [
...imgObjInSlotProps.value.properties,
...attributeNode.value.expression.properties,
];
imgObjInSlotProps.value.properties = newProperties;
} else {
slotPropsAttributeNode.value.expression.properties.push(
j.objectProperty(j.identifier('img'), attributeNode.value),
);
}
} else {
newAttributeNodes.push(
j.jsxAttribute(
j.jsxIdentifier('slotProps'),
j.jsxExpressionContainer(
j.objectExpression([
j.objectProperty(j.identifier('img'), attributeNode.value.expression),
]),
),
),
);
}
});
elementPath.node.openingElement.attributes = newAttributeNodes;
});
}
});
});

const transformed = root.findJSXElements();

return transformed.toSource(printOptions);
}
29 changes: 29 additions & 0 deletions packages/mui-codemod/src/v5.0.0/joy-avatar-remove-imgProps.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import path from 'path';
import { expect } from 'chai';
import jscodeshift from 'jscodeshift';
import transform from './joy-avatar-remove-imgProps';
import readFile from '../util/readFile';

function read(fileName) {
return readFile(path.join(__dirname, fileName));
}

describe('@mui/codemod', () => {
describe('v5.0.0', () => {
describe('joy-avatar-remove-imgProps', () => {
it('transforms `imgProps` prop to `slotProps.img`', () => {
const actual = transform(
{
source: read('./joy-avatar-remove-imgProps.test/actual.js'),
path: require.resolve('./joy-rename-components-to-slots.test/actual.js'),
},
{ jscodeshift },
{},
);

const expected = read('./joy-avatar-remove-imgProps.test/expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// the codemod should transform only Joy UI `Avatar`;
import { Avatar as JoyAvatar } from '@mui/joy';
import Avatar from '@mui/joy/Avatar';
import MaterialAvatar from '@mui/material/Avatar';

<div>
<JoyAvatar imgProps={{ ['aria-hidden']: true }} />
<Avatar
slotProps={{ root: { ['aria-hidden']: false }, img: { ['aria-label']: 'imgSlot' } }}
imgProps={{ ['aria-hidden']: true }}
/>
<MaterialAvatar imgProps={{ ['aria-hidden']: true }} />
</div>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// the codemod should transform only Joy UI `Avatar`;
import { Avatar as JoyAvatar } from '@mui/joy';
import Avatar from '@mui/joy/Avatar';
import MaterialAvatar from '@mui/material/Avatar';

<div>
<JoyAvatar slotProps={{
img: { ['aria-hidden']: true }
}} />
<Avatar
slotProps={{ root: { ['aria-hidden']: false }, img: {
['aria-label']: 'imgSlot',
['aria-hidden']: true
} }} />
<MaterialAvatar imgProps={{ ['aria-hidden']: true }} />
</div>;
1 change: 0 additions & 1 deletion packages/mui-codemod/src/v5.0.0/joy-text-field-to-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export default function transformer(file, api, options) {
}
propNode.value.properties.forEach((prop) => {
const key = prop.key.value;
// const value = prop.value.value;
const newAttributeNode = j.jsxAttribute(
j.jsxIdentifier(key),
j.jsxExpressionContainer(prop.value),
Expand Down
8 changes: 0 additions & 8 deletions packages/mui-joy/src/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ const Avatar = React.forwardRef(function Avatar(inProps, ref) {
color: colorProp = 'neutral',
size: sizeProp = 'md',
variant: variantProp = 'soft',
imgProps,
src,
srcSet,
children: childrenProp,
Expand Down Expand Up @@ -186,7 +185,6 @@ const Avatar = React.forwardRef(function Avatar(inProps, ref) {
alt,
src,
srcSet,
...imgProps,
},
className: classes.img,
elementType: AvatarImg,
Expand All @@ -203,7 +201,6 @@ const Avatar = React.forwardRef(function Avatar(inProps, ref) {

// Use a hook instead of onError on the img element to support server-side rendering.
const loaded = useLoaded({
...imgProps,
...imageProps,
src,
srcSet,
Expand Down Expand Up @@ -248,11 +245,6 @@ Avatar.propTypes /* remove-proptypes */ = {
PropTypes.oneOf(['danger', 'info', 'neutral', 'primary', 'success', 'warning']),
PropTypes.string,
]),
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) applied to the `img` element if the component is used to display an image.
* It can be used to listen for the loading error event.
*/
imgProps: PropTypes.object,
/**
* The size of the component.
* It accepts theme values between 'sm' and 'lg'.
Expand Down
7 changes: 0 additions & 7 deletions packages/mui-joy/src/Avatar/AvatarProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ export interface AvatarTypeMap<P = {}, D extends React.ElementType = 'div'> {
* @default 'neutral'
*/
color?: OverridableStringUnion<ColorPaletteProp, AvatarPropsColorOverrides>;
/**
* [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attributes) applied to the `img` element if the component is used to display an image.
* It can be used to listen for the loading error event.
*/
imgProps?: React.ImgHTMLAttributes<HTMLImageElement> & {
sx?: SxProps;
};
/**
* The size of the component.
* It accepts theme values between 'sm' and 'lg'.
Expand Down

0 comments on commit 07d6161

Please sign in to comment.