Skip to content

Commit 18bbe40

Browse files
authored
[material-ui][Select] Deprecate composed classes (#44925)
1 parent 00c5b99 commit 18bbe40

File tree

17 files changed

+421
-9
lines changed

17 files changed

+421
-9
lines changed

docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1528,6 +1528,52 @@ The Popper's prop `componentsProps` was deprecated in favor of `slotProps`:
15281528
/>
15291529
```
15301530

1531+
## Select
1532+
1533+
Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#select-classes) below to migrate the code as described in the following sections:
1534+
1535+
```bash
1536+
npx @mui/codemod@latest deprecations/select-classes <path>
1537+
```
1538+
1539+
### Composed CSS classes
1540+
1541+
The CSS classes that composed the `icon` class and `variant` prop were removed.
1542+
1543+
Here's how to migrate:
1544+
1545+
```diff
1546+
- .MuiSelect-iconFilled
1547+
+ .MuiSelect-filled ~ .MuiSelect-icon
1548+
- .MuiSelect-iconOutlined
1549+
+ .MuiSelect-outlined ~ .MuiSelect-icon
1550+
- .MuiSelect-iconStandard
1551+
+ .MuiSelect-standard ~ .MuiSelect-icon
1552+
```
1553+
1554+
```diff
1555+
import { selectClasses } from '@mui/material/Select';
1556+
1557+
MuiSelect: {
1558+
styleOverrides: {
1559+
root: {
1560+
- [`& .${selectClasses.iconFilled}`]: {
1561+
+ [`& .${selectClasses.filled} ~ .${selectClasses.icon}`]: {
1562+
color: 'red',
1563+
},
1564+
- [`& .${selectClasses.iconOutlined}`]: {
1565+
+ [`& .${selectClasses.outlined} ~ .${selectClasses.icon}`]: {
1566+
color: 'red',
1567+
},
1568+
- [`& .${selectClasses.iconStandard}`]: {
1569+
+ [`& .${selectClasses.standard} ~ .${selectClasses.icon}`]: {
1570+
color: 'red',
1571+
},
1572+
},
1573+
},
1574+
},
1575+
```
1576+
15311577
## Slider
15321578

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

docs/pages/material-ui/api/select.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@
9696
"key": "iconFilled",
9797
"className": "MuiSelect-iconFilled",
9898
"description": "Styles applied to the icon component if `variant=\"filled\"`.",
99-
"isGlobal": false
99+
"isGlobal": false,
100+
"isDeprecated": true
100101
},
101102
{
102103
"key": "iconOpen",
@@ -108,13 +109,15 @@
108109
"key": "iconOutlined",
109110
"className": "MuiSelect-iconOutlined",
110111
"description": "Styles applied to the icon component if `variant=\"outlined\"`.",
111-
"isGlobal": false
112+
"isGlobal": false,
113+
"isDeprecated": true
112114
},
113115
{
114116
"key": "iconStandard",
115117
"className": "MuiSelect-iconStandard",
116118
"description": "Styles applied to the icon component if `variant=\"standard\"`.",
117-
"isGlobal": false
119+
"isGlobal": false,
120+
"isDeprecated": true
118121
},
119122
{
120123
"key": "multiple",

docs/translations/api-docs/select/select.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
"iconFilled": {
9898
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
9999
"nodeName": "the icon component",
100-
"conditions": "<code>variant=\"filled\"</code>"
100+
"conditions": "<code>variant=\"filled\"</code>",
101+
"deprecationInfo": "Combine the <a href=\"/material-ui/api/select/#select-classes-icon\">.MuiSelect-icon</a> and <a href=\"/material-ui/api/select/#select-classes-filled\">.MuiSelect-filled</a> classes instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
101102
},
102103
"iconOpen": {
103104
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
@@ -107,12 +108,14 @@
107108
"iconOutlined": {
108109
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
109110
"nodeName": "the icon component",
110-
"conditions": "<code>variant=\"outlined\"</code>"
111+
"conditions": "<code>variant=\"outlined\"</code>",
112+
"deprecationInfo": "Combine the <a href=\"/material-ui/api/select/#select-classes-icon\">.MuiSelect-icon</a> and <a href=\"/material-ui/api/select/#select-classes-outlined\">.MuiSelect-outlined</a> classes instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
111113
},
112114
"iconStandard": {
113115
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
114116
"nodeName": "the icon component",
115-
"conditions": "<code>variant=\"standard\"</code>"
117+
"conditions": "<code>variant=\"standard\"</code>",
118+
"deprecationInfo": "Combine the <a href=\"/material-ui/api/select/#select-classes-icon\">.MuiSelect-icon</a> and <a href=\"/material-ui/api/select/#select-classes-standard\">.MuiSelect-standard</a> classes instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
116119
},
117120
"multiple": {
118121
"description": "Styles applied to {{nodeName}} if {{conditions}}.",

packages/mui-codemod/README.md

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1406,6 +1406,54 @@ npx @mui/codemod@latest deprecations/popper-props <path>
14061406
npx @mui/codemod@latest deprecations/outlined-input-props <path>
14071407
```
14081408

1409+
#### `select-classes`
1410+
1411+
JS transforms:
1412+
1413+
```diff
1414+
import { selectClasses } from '@mui/material/Select';
1415+
1416+
MuiSelect: {
1417+
styleOverrides: {
1418+
root: {
1419+
- [`& .${selectClasses.iconFilled}`]: {
1420+
+ [`& .${selectClasses.filled} ~ .${selectClasses.icon}`]: {
1421+
color: 'red',
1422+
},
1423+
- [`& .${selectClasses.iconOutlined}`]: {
1424+
+ [`& .${selectClasses.outlined} ~ .${selectClasses.icon}`]: {
1425+
color: 'red',
1426+
},
1427+
- [`& .${selectClasses.iconStandard}`]: {
1428+
+ [`& .${selectClasses.standard} ~ .${selectClasses.icon}`]: {
1429+
color: 'red',
1430+
},
1431+
},
1432+
},
1433+
},
1434+
```
1435+
1436+
CSS transforms:
1437+
1438+
```diff
1439+
- .MuiSelect-iconFilled
1440+
+ .MuiSelect-filled ~ .MuiSelect-icon
1441+
```
1442+
1443+
```diff
1444+
- .MuiSelect-iconOutlined
1445+
+ .MuiSelect-outlined ~ .MuiSelect-icon
1446+
```
1447+
1448+
```diff
1449+
- .MuiSelect-iconStandard
1450+
+ .MuiSelect-standard ~ .MuiSelect-icon
1451+
```
1452+
1453+
```bash
1454+
npx @mui/codemod@latest deprecations/select-classes <path>
1455+
```
1456+
14091457
#### `slider-props`
14101458

14111459
```diff

packages/mui-codemod/src/deprecations/all/deprecations-all.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import transformOutlinedInputProps from '../outlined-input-props';
2121
import transformPaginationItemClasses from '../pagination-item-classes';
2222
import transformSpeedDialProps from '../speed-dial-props';
2323
import transformTableSortLabelClasses from '../table-sort-label-classes';
24+
import transformSelectClasses from '../select-classes';
2425
import transformStepConnectorClasses from '../step-connector-classes';
2526
import transformStepContentProps from '../step-content-props';
2627
import transformStepLabelProps from '../step-label-props';
@@ -62,6 +63,7 @@ export default function deprecationsAll(file, api, options) {
6263
file.source = transformStepLabelProps(file, api, options);
6364
file.source = transformTableSortLabelClasses(file, api, options);
6465
file.source = transformTextFieldProps(file, api, options);
66+
file.source = transformSelectClasses(file, api, options);
6567
file.source = transformTabClasses(file, api, options);
6668
file.source = transformToggleButtonGroupClasses(file, api, options);
6769
file.source = transformTooltipProps(file, api, options);

packages/mui-codemod/src/deprecations/all/postcss.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const { plugin: tabClassesPlugin } = require('../tab-classes/postcss-plugin');
1919
const {
2020
plugin: tableSortLabelClassesPlugin,
2121
} = require('../table-sort-label-classes/postcss-plugin');
22+
const { plugin: selectClassesPlugin } = require('../select-classes/postcss-plugin');
2223

2324
module.exports = {
2425
plugins: [
@@ -33,5 +34,6 @@ module.exports = {
3334
toggleButtonGroupClassesPlugin,
3435
tabClassesPlugin,
3536
tableSortLabelClassesPlugin,
37+
selectClassesPlugin,
3638
],
3739
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './select-classes';
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const classes = [
2+
{
3+
deprecatedClass: ' .MuiSelect-iconFilled',
4+
replacementSelector: ' .MuiSelect-filled ~ .MuiSelect-icon',
5+
},
6+
{
7+
deprecatedClass: ' .MuiSelect-iconOutlined',
8+
replacementSelector: ' .MuiSelect-outlined ~ .MuiSelect-icon',
9+
},
10+
{
11+
deprecatedClass: ' .MuiSelect-iconStandard',
12+
replacementSelector: ' .MuiSelect-standard ~ .MuiSelect-icon',
13+
},
14+
];
15+
16+
const plugin = () => {
17+
return {
18+
postcssPlugin: `Replace deprecated Select classes with new classes`,
19+
Rule(rule) {
20+
const { selector } = rule;
21+
22+
classes.forEach(({ deprecatedClass, replacementSelector }) => {
23+
const selectorRegex = new RegExp(`${deprecatedClass.trim()}$`);
24+
25+
if (selector.match(selectorRegex)) {
26+
rule.selector = selector.replace(selectorRegex, replacementSelector);
27+
}
28+
});
29+
},
30+
};
31+
};
32+
plugin.postcss = true;
33+
34+
module.exports = {
35+
plugin,
36+
classes,
37+
};
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
const { plugin } = require('./postcss-plugin');
2+
3+
module.exports = {
4+
plugins: [plugin],
5+
};
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import { classes } from './postcss-plugin';
2+
3+
/**
4+
* @param {import('jscodeshift').FileInfo} file
5+
* @param {import('jscodeshift').API} api
6+
*/
7+
export default function transformer(file, api, options) {
8+
const j = api.jscodeshift;
9+
const root = j(file.source);
10+
const printOptions = options.printOptions;
11+
classes.forEach(({ deprecatedClass, replacementSelector }) => {
12+
const replacementSelectorPrefix = '&';
13+
root
14+
.find(j.ImportDeclaration)
15+
.filter((path) => path.node.source.value.match(/^@mui\/material\/Select$/))
16+
.forEach((path) => {
17+
path.node.specifiers.forEach((specifier) => {
18+
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === 'selectClasses') {
19+
const deprecatedAtomicClass = deprecatedClass.replace(
20+
`${deprecatedClass.split('-')[0]}-`,
21+
'',
22+
);
23+
root
24+
.find(j.MemberExpression, {
25+
object: { name: specifier.local.name },
26+
property: { name: deprecatedAtomicClass },
27+
})
28+
.forEach((memberExpression) => {
29+
const parent = memberExpression.parentPath.parentPath.value;
30+
if (parent.type === j.TemplateLiteral.name) {
31+
const memberExpressionIndex = parent.expressions.findIndex(
32+
(expression) => expression === memberExpression.value,
33+
);
34+
const precedingTemplateElement = parent.quasis[memberExpressionIndex];
35+
const atomicClasses = replacementSelector
36+
.replaceAll('MuiSelect-', '')
37+
.replaceAll(replacementSelectorPrefix, '')
38+
.replaceAll(' ~ ', '')
39+
.split('.')
40+
.map((className) => className.trim())
41+
.filter(Boolean);
42+
43+
if (
44+
precedingTemplateElement.value.raw.endsWith(
45+
deprecatedClass.startsWith(' ')
46+
? `${replacementSelectorPrefix} .`
47+
: `${replacementSelectorPrefix}.`,
48+
)
49+
) {
50+
const atomicClassesArgs = [
51+
memberExpressionIndex,
52+
1,
53+
...atomicClasses.map((atomicClass) =>
54+
j.memberExpression(
55+
memberExpression.value.object,
56+
j.identifier(atomicClass),
57+
),
58+
),
59+
];
60+
parent.expressions.splice(...atomicClassesArgs);
61+
62+
if (replacementSelector.includes(' ~ ')) {
63+
const quasisArgs = [
64+
memberExpressionIndex,
65+
1,
66+
j.templateElement(
67+
{
68+
raw: precedingTemplateElement.value.raw,
69+
cooked: precedingTemplateElement.value.cooked.replace(' ', ''),
70+
},
71+
false,
72+
),
73+
j.templateElement({ raw: ' ~ .', cooked: ' ~ .' }, false),
74+
];
75+
76+
if (atomicClasses.length === 3) {
77+
quasisArgs.splice(
78+
3,
79+
0,
80+
j.templateElement({ raw: '.', cooked: '.' }, false),
81+
);
82+
}
83+
84+
parent.quasis.splice(...quasisArgs);
85+
} else {
86+
parent.quasis.splice(
87+
memberExpressionIndex,
88+
1,
89+
j.templateElement(
90+
{
91+
raw: precedingTemplateElement.value.raw,
92+
cooked: precedingTemplateElement.value.cooked,
93+
},
94+
false,
95+
),
96+
97+
j.templateElement({ raw: '.', cooked: '.' }, false),
98+
);
99+
}
100+
}
101+
}
102+
});
103+
}
104+
});
105+
});
106+
107+
const selectorRegex = new RegExp(`${replacementSelectorPrefix}${deprecatedClass}$`);
108+
root
109+
.find(
110+
j.Literal,
111+
(literal) => typeof literal.value === 'string' && literal.value.match(selectorRegex),
112+
)
113+
.forEach((path) => {
114+
path.replace(
115+
j.literal(
116+
path.value.value.replace(
117+
selectorRegex,
118+
`${replacementSelectorPrefix}${replacementSelector}`,
119+
),
120+
),
121+
);
122+
});
123+
});
124+
return root.toSource(printOptions);
125+
}

0 commit comments

Comments
 (0)