Skip to content

Commit d817aa1

Browse files
authored
[material-ui][Dialog] Deprecate composed classes (#45418)
1 parent 9b95ba4 commit d817aa1

File tree

16 files changed

+378
-6
lines changed

16 files changed

+378
-6
lines changed

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

+40
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,46 @@ The Divider's `light` prop was deprecated, Use `sx={{ opacity : "0.6" }}` (or an
11341134
/>
11351135
```
11361136

1137+
## Dialog
1138+
1139+
Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#dialog-classes) below to migrate the code as described in the following sections:
1140+
1141+
```bash
1142+
npx @mui/codemod@latest deprecations/dialog-classes <path>
1143+
```
1144+
1145+
### Composed CSS classes
1146+
1147+
The CSS classes composing the `scroll` prop values have been removed.
1148+
1149+
Here's how to migrate:
1150+
1151+
```diff
1152+
-.MuiDialog-root .MuiDialog-paperScrollBody
1153+
+.MuiDialog-root .MuiDialog-scrollBody > .MuiDialog-paper
1154+
-.MuiDialog-root .MuiDialog-paperScrollPaper
1155+
+.MuiDialog-root .MuiDialog-scrollPaper > .MuiDialog-paper
1156+
```
1157+
1158+
```diff
1159+
import { dialogClasses } from '@mui/material/Dialog';
1160+
1161+
MuiDialog: {
1162+
styleOverrides: {
1163+
root: {
1164+
- [`& .${dialogClasses.paperScrollBody}`]: {
1165+
+ [`& .${dialogClasses.scrollBody} > .${dialogClasses.paper}`]: {
1166+
color: 'red',
1167+
},
1168+
- [`& .${dialogClasses.paperScrollPaper}`]: {
1169+
+ [`& .${dialogClasses.scrollPaper} > .${dialogClasses.paper}`]: {
1170+
color: 'red',
1171+
},
1172+
},
1173+
},
1174+
},
1175+
```
1176+
11371177
## Drawer
11381178

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

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,15 @@
137137
"key": "paperScrollBody",
138138
"className": "MuiDialog-paperScrollBody",
139139
"description": "Styles applied to the Paper component if `scroll=\"body\"`.",
140-
"isGlobal": false
140+
"isGlobal": false,
141+
"isDeprecated": true
141142
},
142143
{
143144
"key": "paperScrollPaper",
144145
"className": "MuiDialog-paperScrollPaper",
145146
"description": "Styles applied to the Paper component if `scroll=\"paper\"`.",
146-
"isGlobal": false
147+
"isGlobal": false,
148+
"isDeprecated": true
147149
},
148150
{
149151
"key": "paperWidthFalse",

docs/translations/api-docs/dialog/dialog.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,14 @@
6363
"paperScrollBody": {
6464
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
6565
"nodeName": "the Paper component",
66-
"conditions": "<code>scroll=\"body\"</code>"
66+
"conditions": "<code>scroll=\"body\"</code>",
67+
"deprecationInfo": "Combine the <a href=\"/material-ui/api/dialog/#dialog-classes-paper\">.MuiDialog-paper</a> and <a href=\"/material-ui/api/dialog/#dialog-classes-scrollBody\">.MuiDialog-scrollBody</a> classes instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
6768
},
6869
"paperScrollPaper": {
6970
"description": "Styles applied to {{nodeName}} if {{conditions}}.",
7071
"nodeName": "the Paper component",
71-
"conditions": "<code>scroll=\"paper\"</code>"
72+
"conditions": "<code>scroll=\"paper\"</code>",
73+
"deprecationInfo": "Combine the <a href=\"/material-ui/api/dialog/#dialog-classes-paper\">.MuiDialog-paper</a> and <a href=\"/material-ui/api/dialog/#dialog-classes-scrollPaper\">.MuiDialog-scrollPaper</a> classes instead. See <a href=\"/material-ui/migration/migrating-from-deprecated-apis/\">Migrating from deprecated APIs</a> for more details."
7274
},
7375
"paperWidthFalse": {
7476
"description": "Styles applied to {{nodeName}} if {{conditions}}.",

packages/mui-codemod/README.md

+36
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,42 @@ npx @mui/codemod@latest deprecations/circular-progress-classes <path>
10371037
npx @mui/codemod@latest deprecations/divider-props <path>
10381038
```
10391039

1040+
#### `dialog-classes`
1041+
1042+
JS transforms:
1043+
1044+
```diff
1045+
import { dialogClasses } from '@mui/material/Dialog';
1046+
1047+
MuiDialog: {
1048+
styleOverrides: {
1049+
root: {
1050+
- [`& .${dialogClasses.paperScrollBody}`]: {
1051+
+ [`& .${dialogClasses.scrollBody} > .${dialogClasses.paper}`]: {
1052+
color: 'red',
1053+
},
1054+
- [`& .${dialogClasses.paperScrollPaper}`]: {
1055+
+ [`& .${dialogClasses.scrollPaper} > .${dialogClasses.paper}`]: {
1056+
color: 'red',
1057+
},
1058+
},
1059+
},
1060+
},
1061+
```
1062+
1063+
CSS transforms:
1064+
1065+
```diff
1066+
-.MuiDialog-root .MuiDialog-paperScrollBody
1067+
+.MuiDialog-root .MuiDialog-scrollBody > .MuiDialog-paper
1068+
-.MuiDialog-root .MuiDialog-paperScrollPaper
1069+
+.MuiDialog-root .MuiDialog-scrollPaper > .MuiDialog-paper
1070+
```
1071+
1072+
```bash
1073+
npx @mui/codemod@latest deprecations/dialog-classes <path>
1074+
```
1075+
10401076
#### `drawer-classes`
10411077

10421078
JS transforms:

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

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import transformChipClasses from '../chip-classes';
1212
import transformCircularProgressClasses from '../circular-progress-classes';
1313
import transformDividerProps from '../divider-props';
1414
import transformDrawerClasses from '../drawer-classes';
15+
import transformDialogClasses from '../dialog-classes';
1516
import transformFilledInputProps from '../filled-input-props';
1617
import transformFormControlLabelProps from '../form-control-label-props';
1718
import transformImageListItemBarClasses from '../image-list-item-bar-classes';
@@ -68,6 +69,7 @@ export default function deprecationsAll(file, api, options) {
6869
file.source = transformCircularProgressClasses(file, api, options);
6970
file.source = transformDividerProps(file, api, options);
7071
file.source = transformDrawerClasses(file, api, options);
72+
file.source = transformDialogClasses(file, api, options);
7173
file.source = transformFilledInputProps(file, api, options);
7274
file.source = transformFormControlLabelProps(file, api, options);
7375
file.source = transformImageListItemBarClasses(file, api, options);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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\/Dialog$/))
16+
.forEach((path) => {
17+
path.node.specifiers.forEach((specifier) => {
18+
if (specifier.type === 'ImportSpecifier' && specifier.imported.name === 'dialogClasses') {
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('MuiDialog-', '')
37+
.replaceAll(replacementSelectorPrefix, '')
38+
.replaceAll(' > ', '')
39+
.replaceAll(' ', '')
40+
.split('.')
41+
.filter(Boolean);
42+
43+
if (
44+
precedingTemplateElement.value.raw.endsWith(
45+
deprecatedClass.startsWith(' ')
46+
? `${replacementSelectorPrefix} .`
47+
: `${replacementSelectorPrefix}.`,
48+
)
49+
) {
50+
parent.expressions.splice(
51+
memberExpressionIndex,
52+
1,
53+
j.memberExpression(
54+
memberExpression.value.object,
55+
j.identifier(atomicClasses[0]),
56+
),
57+
58+
j.memberExpression(
59+
memberExpression.value.object,
60+
j.identifier(atomicClasses[1]),
61+
),
62+
);
63+
64+
if (replacementSelector.includes(' > ')) {
65+
parent.quasis.splice(
66+
memberExpressionIndex,
67+
1,
68+
j.templateElement(
69+
{
70+
raw: precedingTemplateElement.value.raw,
71+
cooked: precedingTemplateElement.value.cooked,
72+
},
73+
false,
74+
),
75+
j.templateElement({ raw: ' > .', cooked: ' > .' }, false),
76+
);
77+
} else {
78+
parent.quasis.splice(
79+
memberExpressionIndex,
80+
1,
81+
j.templateElement(
82+
{
83+
raw: precedingTemplateElement.value.raw,
84+
cooked: precedingTemplateElement.value.cooked,
85+
},
86+
false,
87+
),
88+
89+
j.templateElement({ raw: '.', cooked: '.' }, false),
90+
);
91+
}
92+
}
93+
}
94+
});
95+
}
96+
});
97+
});
98+
99+
const selectorRegex = new RegExp(`^${replacementSelectorPrefix}${deprecatedClass}`);
100+
root
101+
.find(
102+
j.Literal,
103+
(literal) => typeof literal.value === 'string' && literal.value.match(selectorRegex),
104+
)
105+
.forEach((path) => {
106+
path.replace(
107+
j.literal(
108+
path.value.value.replace(
109+
selectorRegex,
110+
`${replacementSelectorPrefix}${deprecatedClass.startsWith(' ') ? ' ' : ''}${replacementSelector.trim()}`,
111+
),
112+
),
113+
);
114+
});
115+
});
116+
return root.toSource(printOptions);
117+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import path from 'path';
2+
import { expect } from 'chai';
3+
import postcss from 'postcss';
4+
import { jscodeshift } from '../../../testUtils';
5+
import jsTransform from './dialog-classes';
6+
import { plugin as postcssPlugin } from './postcss-plugin';
7+
import readFile from '../../util/readFile';
8+
9+
function read(fileName) {
10+
return readFile(path.join(__dirname, fileName));
11+
}
12+
13+
const postcssProcessor = postcss([postcssPlugin]);
14+
15+
describe('@mui/codemod', () => {
16+
describe('deprecations', () => {
17+
describe('dialog-classes', () => {
18+
describe('js-transform', () => {
19+
it('transforms props as needed', () => {
20+
const actual = jsTransform(
21+
{ source: read('./test-cases/actual.js') },
22+
{ jscodeshift },
23+
{ printOptions: { quote: 'double', trailingComma: true } },
24+
);
25+
26+
const expected = read('./test-cases/expected.js');
27+
expect(actual).to.equal(expected, 'The transformed version should be correct');
28+
});
29+
30+
it('should be idempotent', () => {
31+
const actual = jsTransform(
32+
{ source: read('./test-cases/expected.js') },
33+
{ jscodeshift },
34+
{},
35+
);
36+
37+
const expected = read('./test-cases/expected.js');
38+
expect(actual).to.equal(expected, 'The transformed version should be correct');
39+
});
40+
});
41+
42+
describe('css-transform', () => {
43+
it('transforms classes as needed', async () => {
44+
const actual = await postcssProcessor.process(read('./test-cases/actual.css'), {
45+
from: undefined,
46+
});
47+
48+
const expected = read('./test-cases/expected.css');
49+
expect(actual.css).to.equal(expected, 'The transformed version should be correct');
50+
});
51+
52+
it('should be idempotent', async () => {
53+
const actual = await postcssProcessor.process(read('./test-cases/expected.css'), {
54+
from: undefined,
55+
});
56+
57+
const expected = read('./test-cases/expected.css');
58+
expect(actual.css).to.equal(expected, 'The transformed version should be correct');
59+
});
60+
});
61+
62+
describe('test-cases', () => {
63+
it('should not be the same', () => {
64+
const actualJS = read('./test-cases/actual.js');
65+
const expectedJS = read('./test-cases/expected.js');
66+
expect(actualJS).not.to.equal(expectedJS, 'The actual and expected should be different');
67+
68+
const actualCSS = read('./test-cases/actual.css');
69+
const expectedCSS = read('./test-cases/expected.css');
70+
expect(actualCSS).not.to.equal(
71+
expectedCSS,
72+
'The actual and expected should be different',
73+
);
74+
});
75+
});
76+
});
77+
});
78+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from './dialog-classes';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const classes = [
2+
{
3+
deprecatedClass: ' .MuiDialog-paperScrollBody',
4+
replacementSelector: ' .MuiDialog-scrollBody > .MuiDialog-paper',
5+
},
6+
{
7+
deprecatedClass: ' .MuiDialog-paperScrollPaper',
8+
replacementSelector: ' .MuiDialog-scrollPaper > .MuiDialog-paper',
9+
},
10+
];
11+
12+
const plugin = () => {
13+
return {
14+
postcssPlugin: `Replace deprecated Dialog classes with new classes`,
15+
Rule(rule) {
16+
const { selector } = rule;
17+
18+
classes.forEach(({ deprecatedClass, replacementSelector }) => {
19+
const selectorRegex = new RegExp(`${deprecatedClass}`);
20+
21+
if (selector.match(selectorRegex)) {
22+
rule.selector = selector.replace(selectorRegex, replacementSelector);
23+
}
24+
});
25+
},
26+
};
27+
};
28+
plugin.postcss = true;
29+
30+
module.exports = {
31+
plugin,
32+
classes,
33+
};
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+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.MuiDialog-root .MuiDialog-paperScrollBody {
2+
color: red;
3+
}
4+
5+
.MuiDialog-root .MuiDialog-paperScrollPaper {
6+
color: red;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { dialogClasses } from '@mui/material/Dialog';
2+
3+
('& .MuiDialog-paperScrollBody');
4+
('& .MuiDialog-paperScrollPaper');
5+
6+
`& .${dialogClasses.paperScrollBody}`;
7+
`& .${dialogClasses.paperScrollPaper}`;

0 commit comments

Comments
 (0)