Skip to content

Commit

Permalink
[material-ui][Tab] Deprecate iconWrapper class for icon class (#42647)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 committed Jun 18, 2024
1 parent f4fa2a9 commit d3ef601
Show file tree
Hide file tree
Showing 18 changed files with 368 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1465,6 +1465,39 @@ Here's how to migrate:
},
```

## Tab

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

```bash
npx @mui/codemod@next deprecations/tab-classes <path>
```

### Composed CSS classes

The `iconWrapper` class is removed.

Here's how to migrate:

```diff
- .MuiTab-iconWrapper
+ .MuiTab-icon
```

```diff
import { tabClasses } from '@mui/material/Tab';

MuiTab: {
styleOverrides: {
root: {
- [`& .${tabClasses.iconWrapper}`]: {
+ [`& .${tabClasses.icon}`]: {
color: 'red',
},
},
},
```

## TableSortLabel

Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#table-sort-label-classes) below to migrate the code as described in the following sections:
Expand Down
9 changes: 8 additions & 1 deletion docs/pages/material-ui/api/tab.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@
"description": "Styles applied to the root element if `fullWidth={true}` (controlled by the Tabs component).",
"isGlobal": false
},
{
"key": "icon",
"className": "MuiTab-icon",
"description": "Styles applied to the `icon` HTML element if both `icon` and `label` are provided.",
"isGlobal": false
},
{
"key": "iconWrapper",
"className": "MuiTab-iconWrapper",
"description": "Styles applied to the `icon` HTML element if both `icon` and `label` are provided.",
"isGlobal": false
"isGlobal": false,
"isDeprecated": true
},
{
"key": "labelIcon",
Expand Down
8 changes: 7 additions & 1 deletion docs/translations/api-docs/tab/tab.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@
"nodeName": "the root element",
"conditions": "<code>fullWidth={true}</code> (controlled by the Tabs component)"
},
"iconWrapper": {
"icon": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the <code>icon</code> HTML element",
"conditions": "both <code>icon</code> and <code>label</code> are provided"
},
"iconWrapper": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the <code>icon</code> HTML element",
"conditions": "both <code>icon</code> and <code>label</code> are provided",
"deprecationInfo": "Use <code>icon</code> class instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details"
},
"labelIcon": {
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
"nodeName": "the root element",
Expand Down
29 changes: 29 additions & 0 deletions packages/mui-codemod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1510,6 +1510,35 @@ CSS transforms:
npx @mui/codemod@next deprecations/step-connector-classes <path>
```

#### `tab-classes`

JS transforms:

```diff
import { tabClasses } from '@mui/material/Tab';

MuiTab: {
styleOverrides: {
root: {
- [`& .${tabClasses.iconWrapper}`]: {
+ [`& .${tabClasses.icon}`]: {
color: 'red',
},
},
},
```

CSS transforms:

```diff
- .MuiTab-iconWrapper
+ .MuiTab-icon
```

```bash
npx @mui/codemod@next deprecations/tab-classes <path>
```

#### `table-sort-label-classes`

JS transforms:
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-codemod/src/deprecations/all/deprecations-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import transformTableSortLabelClasses from '../table-sort-label-classes';
import transformStepConnectorClasses from '../step-connector-classes';
import transformStepLabelProps from '../step-label-props';
import transformTextFieldProps from '../text-field-props';
import transformTabClasses from '../tab-classes';
import transformToggleButtonGroupClasses from '../toggle-button-group-classes';

/**
Expand Down Expand Up @@ -55,6 +56,7 @@ export default function deprecationsAll(file, api, options) {
file.source = transformStepLabelProps(file, api, options);
file.source = transformTableSortLabelClasses(file, api, options);
file.source = transformTextFieldProps(file, api, options);
file.source = transformTabClasses(file, api, options);
file.source = transformToggleButtonGroupClasses(file, api, options);

return file.source;
Expand Down
2 changes: 2 additions & 0 deletions packages/mui-codemod/src/deprecations/all/postcss.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
const {
plugin: circularProgressClassesPlugin,
} = require('../circular-progress-classes/postcss-plugin');
const { plugin: tabClassesPlugin } = require('../tab-classes/postcss-plugin');
const {
plugin: tableSortLabelClassesPlugin,
} = require('../table-sort-label-classes/postcss-plugin');
Expand All @@ -30,6 +31,7 @@ module.exports = {
paginationItemClassesPlugin,
stepConnectorClassesPlugin,
toggleButtonGroupClassesPlugin,
tabClassesPlugin,
tableSortLabelClassesPlugin,
],
};
1 change: 1 addition & 0 deletions packages/mui-codemod/src/deprecations/tab-classes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './tab-classes';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const classes = [
{
deprecatedClass: ' .MuiTab-iconWrapper',
replacementSelector: ' .MuiTab-icon',
},
];

const plugin = () => {
return {
postcssPlugin: `Replace deprecated Tab classes with new classes`,
Rule(rule) {
const { selector } = rule;

classes.forEach(({ deprecatedClass, replacementSelector }) => {
const selectorRegex = new RegExp(`${deprecatedClass}$`);

if (selector.match(selectorRegex)) {
rule.selector = selector.replace(selectorRegex, replacementSelector);
}
});
},
};
};
plugin.postcss = true;

module.exports = {
plugin,
classes,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const { plugin } = require('./postcss-plugin');

module.exports = {
plugins: [plugin],
};
80 changes: 80 additions & 0 deletions packages/mui-codemod/src/deprecations/tab-classes/tab-classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { classes } from './postcss-plugin';

/**
* @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;
classes.forEach(({ deprecatedClass, replacementSelector }) => {
const replacementSelectorPrefix = '&';
root
.find(j.ImportDeclaration)
.filter((path) => path.node.source.value.match(/^@mui\/material\/Tab$/))
.forEach((path) => {
path.node.specifiers.forEach((specifier) => {
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === 'tabClasses') {
const deprecatedAtomicClass = deprecatedClass.replace(
`${deprecatedClass.split('-')[0]}-`,
'',
);
root
.find(j.MemberExpression, {
object: { name: specifier.local.name },
property: { name: deprecatedAtomicClass },
})
.forEach((memberExpression) => {
const parent = memberExpression.parentPath.parentPath.value;
if (parent.type === j.TemplateLiteral.name) {
const memberExpressionIndex = parent.expressions.findIndex(
(expression) => expression === memberExpression.value,
);
const precedingTemplateElement = parent.quasis[memberExpressionIndex];
const atomicClasses = ['icon'];

if (
precedingTemplateElement.value.raw.endsWith(
deprecatedClass.startsWith(' ')
? `${replacementSelectorPrefix} .`
: `${replacementSelectorPrefix}.`,
)
) {
const atomicClassesArgs = [
memberExpressionIndex,
1,
...atomicClasses.map((atomicClass) =>
j.memberExpression(
memberExpression.value.object,
j.identifier(atomicClass),
),
),
];
parent.expressions.splice(...atomicClassesArgs);
}
}
});
}
});
});

const selectorRegex = new RegExp(`${replacementSelectorPrefix}${deprecatedClass}$`);
root
.find(
j.Literal,
(literal) => typeof literal.value === 'string' && literal.value.match(selectorRegex),
)
.forEach((path) => {
path.replace(
j.literal(
path.value.value.replace(
selectorRegex,
`${replacementSelectorPrefix}${replacementSelector}`,
),
),
);
});
});
return root.toSource(printOptions);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import path from 'path';
import { expect } from 'chai';
import postcss from 'postcss';
import { jscodeshift } from '../../../testUtils';
import jsTransform from './tab-classes';
import { plugin as postcssPlugin } from './postcss-plugin';
import readFile from '../../util/readFile';

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

const postcssProcessor = postcss([postcssPlugin]);

describe('@mui/codemod', () => {
describe('deprecations', () => {
describe('toggle-button-group-classes', () => {
describe('js-transform', () => {
it('transforms props as needed', () => {
const actual = jsTransform(
{ source: read('./test-cases/actual.js') },
{ jscodeshift },
{ printOptions: { quote: 'double', trailingComma: true } },
);

const expected = read('./test-cases/expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});

it('should be idempotent', () => {
const actual = jsTransform(
{ source: read('./test-cases/expected.js') },
{ jscodeshift },
{},
);

const expected = read('./test-cases/expected.js');
expect(actual).to.equal(expected, 'The transformed version should be correct');
});
});

describe('css-transform', () => {
it('transforms classes as needed', async () => {
const actual = await postcssProcessor.process(read('./test-cases/actual.css'), {
from: undefined,
});

const expected = read('./test-cases/expected.css');
expect(actual.css).to.equal(expected, 'The transformed version should be correct');
});

it('should be idempotent', async () => {
const actual = await postcssProcessor.process(read('./test-cases/expected.css'), {
from: undefined,
});

const expected = read('./test-cases/expected.css');
expect(actual.css).to.equal(expected, 'The transformed version should be correct');
});
});

describe('test-cases', () => {
it('should not be the same', () => {
const actualJS = read('./test-cases/actual.js');
const expectedJS = read('./test-cases/expected.js');
expect(actualJS).not.to.equal(expectedJS, 'The actual and expected should be different');

const actualCSS = read('./test-cases/actual.css');
const expectedCSS = read('./test-cases/expected.css');
expect(actualCSS).not.to.equal(
expectedCSS,
'The actual and expected should be different',
);
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.MuiTab-root .MuiTab-iconWrapper {
color: red;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { tabClasses } from '@mui/material/Tab';

('& .MuiTab-iconWrapper');
`& .${tabClasses.iconWrapper}`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.MuiTab-root .MuiTab-icon {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { tabClasses } from '@mui/material/Tab';

("& .MuiTab-icon");
`& .${tabClasses.icon}`;

0 comments on commit d3ef601

Please sign in to comment.