diff --git a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md index e07c813aa7b6a8..d1f997e86a1ff4 100644 --- a/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md +++ b/docs/data/material/migration/migrating-from-deprecated-apis/migrating-from-deprecated-apis.md @@ -1465,6 +1465,46 @@ Here's how to migrate: }, ``` +## 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: + +```bash +npx @mui/codemod@next deprecations/table-sort-label-classes +``` + +### Composed CSS classes + +The CSS classes that composed the `direction` prop and `icon` prop were removed. + +Here's how to migrate: + +```diff +- .MuiTableSortLabel-iconDirectionDesc ++.MuiTableSortLabel-directionDesc > .MuiTableSortLabel-icon +- .MuiTableSortLabel-iconDirectionAsc ++.MuiTableSortLabel-directionAsc > .MuiTableSortLabel-icon +``` + +```diff + import { tableSortLabelClasses } from '@mui/material/TableSortLabel'; + + MuiTableSortLabel: { + styleOverrides: { + root: { +- [`& .${tableSortLabelClasses.iconDirectionDesc}`]: { ++ [`&.${tableSortLabelClasses.directionDesc} > .${tableSortLabelClasses.icon}`]: { + color: 'red', + }, +- [`& .${tableSortLabelClasses.iconDirectionAsc}`]: { ++ [`&.${tableSortLabelClasses.directionAsc} > .${tableSortLabelClasses.icon}`]: { + color: 'red', + }, + }, + }, + }, +``` + ## TextField Use the [codemod](https://github.com/mui/material-ui/tree/HEAD/packages/mui-codemod#text-field-props) below to migrate the code as described in the following sections: diff --git a/docs/pages/material-ui/api/table-sort-label.json b/docs/pages/material-ui/api/table-sort-label.json index a6b1eb65d609e3..5ad394832af712 100644 --- a/docs/pages/material-ui/api/table-sort-label.json +++ b/docs/pages/material-ui/api/table-sort-label.json @@ -29,6 +29,18 @@ "description": "State class applied to the root element if `active={true}`.", "isGlobal": true }, + { + "key": "directionAsc", + "className": "MuiTableSortLabel-directionAsc", + "description": "Styles applied to the root element if `direction=\"asc\"`.", + "isGlobal": false + }, + { + "key": "directionDesc", + "className": "MuiTableSortLabel-directionDesc", + "description": "Styles applied to the root element if `direction=\"desc\"`.", + "isGlobal": false + }, { "key": "icon", "className": "MuiTableSortLabel-icon", @@ -39,13 +51,15 @@ "key": "iconDirectionAsc", "className": "MuiTableSortLabel-iconDirectionAsc", "description": "Styles applied to the icon component if `direction=\"asc\"`.", - "isGlobal": false + "isGlobal": false, + "isDeprecated": true }, { "key": "iconDirectionDesc", "className": "MuiTableSortLabel-iconDirectionDesc", "description": "Styles applied to the icon component if `direction=\"desc\"`.", - "isGlobal": false + "isGlobal": false, + "isDeprecated": true }, { "key": "root", diff --git a/docs/translations/api-docs/table-sort-label/table-sort-label.json b/docs/translations/api-docs/table-sort-label/table-sort-label.json index 8eb717595deea1..32ff329b0747e1 100644 --- a/docs/translations/api-docs/table-sort-label/table-sort-label.json +++ b/docs/translations/api-docs/table-sort-label/table-sort-label.json @@ -19,16 +19,28 @@ "nodeName": "the root element", "conditions": "active={true}" }, + "directionAsc": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "direction=\"asc\"" + }, + "directionDesc": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "direction=\"desc\"" + }, "icon": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the icon component" }, "iconDirectionAsc": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the icon component", - "conditions": "direction=\"asc\"" + "conditions": "direction=\"asc\"", + "deprecationInfo": "Combine the .MuiTableSortLabel-icon and .MuiTableSortLabel-directionAsc classes instead. See Migrating from deprecated APIs for more details." }, "iconDirectionDesc": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the icon component", - "conditions": "direction=\"desc\"" + "conditions": "direction=\"desc\"", + "deprecationInfo": "Combine the .MuiTableSortLabel-icon and .MuiTableSortLabel-directionDesc classes instead. See Migrating from deprecated APIs for more details." }, "root": { "description": "Styles applied to the root element." } } diff --git a/packages/mui-codemod/README.md b/packages/mui-codemod/README.md index d3e6e7a71f47c9..2035f38e06f13e 100644 --- a/packages/mui-codemod/README.md +++ b/packages/mui-codemod/README.md @@ -1510,6 +1510,42 @@ CSS transforms: npx @mui/codemod@next deprecations/step-connector-classes ``` +#### `table-sort-label-classes` + +JS transforms: + +```diff + import { tableSortLabelClasses } from '@mui/material/TableSortLabel'; + + MuiTableSortLabel: { + styleOverrides: { + root: { +- [`& .${tableSortLabelClasses.iconDirectionDesc}`]: { ++ [`&.${tableSortLabelClasses.directionDesc} > .${tableSortLabelClasses.icon}`]: { + color: 'red', + }, +- [`& .${tableSortLabelClasses.iconDirectionAsc}`]: { ++ [`&.${tableSortLabelClasses.directionAsc} > .${tableSortLabelClasses.icon}`]: { + color: 'red', + }, + }, + }, + }, +``` + +CSS transforms: + +```diff +- .MuiTableSortLabel-iconDirectionDesc ++.MuiTableSortLabel-directionDesc > .MuiTableSortLabel-icon +- .MuiTableSortLabel-iconDirectionAsc ++.MuiTableSortLabel-directionAsc > .MuiTableSortLabel-icon +``` + +```bash +npx @mui/codemod@next deprecations/table-sort-label-classes +``` + ### v6.0.0 #### `sx-prop` diff --git a/packages/mui-codemod/src/deprecations/all/deprecations-all.js b/packages/mui-codemod/src/deprecations/all/deprecations-all.js index 4240ca9f6076a0..0b3032fd536a05 100644 --- a/packages/mui-codemod/src/deprecations/all/deprecations-all.js +++ b/packages/mui-codemod/src/deprecations/all/deprecations-all.js @@ -19,6 +19,7 @@ import transformModalProps from '../modal-props'; import transformOutlinedInputProps from '../outlined-input-props'; import transformPaginationItemClasses from '../pagination-item-classes'; import transformSpeedDialProps from '../speed-dial-props'; +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'; @@ -52,6 +53,7 @@ export default function deprecationsAll(file, api, options) { file.source = transformSpeedDialProps(file, api, options); file.source = transformStepConnectorClasses(file, api, options); file.source = transformStepLabelProps(file, api, options); + file.source = transformTableSortLabelClasses(file, api, options); file.source = transformTextFieldProps(file, api, options); file.source = transformToggleButtonGroupClasses(file, api, options); diff --git a/packages/mui-codemod/src/deprecations/all/postcss.config.js b/packages/mui-codemod/src/deprecations/all/postcss.config.js index 6d6e4477e75161..889555e7773c33 100644 --- a/packages/mui-codemod/src/deprecations/all/postcss.config.js +++ b/packages/mui-codemod/src/deprecations/all/postcss.config.js @@ -15,6 +15,9 @@ const { const { plugin: circularProgressClassesPlugin, } = require('../circular-progress-classes/postcss-plugin'); +const { + plugin: tableSortLabelClassesPlugin, +} = require('../table-sort-label-classes/postcss-plugin'); module.exports = { plugins: [ @@ -27,5 +30,6 @@ module.exports = { paginationItemClassesPlugin, stepConnectorClassesPlugin, toggleButtonGroupClassesPlugin, + tableSortLabelClassesPlugin, ], }; diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/index.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/index.js new file mode 100644 index 00000000000000..81981fae05219d --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/index.js @@ -0,0 +1 @@ +export { default } from './table-sort-label-classes'; diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss-plugin.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss-plugin.js new file mode 100644 index 00000000000000..22a5b5f11d4242 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss-plugin.js @@ -0,0 +1,33 @@ +const classes = [ + { + deprecatedClass: ' .MuiTableSortLabel-iconDirectionDesc', + replacementSelector: '.MuiTableSortLabel-directionDesc > .MuiTableSortLabel-icon', + }, + { + deprecatedClass: ' .MuiTableSortLabel-iconDirectionAsc', + replacementSelector: '.MuiTableSortLabel-directionAsc > .MuiTableSortLabel-icon', + }, +]; + +const plugin = () => { + return { + postcssPlugin: `Replace deprecated TableSortLabel 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, +}; diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss.config.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss.config.js new file mode 100644 index 00000000000000..23bebc1125be6e --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/postcss.config.js @@ -0,0 +1,5 @@ +const { plugin } = require('./postcss-plugin'); + +module.exports = { + plugins: [plugin], +}; diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.js new file mode 100644 index 00000000000000..2c575e3d54bad3 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.js @@ -0,0 +1,127 @@ +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\/TableSortLabel$/)) + .forEach((path) => { + path.node.specifiers.forEach((specifier) => { + if ( + specifier.type === 'ImportSpecifier' && + specifier.imported.name === 'tableSortLabelClasses' + ) { + 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 = replacementSelector + .replaceAll('MuiTableSortLabel-', '') + .replaceAll(replacementSelectorPrefix, '') + .replaceAll(' > ', '') + .split('.') + .filter(Boolean); + + 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); + + if (replacementSelector.includes(' > ')) { + const quasisArgs = [ + memberExpressionIndex, + 1, + j.templateElement( + { + raw: precedingTemplateElement.value.raw.replace(' ', ''), + cooked: precedingTemplateElement.value.cooked.replace(' ', ''), + }, + false, + ), + j.templateElement({ raw: ' > .', cooked: ' > .' }, false), + ]; + + if (atomicClasses.length === 3) { + quasisArgs.splice( + 3, + 0, + j.templateElement({ raw: '.', cooked: '.' }, false), + ); + } + + parent.quasis.splice(...quasisArgs); + } else { + parent.quasis.splice( + memberExpressionIndex, + 1, + j.templateElement( + { + raw: precedingTemplateElement.value.raw, + cooked: precedingTemplateElement.value.cooked, + }, + false, + ), + + j.templateElement({ raw: '.', cooked: '.' }, false), + ); + } + } + } + }); + } + }); + }); + + 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); +} diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.test.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.test.js new file mode 100644 index 00000000000000..69dd52f93ce90f --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/table-sort-label-classes.test.js @@ -0,0 +1,78 @@ +import path from 'path'; +import { expect } from 'chai'; +import postcss from 'postcss'; +import { jscodeshift } from '../../../testUtils'; +import jsTransform from './table-sort-label-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', + ); + }); + }); + }); + }); +}); diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.css b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.css new file mode 100644 index 00000000000000..007e0b170e2d39 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.css @@ -0,0 +1,7 @@ +.MuiTableSortLabel-root .MuiTableSortLabel-iconDirectionDesc { + color: red; +} + +.MuiTableSortLabel-root .MuiTableSortLabel-iconDirectionAsc { + color: red; +} diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.js new file mode 100644 index 00000000000000..e375d29b70fb63 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/actual.js @@ -0,0 +1,6 @@ +import { tableSortLabelClasses } from '@mui/material/TableSortLabel'; + +('& .MuiTableSortLabel-iconDirectionDesc'); +('& .MuiTableSortLabel-iconDirectionAsc'); +`& .${tableSortLabelClasses.iconDirectionDesc}`; +`& .${tableSortLabelClasses.iconDirectionAsc}`; diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.css b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.css new file mode 100644 index 00000000000000..ccaaf4a27b9ce1 --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.css @@ -0,0 +1,7 @@ +.MuiTableSortLabel-root.MuiTableSortLabel-directionDesc > .MuiTableSortLabel-icon { + color: red; +} + +.MuiTableSortLabel-root.MuiTableSortLabel-directionAsc > .MuiTableSortLabel-icon { + color: red; +} diff --git a/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.js b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.js new file mode 100644 index 00000000000000..41d90b56c056dd --- /dev/null +++ b/packages/mui-codemod/src/deprecations/table-sort-label-classes/test-cases/expected.js @@ -0,0 +1,6 @@ +import { tableSortLabelClasses } from '@mui/material/TableSortLabel'; + +("&.MuiTableSortLabel-directionDesc > .MuiTableSortLabel-icon"); +("&.MuiTableSortLabel-directionAsc > .MuiTableSortLabel-icon"); +`&.${tableSortLabelClasses.directionDesc} > .${tableSortLabelClasses.icon}`; +`&.${tableSortLabelClasses.directionAsc} > .${tableSortLabelClasses.icon}`; diff --git a/packages/mui-material/src/TableSortLabel/TableSortLabel.js b/packages/mui-material/src/TableSortLabel/TableSortLabel.js index 25d11cb4f40ca9..67da18573c774b 100644 --- a/packages/mui-material/src/TableSortLabel/TableSortLabel.js +++ b/packages/mui-material/src/TableSortLabel/TableSortLabel.js @@ -15,7 +15,7 @@ const useUtilityClasses = (ownerState) => { const { classes, direction, active } = ownerState; const slots = { - root: ['root', active && 'active'], + root: ['root', active && 'active', `direction${capitalize(direction)}`], icon: ['icon', `iconDirection${capitalize(direction)}`], }; diff --git a/packages/mui-material/src/TableSortLabel/TableSortLabel.test.js b/packages/mui-material/src/TableSortLabel/TableSortLabel.test.js index 6865b0907d7e3e..af153e529d721c 100644 --- a/packages/mui-material/src/TableSortLabel/TableSortLabel.test.js +++ b/packages/mui-material/src/TableSortLabel/TableSortLabel.test.js @@ -46,6 +46,10 @@ describe('', () => { const icon = container.querySelector(`.${classes.icon}`); expect(icon).not.to.have.class(classes.iconDirectionAsc); expect(icon).to.have.class(classes.iconDirectionDesc); + expect(container.firstChild).to.have.class(classes.directionDesc); + expect(container.querySelector(`.${classes.directionDesc} > .${classes.icon}`)).not.equal( + null, + ); }); it('when given direction asc should have asc direction class', () => { @@ -53,6 +57,10 @@ describe('', () => { const icon = container.querySelector(`.${classes.icon}`); expect(icon).not.to.have.class(classes.iconDirectionDesc); expect(icon).to.have.class(classes.iconDirectionAsc); + expect(container.firstChild).to.have.class(classes.directionAsc); + expect(container.querySelector(`.${classes.directionAsc} > .${classes.icon}`)).not.equal( + null, + ); }); it('should accept a custom icon for the sort icon', () => { diff --git a/packages/mui-material/src/TableSortLabel/tableSortLabelClasses.ts b/packages/mui-material/src/TableSortLabel/tableSortLabelClasses.ts index 7f50799802a8d4..2508178c9f7a64 100644 --- a/packages/mui-material/src/TableSortLabel/tableSortLabelClasses.ts +++ b/packages/mui-material/src/TableSortLabel/tableSortLabelClasses.ts @@ -4,13 +4,21 @@ import generateUtilityClass from '@mui/utils/generateUtilityClass'; export interface TableSortLabelClasses { /** Styles applied to the root element. */ root: string; + /** Styles applied to the root element if `direction="desc"`. */ + directionDesc: string; + /** Styles applied to the root element if `direction="asc"`. */ + directionAsc: string; /** State class applied to the root element if `active={true}`. */ active: string; /** Styles applied to the icon component. */ icon: string; - /** Styles applied to the icon component if `direction="desc"`. */ + /** Styles applied to the icon component if `direction="desc"`. + * @deprecated Combine the [.MuiTableSortLabel-icon](/material-ui/api/table-sort-label/#table-sort-label-classes-icon) and [.MuiTableSortLabel-directionDesc](/material-ui/api/table-sort-label/#table-sort-label-classes-direction-desc) classes instead. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details. + */ iconDirectionDesc: string; - /** Styles applied to the icon component if `direction="asc"`. */ + /** Styles applied to the icon component if `direction="asc"`. + * @deprecated Combine the [.MuiTableSortLabel-icon](/material-ui/api/table-sort-label/#table-sort-label-classes-icon) and [.MuiTableSortLabel-directionAsc](/material-ui/api/table-sort-label/#table-sort-label-classes-direction-asc) classes instead. See [Migrating from deprecated APIs](/material-ui/migration/migrating-from-deprecated-apis/) for more details. + */ iconDirectionAsc: string; } @@ -26,6 +34,8 @@ const tableSortLabelClasses: TableSortLabelClasses = generateUtilityClasses('Mui 'icon', 'iconDirectionDesc', 'iconDirectionAsc', + 'directionDesc', + 'directionAsc', ]); export default tableSortLabelClasses;