From 2414dcfe87b8bd4507361a80ab43c8d284ddc4de Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:49:16 +0100 Subject: [PATCH 01/49] [test] Add `Charts` test (#11551) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Jan Potoms <2109932+Janpot@users.noreply.github.com> --- .circleci/config.yml | 3 + .mocharc.js | 2 + package.json | 2 + packages/x-charts/.mocharc.js | 34 ++++ .../x-charts/src/LineChart/formatter.test.ts | 35 ++++ yarn.lock | 168 +++++++++++++++++- 6 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 packages/x-charts/.mocharc.js create mode 100644 packages/x-charts/src/LineChart/formatter.test.ts diff --git a/.circleci/config.yml b/.circleci/config.yml index 75d18f04b5514..49cb3d59e8a9b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -129,6 +129,9 @@ jobs: steps: - checkout - install_js + - run: + name: Tests charts + command: yarn test:charts:unit # Run special test for charts due to ESM compatibility issue - run: name: Tests fake browser command: yarn test:coverage diff --git a/.mocharc.js b/.mocharc.js index 3f1b312f2f7c5..f53ea7795363a 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -7,6 +7,8 @@ module.exports = { // Mocha seems to ignore .next anyway (maybe because dotfiles?). // We're leaving this to make sure. 'docs/.next/**', + // x-charts requires 'tsx/cjs' which conflict with the babel date-fns override for picker tests + 'packages/x-charts/**', ], recursive: true, timeout: (process.env.CIRCLECI === 'true' ? 5 : 2) * 1000, // Circle CI has low-performance CPUs. diff --git a/package.json b/package.json index d061bfed5b07d..797f43162b59b 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "test:karma": "cross-env NODE_ENV=test TZ=UTC karma start test/karma.conf.js", "test:karma:parallel": "cross-env NODE_ENV=test TZ=UTC PARALLEL=true karma start test/karma.conf.js", "test:unit": "cross-env NODE_ENV=test TZ=UTC mocha -n expose_gc", + "test:charts:unit": "cross-env NODE_ENV=test TZ=UTC mocha -n expose_gc --config packages/x-charts/.mocharc.js", "test:e2e": "cross-env NODE_ENV=production yarn test:e2e:build && concurrently --success first --kill-others \"yarn test:e2e:run\" \"yarn test:e2e:server\"", "test:e2e:build": "webpack --config test/e2e/webpack.config.js", "test:e2e:dev": "concurrently \"yarn test:e2e:build --watch\" \"yarn test:e2e:server\"", @@ -167,6 +168,7 @@ "sinon": "^16.1.3", "stream-browserify": "^3.0.0", "string-replace-loader": "^3.1.0", + "tsx": "^4.7.0", "typescript": "^5.3.3", "unist-util-visit": "^2.0.3", "util": "^0.12.5", diff --git a/packages/x-charts/.mocharc.js b/packages/x-charts/.mocharc.js new file mode 100644 index 0000000000000..8fad90b9b6f09 --- /dev/null +++ b/packages/x-charts/.mocharc.js @@ -0,0 +1,34 @@ +// We can't import the `.mocharc.js` of the monorepo, otherwise we trigger its `setupBabel`. +module.exports = { + extension: ['js', 'ts', 'tsx'], + ignore: [ + '**/build/**', + '**/node_modules/**', + // Mocha seems to ignore .next anyway (maybe because dotfiles?). + // We're leaving this to make sure. + 'docs/.next/**', + ], + recursive: true, + timeout: (process.env.CIRCLECI === 'true' ? 5 : 2) * 1000, // Circle CI has low-performance CPUs. + reporter: 'dot', + require: [ + require.resolve('../../test/utils/setupBabel'), + // Not strictly necessary, but just to keep the babel plugins in the loop for the tests + // For compiling pure ESM modules that @babel/register can't handle. + // See https://babeljs.io/docs/babel-register#experimental-babel-8-implementation + // Note: @babel/register does not support compiling native Node.js ES modules on the fly, + // since currently there is no stable API for intercepting ES modules loading. + require.resolve('tsx/cjs'), + require.resolve('../../test/utils/setupJSDOM'), + ], + 'watch-ignore': [ + // default + '.git', + // node_modules can be nested with workspaces + '**/node_modules/**', + // Unrelated directories with a large number of files + '**/build/**', + 'docs/.next/**', + ], + spec: ['packages/x-charts/**/*.test.{js,ts,tsx}'], +}; diff --git a/packages/x-charts/src/LineChart/formatter.test.ts b/packages/x-charts/src/LineChart/formatter.test.ts new file mode 100644 index 0000000000000..2066391c29abf --- /dev/null +++ b/packages/x-charts/src/LineChart/formatter.test.ts @@ -0,0 +1,35 @@ +import { expect } from 'chai'; +import { FormatterParams } from '../models/seriesType/config'; +import lineFormatter from './formatter'; + +const seriesOrder = ['id1']; +const seriesDataset: FormatterParams<'line'>['series'] = { + id1: { + // useless info + type: 'line', + id: 'id1', + color: 'red', + // usefull info + dataKey: 'k', + }, +}; + +const dataSet = [{ k: 1 }, { k: 2 }, { k: 3 }]; + +describe('LineChart - formatter', () => { + describe('data from dataset', () => { + it('should get data from the dataset', () => { + const { series } = lineFormatter({ seriesOrder, series: seriesDataset }, dataSet); + expect(series.id1.data).to.deep.equal([1, 2, 3]); + }); + + it('should support missing values', () => { + const { series } = lineFormatter({ seriesOrder, series: seriesDataset }, [ + { k: 1 }, + { k: null }, + { k: 2 }, + ]); + expect(series.id1.data).to.deep.equal([1, null, 2]); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index eb98506862d27..151fe00fe4f5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1481,6 +1481,121 @@ esquery "^1.5.0" jsdoc-type-pratt-parser "~4.0.0" +"@esbuild/aix-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz#d1bc06aedb6936b3b6d313bf809a5a40387d2b7f" + integrity sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA== + +"@esbuild/android-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz#7ad65a36cfdb7e0d429c353e00f680d737c2aed4" + integrity sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA== + +"@esbuild/android-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz#b0c26536f37776162ca8bde25e42040c203f2824" + integrity sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w== + +"@esbuild/android-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz#cb13e2211282012194d89bf3bfe7721273473b3d" + integrity sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew== + +"@esbuild/darwin-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz#cbee41e988020d4b516e9d9e44dd29200996275e" + integrity sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g== + +"@esbuild/darwin-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz#e37d9633246d52aecf491ee916ece709f9d5f4cd" + integrity sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A== + +"@esbuild/freebsd-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz#1ee4d8b682ed363b08af74d1ea2b2b4dbba76487" + integrity sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA== + +"@esbuild/freebsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz#37a693553d42ff77cd7126764b535fb6cc28a11c" + integrity sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg== + +"@esbuild/linux-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz#be9b145985ec6c57470e0e051d887b09dddb2d4b" + integrity sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA== + +"@esbuild/linux-arm@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz#207ecd982a8db95f7b5279207d0ff2331acf5eef" + integrity sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w== + +"@esbuild/linux-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz#d0d86b5ca1562523dc284a6723293a52d5860601" + integrity sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA== + +"@esbuild/linux-loong64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz#9a37f87fec4b8408e682b528391fa22afd952299" + integrity sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA== + +"@esbuild/linux-mips64el@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz#4ddebd4e6eeba20b509d8e74c8e30d8ace0b89ec" + integrity sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w== + +"@esbuild/linux-ppc64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz#adb67dadb73656849f63cd522f5ecb351dd8dee8" + integrity sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg== + +"@esbuild/linux-riscv64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz#11bc0698bf0a2abf8727f1c7ace2112612c15adf" + integrity sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg== + +"@esbuild/linux-s390x@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz#e86fb8ffba7c5c92ba91fc3b27ed5a70196c3cc8" + integrity sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg== + +"@esbuild/linux-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz#5f37cfdc705aea687dfe5dfbec086a05acfe9c78" + integrity sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg== + +"@esbuild/netbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz#29da566a75324e0d0dd7e47519ba2f7ef168657b" + integrity sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA== + +"@esbuild/openbsd-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz#306c0acbdb5a99c95be98bdd1d47c916e7dc3ff0" + integrity sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw== + +"@esbuild/sunos-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz#0933eaab9af8b9b2c930236f62aae3fc593faf30" + integrity sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA== + +"@esbuild/win32-arm64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz#773bdbaa1971b36db2f6560088639ccd1e6773ae" + integrity sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A== + +"@esbuild/win32-ia32@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz#000516cad06354cc84a73f0943a4aa690ef6fd67" + integrity sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ== + +"@esbuild/win32-x64@0.19.12": + version "0.19.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz#c57c8afbb4054a3ab8317591a0b7320360b444ae" + integrity sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA== + "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -6634,6 +6749,35 @@ es6-error@^4.0.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== +esbuild@~0.19.10: + version "0.19.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.12.tgz#dc82ee5dc79e82f5a5c3b4323a2a641827db3e04" + integrity sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg== + optionalDependencies: + "@esbuild/aix-ppc64" "0.19.12" + "@esbuild/android-arm" "0.19.12" + "@esbuild/android-arm64" "0.19.12" + "@esbuild/android-x64" "0.19.12" + "@esbuild/darwin-arm64" "0.19.12" + "@esbuild/darwin-x64" "0.19.12" + "@esbuild/freebsd-arm64" "0.19.12" + "@esbuild/freebsd-x64" "0.19.12" + "@esbuild/linux-arm" "0.19.12" + "@esbuild/linux-arm64" "0.19.12" + "@esbuild/linux-ia32" "0.19.12" + "@esbuild/linux-loong64" "0.19.12" + "@esbuild/linux-mips64el" "0.19.12" + "@esbuild/linux-ppc64" "0.19.12" + "@esbuild/linux-riscv64" "0.19.12" + "@esbuild/linux-s390x" "0.19.12" + "@esbuild/linux-x64" "0.19.12" + "@esbuild/netbsd-x64" "0.19.12" + "@esbuild/openbsd-x64" "0.19.12" + "@esbuild/sunos-x64" "0.19.12" + "@esbuild/win32-arm64" "0.19.12" + "@esbuild/win32-ia32" "0.19.12" + "@esbuild/win32-x64" "0.19.12" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -7582,7 +7726,7 @@ fsevents@2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== @@ -7729,6 +7873,13 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-tsconfig@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.2.tgz#0dcd6fb330391d46332f4c6c1bf89a6514c2ddce" + integrity sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A== + dependencies: + resolve-pkg-maps "^1.0.0" + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -12882,6 +13033,11 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -14287,6 +14443,16 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tsx@^4.7.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.7.1.tgz#27af6cbf4e1cdfcb9b5425b1c61bb7e668eb5e84" + integrity sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g== + dependencies: + esbuild "~0.19.10" + get-tsconfig "^4.7.2" + optionalDependencies: + fsevents "~2.3.3" + tuf-js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/tuf-js/-/tuf-js-1.1.7.tgz#21b7ae92a9373015be77dfe0cb282a80ec3bbe43" From 4e5c0875890542709a0f159d91d4ec9427bf4899 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:15:55 +0100 Subject: [PATCH 02/49] [charts] Customize tick position for band scale (#12316) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Flavien DELANGLE --- docs/data/charts/bars/TickPlacementBars.js | 184 ++++++++++++++++ docs/data/charts/bars/TickPlacementBars.tsx | 203 ++++++++++++++++++ .../charts/bars/TickPlacementBars.tsx.preview | 13 ++ docs/data/charts/bars/bars.md | 13 ++ docs/pages/x/api/charts/bar-chart.json | 12 +- docs/pages/x/api/charts/chart-container.json | 4 +- docs/pages/x/api/charts/charts-axis.json | 8 +- docs/pages/x/api/charts/line-chart.json | 12 +- docs/pages/x/api/charts/pie-chart.json | 12 +- .../charts/responsive-chart-container.json | 4 +- docs/pages/x/api/charts/scatter-chart.json | 12 +- docs/pages/x/api/charts/spark-line-chart.json | 2 +- packages/x-charts/src/BarChart/BarChart.tsx | 12 ++ .../src/ChartContainer/ChartContainer.tsx | 4 + .../x-charts/src/ChartsAxis/ChartsAxis.tsx | 8 + .../x-charts/src/ChartsXAxis/ChartsXAxis.tsx | 23 +- .../x-charts/src/ChartsYAxis/ChartsYAxis.tsx | 22 +- packages/x-charts/src/LineChart/LineChart.tsx | 12 ++ packages/x-charts/src/PieChart/PieChart.tsx | 12 ++ .../ResponsiveChartContainer.tsx | 4 + .../src/ScatterChart/ScatterChart.tsx | 12 ++ .../src/SparkLineChart/SparkLineChart.tsx | 2 + packages/x-charts/src/hooks/useTicks.ts | 56 ++++- packages/x-charts/src/models/axis.ts | 2 +- 24 files changed, 602 insertions(+), 46 deletions(-) create mode 100644 docs/data/charts/bars/TickPlacementBars.js create mode 100644 docs/data/charts/bars/TickPlacementBars.tsx create mode 100644 docs/data/charts/bars/TickPlacementBars.tsx.preview diff --git a/docs/data/charts/bars/TickPlacementBars.js b/docs/data/charts/bars/TickPlacementBars.js new file mode 100644 index 0000000000000..df201e0c5943c --- /dev/null +++ b/docs/data/charts/bars/TickPlacementBars.js @@ -0,0 +1,184 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControl from '@mui/material/FormControl'; +import FormLabel from '@mui/material/FormLabel'; +import RadioGroup from '@mui/material/RadioGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Radio from '@mui/material/Radio'; +import { BarChart } from '@mui/x-charts/BarChart'; +import { axisClasses } from '@mui/x-charts/ChartsAxis'; + +function TickParamsSelector({ + tickPlacement, + tickLabelPlacement, + setTickPlacement, + setTickLabelPlacement, +}) { + return ( + + + + tickPlacement + + setTickPlacement(event.target.value)} + > + } label="start" /> + } label="end" /> + } label="middle" /> + } + label="extremities" + /> + + + + + tickLabelPlacement + + setTickLabelPlacement(event.target.value)} + > + } label="tick" /> + } label="middle" /> + + + + ); +} + +const dataset = [ + { + london: 59, + paris: 57, + newYork: 86, + seoul: 21, + month: 'Jan', + }, + { + london: 50, + paris: 52, + newYork: 78, + seoul: 28, + month: 'Fev', + }, + { + london: 47, + paris: 53, + newYork: 106, + seoul: 41, + month: 'Mar', + }, + { + london: 54, + paris: 56, + newYork: 92, + seoul: 73, + month: 'Apr', + }, + { + london: 57, + paris: 69, + newYork: 92, + seoul: 99, + month: 'May', + }, + { + london: 60, + paris: 63, + newYork: 103, + seoul: 144, + month: 'June', + }, + { + london: 59, + paris: 60, + newYork: 105, + seoul: 319, + month: 'July', + }, + { + london: 65, + paris: 60, + newYork: 106, + seoul: 249, + month: 'Aug', + }, + { + london: 51, + paris: 51, + newYork: 95, + seoul: 131, + month: 'Sept', + }, + { + london: 60, + paris: 65, + newYork: 97, + seoul: 55, + month: 'Oct', + }, + { + london: 67, + paris: 64, + newYork: 76, + seoul: 48, + month: 'Nov', + }, + { + london: 61, + paris: 70, + newYork: 103, + seoul: 25, + month: 'Dec', + }, +]; + +const valueFormatter = (value) => `${value}mm`; + +const chartSetting = { + yAxis: [ + { + label: 'rainfall (mm)', + }, + ], + series: [{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }], + height: 300, + sx: { + [`& .${axisClasses.directionY} .${axisClasses.label}`]: { + transform: 'translateX(-10px)', + }, + }, +}; + +export default function TickPlacementBars() { + const [tickPlacement, setTickPlacement] = React.useState('middle'); + const [tickLabelPlacement, setTickLabelPlacement] = React.useState('middle'); + + return ( +
+ + +
+ ); +} diff --git a/docs/data/charts/bars/TickPlacementBars.tsx b/docs/data/charts/bars/TickPlacementBars.tsx new file mode 100644 index 0000000000000..b30b6ff39500d --- /dev/null +++ b/docs/data/charts/bars/TickPlacementBars.tsx @@ -0,0 +1,203 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControl from '@mui/material/FormControl'; +import FormLabel from '@mui/material/FormLabel'; +import RadioGroup from '@mui/material/RadioGroup'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Radio from '@mui/material/Radio'; +import { BarChart } from '@mui/x-charts/BarChart'; +import { axisClasses } from '@mui/x-charts/ChartsAxis'; + +type TickParamsSelectorProps = { + tickPlacement: 'end' | 'start' | 'middle' | 'extremities'; + tickLabelPlacement: 'tick' | 'middle'; + setTickPlacement: React.Dispatch< + React.SetStateAction<'end' | 'start' | 'middle' | 'extremities'> + >; + setTickLabelPlacement: React.Dispatch>; +}; + +function TickParamsSelector({ + tickPlacement, + tickLabelPlacement, + setTickPlacement, + setTickLabelPlacement, +}: TickParamsSelectorProps) { + return ( + + + + tickPlacement + + + setTickPlacement( + event.target.value as 'start' | 'end' | 'middle' | 'extremities', + ) + } + > + } label="start" /> + } label="end" /> + } label="middle" /> + } + label="extremities" + /> + + + + + tickLabelPlacement + + + setTickLabelPlacement(event.target.value as 'tick' | 'middle') + } + > + } label="tick" /> + } label="middle" /> + + + + ); +} + +const dataset = [ + { + london: 59, + paris: 57, + newYork: 86, + seoul: 21, + month: 'Jan', + }, + { + london: 50, + paris: 52, + newYork: 78, + seoul: 28, + month: 'Fev', + }, + { + london: 47, + paris: 53, + newYork: 106, + seoul: 41, + month: 'Mar', + }, + { + london: 54, + paris: 56, + newYork: 92, + seoul: 73, + month: 'Apr', + }, + { + london: 57, + paris: 69, + newYork: 92, + seoul: 99, + month: 'May', + }, + { + london: 60, + paris: 63, + newYork: 103, + seoul: 144, + month: 'June', + }, + { + london: 59, + paris: 60, + newYork: 105, + seoul: 319, + month: 'July', + }, + { + london: 65, + paris: 60, + newYork: 106, + seoul: 249, + month: 'Aug', + }, + { + london: 51, + paris: 51, + newYork: 95, + seoul: 131, + month: 'Sept', + }, + { + london: 60, + paris: 65, + newYork: 97, + seoul: 55, + month: 'Oct', + }, + { + london: 67, + paris: 64, + newYork: 76, + seoul: 48, + month: 'Nov', + }, + { + london: 61, + paris: 70, + newYork: 103, + seoul: 25, + month: 'Dec', + }, +]; + +const valueFormatter = (value: number | null) => `${value}mm`; + +const chartSetting = { + yAxis: [ + { + label: 'rainfall (mm)', + }, + ], + series: [{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }], + height: 300, + sx: { + [`& .${axisClasses.directionY} .${axisClasses.label}`]: { + transform: 'translateX(-10px)', + }, + }, +}; + +export default function TickPlacementBars() { + const [tickPlacement, setTickPlacement] = React.useState< + 'start' | 'end' | 'middle' | 'extremities' + >('middle'); + const [tickLabelPlacement, setTickLabelPlacement] = React.useState< + 'middle' | 'tick' + >('middle'); + + return ( +
+ + +
+ ); +} diff --git a/docs/data/charts/bars/TickPlacementBars.tsx.preview b/docs/data/charts/bars/TickPlacementBars.tsx.preview new file mode 100644 index 0000000000000..27adae0d5804b --- /dev/null +++ b/docs/data/charts/bars/TickPlacementBars.tsx.preview @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/docs/data/charts/bars/bars.md b/docs/data/charts/bars/bars.md index 539f6fb6f86bf..8c74fde88ac71 100644 --- a/docs/data/charts/bars/bars.md +++ b/docs/data/charts/bars/bars.md @@ -59,11 +59,24 @@ For more information, see [stacking docs](/x/react-charts/stacking/). ## Layout +### Bar direction + Bar charts can be rendered with a horizontal layout by providing the `layout="horizontal"` prop. If you're using [composition](/x/react-charts/composition/), you should set the property `layout: 'horizontal'` to each bar series object. {{"demo": "HorizontalBars.js"}} +### Tick placement + +When using a `"band"` scale, the axis has some additional customization properties about the tick position. + +- `tickPlacement` for the position of ticks +- `tickLabelPlacement` for the position of the label associated with the tick + +You can test all configuration options in the following demo: + +{{"demo": "TickPlacementBars.js"}} + ### Grid You can add a grid in the background of the chart with the `grid` prop. diff --git a/docs/pages/x/api/charts/bar-chart.json b/docs/pages/x/api/charts/bar-chart.json index 3b7a9619b0e07..5329fb31e47f3 100644 --- a/docs/pages/x/api/charts/bar-chart.json +++ b/docs/pages/x/api/charts/bar-chart.json @@ -17,7 +17,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -38,7 +38,7 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, @@ -66,7 +66,7 @@ "rightAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -87,7 +87,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -95,13 +95,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/chart-container.json b/docs/pages/x/api/charts/chart-container.json index 46d245c739233..74e710cbacf84 100644 --- a/docs/pages/x/api/charts/chart-container.json +++ b/docs/pages/x/api/charts/chart-container.json @@ -22,13 +22,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/charts-axis.json b/docs/pages/x/api/charts/charts-axis.json index 0f8af50089b95..50ca8a9ef8940 100644 --- a/docs/pages/x/api/charts/charts-axis.json +++ b/docs/pages/x/api/charts/charts-axis.json @@ -3,21 +3,21 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -30,7 +30,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" } diff --git a/docs/pages/x/api/charts/line-chart.json b/docs/pages/x/api/charts/line-chart.json index 3531457fbd810..1dce9cfb8b3fd 100644 --- a/docs/pages/x/api/charts/line-chart.json +++ b/docs/pages/x/api/charts/line-chart.json @@ -18,7 +18,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -36,7 +36,7 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, @@ -60,7 +60,7 @@ "rightAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -82,7 +82,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -90,13 +90,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/pie-chart.json b/docs/pages/x/api/charts/pie-chart.json index d2877b768615b..e1555deb9bb82 100644 --- a/docs/pages/x/api/charts/pie-chart.json +++ b/docs/pages/x/api/charts/pie-chart.json @@ -18,7 +18,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -32,7 +32,7 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -56,7 +56,7 @@ "rightAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -78,7 +78,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -86,13 +86,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/responsive-chart-container.json b/docs/pages/x/api/charts/responsive-chart-container.json index 12c7e23846549..216f02afe84b0 100644 --- a/docs/pages/x/api/charts/responsive-chart-container.json +++ b/docs/pages/x/api/charts/responsive-chart-container.json @@ -22,13 +22,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/scatter-chart.json b/docs/pages/x/api/charts/scatter-chart.json index d32377bc685b0..d6f1342a7e107 100644 --- a/docs/pages/x/api/charts/scatter-chart.json +++ b/docs/pages/x/api/charts/scatter-chart.json @@ -18,7 +18,7 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "xAxisIds[0] The id of the first provided axis" }, @@ -36,7 +36,7 @@ "leftAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "yAxisIds[0] The id of the first provided axis" }, @@ -57,7 +57,7 @@ "rightAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -78,7 +78,7 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: number
| string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, labelStyle?: object, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number }
| string" }, "default": "null" }, @@ -87,13 +87,13 @@ "xAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } }, "yAxis": { "type": { "name": "arrayOf", - "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }>" + "description": "Array<{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }>" } } }, diff --git a/docs/pages/x/api/charts/spark-line-chart.json b/docs/pages/x/api/charts/spark-line-chart.json index 264a7a0103d51..22a0dab3b73df 100644 --- a/docs/pages/x/api/charts/spark-line-chart.json +++ b/docs/pages/x/api/charts/spark-line-chart.json @@ -44,7 +44,7 @@ "xAxis": { "type": { "name": "shape", - "description": "{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }" + "description": "{ axisId?: number
| string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: number
| string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', reverse?: bool, scaleType?: 'band'
| 'linear'
| 'log'
| 'point'
| 'pow'
| 'sqrt'
| 'time'
| 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickInterval?: 'auto'
| array
| func, tickLabelInterval?: 'auto'
| func, tickLabelPlacement?: 'middle'
| 'tick', tickLabelStyle?: object, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickPlacement?: 'end'
| 'extremities'
| 'middle'
| 'start', tickSize?: number, valueFormatter?: func }" } } }, diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index 02494714379f2..4ffa35775888b 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -245,10 +245,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -313,10 +315,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -386,10 +390,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -457,10 +463,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -508,10 +516,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -549,10 +559,12 @@ BarChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/ChartContainer/ChartContainer.tsx b/packages/x-charts/src/ChartContainer/ChartContainer.tsx index 748552c3d10eb..8a129a397954b 100644 --- a/packages/x-charts/src/ChartContainer/ChartContainer.tsx +++ b/packages/x-charts/src/ChartContainer/ChartContainer.tsx @@ -165,10 +165,12 @@ ChartContainer.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -206,10 +208,12 @@ ChartContainer.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx index 070f0c67d7107..463256ba29396 100644 --- a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx +++ b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx @@ -173,10 +173,12 @@ ChartsAxis.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -207,10 +209,12 @@ ChartsAxis.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -241,10 +245,12 @@ ChartsAxis.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -285,10 +291,12 @@ ChartsAxis.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, diff --git a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx index 02aad382654ea..967e95415b9ab 100644 --- a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx +++ b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx @@ -124,6 +124,8 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { slotProps, tickInterval, tickLabelInterval, + tickPlacement, + tickLabelPlacement, } = defaultizedProps; const theme = useTheme(); @@ -156,7 +158,14 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { ownerState: {}, }); - const xTicks = useTicks({ scale: xScale, tickNumber, valueFormatter, tickInterval }); + const xTicks = useTicks({ + scale: xScale, + tickNumber, + valueFormatter, + tickInterval, + tickPlacement, + tickLabelPlacement, + }); const xTicksWithDimension = addLabelDimension(xTicks, { tickLabelStyle: axisTickLabelProps.style, @@ -315,6 +324,12 @@ ChartsXAxis.propTypes = { * @default 'auto' */ tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The placement of ticks label. Can be the middle of the band, or the tick position. + * Only used if scale is 'band'. + * @default 'middle' + */ + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), /** * The style applied to ticks text. */ @@ -336,6 +351,12 @@ ChartsXAxis.propTypes = { * Not supported by categorical axis (band, points). */ tickNumber: PropTypes.number, + /** + * The placement of ticks in regard to the band interval. + * Only used if scale is 'band'. + * @default 'extremities' + */ + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), /** * The size of the ticks. * @default 6 diff --git a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx index 5eb6fb73eeda3..440f95913839d 100644 --- a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx +++ b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx @@ -66,6 +66,8 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { valueFormatter, slots, slotProps, + tickPlacement, + tickLabelPlacement, } = defaultizedProps; const theme = useTheme(); @@ -75,7 +77,13 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { const tickSize = disableTicks ? 4 : tickSizeProp; - const yTicks = useTicks({ scale: yScale, tickNumber, valueFormatter }); + const yTicks = useTicks({ + scale: yScale, + tickNumber, + valueFormatter, + tickPlacement, + tickLabelPlacement, + }); const positionSign = position === 'right' ? 1 : -1; @@ -249,6 +257,12 @@ ChartsYAxis.propTypes = { * @default 'auto' */ tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The placement of ticks label. Can be the middle of the band, or the tick position. + * Only used if scale is 'band'. + * @default 'middle' + */ + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), /** * The style applied to ticks text. */ @@ -270,6 +284,12 @@ ChartsYAxis.propTypes = { * Not supported by categorical axis (band, points). */ tickNumber: PropTypes.number, + /** + * The placement of ticks in regard to the band interval. + * Only used if scale is 'band'. + * @default 'extremities' + */ + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), /** * The size of the ticks. * @default 6 diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index 4e27271314582..d9e8638f4ad65 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -273,10 +273,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -340,10 +342,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -419,10 +423,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -491,10 +497,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -542,10 +550,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -583,10 +593,12 @@ LineChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/PieChart/PieChart.tsx b/packages/x-charts/src/PieChart/PieChart.tsx index a21569f4248c2..3b0a054423e49 100644 --- a/packages/x-charts/src/PieChart/PieChart.tsx +++ b/packages/x-charts/src/PieChart/PieChart.tsx @@ -219,10 +219,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -275,10 +277,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -341,10 +345,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -413,10 +419,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -464,10 +472,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -505,10 +515,12 @@ PieChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/ResponsiveChartContainer/ResponsiveChartContainer.tsx b/packages/x-charts/src/ResponsiveChartContainer/ResponsiveChartContainer.tsx index 5ad8f690eb916..332bfbe77e1d1 100644 --- a/packages/x-charts/src/ResponsiveChartContainer/ResponsiveChartContainer.tsx +++ b/packages/x-charts/src/ResponsiveChartContainer/ResponsiveChartContainer.tsx @@ -145,10 +145,12 @@ ResponsiveChartContainer.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -186,10 +188,12 @@ ResponsiveChartContainer.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx index 02f8994dcc6dc..6ed8c16779dbd 100644 --- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx @@ -211,10 +211,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -279,10 +281,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -345,10 +349,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -412,10 +418,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, }), PropTypes.string, @@ -468,10 +476,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), @@ -509,10 +519,12 @@ ScatterChart.propTypes = { PropTypes.func, ]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx index fdb91ec75a0ed..d9c036d021857 100644 --- a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx +++ b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx @@ -368,10 +368,12 @@ SparkLineChart.propTypes = { tickFontSize: PropTypes.number, tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelPlacement: PropTypes.oneOf(['middle', 'tick']), tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, + tickPlacement: PropTypes.oneOf(['end', 'extremities', 'middle', 'start']), tickSize: PropTypes.number, valueFormatter: PropTypes.func, }), diff --git a/packages/x-charts/src/hooks/useTicks.ts b/packages/x-charts/src/hooks/useTicks.ts index 090442d052810..004de71fe8339 100644 --- a/packages/x-charts/src/hooks/useTicks.ts +++ b/packages/x-charts/src/hooks/useTicks.ts @@ -28,6 +28,18 @@ export interface TickParams { * @default 'auto' */ tickInterval?: 'auto' | ((value: any, index: number) => boolean) | any[]; + /** + * The placement of ticks in regard to the band interval. + * Only used if scale is 'band'. + * @default 'extremities' + */ + tickPlacement?: 'start' | 'end' | 'middle' | 'extremities'; + /** + * The placement of ticks label. Can be the middle of the band, or the tick position. + * Only used if scale is 'band'. + * @default 'middle' + */ + tickLabelPlacement?: 'middle' | 'tick'; } export function getTickNumber( @@ -48,6 +60,13 @@ export function getTickNumber( return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber)); } +const offsetRatio = { + start: 0, + extremities: 0, + end: 1, + middle: 0.5, +} as const; + export type TickItemType = { /** * This property is undefined only if it's the tick closing the last band @@ -62,9 +81,16 @@ export function useTicks( options: { scale: D3Scale; valueFormatter?: (value: any) => string; - } & Pick, + } & Pick, ): TickItemType[] { - const { scale, tickNumber, valueFormatter, tickInterval } = options; + const { + scale, + tickNumber, + valueFormatter, + tickInterval, + tickPlacement = 'extremities', + tickLabelPlacement = 'middle', + } = options; return React.useMemo(() => { // band scale @@ -77,15 +103,25 @@ export function useTicks( ...domain.map((value) => ({ value, formattedValue: valueFormatter?.(value) ?? `${value}`, - offset: scale(value)! - (scale.step() - scale.bandwidth()) / 2, - labelOffset: scale.step() / 2, + offset: + scale(value)! - + (scale.step() - scale.bandwidth()) / 2 + + offsetRatio[tickPlacement] * scale.step(), + labelOffset: + tickLabelPlacement === 'tick' + ? 0 + : scale.step() * (offsetRatio[tickLabelPlacement] - offsetRatio[tickPlacement]), })), - { - formattedValue: undefined, - offset: scale.range()[1], - labelOffset: 0, - }, + ...(tickPlacement === 'extremities' + ? [ + { + formattedValue: undefined, + offset: scale.range()[1], + labelOffset: 0, + }, + ] + : []), ]; } @@ -110,5 +146,5 @@ export function useTicks( offset: scale(value), labelOffset: 0, })); - }, [tickNumber, scale, valueFormatter, tickInterval]); + }, [scale, tickInterval, tickNumber, valueFormatter, tickPlacement, tickLabelPlacement]); } diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index 9f49ac4749f1b..0eeeae2dbfaa0 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -155,7 +155,7 @@ interface AxisScaleConfig { * @default 0.1 */ barGapRatio: number; - }; + } & Pick; point: { scaleType: 'point'; scale: ScalePoint; From f68925c45e52ed8c44495c54d8bbe8bb36121bb4 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 7 Mar 2024 10:00:08 -0500 Subject: [PATCH 03/49] [DataGrid] Performance: small optimizations (#12346) --- .../cellSelection/useGridCellSelection.ts | 4 +- .../src/components/GridPinnedRows.tsx | 32 +++++----- .../src/components/GridPinnedRows.tsx | 3 +- .../x-data-grid/src/components/GridRow.tsx | 5 +- .../src/components/cell/GridCell.tsx | 13 ++-- .../components/containers/GridRootStyles.ts | 3 - .../pipeProcessing/useGridPipeProcessing.ts | 61 ++++++++++--------- .../features/dimensions/useGridDimensions.ts | 1 + .../hooks/features/rows/useGridParamsApi.ts | 23 +++---- packages/x-data-grid/src/internals/index.ts | 6 +- .../x-data-grid/src/utils/createSelector.ts | 32 ++++------ 11 files changed, 83 insertions(+), 100 deletions(-) diff --git a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts index 946756162101a..0f69b04b68af9 100644 --- a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts +++ b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts @@ -473,12 +473,12 @@ export const useGridCellSelection = ( const addClassesToCells = React.useCallback>( (classes, { id, field }) => { - const newClasses = [...classes]; - if (!visibleRows.range || !apiRef.current.isCellSelected(id, field)) { return classes; } + const newClasses = [...classes]; + const rowIndex = apiRef.current.getRowIndexRelativeToVisibleRows(id); const columnIndex = apiRef.current.getColumnIndex(field); const visibleColumns = apiRef.current.getVisibleColumns(); diff --git a/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx b/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx index 46ea0b06e976a..a1060d680c0f6 100644 --- a/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx +++ b/packages/x-data-grid-pro/src/components/GridPinnedRows.tsx @@ -16,7 +16,7 @@ const useUtilityClasses = () => { return composeClasses(slots, getDataGridUtilityClass, {}); }; -export function GridPinnedRows({ position, virtualScroller, ...other }: GridPinnedRowsProps) { +export function GridPinnedRows({ position, virtualScroller }: GridPinnedRowsProps) { const classes = useUtilityClasses(); const apiRef = useGridPrivateApiContext(); @@ -24,26 +24,28 @@ export function GridPinnedRows({ position, virtualScroller, ...other }: GridPinn const pinnedRowsData = useGridSelector(apiRef, gridPinnedRowsSelector); const rows = pinnedRowsData[position]; + const pinnedRenderContext = React.useMemo( + () => ({ + firstRowIndex: 0, + lastRowIndex: rows.length, + firstColumnIndex: renderContext.firstColumnIndex, + lastColumnIndex: renderContext.lastColumnIndex, + }), + [rows, renderContext.firstColumnIndex, renderContext.lastColumnIndex], + ); + + if (rows.length === 0) { + return null; + } + const pinnedRows = virtualScroller.getRows({ position, rows, - renderContext: React.useMemo( - () => ({ - firstRowIndex: 0, - lastRowIndex: rows.length, - firstColumnIndex: renderContext.firstColumnIndex, - lastColumnIndex: renderContext.lastColumnIndex, - }), - [rows, renderContext.firstColumnIndex, renderContext.lastColumnIndex], - ), + renderContext: pinnedRenderContext, }); return ( -
+
{pinnedRows}
); diff --git a/packages/x-data-grid/src/components/GridPinnedRows.tsx b/packages/x-data-grid/src/components/GridPinnedRows.tsx index e895f820b3ab2..a1437fb62dbeb 100644 --- a/packages/x-data-grid/src/components/GridPinnedRows.tsx +++ b/packages/x-data-grid/src/components/GridPinnedRows.tsx @@ -1,7 +1,6 @@ -import * as React from 'react'; import type { VirtualScroller } from '../hooks/features/virtualization/useGridVirtualScroller'; -export interface GridPinnedRowsProps extends React.HTMLAttributes { +export interface GridPinnedRowsProps { position: 'top' | 'bottom'; virtualScroller: VirtualScroller; } diff --git a/packages/x-data-grid/src/components/GridRow.tsx b/packages/x-data-grid/src/components/GridRow.tsx index 7253f0aa4070a..a93275999b277 100644 --- a/packages/x-data-grid/src/components/GridRow.tsx +++ b/packages/x-data-grid/src/components/GridRow.tsx @@ -314,6 +314,7 @@ const GridRow = React.forwardRef(function GridRow( ...styleProp, maxHeight: rowHeight === 'auto' ? 'none' : rowHeight, // max-height doesn't support "auto" minHeight, + '--height': typeof rowHeight === 'number' ? `${rowHeight}px` : rowHeight, }; if (sizes?.spacingTop) { @@ -420,19 +421,17 @@ const GridRow = React.forwardRef(function GridRow( column={column} width={width} rowId={rowId} - height={rowHeight} align={column.align || 'left'} colIndex={indexRelativeToAllColumns} colSpan={colSpan} disableDragEvents={disableDragEvents} editCellState={editCellState} isNotVisible={cellIsNotVisible} - {...slotProps?.cell} pinnedOffset={pinnedOffset} pinnedPosition={pinnedPosition} sectionIndex={indexInSection} sectionLength={sectionLength} - gridHasScrollX={dimensions.hasScrollX} + {...slotProps?.cell} /> ); }; diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 0e2531316a157..88165af4e02a3 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -46,7 +46,6 @@ export type GridCellProps = { colIndex: number; column: GridColDef; rowId: GridRowId; - height: number | 'auto'; width: number; colSpan?: number; disableDragEvents?: boolean; @@ -56,7 +55,6 @@ export type GridCellProps = { pinnedPosition: PinnedPosition; sectionIndex: number; sectionLength: number; - gridHasScrollX: boolean; onClick?: React.MouseEventHandler; onDoubleClick?: React.MouseEventHandler; onMouseDown?: React.MouseEventHandler; @@ -145,7 +143,6 @@ const GridCell = React.forwardRef((props, ref) => align, children: childrenProp, colIndex, - height, width, className, style: styleProp, @@ -326,11 +323,11 @@ const GridCell = React.forwardRef((props, ref) => border: 0, }; } - const cellStyle = { + + const cellStyle: React.CSSProperties = { '--width': `${width}px`, - '--height': typeof height === 'number' ? `${height}px` : height, ...styleProp, - } as React.CSSProperties; + }; if (pinnedPosition === PinnedPosition.LEFT) { cellStyle.left = pinnedOffset; @@ -341,7 +338,7 @@ const GridCell = React.forwardRef((props, ref) => } return cellStyle; - }, [width, height, isNotVisible, styleProp, pinnedOffset, pinnedPosition]); + }, [width, isNotVisible, styleProp, pinnedOffset, pinnedPosition]); React.useEffect(() => { if (!hasFocus || cellMode === GridCellModes.Edit) { @@ -484,8 +481,6 @@ GridCell.propTypes = { isValidating: PropTypes.bool, value: PropTypes.any, }), - gridHasScrollX: PropTypes.bool.isRequired, - height: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.number]).isRequired, isNotVisible: PropTypes.bool.isRequired, onClick: PropTypes.func, onDoubleClick: PropTypes.func, diff --git a/packages/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/x-data-grid/src/components/containers/GridRootStyles.ts index fa76ca44813aa..691cc8643c79b 100644 --- a/packages/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/x-data-grid/src/components/containers/GridRootStyles.ts @@ -452,9 +452,6 @@ export const GridRootStyles = styled('div', { maxWidth: 'var(--width)', lineHeight: 'calc(var(--height) - 1px)', // -1px for the border - '--width': '0px', - '--height': '0px', - boxSizing: 'border-box', borderTop: `1px solid var(--rowBorderColor)`, diff --git a/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridPipeProcessing.ts b/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridPipeProcessing.ts index 200d3ec8f0f6f..5b8f5e4c40464 100644 --- a/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridPipeProcessing.ts +++ b/packages/x-data-grid/src/hooks/core/pipeProcessing/useGridPipeProcessing.ts @@ -8,12 +8,17 @@ import { } from './gridPipeProcessingApi'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; -interface GridPipeGroupCache { - processors: Map | null>; +type Cache = { + [G in GridPipeProcessorGroup]?: GroupCache; +}; + +type GroupCache = { + processors: Map>; + processorsAsArray: GridPipeProcessor[]; appliers: { [applierId: string]: () => void; }; -} +}; /** * Implement the Pipeline Pattern @@ -45,12 +50,10 @@ interface GridPipeGroupCache { * * `apiRef.current.requestPipeProcessorsApplication` is called for the given group. */ export const useGridPipeProcessing = (apiRef: React.MutableRefObject) => { - const processorsCache = React.useRef<{ - [G in GridPipeProcessorGroup]?: GridPipeGroupCache; - }>({}); + const cache = React.useRef({}); const isRunning = React.useRef(false); - const runAppliers = React.useCallback((groupCache: GridPipeGroupCache | undefined) => { + const runAppliers = React.useCallback((groupCache: GroupCache | undefined) => { if (isRunning.current || !groupCache) { return; } @@ -65,22 +68,27 @@ export const useGridPipeProcessing = (apiRef: React.MutableRefObject( (group, id, processor) => { - if (!processorsCache.current[group]) { - processorsCache.current[group] = { + if (!cache.current[group]) { + cache.current[group] = { processors: new Map(), + processorsAsArray: [], appliers: {}, }; } - const groupCache = processorsCache.current[group]!; + const groupCache = cache.current[group]!; const oldProcessor = groupCache.processors.get(id); if (oldProcessor !== processor) { groupCache.processors.set(id, processor); + groupCache.processorsAsArray = Array.from(cache.current[group]!.processors.values()); runAppliers(groupCache); } return () => { - processorsCache.current[group]!.processors.set(id, null); + cache.current[group]!.processors.delete(id); + cache.current[group]!.processorsAsArray = Array.from( + cache.current[group]!.processors.values(), + ); }; }, [runAppliers], @@ -89,19 +97,19 @@ export const useGridPipeProcessing = (apiRef: React.MutableRefObject((group, id, applier) => { - if (!processorsCache.current[group]) { - processorsCache.current[group] = { + if (!cache.current[group]) { + cache.current[group] = { processors: new Map(), + processorsAsArray: [], appliers: {}, }; } - processorsCache.current[group]!.appliers[id] = applier; + cache.current[group]!.appliers[id] = applier; return () => { - const { [id]: removedGroupApplier, ...otherAppliers } = - processorsCache.current[group]!.appliers; - processorsCache.current[group]!.appliers = otherAppliers; + const { [id]: removedGroupApplier, ...otherAppliers } = cache.current[group]!.appliers; + cache.current[group]!.appliers = otherAppliers; }; }, []); @@ -109,8 +117,7 @@ export const useGridPipeProcessing = (apiRef: React.MutableRefObject( (group) => { - const groupCache = processorsCache.current[group]; - runAppliers(groupCache); + runAppliers(cache.current[group]); }, [runAppliers], ); @@ -119,18 +126,16 @@ export const useGridPipeProcessing = (apiRef: React.MutableRefObject((...args) => { const [group, value, context] = args as [GridPipeProcessorGroup, any, any]; - if (!processorsCache.current[group]) { + if (!cache.current[group]) { return value; } - const preProcessors = Array.from(processorsCache.current[group]!.processors.values()); - return preProcessors.reduce((acc, preProcessor) => { - if (!preProcessor) { - return acc; - } - - return preProcessor(acc, context); - }, value); + const processors = cache.current[group]!.processorsAsArray; + let result = value; + for (let i = 0; i < processors.length; i += 1) { + result = processors[i](result, context); + } + return result; }, []); const preProcessingPrivateApi: GridPipeProcessingPrivateApi = { diff --git a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts index bb7f43dfbee01..f20acf1202265 100644 --- a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts +++ b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts @@ -316,6 +316,7 @@ export function useGridDimensions( set('--DataGrid-headersTotalHeight', `${dimensions.headersTotalHeight}px`); set('--DataGrid-topContainerHeight', `${dimensions.topContainerHeight}px`); set('--DataGrid-bottomContainerHeight', `${dimensions.bottomContainerHeight}px`); + set('--height', `${dimensions.rowHeight}px`); }, [root, dimensions]); const isFirstSizing = React.useRef(true); diff --git a/packages/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts b/packages/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts index ae7b518d64e8e..165ba3ee211c0 100644 --- a/packages/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts +++ b/packages/x-data-grid/src/hooks/features/rows/useGridParamsApi.ts @@ -51,7 +51,6 @@ export function useGridParamsApi(apiRef: React.MutableRefObject( (id, field) => { const colDef = apiRef.current.getColumn(field); - const value = apiRef.current.getCellValue(id, field); const row = apiRef.current.getRow(id); const rowNode = apiRef.current.getRowNode(id); @@ -59,6 +58,10 @@ export function useGridParamsApi(apiRef: React.MutableRefObject { const colDef = apiRef.current.getColumn(field); - if (!colDef || !colDef.valueGetter) { - const rowModel = apiRef.current.getRow(id); - - if (!rowModel) { - throw new MissingRowIdError(`No row with id #${id} found`); - } - - return rowModel[field]; - } - const row = apiRef.current.getRow(id); if (!row) { throw new MissingRowIdError(`No row with id #${id} found`); } - const value = row[colDef.field]; - return colDef.valueGetter(value as never, row, colDef, apiRef); + + if (!colDef || !colDef.valueGetter) { + return row[field]; + } + + return colDef.valueGetter(row[colDef.field] as never, row, colDef, apiRef); }, [apiRef], ); diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 834e6910337c1..8cb5686021445 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -136,11 +136,7 @@ export type { export { getColumnsToExport, defaultGetRowsToExport } from '../hooks/features/export/utils'; export * from '../utils/createControllablePromise'; -export { - createSelector, - createSelectorMemoized, - unstable_resetCreateSelectorCache, -} from '../utils/createSelector'; +export { createSelector, createSelectorMemoized } from '../utils/createSelector'; export { findParentElementFromClassName, getActiveElement, diff --git a/packages/x-data-grid/src/utils/createSelector.ts b/packages/x-data-grid/src/utils/createSelector.ts index a299c32aacbbc..582c6fa696b69 100644 --- a/packages/x-data-grid/src/utils/createSelector.ts +++ b/packages/x-data-grid/src/utils/createSelector.ts @@ -5,10 +5,6 @@ import { buildWarning } from './warning'; type CacheKey = { id: number }; -interface CacheContainer { - cache: WeakMap>; -} - export interface OutputSelector { (apiRef: React.MutableRefObject<{ state: State; instanceId: GridCoreApi['instanceId'] }>): Result; (state: State, instanceId: GridCoreApi['instanceId']): Result; @@ -40,7 +36,7 @@ type CreateSelectorFunction = >, R ...items: SelectorArgs ) => OutputSelector, Result>; -const cacheContainer: CacheContainer = { cache: new WeakMap() }; +const cache = new WeakMap>(); const missingInstanceIdWarning = buildWarning([ 'MUI X: A selector was called without passing the instance ID, which may impact the performance of the grid.', @@ -135,8 +131,7 @@ export const createSelector = (( }) as unknown as CreateSelectorFunction; export const createSelectorMemoized: CreateSelectorFunction = (...args: any) => { - const selector = (...selectorArgs: any[]) => { - const [stateOrApiRef, instanceId] = selectorArgs; + const selector = (stateOrApiRef: any, instanceId?: any) => { const isAPIRef = checkIsAPIRef(stateOrApiRef); const cacheKey = isAPIRef ? stateOrApiRef.current.instanceId @@ -149,22 +144,24 @@ export const createSelectorMemoized: CreateSelectorFunction = (...args: any) => } } - const { cache } = cacheContainer; + const cacheArgsInit = cache.get(cacheKey); + const cacheArgs = cacheArgsInit ?? new Map(); + const cacheFn = cacheArgs?.get(args); - if (cache.get(cacheKey) && cache.get(cacheKey)?.get(args)) { + if (cacheArgs && cacheFn) { // We pass the cache key because the called selector might have as // dependency another selector created with this `createSelector`. - return cache.get(cacheKey)?.get(args)(state, cacheKey); + return cacheFn(state, cacheKey); } - const newSelector = reselectCreateSelector(...args); + const fn = reselectCreateSelector(...args); - if (!cache.get(cacheKey)) { - cache.set(cacheKey, new Map()); + if (!cacheArgsInit) { + cache.set(cacheKey, cacheArgs); } - cache.get(cacheKey)?.set(args, newSelector); + cacheArgs.set(args, fn); - return newSelector(state, cacheKey); + return fn(state, cacheKey); }; // We use this property to detect if the selector was created with createSelector @@ -173,8 +170,3 @@ export const createSelectorMemoized: CreateSelectorFunction = (...args: any) => return selector; }; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const unstable_resetCreateSelectorCache = () => { - cacheContainer.cache = new WeakMap(); -}; From c66d8ffa279977b57363d55a3267cfd0c0cef58e Mon Sep 17 00:00:00 2001 From: Nora <72460825+noraleonte@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:18:53 +0200 Subject: [PATCH 04/49] [docs] `RichTreeView` customization docs (#12231) --- docs/data/pages.ts | 1 + .../customization/CustomAnimation.js | 62 ++++++++++ .../customization/CustomAnimation.tsx | 62 ++++++++++ .../customization/CustomAnimation.tsx.preview | 7 ++ .../customization/CustomContentTreeView.js | 110 +++++++++++++++++ .../customization/CustomContentTreeView.tsx | 114 ++++++++++++++++++ .../CustomContentTreeView.tsx.preview | 7 ++ .../customization/CustomIcons.js | 75 ++++++++++++ .../customization/CustomIcons.tsx | 76 ++++++++++++ .../customization/CustomIcons.tsx.preview | 12 ++ .../customization/CustomStyling.js | 75 ++++++++++++ .../customization/CustomStyling.tsx | 77 ++++++++++++ .../customization/CustomStyling.tsx.preview | 7 ++ .../customization/IconExpansionTreeView.js | 100 +++++++++++++++ .../customization/IconExpansionTreeView.tsx | 108 +++++++++++++++++ .../IconExpansionTreeView.tsx.preview | 5 + .../customization/customization.md | 50 ++++++++ .../customization/customization.md | 2 +- .../pages/x/api/tree-view/rich-tree-view.json | 2 +- docs/pages/x/api/tree-view/tree-item.json | 2 +- .../rich-tree-view/customization.js | 7 ++ 21 files changed, 958 insertions(+), 3 deletions(-) create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomIcons.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomStyling.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/customization.md create mode 100644 docs/pages/x/react-tree-view/rich-tree-view/customization.js diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 97bf3da4366a4..5ca6c1bd5d8f4 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -501,6 +501,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-tree-view/rich-tree-view/items' }, { pathname: '/x/react-tree-view/rich-tree-view/selection' }, { pathname: '/x/react-tree-view/rich-tree-view/expansion' }, + { pathname: '/x/react-tree-view/rich-tree-view/customization' }, { pathname: '/x/react-tree-view/rich-tree-view/focus' }, ], }, diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js new file mode 100644 index 0000000000000..6900116338403 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js @@ -0,0 +1,62 @@ +import * as React from 'react'; +import Collapse from '@mui/material/Collapse'; + +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +import { useSpring, animated } from '@react-spring/web'; + +const ITEMS = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +function TransitionComponent(props) { + const style = useSpring({ + to: { + opacity: props.in ? 1 : 0, + transform: `translate3d(${props.in ? 0 : 20}px,0,0)`, + }, + }); + + return ( + + + + ); +} + +export default function CustomAnimation() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx new file mode 100644 index 0000000000000..6d45c9ea141b3 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx @@ -0,0 +1,62 @@ +import * as React from 'react'; +import Collapse from '@mui/material/Collapse'; +import { TransitionProps } from '@mui/material/transitions'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { useSpring, animated } from '@react-spring/web'; + +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +function TransitionComponent(props: TransitionProps) { + const style = useSpring({ + to: { + opacity: props.in ? 1 : 0, + transform: `translate3d(${props.in ? 0 : 20}px,0,0)`, + }, + }); + + return ( + + + + ); +} + +export default function CustomAnimation() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx.preview new file mode 100644 index 0000000000000..bd511807c3955 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js new file mode 100644 index 0000000000000..4643d3aa8f88a --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js @@ -0,0 +1,110 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import Avatar from '@mui/material/Avatar'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItemState } from '@mui/x-tree-view/TreeItem'; + +const ITEMS = [ + { + id: '1', + label: 'Amelia Hart', + children: [{ id: '2', label: 'Jane Fisher' }], + }, + { + id: '3', + label: 'Bailey Monroe', + children: [ + { id: '4', label: 'Freddie Reed' }, + { + id: '5', + label: 'Georgia Johnson', + children: [{ id: '6', label: 'Samantha Malone' }], + }, + ], + }, +]; + +const CustomContentRoot = styled('div')(({ theme }) => ({ + '&': { padding: theme.spacing(0.5, 1) }, +})); + +const CustomContent = React.forwardRef(function CustomContent(props, ref) { + const { + className, + classes, + label, + nodeId, + icon: iconProp, + expansionIcon, + displayIcon, + } = props; + + const { + disabled, + expanded, + selected, + focused, + handleExpansion, + handleSelection, + preventSelection, + } = useTreeItemState(nodeId); + + const icon = iconProp || expansionIcon || displayIcon; + + const handleMouseDown = (event) => { + preventSelection(event); + }; + + const handleClick = (event) => { + handleExpansion(event); + handleSelection(event); + }; + + return ( + +
{icon}
+ + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {label[0]} + + + {label} + + +
+ ); +}); + +export default function CustomContentTreeView() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx new file mode 100644 index 0000000000000..1d351739796a5 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx @@ -0,0 +1,114 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import Avatar from '@mui/material/Avatar'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItemState, TreeItemContentProps } from '@mui/x-tree-view/TreeItem'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'Amelia Hart', + children: [{ id: '2', label: 'Jane Fisher' }], + }, + { + id: '3', + label: 'Bailey Monroe', + children: [ + { id: '4', label: 'Freddie Reed' }, + { + id: '5', + label: 'Georgia Johnson', + children: [{ id: '6', label: 'Samantha Malone' }], + }, + ], + }, +]; + +const CustomContentRoot = styled('div')(({ theme }) => ({ + '&': { padding: theme.spacing(0.5, 1) }, +})); + +const CustomContent = React.forwardRef(function CustomContent( + props: TreeItemContentProps, + ref, +) { + const { + className, + classes, + label, + nodeId, + icon: iconProp, + expansionIcon, + displayIcon, + } = props; + + const { + disabled, + expanded, + selected, + focused, + handleExpansion, + handleSelection, + preventSelection, + } = useTreeItemState(nodeId); + + const icon = iconProp || expansionIcon || displayIcon; + + const handleMouseDown = (event: React.MouseEvent) => { + preventSelection(event); + }; + + const handleClick = (event: React.MouseEvent) => { + handleExpansion(event); + handleSelection(event); + }; + + return ( + } + > +
{icon}
+ + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {(label as string)[0]} + + + {label} + + +
+ ); +}); + +export default function CustomContentTreeView() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview new file mode 100644 index 0000000000000..163c474b4f991 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js new file mode 100644 index 0000000000000..a955b32be421b --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import AddBoxIcon from '@mui/icons-material/AddBox'; +import IndeterminateCheckBoxIcon from '@mui/icons-material/IndeterminateCheckBox'; +import SvgIcon from '@mui/material/SvgIcon'; +import { styled } from '@mui/material/styles'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem'; + +const ITEMS = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +const CustomTreeItem = styled(TreeItem)({ + [`& .${treeItemClasses.iconContainer}`]: { + '& .close': { + opacity: 0.3, + }, + }, +}); + +function CloseSquare(props) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +export default function CustomIcons() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx new file mode 100644 index 0000000000000..ec655c6014034 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import AddBoxIcon from '@mui/icons-material/AddBox'; +import IndeterminateCheckBoxIcon from '@mui/icons-material/IndeterminateCheckBox'; +import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; +import { styled } from '@mui/material/styles'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +const CustomTreeItem = styled(TreeItem)({ + [`& .${treeItemClasses.iconContainer}`]: { + '& .close': { + opacity: 0.3, + }, + }, +}); + +function CloseSquare(props: SvgIconProps) { + return ( + + {/* tslint:disable-next-line: max-line-length */} + + + ); +} + +export default function CustomIcons() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx.preview new file mode 100644 index 0000000000000..130c85e64ed9a --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.tsx.preview @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomStyling.js b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.js new file mode 100644 index 0000000000000..dae03dc07bcfe --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { styled, alpha } from '@mui/material/styles'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem'; + +const ITEMS = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +const StyledTreeItem = styled(TreeItem)(({ theme }) => ({ + color: + theme.palette.mode === 'light' + ? theme.palette.grey[800] + : theme.palette.grey[200], + [`& .${treeItemClasses.content}`]: { + borderRadius: theme.spacing(0.5), + padding: theme.spacing(0.5, 1), + margin: theme.spacing(0.2, 0), + [`& .${treeItemClasses.label}`]: { + fontSize: '0.8rem', + fontWeight: 500, + }, + }, + [`& .${treeItemClasses.iconContainer}`]: { + borderRadius: '50%', + backgroundColor: + theme.palette.mode === 'light' + ? alpha(theme.palette.primary.main, 0.25) + : theme.palette.primary.dark, + color: theme.palette.mode === 'dark' && theme.palette.primary.contrastText, + padding: theme.spacing(0, 1.2), + }, + [`& .${treeItemClasses.groupTransition}`]: { + marginLeft: 15, + paddingLeft: 18, + borderLeft: `1px dashed ${alpha(theme.palette.text.primary, 0.4)}`, + }, +})); + +export default function CustomStyling() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx new file mode 100644 index 0000000000000..942da8f9c9012 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { styled, alpha } from '@mui/material/styles'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'Main', + children: [ + { id: '2', label: 'Hello' }, + { + id: '3', + label: 'Subtree with children', + children: [ + { id: '6', label: 'Hello' }, + { + id: '7', + label: 'Sub-subtree with children', + children: [ + { id: '9', label: 'Child 1' }, + { id: '10', label: 'Child 2' }, + { id: '11', label: 'Child 3' }, + ], + }, + { id: '8', label: 'Hello' }, + ], + }, + { id: '4', label: 'World' }, + { id: '5', label: 'Something something' }, + ], + }, +]; + +const StyledTreeItem = styled(TreeItem)(({ theme }) => ({ + color: + theme.palette.mode === 'light' + ? theme.palette.grey[800] + : theme.palette.grey[200], + + [`& .${treeItemClasses.content}`]: { + borderRadius: theme.spacing(0.5), + padding: theme.spacing(0.5, 1), + margin: theme.spacing(0.2, 0), + [`& .${treeItemClasses.label}`]: { + fontSize: '0.8rem', + fontWeight: 500, + }, + }, + [`& .${treeItemClasses.iconContainer}`]: { + borderRadius: '50%', + backgroundColor: + theme.palette.mode === 'light' + ? alpha(theme.palette.primary.main, 0.25) + : theme.palette.primary.dark, + color: theme.palette.mode === 'dark' && theme.palette.primary.contrastText, + padding: theme.spacing(0, 1.2), + }, + [`& .${treeItemClasses.groupTransition}`]: { + marginLeft: 15, + paddingLeft: 18, + borderLeft: `1px dashed ${alpha(theme.palette.text.primary, 0.4)}`, + }, +})); + +export default function CustomStyling() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx.preview new file mode 100644 index 0000000000000..f1af3260749b7 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/CustomStyling.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js new file mode 100644 index 0000000000000..c6eb49a5783bb --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js @@ -0,0 +1,100 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItemState } from '@mui/x-tree-view/TreeItem'; + +const ITEMS = [ + { + id: '1', + label: 'Applications', + children: [{ id: '2', label: 'Calendar' }], + }, + { + id: '3', + label: 'Documents', + children: [ + { id: '6', label: 'OSS' }, + { + id: '4', + label: 'MUI', + children: [{ id: '5', label: 'index.js' }], + }, + ], + }, +]; + +const CustomContent = React.forwardRef(function CustomContent(props, ref) { + const { + classes, + className, + label, + nodeId, + icon: iconProp, + expansionIcon, + displayIcon, + } = props; + + const { + disabled, + expanded, + selected, + focused, + handleExpansion, + handleSelection, + preventSelection, + } = useTreeItemState(nodeId); + + const icon = iconProp || expansionIcon || displayIcon; + + const handleMouseDown = (event) => { + preventSelection(event); + }; + + const handleExpansionClick = (event) => { + handleExpansion(event); + }; + + const handleSelectionClick = (event) => { + handleSelection(event); + }; + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
+ {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} +
+ {icon} +
+ + {label} + +
+ ); +}); + +export default function IconExpansionTreeView() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx new file mode 100644 index 0000000000000..fa1b76d6032fd --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx @@ -0,0 +1,108 @@ +import * as React from 'react'; +import clsx from 'clsx'; +import Typography from '@mui/material/Typography'; +import Box from '@mui/material/Box'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { useTreeItemState, TreeItemContentProps } from '@mui/x-tree-view/TreeItem'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +const ITEMS: TreeViewBaseItem[] = [ + { + id: '1', + label: 'Applications', + children: [{ id: '2', label: 'Calendar' }], + }, + { + id: '3', + label: 'Documents', + children: [ + { id: '6', label: 'OSS' }, + { + id: '4', + label: 'MUI', + children: [{ id: '5', label: 'index.js' }], + }, + ], + }, +]; + +const CustomContent = React.forwardRef(function CustomContent( + props: TreeItemContentProps, + ref, +) { + const { + classes, + className, + label, + nodeId, + icon: iconProp, + expansionIcon, + displayIcon, + } = props; + + const { + disabled, + expanded, + selected, + focused, + handleExpansion, + handleSelection, + preventSelection, + } = useTreeItemState(nodeId); + + const icon = iconProp || expansionIcon || displayIcon; + + const handleMouseDown = (event: React.MouseEvent) => { + preventSelection(event); + }; + + const handleExpansionClick = ( + event: React.MouseEvent, + ) => { + handleExpansion(event); + }; + + const handleSelectionClick = ( + event: React.MouseEvent, + ) => { + handleSelection(event); + }; + + return ( + // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
} + > + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} +
+ {icon} +
+ + {label} + +
+ ); +}); + +export default function IconExpansionTreeView() { + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx.preview new file mode 100644 index 0000000000000..9a621283b2c07 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/customization.md b/docs/data/tree-view/rich-tree-view/customization/customization.md new file mode 100644 index 0000000000000..771dbd8aefaaf --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/customization.md @@ -0,0 +1,50 @@ +--- +productId: x-tree-view +title: Rich Tree View - Customization +components: RichTreeView, TreeItem +packageName: '@mui/x-tree-view' +githubLabel: 'component: tree view' +waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ +--- + +# Rich Tree View - Customization + +

Learn how to customize the rich version of the Tree View component.

+ +## Basics + +### Custom icons + +Use the `collapseIcon` slot, the `expandIcon` slot and the `defaultEndIcon` prop to customize the Tree View icons. +The demo below shows how to add icons using both an existing icon library, such as [Material Icons](/material-ui/material-icons/), and creating an icon from scratch using Material UI's [SVG Icon component](/material-ui/icons/#svgicon). + +{{"demo": "CustomIcons.js", "defaultCodeOpen": false}} + +### Custom toggle animations + +Use the `groupTransition` slot on the `TreeItem` to pass a component that handles your animation. + +The demo below is animated using Material UI's [Collapse](/material-ui/transitions/#collapse) component together with the [react-spring](https://www.react-spring.dev/) library. + +{{"demo": "CustomAnimation.js", "defaultCodeOpen": false}} + +### Custom styling + +Use `treeItemClasses` to target internal elements of the Tree Item component and change their styles. + +{{"demo": "CustomStyling.js"}} + +### Adding custom content + +Use the `ContentComponent` prop and the `useTreeItemState` hook to replace the Tree Item content with an entirely custom component. +The demo below shows how to add an avatar and custom typography elements. + +{{"demo": "CustomContentTreeView.js"}} + +## Common examples + +### Limit expansion to icon container + +The demo below shows how to trigger the expansion interaction just by clicking on the icon container instead of the whole Tree Item surface. + +{{"demo": "IconExpansionTreeView.js", "defaultCodeOpen": false}} diff --git a/docs/data/tree-view/simple-tree-view/customization/customization.md b/docs/data/tree-view/simple-tree-view/customization/customization.md index f1b7f66e68557..319ebcba5b271 100644 --- a/docs/data/tree-view/simple-tree-view/customization/customization.md +++ b/docs/data/tree-view/simple-tree-view/customization/customization.md @@ -22,7 +22,7 @@ The demo below shows how to add icons using both an existing icon library, such ### Custom toggle animations -Use the `TransitionComponent` prop on the `TreeItem` to pass a component that handles your animation. +Use the `groupTransition` slot on the `TreeItem` to pass a component that handles your animation. The demo below is animated using Material UI's [Collapse](/material-ui/transitions/#collapse) component together with the [react-spring](https://www.react-spring.dev/) library. diff --git a/docs/pages/x/api/tree-view/rich-tree-view.json b/docs/pages/x/api/tree-view/rich-tree-view.json index 3ef550f36821a..663ab19ef16a4 100644 --- a/docs/pages/x/api/tree-view/rich-tree-view.json +++ b/docs/pages/x/api/tree-view/rich-tree-view.json @@ -131,6 +131,6 @@ "forwardsRefTo": "HTMLUListElement", "filename": "/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/pages/x/api/tree-view/tree-item.json b/docs/pages/x/api/tree-view/tree-item.json index 93c937cfd477b..5835afabd5928 100644 --- a/docs/pages/x/api/tree-view/tree-item.json +++ b/docs/pages/x/api/tree-view/tree-item.json @@ -102,6 +102,6 @@ "forwardsRefTo": "HTMLLIElement", "filename": "/packages/x-tree-view/src/TreeItem/TreeItem.tsx", "inheritance": null, - "demos": "", + "demos": "", "cssComponent": false } diff --git a/docs/pages/x/react-tree-view/rich-tree-view/customization.js b/docs/pages/x/react-tree-view/rich-tree-view/customization.js new file mode 100644 index 0000000000000..84fa88a51b3a8 --- /dev/null +++ b/docs/pages/x/react-tree-view/rich-tree-view/customization.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/tree-view/rich-tree-view/customization/customization.md?@mui/markdown'; + +export default function Page() { + return ; +} From 11412db20fcb320640999da4294f5ff04bc75d0b Mon Sep 17 00:00:00 2001 From: Danail Hadjiatanasov Date: Thu, 7 Mar 2024 21:43:04 +0200 Subject: [PATCH 05/49] [DataGridPro] Rework `onRowsScrollEnd` to use `IntersectionObserver` (#8672) Co-authored-by: Andrew Cherniavskyi --- .../row-updates/InfiniteLoadingGrid.js | 161 +++++++++++++--- .../row-updates/InfiniteLoadingGrid.tsx | 180 +++++++++++++++--- .../InfiniteLoadingGrid.tsx.preview | 10 - .../data/data-grid/row-updates/row-updates.md | 5 + .../infiniteLoader/useGridInfiniteLoader.ts | 83 -------- .../infiniteLoader/useGridInfiniteLoader.tsx | 130 +++++++++++++ .../src/tests/events.DataGridPro.test.tsx | 57 +----- .../tests/infiniteLoader.DataGridPro.test.tsx | 153 +++++++++++++++ .../virtualization/useGridVirtualScroller.tsx | 6 +- .../src/models/api/gridApiCommon.ts | 4 +- .../src/models/api/gridInfiniteLoaderApi.ts | 7 + 11 files changed, 587 insertions(+), 209 deletions(-) delete mode 100644 docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx.preview delete mode 100644 packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.ts create mode 100644 packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx create mode 100644 packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx create mode 100644 packages/x-data-grid/src/models/api/gridInfiniteLoaderApi.ts diff --git a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.js b/docs/data/data-grid/row-updates/InfiniteLoadingGrid.js index 1d49108489729..6eb99d48c82e0 100644 --- a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.js +++ b/docs/data/data-grid/row-updates/InfiniteLoadingGrid.js @@ -1,14 +1,17 @@ import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; import { - useDemoData, + DataGridPro, + gridStringOrNumberComparator, + getGridStringOperators, +} from '@mui/x-data-grid-pro'; +import { getRealGridData, getCommodityColumns, randomInt, } from '@mui/x-data-grid-generator'; import LinearProgress from '@mui/material/LinearProgress'; -const MAX_ROW_LENGTH = 500; +const MAX_ROW_LENGTH = 1000; function sleep(duration) { return new Promise((resolve) => { @@ -18,51 +21,149 @@ function sleep(duration) { }); } +let allData; + +const columnFields = [ + 'id', + 'desk', + 'commodity', + 'traderName', + 'traderEmail', + 'brokerId', + 'brokerName', + 'counterPartyName', +]; + +const columns = getCommodityColumns().filter((column) => + columnFields.includes(column.field), +); + +const filterOperators = getGridStringOperators(); +const filterOperatorsLookup = filterOperators.reduce((acc, operator) => { + acc[operator.value] = operator; + return acc; +}, {}); + +async function fetchRows({ fromIndex, toIndex, sortModel, filterModel }) { + if (!allData) { + allData = await getRealGridData(MAX_ROW_LENGTH, columns); + } + await sleep(randomInt(100, 600)); + + fromIndex = Math.max(0, fromIndex); + fromIndex = Math.min(fromIndex, allData.rows.length); + + toIndex = Math.max(0, toIndex); + toIndex = Math.min(toIndex, allData.rows.length); + + let allRows = [...allData.rows]; + + if (sortModel && sortModel.length > 0) { + sortModel.forEach(({ field, sort }) => { + if (field && sort) { + allRows = allRows.sort((a, b) => { + return ( + gridStringOrNumberComparator(a[field], b[field], {}, {}) * + (sort === 'asc' ? 1 : -1) + ); + }); + } + }); + } + + if (filterModel && filterModel.items.length > 0) { + const method = filterModel.logicOperator === 'or' ? 'some' : 'every'; + + allRows = allRows.filter((row) => { + return filterModel.items[method]((item) => { + const filter = filterOperatorsLookup[item.operator]; + if (!filter) { + return true; + } + if (!filter.requiresFilterValue !== false && !item.value) { + return true; + } + const colDef = {}; + const apiRef = {}; + return filter.getApplyFilterFn(item, colDef)?.( + row[item.field], + row, + colDef, + apiRef, + ); + }); + }); + } + + const rows = allRows.slice(fromIndex, toIndex); + return rows; +} + export default function InfiniteLoadingGrid() { const [loading, setLoading] = React.useState(false); - const [loadedRows, setLoadedRows] = React.useState([]); - const mounted = React.useRef(true); - const { data } = useDemoData({ - dataSet: 'Commodity', - rowLength: 20, - maxColumns: 6, + const [rows, setRows] = React.useState([]); + const [sortModel, setSortModel] = React.useState([]); + const [filterModel, setFilterModel] = React.useState({ + items: [], }); - const loadServerRows = async (newRowLength) => { - setLoading(true); - const newData = await getRealGridData(newRowLength, getCommodityColumns()); - // Simulate network throttle - await sleep(randomInt(100, 600)); - - if (mounted.current) { + const handleOnRowsScrollEnd = React.useCallback( + async (params) => { + setLoading(true); + const fetchedRows = await fetchRows({ + fromIndex: rows.length, + toIndex: rows.length + params.viewportPageSize * 2, + sortModel, + filterModel, + }); setLoading(false); - setLoadedRows(loadedRows.concat(newData.rows)); - } - }; - - const handleOnRowsScrollEnd = (params) => { - if (loadedRows.length <= MAX_ROW_LENGTH) { - loadServerRows(params.viewportPageSize); - } - }; + setRows((prevRows) => prevRows.concat(fetchedRows)); + }, + [rows.length, sortModel, filterModel], + ); React.useEffect(() => { + let mounted = true; + (async () => { + setLoading(true); + const fetchedRows = await fetchRows({ + fromIndex: 0, + toIndex: 20, + sortModel, + filterModel, + }); + if (mounted) { + setLoading(false); + setRows(fetchedRows); + } + })(); + return () => { - mounted.current = true; + mounted = false; }; - }, []); + }, [sortModel, filterModel]); return (
); diff --git a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx b/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx index 7ab60d8647492..88210c82cd158 100644 --- a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx +++ b/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx @@ -1,14 +1,23 @@ import * as React from 'react'; -import { DataGridPro, DataGridProProps, GridSlots } from '@mui/x-data-grid-pro'; import { - useDemoData, + DataGridPro, + DataGridProProps, + GridSlots, + GridSortModel, + gridStringOrNumberComparator, + GridFilterModel, + getGridStringOperators, + GridFilterOperator, +} from '@mui/x-data-grid-pro'; +import { getRealGridData, getCommodityColumns, randomInt, + GridDemoData, } from '@mui/x-data-grid-generator'; import LinearProgress from '@mui/material/LinearProgress'; -const MAX_ROW_LENGTH = 500; +const MAX_ROW_LENGTH = 1000; function sleep(duration: number) { return new Promise((resolve) => { @@ -18,51 +27,162 @@ function sleep(duration: number) { }); } +let allData: GridDemoData | undefined; + +const columnFields = [ + 'id', + 'desk', + 'commodity', + 'traderName', + 'traderEmail', + 'brokerId', + 'brokerName', + 'counterPartyName', +]; +const columns = getCommodityColumns().filter((column) => + columnFields.includes(column.field), +); + +const filterOperators = getGridStringOperators(); +const filterOperatorsLookup = filterOperators.reduce< + Record +>((acc, operator) => { + acc[operator.value] = operator; + return acc; +}, {}); + +async function fetchRows({ + fromIndex, + toIndex, + sortModel, + filterModel, +}: { + fromIndex: number; + toIndex: number; + sortModel: GridSortModel; + filterModel: GridFilterModel; +}) { + if (!allData) { + allData = await getRealGridData(MAX_ROW_LENGTH, columns); + } + await sleep(randomInt(100, 600)); + + fromIndex = Math.max(0, fromIndex); + fromIndex = Math.min(fromIndex, allData.rows.length); + + toIndex = Math.max(0, toIndex); + toIndex = Math.min(toIndex, allData.rows.length); + + let allRows = [...allData.rows]; + + if (sortModel && sortModel.length > 0) { + sortModel.forEach(({ field, sort }) => { + if (field && sort) { + allRows = allRows.sort((a, b) => { + return ( + gridStringOrNumberComparator(a[field], b[field], {} as any, {} as any) * + (sort === 'asc' ? 1 : -1) + ); + }); + } + }); + } + + if (filterModel && filterModel.items.length > 0) { + const method = filterModel.logicOperator === 'or' ? 'some' : 'every'; + + allRows = allRows.filter((row) => { + return filterModel.items[method]((item) => { + const filter = filterOperatorsLookup[item.operator]; + if (!filter) { + return true; + } + if (!filter.requiresFilterValue !== false && !item.value) { + return true; + } + const colDef = {} as any; + const apiRef = {} as any; + return filter.getApplyFilterFn(item, colDef)?.( + row[item.field], + row, + colDef, + apiRef, + ); + }); + }); + } + + const rows = allRows.slice(fromIndex, toIndex); + return rows; +} + export default function InfiniteLoadingGrid() { const [loading, setLoading] = React.useState(false); - const [loadedRows, setLoadedRows] = React.useState([]); - const mounted = React.useRef(true); - const { data } = useDemoData({ - dataSet: 'Commodity', - rowLength: 20, - maxColumns: 6, + const [rows, setRows] = React.useState([]); + const [sortModel, setSortModel] = React.useState([]); + const [filterModel, setFilterModel] = React.useState({ + items: [], }); - const loadServerRows = async (newRowLength: number) => { - setLoading(true); - const newData = await getRealGridData(newRowLength, getCommodityColumns()); - // Simulate network throttle - await sleep(randomInt(100, 600)); - - if (mounted.current) { + const handleOnRowsScrollEnd = React.useCallback< + NonNullable + >( + async (params) => { + setLoading(true); + const fetchedRows = await fetchRows({ + fromIndex: rows.length, + toIndex: rows.length + params.viewportPageSize * 2, + sortModel, + filterModel, + }); setLoading(false); - setLoadedRows(loadedRows.concat(newData.rows)); - } - }; - - const handleOnRowsScrollEnd: DataGridProProps['onRowsScrollEnd'] = (params) => { - if (loadedRows.length <= MAX_ROW_LENGTH) { - loadServerRows(params.viewportPageSize); - } - }; + setRows((prevRows) => prevRows.concat(fetchedRows)); + }, + [rows.length, sortModel, filterModel], + ); React.useEffect(() => { + let mounted = true; + (async () => { + setLoading(true); + const fetchedRows = await fetchRows({ + fromIndex: 0, + toIndex: 20, + sortModel, + filterModel, + }); + if (mounted) { + setLoading(false); + setRows(fetchedRows); + } + })(); + return () => { - mounted.current = true; + mounted = false; }; - }, []); + }, [sortModel, filterModel]); return (
); diff --git a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx.preview b/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx.preview deleted file mode 100644 index d8a614229f5c7..0000000000000 --- a/docs/data/data-grid/row-updates/InfiniteLoadingGrid.tsx.preview +++ /dev/null @@ -1,10 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/data-grid/row-updates/row-updates.md b/docs/data/data-grid/row-updates/row-updates.md index 1462d826b041a..3bbb173476c3f 100644 --- a/docs/data/data-grid/row-updates/row-updates.md +++ b/docs/data/data-grid/row-updates/row-updates.md @@ -40,6 +40,11 @@ In addition, the area in which `onRowsScrollEnd` is called can be changed using {{"demo": "InfiniteLoadingGrid.js", "bg": "inline", "disableAd": true}} +:::info +For sorting and filtering to work properly with the infinite loading, they should be applied on the server side. +Otherwise, the sorting and filtering will only be applied to the subset of rows that have been loaded. +::: + ## Lazy loading [](/x/introduction/licensing/#pro-plan 'Pro plan') :::warning diff --git a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.ts b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.ts deleted file mode 100644 index 1842c93599777..0000000000000 --- a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from 'react'; -import { - useGridSelector, - GridEventListener, - GridScrollParams, - useGridApiEventHandler, - useGridApiOptionHandler, - gridVisibleColumnDefinitionsSelector, - gridRowsMetaSelector, -} from '@mui/x-data-grid'; -import { useGridVisibleRows } from '@mui/x-data-grid/internals'; -import { GridRowScrollEndParams } from '../../../models'; -import { GridPrivateApiPro } from '../../../models/gridApiPro'; -import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; - -/** - * @requires useGridColumns (state) - * @requires useGridDimensions (method) - can be after - * @requires useGridScroll (method - */ -export const useGridInfiniteLoader = ( - apiRef: React.MutableRefObject, - props: Pick< - DataGridProProcessedProps, - 'onRowsScrollEnd' | 'scrollEndThreshold' | 'pagination' | 'paginationMode' | 'rowsLoadingMode' - >, -): void => { - const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); - const currentPage = useGridVisibleRows(apiRef, props); - const rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector); - const contentHeight = Math.max(rowsMeta.currentPageTotalHeight, 1); - - const isInScrollBottomArea = React.useRef(false); - - const handleRowsScrollEnd = React.useCallback( - (scrollPosition: GridScrollParams) => { - const dimensions = apiRef.current.getRootDimensions(); - - // Prevent the infite loading working in combination with lazy loading - if (!dimensions.isReady || props.rowsLoadingMode !== 'client') { - return; - } - - const scrollPositionBottom = scrollPosition.top + dimensions.viewportOuterSize.height; - const viewportPageSize = apiRef.current.getViewportPageSize(); - - if (scrollPositionBottom < contentHeight - props.scrollEndThreshold) { - isInScrollBottomArea.current = false; - } - - if ( - scrollPositionBottom >= contentHeight - props.scrollEndThreshold && - !isInScrollBottomArea.current - ) { - const rowScrollEndParam: GridRowScrollEndParams = { - visibleColumns, - viewportPageSize, - visibleRowsCount: currentPage.rows.length, - }; - apiRef.current.publishEvent('rowsScrollEnd', rowScrollEndParam); - isInScrollBottomArea.current = true; - } - }, - [ - contentHeight, - props.scrollEndThreshold, - props.rowsLoadingMode, - visibleColumns, - apiRef, - currentPage.rows.length, - ], - ); - - const handleGridScroll = React.useCallback>( - ({ left, top }) => { - handleRowsScrollEnd({ left, top }); - }, - [handleRowsScrollEnd], - ); - - useGridApiEventHandler(apiRef, 'scrollPositionChange', handleGridScroll); - useGridApiOptionHandler(apiRef, 'rowsScrollEnd', props.onRowsScrollEnd); -}; diff --git a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx new file mode 100644 index 0000000000000..048dfb47a4166 --- /dev/null +++ b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx @@ -0,0 +1,130 @@ +import * as React from 'react'; +import { + useGridSelector, + useGridApiOptionHandler, + gridVisibleColumnDefinitionsSelector, + useGridApiMethod, + gridDimensionsSelector, +} from '@mui/x-data-grid'; +import { useGridVisibleRows } from '@mui/x-data-grid/internals'; +import useEventCallback from '@mui/utils/useEventCallback'; +import { styled } from '@mui/system'; +import type { GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/models/api/gridInfiniteLoaderApi'; +import { GridRowScrollEndParams } from '../../../models'; +import { GridPrivateApiPro } from '../../../models/gridApiPro'; +import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; + +const InfiniteLoadingTriggerElement = styled('div')({ + position: 'sticky', + left: 0, + width: 0, + height: 0, +}); + +/** + * @requires useGridColumns (state) + * @requires useGridDimensions (method) - can be after + * @requires useGridScroll (method + */ +export const useGridInfiniteLoader = ( + apiRef: React.MutableRefObject, + props: Pick< + DataGridProProcessedProps, + 'onRowsScrollEnd' | 'pagination' | 'paginationMode' | 'rowsLoadingMode' | 'scrollEndThreshold' + >, +): void => { + const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); + const currentPage = useGridVisibleRows(apiRef, props); + const observer = React.useRef(); + const triggerElement = React.useRef(null); + + const isEnabled = props.rowsLoadingMode === 'client' && !!props.onRowsScrollEnd; + + const handleLoadMoreRows = useEventCallback(([entry]: IntersectionObserverEntry[]) => { + const currentRatio = entry.intersectionRatio; + const isIntersecting = entry.isIntersecting; + + if (isIntersecting && currentRatio === 1) { + const viewportPageSize = apiRef.current.getViewportPageSize(); + const rowScrollEndParams: GridRowScrollEndParams = { + visibleColumns, + viewportPageSize, + visibleRowsCount: currentPage.rows.length, + }; + apiRef.current.publishEvent('rowsScrollEnd', rowScrollEndParams); + observer.current?.disconnect(); + // do not observe this node anymore + triggerElement.current = null; + } + }); + + const virtualScroller = apiRef.current.virtualScrollerRef.current; + const dimensions = useGridSelector(apiRef, gridDimensionsSelector); + + const marginBottom = + props.scrollEndThreshold - (dimensions.hasScrollX ? dimensions.scrollbarSize : 0); + + React.useEffect(() => { + if (!isEnabled) { + return; + } + if (!virtualScroller) { + return; + } + observer.current?.disconnect(); + + observer.current = new IntersectionObserver(handleLoadMoreRows, { + threshold: 1, + root: virtualScroller, + rootMargin: `0px 0px ${marginBottom}px 0px`, + }); + if (triggerElement.current) { + observer.current.observe(triggerElement.current); + } + }, [virtualScroller, handleLoadMoreRows, isEnabled, marginBottom]); + + const triggerRef = React.useCallback( + (node: HTMLElement | null) => { + // Prevent the infite loading working in combination with lazy loading + if (!isEnabled) { + return; + } + + if (triggerElement.current !== node) { + observer.current?.disconnect(); + + triggerElement.current = node; + if (triggerElement.current) { + observer.current?.observe(triggerElement.current); + } + } + }, + [isEnabled], + ); + + const getInfiniteLoadingTriggerElement = React.useCallback< + NonNullable + >( + ({ lastRowId }) => { + if (!isEnabled) { + return null; + } + return ( + + ); + }, + [isEnabled, triggerRef], + ); + + const infiteLoaderPrivateApi: GridInfiniteLoaderPrivateApi = { + getInfiniteLoadingTriggerElement, + }; + + useGridApiMethod(apiRef, infiteLoaderPrivateApi, 'private'); + useGridApiOptionHandler(apiRef, 'rowsScrollEnd', props.onRowsScrollEnd); +}; diff --git a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx index 62f2fde968c28..81275e69b7858 100644 --- a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx @@ -329,14 +329,10 @@ describe(' - Events params', () => { }); }); - it('publishing GRID_ROWS_SCROLL should call onRowsScrollEnd callback', () => { - const handleRowsScrollEnd = spy(); - render(); - act(() => apiRef.current.publishEvent('scrollPositionChange', { left: 0, top: 3 * 52 })); - expect(handleRowsScrollEnd.callCount).to.equal(1); - }); - - it('publishing GRID_ROWS_SCROLL should call onFetchRows callback when rows lazy loading is enabled', () => { + it('publishing GRID_ROWS_SCROLL should call onFetchRows callback when rows lazy loading is enabled', function test() { + if (isJSDOM) { + this.skip(); // Needs layout + } const handleFetchRows = spy(); render( - Events params', () => { expect(handleFetchRows.callCount).to.equal(1); }); - it('call onRowsScrollEnd when viewport scroll reaches the bottom', function test() { - if (isJSDOM) { - this.skip(); // Needs layout - } - const baseRows = [ - { id: 0, brand: 'Nike' }, - { id: 1, brand: 'Adidas' }, - { id: 2, brand: 'Puma' }, - { id: 3, brand: 'Under Armor' }, - { id: 4, brand: 'Jordan' }, - { id: 5, brand: 'Reebok' }, - ]; - const handleRowsScrollEnd = spy(); - function TestCase({ rows }: { rows: typeof baseRows }) { - return ( -
- -
- ); - } - const { container, setProps } = render(); - const virtualScroller = container.querySelector('.MuiDataGrid-virtualScroller')!; - // arbitrary number to make sure that the bottom of the grid window is reached. - virtualScroller.scrollTop = 12345; - virtualScroller.dispatchEvent(new Event('scroll')); - expect(handleRowsScrollEnd.callCount).to.equal(1); - setProps({ - rows: baseRows.concat( - { id: 6, brand: 'Gucci' }, - { id: 7, brand: "Levi's" }, - { id: 8, brand: 'Ray-Ban' }, - ), - }); - // Trigger a scroll again to notify the grid that we're not in the bottom area anymore - virtualScroller.dispatchEvent(new Event('scroll')); - expect(handleRowsScrollEnd.callCount).to.equal(1); - virtualScroller.scrollTop = 12345; - virtualScroller.dispatchEvent(new Event('scroll')); - expect(handleRowsScrollEnd.callCount).to.equal(2); - }); - it('should publish "unmount" event when unmounting the Grid', () => { const onUnmount = spy(); diff --git a/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx new file mode 100644 index 0000000000000..9973b2a200b45 --- /dev/null +++ b/packages/x-data-grid-pro/src/tests/infiniteLoader.DataGridPro.test.tsx @@ -0,0 +1,153 @@ +import * as React from 'react'; +import { createRenderer, waitFor } from '@mui-internal/test-utils'; +import { expect } from 'chai'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { spy } from 'sinon'; +import { getColumnValues, sleep } from 'test/utils/helperFn'; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + +describe(' - Infnite loader', () => { + const { render } = createRenderer(); + + it('should call `onRowsScrollEnd` when viewport scroll reaches the bottom', async function test() { + if (isJSDOM) { + this.skip(); // Needs layout + } + const baseRows = [ + { id: 0, brand: 'Nike' }, + { id: 1, brand: 'Adidas' }, + { id: 2, brand: 'Puma' }, + { id: 3, brand: 'Under Armor' }, + { id: 4, brand: 'Jordan' }, + { id: 5, brand: 'Reebok' }, + ]; + const handleRowsScrollEnd = spy(); + function TestCase({ rows }: { rows: typeof baseRows }) { + return ( +
+ +
+ ); + } + const { container, setProps } = render(); + const virtualScroller = container.querySelector('.MuiDataGrid-virtualScroller')!; + // arbitrary number to make sure that the bottom of the grid window is reached. + virtualScroller.scrollTop = 12345; + virtualScroller.dispatchEvent(new Event('scroll')); + await waitFor(() => { + expect(handleRowsScrollEnd.callCount).to.equal(1); + }); + setProps({ + rows: baseRows.concat( + { id: 6, brand: 'Gucci' }, + { id: 7, brand: "Levi's" }, + { id: 8, brand: 'Ray-Ban' }, + ), + }); + // Trigger a scroll again to notify the grid that we're not in the bottom area anymore + virtualScroller.dispatchEvent(new Event('scroll')); + expect(handleRowsScrollEnd.callCount).to.equal(1); + virtualScroller.scrollTop = 12345; + virtualScroller.dispatchEvent(new Event('scroll')); + await waitFor(() => { + expect(handleRowsScrollEnd.callCount).to.equal(2); + }); + }); + + it('should call `onRowsScrollEnd` when there is not enough rows to cover the viewport height', async function test() { + if (isJSDOM) { + this.skip(); // Needs layout + } + + const allRows = [ + { id: 0, brand: 'Nike' }, + { id: 1, brand: 'Adidas' }, + { id: 2, brand: 'Puma' }, + { id: 3, brand: 'Under Armor' }, + { id: 4, brand: 'Jordan' }, + { id: 5, brand: 'Reebok' }, + ]; + const initialRows = [allRows[0]]; + const getRow = spy((id) => { + return allRows.find((row) => row.id === id); + }); + + const scrollEndThreshold = 60; + const rowHeight = 50; + const columnHeaderHeight = 50; + const gridHeight = + 4 * rowHeight + + columnHeaderHeight + + // border + 2; + + function TestCase() { + const [rows, setRows] = React.useState(initialRows); + const [loading, setLoading] = React.useState(false); + const handleRowsScrollEnd = React.useCallback(async () => { + setLoading(true); + await sleep(50); + setRows((prevRows) => { + const lastRowId = prevRows[prevRows.length - 1].id; + const nextRow = getRow(lastRowId + 1); + return nextRow ? prevRows.concat(nextRow) : prevRows; + }); + setLoading(false); + }, []); + return ( +
+ +
+ ); + } + render(); + + // data grid should have loaded 6 rows: + // 1 initial row + // 5 rows loaded one by one through `onRowsScrollEnd` callback + + expect(getColumnValues(0)).to.deep.equal(['0']); + await waitFor(() => { + expect(getRow.callCount).to.equal(1); + }); + expect(getColumnValues(0)).to.deep.equal(['0', '1']); + + await waitFor(() => { + expect(getRow.callCount).to.equal(2); + }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); + + await waitFor(() => { + expect(getRow.callCount).to.equal(3); + }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3']); + + await waitFor(() => { + expect(getRow.callCount).to.equal(4); + }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4']); + + await waitFor(() => { + expect(getRow.callCount).to.equal(5); + }); + expect(getColumnValues(0)).to.deep.equal(['0', '1', '2', '3', '4', '5']); + + await sleep(200); + // should not load more rows because the threshold is not reached + expect(getRow.callCount).to.equal(5); + }); +}); diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index 1d533ee81bd05..ec45b0a581b62 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -5,7 +5,7 @@ import { unstable_useEventCallback as useEventCallback, } from '@mui/utils'; import { useTheme, Theme } from '@mui/material/styles'; -import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import type { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext'; import { useGridRootProps } from '../../utils/useGridRootProps'; import { useGridSelector } from '../../utils/useGridSelector'; @@ -376,8 +376,10 @@ export const useGridVirtualScroller = () => { if (panel) { rows.push(panel); } + if (isLastVisible) { + rows.push(apiRef.current.getInfiniteLoadingTriggerElement?.({ lastRowId: id })); + } }); - return rows; }; diff --git a/packages/x-data-grid/src/models/api/gridApiCommon.ts b/packages/x-data-grid/src/models/api/gridApiCommon.ts index fc87a36a4d8b6..8338cd42d2c7d 100644 --- a/packages/x-data-grid/src/models/api/gridApiCommon.ts +++ b/packages/x-data-grid/src/models/api/gridApiCommon.ts @@ -34,6 +34,7 @@ import { GridColumnGroupingApi } from './gridColumnGroupingApi'; import type { GridInitialStateCommunity, GridStateCommunity } from '../gridStateCommunity'; import { GridHeaderFilteringApi, GridHeaderFilteringPrivateApi } from './gridHeaderFilteringApi'; import type { DataGridProcessedProps } from '../props/DataGridProps'; +import type { GridInfiniteLoaderPrivateApi } from './gridInfiniteLoaderApi'; export interface GridApiCommon< GridState extends GridStateCommunity = any, @@ -80,7 +81,8 @@ export interface GridPrivateOnlyApiCommon< GridLoggerApi, GridFocusPrivateApi, GridHeaderFilteringPrivateApi, - GridVirtualizationPrivateApi {} + GridVirtualizationPrivateApi, + GridInfiniteLoaderPrivateApi {} export interface GridPrivateApiCommon extends GridApiCommon, diff --git a/packages/x-data-grid/src/models/api/gridInfiniteLoaderApi.ts b/packages/x-data-grid/src/models/api/gridInfiniteLoaderApi.ts new file mode 100644 index 0000000000000..061f9623154ef --- /dev/null +++ b/packages/x-data-grid/src/models/api/gridInfiniteLoaderApi.ts @@ -0,0 +1,7 @@ +export interface GridInfiniteLoaderPrivateApi { + getInfiniteLoadingTriggerElement?: ({ + lastRowId, + }: { + lastRowId: string | number; + }) => React.ReactNode; +} From 2e443fb5b4e8401f5ec44083ce1dfccc73addbe2 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Fri, 8 Mar 2024 06:18:27 +0100 Subject: [PATCH 06/49] [TreeView] Fix invalid nodes state when updating `props.items` (#12359) --- .../src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts index 5a2eee61e155c..ecbd95aa5eb3a 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts @@ -169,7 +169,7 @@ export const useTreeViewNodes: TreeViewPlugin = ({ } }); - return { ...prevState, ...newState }; + return { ...prevState, nodes: newState }; }); }, [ instance, From 4f8f20ac01c06c0168a7c6c87bbdb860fd300e40 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 8 Mar 2024 09:45:26 +0100 Subject: [PATCH 07/49] [charts] Fix RTL legend (#12175) --- .../src/ChartsLegend/DefaultChartsLegend.tsx | 94 +++++++++---------- packages/x-charts/src/PieChart/PieChart.tsx | 13 ++- packages/x-charts/src/internals/useIsRTL.ts | 6 ++ 3 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 packages/x-charts/src/internals/useIsRTL.ts diff --git a/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx b/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx index 5aa0e10e21f7b..14d05225c2e4e 100644 --- a/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx +++ b/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx @@ -106,6 +106,7 @@ function DefaultChartsLegend(props: LegendRendererProps) { labelStyle: inLabelStyle, } = props; const theme = useTheme(); + const isRTL = theme.direction === 'rtl'; const labelStyle = React.useMemo( () => @@ -146,7 +147,7 @@ function DefaultChartsLegend(props: LegendRendererProps) { const availableWidth = totalWidth - padding.left - padding.right; const availableHeight = totalHeight - padding.top - padding.bottom; - const seriesWithPosition = React.useMemo(() => { + const [seriesWithPosition, legendWidth, legendHeight] = React.useMemo(() => { // Start at 0, 0. Will be modified later by padding and position. let x = 0; let y = 0; @@ -214,60 +215,49 @@ function DefaultChartsLegend(props: LegendRendererProps) { return rep; }); - // Move the legend according to padding and position - let gapX = 0; - let gapY = 0; + return [ + seriesWithRawPosition.map((item) => ({ + ...item, + positionY: + item.positionY + + (direction === 'row' + ? rowMaxHeight[item.rowIndex] / 2 // Get the center of the entire row + : item.outerHeight / 2), // Get the center of the item + })), + totalWidthUsed, + totalHeightUsed, + ]; + }, [ + seriesToDisplay, + getItemSpace, + labelStyle, + direction, + availableWidth, + availableHeight, + itemGap, + ]); + + const gapX = React.useMemo(() => { switch (position.horizontal) { case 'left': - gapX = padding.left; - break; + return padding.left; case 'right': - gapX = totalWidth - padding.right - totalWidthUsed; - break; + return totalWidth - padding.right - legendWidth; default: - gapX = (totalWidth - totalWidthUsed) / 2; - break; + return (totalWidth - legendWidth) / 2; } + }, [position.horizontal, padding.left, padding.right, totalWidth, legendWidth]); + + const gapY = React.useMemo(() => { switch (position.vertical) { case 'top': - gapY = padding.top; - break; + return padding.top; case 'bottom': - gapY = totalHeight - padding.bottom - totalHeightUsed; - break; + return totalHeight - padding.bottom - legendHeight; default: - gapY = (totalHeight - totalHeightUsed) / 2; - break; + return (totalHeight - legendHeight) / 2; } - return seriesWithRawPosition.map((item) => ({ - ...item, - // Add the gap due to the position - positionX: item.positionX + gapX, - // Add the gap due to the position - positionY: - item.positionY + - gapY + - (direction === 'row' - ? rowMaxHeight[item.rowIndex] / 2 // Get the center of the entire row - : item.outerHeight / 2), // Get the center of the item - })); - }, [ - seriesToDisplay, - position.horizontal, - position.vertical, - getItemSpace, - labelStyle, - direction, - availableWidth, - availableHeight, - itemGap, - padding.left, - padding.right, - padding.top, - padding.bottom, - totalWidth, - totalHeight, - ]); + }, [position.vertical, padding.top, padding.bottom, totalHeight, legendHeight]); if (hidden) { return null; @@ -277,15 +267,25 @@ function DefaultChartsLegend(props: LegendRendererProps) { {seriesWithPosition.map(({ id, label, color, positionX, positionY }) => ( - + - + ))} diff --git a/packages/x-charts/src/PieChart/PieChart.tsx b/packages/x-charts/src/PieChart/PieChart.tsx index 3b0a054423e49..2100b6f194a9f 100644 --- a/packages/x-charts/src/PieChart/PieChart.tsx +++ b/packages/x-charts/src/PieChart/PieChart.tsx @@ -29,6 +29,7 @@ import { ChartsXAxisProps, ChartsYAxisProps, } from '../models/axis'; +import { useIsRTL } from '../internals/useIsRTL'; export interface PieChartSlots extends ChartsAxisSlots, @@ -97,6 +98,7 @@ export interface PieChartProps } const defaultMargin = { top: 5, bottom: 5, left: 5, right: 100 }; +const defaultRTLMargin = { top: 5, bottom: 5, left: 100, right: 5 }; /** * Demos: @@ -121,7 +123,7 @@ function PieChart(props: PieChartProps) { tooltip = { trigger: 'item' }, axisHighlight = { x: 'none', y: 'none' }, skipAnimation, - legend = { direction: 'column', position: { vertical: 'middle', horizontal: 'right' } }, + legend: legendProps, topAxis = null, leftAxis = null, rightAxis = null, @@ -131,8 +133,15 @@ function PieChart(props: PieChartProps) { slotProps, onItemClick, } = props; + const isRTL = useIsRTL(); + + const margin = { ...(isRTL ? defaultRTLMargin : defaultMargin), ...marginProps }; + const legend: ChartsLegendProps = { + direction: 'column', + position: { vertical: 'middle', horizontal: isRTL ? 'left' : 'right' }, + ...legendProps, + }; - const margin = { ...defaultMargin, ...marginProps }; return ( ({ type: 'pie', ...s }))} diff --git a/packages/x-charts/src/internals/useIsRTL.ts b/packages/x-charts/src/internals/useIsRTL.ts new file mode 100644 index 0000000000000..21e7f0fe692d6 --- /dev/null +++ b/packages/x-charts/src/internals/useIsRTL.ts @@ -0,0 +1,6 @@ +import { useTheme } from '@mui/material/styles'; + +export const useIsRTL = () => { + const theme = useTheme(); + return theme.direction === 'rtl'; +}; From b66a47275554caebbd61c3ad97a83defa6dab01c Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Fri, 8 Mar 2024 10:06:36 +0100 Subject: [PATCH 08/49] [charts] Add context to axis value formatter (#12172) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Lukas --- docs/data/charts/axis/FormatterDemoNoSnap.js | 123 ++++++++++++++++++ docs/data/charts/axis/FormatterDemoNoSnap.tsx | 123 ++++++++++++++++++ .../axis/FormatterDemoNoSnap.tsx.preview | 15 +++ docs/data/charts/axis/axis.md | 12 ++ .../DefaultChartsAxisTooltipContent.tsx | 2 +- packages/x-charts/src/hooks/useTicks.ts | 11 +- packages/x-charts/src/models/axis.ts | 12 +- 7 files changed, 291 insertions(+), 7 deletions(-) create mode 100644 docs/data/charts/axis/FormatterDemoNoSnap.js create mode 100644 docs/data/charts/axis/FormatterDemoNoSnap.tsx create mode 100644 docs/data/charts/axis/FormatterDemoNoSnap.tsx.preview diff --git a/docs/data/charts/axis/FormatterDemoNoSnap.js b/docs/data/charts/axis/FormatterDemoNoSnap.js new file mode 100644 index 0000000000000..8deeeddfd4883 --- /dev/null +++ b/docs/data/charts/axis/FormatterDemoNoSnap.js @@ -0,0 +1,123 @@ +import * as React from 'react'; +import { axisClasses } from '@mui/x-charts/ChartsAxis'; +import { BarChart } from '@mui/x-charts/BarChart'; + +const otherSetting = { + height: 300, + yAxis: [{ label: 'rainfall (mm)' }], + grid: { horizontal: true }, + sx: { + [`& .${axisClasses.left} .${axisClasses.label}`]: { + transform: 'translateX(-10px)', + }, + }, +}; + +const dataset = [ + { + london: 59, + paris: 57, + newYork: 86, + seoul: 21, + month: 'January', + }, + { + london: 50, + paris: 52, + newYork: 78, + seoul: 28, + month: 'February', + }, + { + london: 47, + paris: 53, + newYork: 106, + seoul: 41, + month: 'March', + }, + { + london: 54, + paris: 56, + newYork: 92, + seoul: 73, + month: 'April', + }, + { + london: 57, + paris: 69, + newYork: 92, + seoul: 99, + month: 'May', + }, + { + london: 60, + paris: 63, + newYork: 103, + seoul: 144, + month: 'June', + }, + { + london: 59, + paris: 60, + newYork: 105, + seoul: 319, + month: 'July', + }, + { + london: 65, + paris: 60, + newYork: 106, + seoul: 249, + month: 'August', + }, + { + london: 51, + paris: 51, + newYork: 95, + seoul: 131, + month: 'September', + }, + { + london: 60, + paris: 65, + newYork: 97, + seoul: 55, + month: 'October', + }, + { + london: 67, + paris: 64, + newYork: 76, + seoul: 48, + month: 'November', + }, + { + london: 61, + paris: 70, + newYork: 103, + seoul: 25, + month: 'December', + }, +]; + +const valueFormatter = (value) => `${value}mm`; + +export default function FormatterDemoNoSnap() { + return ( + + context.location === 'tick' + ? `${month.slice(0, 3)} \n2023` + : `${month} 2023`, + }, + ]} + series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]} + {...otherSetting} + /> + ); +} diff --git a/docs/data/charts/axis/FormatterDemoNoSnap.tsx b/docs/data/charts/axis/FormatterDemoNoSnap.tsx new file mode 100644 index 0000000000000..13ecee9dcaabc --- /dev/null +++ b/docs/data/charts/axis/FormatterDemoNoSnap.tsx @@ -0,0 +1,123 @@ +import * as React from 'react'; +import { axisClasses } from '@mui/x-charts/ChartsAxis'; +import { BarChart } from '@mui/x-charts/BarChart'; + +const otherSetting = { + height: 300, + yAxis: [{ label: 'rainfall (mm)' }], + grid: { horizontal: true }, + sx: { + [`& .${axisClasses.left} .${axisClasses.label}`]: { + transform: 'translateX(-10px)', + }, + }, +}; + +const dataset = [ + { + london: 59, + paris: 57, + newYork: 86, + seoul: 21, + month: 'January', + }, + { + london: 50, + paris: 52, + newYork: 78, + seoul: 28, + month: 'February', + }, + { + london: 47, + paris: 53, + newYork: 106, + seoul: 41, + month: 'March', + }, + { + london: 54, + paris: 56, + newYork: 92, + seoul: 73, + month: 'April', + }, + { + london: 57, + paris: 69, + newYork: 92, + seoul: 99, + month: 'May', + }, + { + london: 60, + paris: 63, + newYork: 103, + seoul: 144, + month: 'June', + }, + { + london: 59, + paris: 60, + newYork: 105, + seoul: 319, + month: 'July', + }, + { + london: 65, + paris: 60, + newYork: 106, + seoul: 249, + month: 'August', + }, + { + london: 51, + paris: 51, + newYork: 95, + seoul: 131, + month: 'September', + }, + { + london: 60, + paris: 65, + newYork: 97, + seoul: 55, + month: 'October', + }, + { + london: 67, + paris: 64, + newYork: 76, + seoul: 48, + month: 'November', + }, + { + london: 61, + paris: 70, + newYork: 103, + seoul: 25, + month: 'December', + }, +]; + +const valueFormatter = (value: number | null) => `${value}mm`; + +export default function FormatterDemoNoSnap() { + return ( + + context.location === 'tick' + ? `${month.slice(0, 3)} \n2023` + : `${month} 2023`, + }, + ]} + series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]} + {...otherSetting} + /> + ); +} diff --git a/docs/data/charts/axis/FormatterDemoNoSnap.tsx.preview b/docs/data/charts/axis/FormatterDemoNoSnap.tsx.preview new file mode 100644 index 0000000000000..2184f51b947ff --- /dev/null +++ b/docs/data/charts/axis/FormatterDemoNoSnap.tsx.preview @@ -0,0 +1,15 @@ + + context.location === 'tick' + ? `${month.slice(0, 3)} \n2023` + : `${month} 2023`, + }, + ]} + series={[{ dataKey: 'seoul', label: 'Seoul rainfall', valueFormatter }]} + {...otherSetting} +/> \ No newline at end of file diff --git a/docs/data/charts/axis/axis.md b/docs/data/charts/axis/axis.md index 0fa220dbc0fa3..71cbc5d78c6ed 100644 --- a/docs/data/charts/axis/axis.md +++ b/docs/data/charts/axis/axis.md @@ -59,6 +59,18 @@ Some series types also require specific axis attributes: - line plots require an `xAxis` to have `data` provided - bar plots require an `xAxis` with `scaleType='band'` and some `data` provided. +### Axis formatter + +Axis data can be displayed in the axes ticks and the tooltip. +To modify how data is displayed use the `valueFormatter` property. + +The second argument of `valueFormatter` provides some rendering context for advanced use cases. + +In the next demo, `valueFormatter` is used to shorten months and introduce a breaking space for ticks only. +To distinguish tick and tooltip, it uses the `context.location`. + +{{"demo": "FormatterDemoNoSnap.js"}} + ### Axis sub domain By default, the axis domain is computed such that all your data is visible. diff --git a/packages/x-charts/src/ChartsTooltip/DefaultChartsAxisTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/DefaultChartsAxisTooltipContent.tsx index c53f431311fcb..856a45da8138a 100644 --- a/packages/x-charts/src/ChartsTooltip/DefaultChartsAxisTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/DefaultChartsAxisTooltipContent.tsx @@ -31,7 +31,7 @@ function DefaultChartsAxisTooltipContent(props: ChartsAxisContentProps) { - {axisFormatter(axisValue)} + {axisFormatter(axisValue, { location: 'tooltip' })} diff --git a/packages/x-charts/src/hooks/useTicks.ts b/packages/x-charts/src/hooks/useTicks.ts index 004de71fe8339..25d4a808ae7ad 100644 --- a/packages/x-charts/src/hooks/useTicks.ts +++ b/packages/x-charts/src/hooks/useTicks.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { D3Scale } from '../models/axis'; +import { AxisConfig, D3Scale } from '../models/axis'; import { isBandScale } from '../internals/isBandScale'; export interface TickParams { @@ -80,7 +80,7 @@ export type TickItemType = { export function useTicks( options: { scale: D3Scale; - valueFormatter?: (value: any) => string; + valueFormatter?: AxisConfig['valueFormatter']; } & Pick, ): TickItemType[] { const { @@ -102,7 +102,7 @@ export function useTicks( return [ ...domain.map((value) => ({ value, - formattedValue: valueFormatter?.(value) ?? `${value}`, + formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`, offset: scale(value)! - (scale.step() - scale.bandwidth()) / 2 + @@ -133,7 +133,7 @@ export function useTicks( return filteredDomain.map((value) => ({ value, - formattedValue: valueFormatter?.(value) ?? `${value}`, + formattedValue: valueFormatter?.(value, { location: 'tick' }) ?? `${value}`, offset: scale(value)!, labelOffset: 0, })); @@ -142,7 +142,8 @@ export function useTicks( const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber); return ticks.map((value: any) => ({ value, - formattedValue: valueFormatter?.(value) ?? scale.tickFormat(tickNumber)(value), + formattedValue: + valueFormatter?.(value, { location: 'tick' }) ?? scale.tickFormat(tickNumber)(value), offset: scale(value), labelOffset: 0, })); diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index 0eeeae2dbfaa0..b469b8075d2d3 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -186,6 +186,10 @@ interface AxisScaleConfig { }; } +export type AxisValueFormatterContext = { + location: 'tick' | 'tooltip'; +}; + export type AxisConfig = { /** * Id used to identify the axis. @@ -209,7 +213,13 @@ export type AxisConfig = { * The key used to retrieve `data` from the `dataset` prop. */ dataKey?: string; - valueFormatter?: (value: V) => string; + /** + * Formats the axis value. + * @param {V} value The value to format. + * @param {AxisValueFormatterContext} context The rendering context of the value. + * @returns {string} The string to display. + */ + valueFormatter?: (value: V, context: AxisValueFormatterContext) => string; /** * If `true`, hide this value in the tooltip */ From f96c3198c59f4ef6796587de316919d28e645bd1 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Fri, 8 Mar 2024 16:35:21 +0100 Subject: [PATCH 09/49] v7.0.0-beta.6 (#12375) Signed-off-by: Flavien DELANGLE Co-authored-by: Lukas Co-authored-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> --- CHANGELOG.md | 184 ++++++++++++++------ package.json | 2 +- packages/x-charts/package.json | 2 +- packages/x-codemod/package.json | 2 +- packages/x-data-grid-generator/package.json | 4 +- packages/x-data-grid-premium/package.json | 8 +- packages/x-data-grid-pro/package.json | 6 +- packages/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 6 +- packages/x-date-pickers/package.json | 2 +- packages/x-license/package.json | 2 +- packages/x-tree-view/package.json | 2 +- scripts/releaseChangelog.mjs | 10 +- 13 files changed, 161 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5347ebac33cf0..db25637ddd28d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,92 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 7.0.0-beta.6 + +_Mar 8, 2024_ + +We'd like to offer a big thanks to the 8 contributors who made this release possible. Here are some highlights ✨: + +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@7.0.0-beta.6` + +- [DataGrid] Fix crashing of demos on rating change (#12315) @sai6855 +- [DataGrid] Fix double border below header (#12349) @joespeargresham +- [DataGrid] Fix empty sort being saved in the `sortModel` (#12325) @MBilalShafi +- [DataGrid] Remove unnecessary `stopCellMode` event in `renderEditRating` component (#12335) @sai6855 +- [DataGrid] Small performance optimizations (#12346) @romgrk + +#### `@mui/x-data-grid-pro@7.0.0-beta.6` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-data-grid@7.0.0-beta.6`, plus: + +- [DataGridPro] Rework `onRowsScrollEnd` to use `IntersectionObserver` (#8672) @DanailH + +#### `@mui/x-data-grid-premium@7.0.0-beta.6` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan') + +Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.6`. + +### Charts + +#### `@mui/x-charts@7.0.0-beta.6` + +- [charts] Add context to axis value formatter (#12172) @alexfauquette +- [charts] Customize tick position for band scale (#12316) @alexfauquette +- [charts] Fix RTL legend (#12175) @alexfauquette + +### Tree View + +#### Breaking changes + +- The component used to animate the item children is now defined as a slot on the `TreeItem` component. + + If you were passing a `TransitionComponent` or `TransitionProps` to your `TreeItem` component, + you need to use the new `groupTransition` slot on this component: + + ```diff + + + + ``` + +- The `group` class of the `TreeItem` component has been renamed to `groupTransition` to match with its new slot name. + + ```diff + const StyledTreeItem = styled(TreeItem)({ + - [`& .${treeItemClasses.group}`]: { + + [`& .${treeItemClasses.groupTransition}`]: { + marginLeft: 20, + }, + }); + ``` + +#### `@mui/x-tree-view@7.0.0-beta.6` + +- [TreeView] Fix invalid nodes state when updating `props.items` (#12359) @flaviendelangle +- [TreeView] In the `RichTreeView`, do not use the item id as the HTML id attribute (#12319) @flaviendelangle +- [TreeView] New instance and publicAPI method: `getItem` (#12251) @flaviendelangle +- [TreeView] Replace `TransitionComponent` and `TransitionProps` with a `groupTransition` slot (#12336) @flaviendelangle + +### Docs + +- [docs] Add a note about `z-index` usage in SVG (#12337) @alexfauquette +- [docs] `RichTreeView` customization docs (#12231) @noraleonte + +### Core + +- [test] Add `Charts` test (#11551) @alexfauquette + ## 7.0.0-beta.5 _Mar 1, 2024_ @@ -45,7 +131,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.5`, plus: - [DataGridPremium] Make clipboard copy respect the sorting during cell selection (#12235) @MBilalShafi -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-beta.5` @@ -121,7 +207,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-beta.4`. Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.4`. -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -335,7 +421,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.2`, plus: - [DataGridPremium] Fix autosize grouping cell (#11870) @romgrk - [DataGridPremium] Fix clipboard paste not working with Caps Lock enabled (#11965) @shaharyar-shamshi -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-beta.2` @@ -522,7 +608,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-beta.1`. Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-beta.1` @@ -627,7 +713,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-beta.0`. Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-beta.0` @@ -766,7 +852,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.9`, plus: - [DataGridPremium] Allow aggregation to be applied for non-aggregable columns (#11574) @MBilalShafi - [DataGridPremium] Allow programmatically grouping non-groupable columns (#11539) @MBilalShafi -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -1051,7 +1137,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.8`. Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.8`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-alpha.8` @@ -1141,7 +1227,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.7`. Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.7`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-alpha.7` @@ -1283,7 +1369,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.6`. Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.6`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-alpha.6` @@ -1351,7 +1437,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.5`. Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.5`. -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -1535,7 +1621,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.4`, plus: Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.4`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-alpha.4` @@ -1635,7 +1721,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.3`, plus: - [DataGridPremium] Make Cell selection feature stable (#11246) @MBilalShafi - [DataGridPremium] Make Clipboard paste feature stable (#11248) @MBilalShafi -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -1772,7 +1858,7 @@ Same changes as in `@mui/x-data-grid@7.0.0-alpha.2`. Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.2`. -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -1878,7 +1964,7 @@ We'd like to offer a big thanks to the 3 contributors who made this release poss - 🐞 Bugfixes - 📚 Documentation improvements -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@7.0.0-alpha.1` / `@mui/x-date-pickers-pro@7.0.0-alpha.1` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') @@ -2374,7 +2460,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.0`, plus: - [DataGridPremium] Render aggregation label when `renderHeader` is used (#10936) @cherniavskii -### Date Pickers +### Date and Time Pickers #### Breaking changes @@ -2461,7 +2547,7 @@ Same changes as in `@mui/x-data-grid-pro@6.19.6`, plus: - [DataGridPremium] Make clipboard copy respect the sorting during cell selection (#12255) @MBilalShafi -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.6` @@ -2499,7 +2585,7 @@ Same changes as in `@mui/x-data-grid@6.19.5`. Same changes as in `@mui/x-data-grid-pro@6.19.5`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.5` @@ -2568,7 +2654,7 @@ Same changes as in `@mui/x-data-grid-pro@6.19.4`, plus: - [DataGridPremium] Fix autosize grouping cell (#11990) @romgrk - [DataGridPremium] Fix error after closing print export (#11889) @cherniavskii -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.4` @@ -2617,7 +2703,7 @@ Same changes as in `@mui/x-data-grid@6.19.3`. Same changes as in `@mui/x-data-grid-pro@6.19.3`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.3` @@ -2651,7 +2737,7 @@ We'd like to offer a big thanks to the 2 contributors who made this release poss - 🚀 Apply the `layout.tabs` class to `Tabs` slot (@LukasTy) (#11782) - 🐞 Bugfixes -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.2` @@ -2725,7 +2811,7 @@ We'd like to offer a big thanks to the 3 contributors who made this release poss import { de } from 'date-fns/locale/de'; ``` -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.19.0` @@ -2772,7 +2858,7 @@ Same changes as in `@mui/x-data-grid@6.18.7`. Same changes as in `@mui/x-data-grid-pro@6.18.7`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.7` @@ -2814,7 +2900,7 @@ Same changes as in `@mui/x-data-grid@6.18.6`. Same changes as in `@mui/x-data-grid-pro@6.18.6`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.6` @@ -2863,7 +2949,7 @@ Same changes as in `@mui/x-data-grid@6.18.5`. Same changes as in `@mui/x-data-grid-pro@6.18.5`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.5` @@ -2906,7 +2992,7 @@ Same changes as in `@mui/x-data-grid@6.18.4`. Same changes as in `@mui/x-data-grid-pro@6.18.4`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.4` @@ -2952,7 +3038,7 @@ Same changes as in `@mui/x-data-grid-pro@6.18.3`, plus: - [DataGridPremium] Fix aggregated column ignoring column definition changes (#11176) @cherniavskii - [DataGridPremium] Fix custom filter operators not working on aggregated column (#11201) @cherniavskii -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.3` @@ -3008,7 +3094,7 @@ Same changes as in `@mui/x-data-grid@6.18.2`. Same changes as in `@mui/x-data-grid-pro@6.18.2`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.2` @@ -3065,7 +3151,7 @@ Same changes as in `@mui/x-data-grid-pro@6.18.1`, plus: - [DataGridPremium] Render aggregation label when `renderHeader` is used (#10961) @cherniavskii -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.1` @@ -3124,7 +3210,7 @@ Same changes as in `@mui/x-data-grid@6.18.0`. Same changes as in `@mui/x-data-grid-pro@6.18.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.18.0` @@ -3189,7 +3275,7 @@ Same changes as in `@mui/x-data-grid-pro@6.17.0`, plus: - [DataGridPremium] Fix `sum` aggregation to ignore non-numeric values (#10730) @cherniavskii - [DataGridPremium] Fix cell selection throwing index error on second page and beyond (#10784) @MBilalShafi -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.17.0` @@ -3243,7 +3329,7 @@ Same changes as in `@mui/x-data-grid@6.16.3`. Same changes as in `@mui/x-data-grid-pro@6.16.3`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.16.3` @@ -3319,7 +3405,7 @@ Same changes as in `@mui/x-data-grid@6.16.2`, plus: Same changes as in `@mui/x-data-grid-pro@6.16.2`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.16.2` @@ -3414,7 +3500,7 @@ Same changes as in `@mui/x-data-grid@6.16.1`. Same changes as in `@mui/x-data-grid-pro@6.16.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.16.1` @@ -3490,7 +3576,7 @@ Same changes as in `@mui/x-data-grid@6.16.0`, plus: Same changes as in `@mui/x-data-grid-pro@6.16.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.16.0` @@ -3569,7 +3655,7 @@ Same changes as in `@mui/x-data-grid@6.15.0`, plus: Same changes as in `@mui/x-data-grid-pro@6.15.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.15.0` @@ -3648,7 +3734,7 @@ Same changes as in `@mui/x-data-grid-pro@6.14.0`, plus: - [DataGridPremium] Fix clipboard import cutting off at 100 rows (#9930) @gitstart -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.14.0` @@ -3729,7 +3815,7 @@ Same changes as in `@mui/x-data-grid-pro@6.13.0`, plus: - [DataGridPremium] Fix aggregated column resizing (#10079) @cherniavskii -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.13.0` @@ -3801,7 +3887,7 @@ Same changes as in `@mui/x-data-grid@6.12.1`. Same changes as in `@mui/x-data-grid-pro@6.12.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.12.1` @@ -3857,7 +3943,7 @@ Same changes as in `@mui/x-data-grid@6.12.0`. Same changes as in `@mui/x-data-grid-pro@6.12.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.12.0` @@ -3928,7 +4014,7 @@ Same changes as in `@mui/x-data-grid@6.11.2`. Same changes as in `@mui/x-data-grid-pro@6.11.2`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.11.2` @@ -3979,7 +4065,7 @@ Same changes as in `@mui/x-data-grid@6.11.1`. Same changes as in `@mui/x-data-grid-pro@6.11.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.11.1` @@ -4048,7 +4134,7 @@ Same changes as in `@mui/x-data-grid@6.11.0`. Same changes as in `@mui/x-data-grid-pro@6.11.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.11.0` @@ -4136,7 +4222,7 @@ Same changes as in `@mui/x-data-grid-pro@6.10.2`, plus: - [DataGridPremium] Allow to customize grouping cell offset (#9417) @cherniavskii -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.10.2` @@ -4205,7 +4291,7 @@ Same changes as in `@mui/x-data-grid@6.10.1`, plus: Same changes as in `@mui/x-data-grid-pro@6.10.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.10.1` @@ -4271,7 +4357,7 @@ Same changes as in `@mui/x-data-grid@6.10.0`. Same changes as in `@mui/x-data-grid-pro@6.10.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.10.0` @@ -4341,7 +4427,7 @@ Same changes as in `@mui/x-data-grid-pro@6.9.2`, plus: - [DataGridPremium] Auto-scroll when making range selection (#8661) @m4theushw -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.9.2` @@ -4407,7 +4493,7 @@ Same changes as in `@mui/x-data-grid@6.9.1`, plus: Same changes as in `@mui/x-data-grid-pro@6.9.1`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.9.1` @@ -4489,7 +4575,7 @@ Same changes as in `@mui/x-data-grid@6.9.0`. Same changes as in `@mui/x-data-grid-pro@6.9.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.9.0` @@ -4565,7 +4651,7 @@ Same changes as in `@mui/x-data-grid@6.8.0`. Same changes as in `@mui/x-data-grid-pro@6.8.0`. -### Date Pickers +### Date and Time Pickers #### `@mui/x-date-pickers@6.8.0` diff --git a/package.json b/package.json index 797f43162b59b..f72a674494976 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "private": true, "scripts": { "start": "yarn && yarn docs:dev", diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index 470608b490bfb..dcfa223d0e6b7 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The community edition of the charts components (MUI X).", "author": "MUI Team", "main": "./src/index.js", diff --git a/packages/x-codemod/package.json b/packages/x-codemod/package.json index b8e0266754886..584e7656f02ee 100644 --- a/packages/x-codemod/package.json +++ b/packages/x-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-codemod", - "version": "7.0.0-beta.0", + "version": "7.0.0-beta.6", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/x-data-grid-generator/package.json b/packages/x-data-grid-generator/package.json index a1204ba77fba0..2e443e622ccac 100644 --- a/packages/x-data-grid-generator/package.json +++ b/packages/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", @@ -34,7 +34,7 @@ "dependencies": { "@babel/runtime": "^7.24.0", "@mui/base": "^5.0.0-beta.36", - "@mui/x-data-grid-premium": "7.0.0-beta.5", + "@mui/x-data-grid-premium": "7.0.0-beta.6", "chance": "^1.1.11", "clsx": "^2.1.0", "lru-cache": "^7.18.3" diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index 5fde4b4827c2f..bd44e729ef13d 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The Premium plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,9 +45,9 @@ "@babel/runtime": "^7.24.0", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-data-grid": "7.0.0-beta.5", - "@mui/x-data-grid-pro": "7.0.0-beta.5", - "@mui/x-license": "7.0.0-beta.2", + "@mui/x-data-grid": "7.0.0-beta.6", + "@mui/x-data-grid-pro": "7.0.0-beta.6", + "@mui/x-license": "7.0.0-beta.6", "@types/format-util": "^1.0.4", "clsx": "^2.1.0", "exceljs": "^4.4.0", diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index e1c10af5d56a6..c837376f80920 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The Pro plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,8 +45,8 @@ "@babel/runtime": "^7.24.0", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-data-grid": "7.0.0-beta.5", - "@mui/x-license": "7.0.0-beta.2", + "@mui/x-data-grid": "7.0.0-beta.6", + "@mui/x-license": "7.0.0-beta.6", "@types/format-util": "^1.0.4", "clsx": "^2.1.0", "prop-types": "^15.8.1", diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index eee28b6cac29d..cc54a34e3ce8d 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The community edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 4dcfd8e2c3803..0ed650b4ee7a4 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The commercial edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,8 +45,8 @@ "@mui/base": "^5.0.0-beta.36", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-date-pickers": "7.0.0-beta.5", - "@mui/x-license": "7.0.0-beta.2", + "@mui/x-date-pickers": "7.0.0-beta.6", + "@mui/x-license": "7.0.0-beta.6", "clsx": "^2.1.0", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 6eef061749772..7a6a2e959de78 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The community edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-license/package.json b/packages/x-license/package.json index 631323c2e6e65..f640f9a2e22ce 100644 --- a/packages/x-license/package.json +++ b/packages/x-license/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-license", - "version": "7.0.0-beta.2", + "version": "7.0.0-beta.6", "description": "MUI X License verification", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index c6f3ff83030bb..e62f932681f9d 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view", - "version": "7.0.0-beta.5", + "version": "7.0.0-beta.6", "description": "The community edition of the tree view components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/scripts/releaseChangelog.mjs b/scripts/releaseChangelog.mjs index f616cf4b5e5fe..f05642243277f 100644 --- a/scripts/releaseChangelog.mjs +++ b/scripts/releaseChangelog.mjs @@ -246,7 +246,7 @@ Same changes as in \`@mui/x-data-grid-pro@__VERSION__\`${ dataGridPremiumCommits.length > 0 ? ', plus:\n' : '.' } ${logChangelogSection(dataGridPremiumCommits)}${dataGridPremiumCommits.length > 0 ? '\n' : ''} -### Date Pickers +### Date and Time Pickers #### \`@mui/x-date-pickers@__VERSION__\` @@ -259,11 +259,15 @@ Same changes as in \`@mui/x-date-pickers@__VERSION__\`${ } ${logChangelogSection(pickersProCommits)} -### Charts / \`@mui/x-charts@__VERSION__\` +### Charts + +#### \`@mui/x-charts@__VERSION__\` ${logChangelogSection(chartsCommits)} -### Tree View / \`@mui/x-tree-view@__VERSION__\` +### Tree View + +#### \`@mui/x-tree-view@__VERSION__\` ${logChangelogSection(treeViewCommits)} ${logChangelogSection(codemodCommits, `### \`@mui/x-codemod@__VERSION__\``)} From df31301527846759ef17e65f0533b1a09933ce3f Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Fri, 8 Mar 2024 16:38:45 +0100 Subject: [PATCH 10/49] [fields] Fix `tabIndex` on accessible field DOM structure (#12311) --- .../src/internals/hooks/useField/useFieldV7TextField.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useFieldV7TextField.ts b/packages/x-date-pickers/src/internals/hooks/useField/useFieldV7TextField.ts index f369bdbd8f3cb..c0c707676fd12 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useFieldV7TextField.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useFieldV7TextField.ts @@ -420,7 +420,7 @@ export const useFieldV7TextField: UseFieldTextField = (params) => { onClick: getInputContainerClickHandler(index), } as React.HTMLAttributes, content: { - tabIndex: isContainerEditable ? undefined : 0, + tabIndex: isContainerEditable || index > 0 ? -1 : 0, contentEditable: !isContainerEditable && !disabled && !readOnly, role: 'spinbutton', id: `${id}-${section.type}`, @@ -522,7 +522,7 @@ export const useFieldV7TextField: UseFieldTextField = (params) => { enableAccessibleFieldDOMStructure: true, elements, // TODO v7: Try to set to undefined when there is a section selected. - tabIndex: 0, + tabIndex: parsedSelectedSections === 0 ? -1 : 0, contentEditable: isContainerEditable, value: valueStr, onChange: handleValueStrChange, From 1c2d729436a0f6c4582c60d0be9574eeedf20de3 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Fri, 8 Mar 2024 16:39:00 +0100 Subject: [PATCH 11/49] [pickers] Improve the customization of the range picker calendar header (#11988) --- docs/data/date-pickers-component-api-pages.ts | 5 + .../CalendarHeaderComponent.js | 64 ++++++++ .../CalendarHeaderComponent.tsx | 65 ++++++++ .../CalendarHeaderComponent.tsx.preview | 1 + .../CalendarHeaderComponentProps.js | 17 ++ .../CalendarHeaderComponentProps.tsx | 17 ++ .../CalendarHeaderComponentProps.tsx.preview | 3 + .../CalendarHeaderComponentRange.js | 57 +++++++ .../CalendarHeaderComponentRange.tsx | 58 +++++++ .../CalendarHeaderComponentRange.tsx.preview | 1 + .../custom-components/custom-components.md | 32 +++- .../pickers-range-calendar-header.js | 23 +++ .../pickers-range-calendar-header.json | 70 +++++++++ docs/scripts/generateProptypes.ts | 1 + .../pickers-range-calendar-header.json | 31 ++++ .../DateRangeCalendar/DateRangeCalendar.tsx | 142 ++++++----------- .../DateRangeCalendar.types.ts | 4 +- .../PickersRangeCalendarHeader.tsx | 145 ++++++++++++++++++ .../PickersRangeCalendarHeader.types.ts | 24 +++ .../src/PickersRangeCalendarHeader/index.ts | 5 + packages/x-date-pickers-pro/src/index.ts | 1 + .../src/themeAugmentation/components.d.ts | 3 + .../src/themeAugmentation/props.d.ts | 2 + .../src/DateCalendar/DayCalendar.tsx | 3 +- .../tests/MobileDateTimePicker.test.tsx | 4 +- .../PickersCalendarHeader.tsx | 2 +- scripts/x-date-pickers-pro.exports.json | 3 + 27 files changed, 680 insertions(+), 103 deletions(-) create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponent.js create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx.preview create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.js create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx.preview create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.js create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx create mode 100644 docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx.preview create mode 100644 docs/pages/x/api/date-pickers/pickers-range-calendar-header.js create mode 100644 docs/pages/x/api/date-pickers/pickers-range-calendar-header.json create mode 100644 docs/translations/api-docs/date-pickers/pickers-range-calendar-header/pickers-range-calendar-header.json create mode 100644 packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.tsx create mode 100644 packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.types.ts create mode 100644 packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/index.ts diff --git a/docs/data/date-pickers-component-api-pages.ts b/docs/data/date-pickers-component-api-pages.ts index a4613479d4504..739009bd7113f 100644 --- a/docs/data/date-pickers-component-api-pages.ts +++ b/docs/data/date-pickers-component-api-pages.ts @@ -163,6 +163,11 @@ const apiPages: MuiPage[] = [ pathname: '/x/api/date-pickers/pickers-layout', title: 'PickersLayout', }, + { + pathname: '/x/api/date-pickers/pickers-range-calendar-header', + title: 'PickersRangeCalendarHeader', + plan: 'pro', + }, { pathname: '/x/api/date-pickers/pickers-section-list', title: 'PickersSectionList', diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponent.js b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.js new file mode 100644 index 0000000000000..b789f8a216ed4 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.js @@ -0,0 +1,64 @@ +import * as React from 'react'; + +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import ChevronLeft from '@mui/icons-material/ChevronLeft'; +import ChevronRight from '@mui/icons-material/ChevronRight'; +import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft'; +import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; + +const CustomCalendarHeaderRoot = styled('div')({ + display: 'flex', + justifyContent: 'space-between', + padding: '8px 16px', + alignItems: 'center', +}); + +function CustomCalendarHeader(props) { + const { currentMonth, onMonthChange } = props; + + const selectNextMonth = () => onMonthChange(currentMonth.add(1, 'month'), 'left'); + const selectNextYear = () => onMonthChange(currentMonth.add(1, 'year'), 'left'); + const selectPreviousMonth = () => + onMonthChange(currentMonth.subtract(1, 'month'), 'right'); + const selectPreviousYear = () => + onMonthChange(currentMonth.subtract(1, 'year'), 'right'); + + return ( + + + + + + + + + + {currentMonth.format('MMMM YYYY')} + + + + + + + + + + ); +} + +export default function CalendarHeaderComponent() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx new file mode 100644 index 0000000000000..9810d5f955c0c --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx @@ -0,0 +1,65 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import ChevronLeft from '@mui/icons-material/ChevronLeft'; +import ChevronRight from '@mui/icons-material/ChevronRight'; +import KeyboardDoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeft'; +import KeyboardDoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRight'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; +import { PickersCalendarHeaderProps } from '@mui/x-date-pickers/PickersCalendarHeader'; + +const CustomCalendarHeaderRoot = styled('div')({ + display: 'flex', + justifyContent: 'space-between', + padding: '8px 16px', + alignItems: 'center', +}); + +function CustomCalendarHeader(props: PickersCalendarHeaderProps) { + const { currentMonth, onMonthChange } = props; + + const selectNextMonth = () => onMonthChange(currentMonth.add(1, 'month'), 'left'); + const selectNextYear = () => onMonthChange(currentMonth.add(1, 'year'), 'left'); + const selectPreviousMonth = () => + onMonthChange(currentMonth.subtract(1, 'month'), 'right'); + const selectPreviousYear = () => + onMonthChange(currentMonth.subtract(1, 'year'), 'right'); + + return ( + + + + + + + + + + {currentMonth.format('MMMM YYYY')} + + + + + + + + + + ); +} + +export default function CalendarHeaderComponent() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx.preview b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx.preview new file mode 100644 index 0000000000000..6d3ca5d9099c0 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponent.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.js b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.js new file mode 100644 index 0000000000000..cfc533c919ec9 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.js @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; + +export default function CalendarHeaderComponentProps() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx new file mode 100644 index 0000000000000..cfc533c919ec9 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; + +export default function CalendarHeaderComponentProps() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx.preview b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx.preview new file mode 100644 index 0000000000000..bec7214e9bd45 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentProps.tsx.preview @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.js b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.js new file mode 100644 index 0000000000000..9625c51e9622e --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.js @@ -0,0 +1,57 @@ +import * as React from 'react'; + +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import ChevronLeft from '@mui/icons-material/ChevronLeft'; +import ChevronRight from '@mui/icons-material/ChevronRight'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateRangeCalendar } from '@mui/x-date-pickers-pro/DateRangeCalendar'; + +const CustomCalendarHeaderRoot = styled('div')({ + display: 'flex', + justifyContent: 'space-between', + padding: '8px 16px', + alignItems: 'center', +}); + +function CustomCalendarHeader(props) { + const { currentMonth, onMonthChange, month, calendars, monthIndex } = props; + + const selectNextMonth = () => + onMonthChange(currentMonth.add(calendars, 'month'), 'left'); + const selectPreviousMonth = () => + onMonthChange(currentMonth.subtract(calendars, 'month'), 'right'); + + return ( + + + + + {month.format('MMMM YYYY')} + + + + + ); +} + +export default function CalendarHeaderComponentRange() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx new file mode 100644 index 0000000000000..5ff5398aef2be --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { styled } from '@mui/material/styles'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import ChevronLeft from '@mui/icons-material/ChevronLeft'; +import ChevronRight from '@mui/icons-material/ChevronRight'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateRangeCalendar } from '@mui/x-date-pickers-pro/DateRangeCalendar'; +import { PickersRangeCalendarHeaderProps } from '@mui/x-date-pickers-pro/PickersRangeCalendarHeader'; + +const CustomCalendarHeaderRoot = styled('div')({ + display: 'flex', + justifyContent: 'space-between', + padding: '8px 16px', + alignItems: 'center', +}); + +function CustomCalendarHeader(props: PickersRangeCalendarHeaderProps) { + const { currentMonth, onMonthChange, month, calendars, monthIndex } = props; + + const selectNextMonth = () => + onMonthChange(currentMonth.add(calendars, 'month'), 'left'); + const selectPreviousMonth = () => + onMonthChange(currentMonth.subtract(calendars, 'month'), 'right'); + + return ( + + + + + {month.format('MMMM YYYY')} + + + + + ); +} + +export default function CalendarHeaderComponentRange() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx.preview b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx.preview new file mode 100644 index 0000000000000..16929c4b6d371 --- /dev/null +++ b/docs/data/date-pickers/custom-components/CalendarHeaderComponentRange.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-components/custom-components.md b/docs/data/date-pickers/custom-components/custom-components.md index 4aec6939089c0..3ecf81afeb3c7 100644 --- a/docs/data/date-pickers/custom-components/custom-components.md +++ b/docs/data/date-pickers/custom-components/custom-components.md @@ -1,7 +1,7 @@ --- productId: x-date-pickers title: Date and Time Pickers - Custom slots and subcomponents -components: DateTimePickerTabs, PickersActionBar, DatePickerToolbar, TimePickerToolbar, DateTimePickerToolbar, PickersCalendarHeader, PickersShortcuts, DateRangePickerToolbar +components: DateTimePickerTabs, PickersActionBar, DatePickerToolbar, TimePickerToolbar, DateTimePickerToolbar, PickersCalendarHeader, PickersRangeCalendarHeader, PickersShortcuts, DateRangePickerToolbar --- # Custom slots and subcomponents @@ -219,6 +219,34 @@ Each component comes with its own toolbar (`DatePickerToolbar`, `TimePickerToolb {{"demo": "ToolbarComponent.js"}} +## Calendar header + +The calendar header is available on any component that renders a calendar to select a date or a range of dates. +It allows the user to navigate through months and to switch to the month and year views when available. + +### Component props + +You can pass props to the calendar header as shown below: + +{{"demo": "CalendarHeaderComponentProps.js", "defaultCodeOpen": false}} + +### Component + +You can pass custom components to replace the header, as shown below: + +{{"demo": "CalendarHeaderComponent.js", "defaultCodeOpen": false}} + +When used with a date range component, +you receive three additional props to let you handle scenarios where multiple months are rendered: + +- `calendars`: The number of calendars rendered +- `month`: The month used for the header being rendered +- `monthIndex`: The index of the month used for the header being rendered + +The demo below shows how to navigate the months two by two: + +{{"demo": "CalendarHeaderComponentRange.js", "defaultCodeOpen": false}} + ## Arrow switcher The following slots let you customize how to render the buttons and icons for an arrow switcher component—the component @@ -232,7 +260,7 @@ You can pass props to the icons and buttons as shown below: ### Component -You can pass custom components—to replace the icons, for example—as shown below: +You can pass custom components to replace the icons, as shown below: {{"demo": "ArrowSwitcherComponent.js", "defaultCodeOpen": false}} diff --git a/docs/pages/x/api/date-pickers/pickers-range-calendar-header.js b/docs/pages/x/api/date-pickers/pickers-range-calendar-header.js new file mode 100644 index 0000000000000..e2bc950a76501 --- /dev/null +++ b/docs/pages/x/api/date-pickers/pickers-range-calendar-header.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './pickers-range-calendar-header.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/date-pickers/pickers-range-calendar-header', + false, + /\.\/pickers-range-calendar-header.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/date-pickers/pickers-range-calendar-header.json b/docs/pages/x/api/date-pickers/pickers-range-calendar-header.json new file mode 100644 index 0000000000000..fbf2f5e605c2a --- /dev/null +++ b/docs/pages/x/api/date-pickers/pickers-range-calendar-header.json @@ -0,0 +1,70 @@ +{ + "props": { + "calendars": { + "type": { "name": "enum", "description": "1
| 2
| 3" }, + "required": true + }, + "month": { "type": { "name": "object" }, "required": true }, + "monthIndex": { "type": { "name": "number" }, "required": true }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "format": { + "type": { "name": "string" }, + "default": "`${adapter.formats.month} ${adapter.formats.year}`" + }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { + "type": { "name": "object" }, + "default": "{}", + "additionalInfo": { "slotsApi": true } + }, + "sx": { + "type": { + "name": "union", + "description": "Array<func
| object
| bool>
| func
| object" + }, + "additionalInfo": { "sx": true } + } + }, + "name": "PickersRangeCalendarHeader", + "imports": [ + "import { PickersRangeCalendarHeader } from '@mui/x-date-pickers-pro/PickersRangeCalendarHeader';", + "import { PickersRangeCalendarHeader } from '@mui/x-date-pickers-pro';" + ], + "classes": [ + { + "key": "label", + "className": "MuiPickersRangeCalendarHeader-label", + "description": "Styles applied to the label element.", + "isGlobal": false + }, + { + "key": "labelContainer", + "className": "MuiPickersRangeCalendarHeader-labelContainer", + "description": "Styles applied to the label container element.", + "isGlobal": false + }, + { + "key": "root", + "className": "MuiPickersRangeCalendarHeader-root", + "description": "Styles applied to the root element.", + "isGlobal": false + }, + { + "key": "switchViewButton", + "className": "MuiPickersRangeCalendarHeader-switchViewButton", + "description": "Styles applied to the switch view button element.", + "isGlobal": false + }, + { + "key": "switchViewIcon", + "className": "MuiPickersRangeCalendarHeader-switchViewIcon", + "description": "Styles applied to the switch view icon element.", + "isGlobal": false + } + ], + "muiName": "MuiPickersRangeCalendarHeader", + "filename": "/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/scripts/generateProptypes.ts b/docs/scripts/generateProptypes.ts index c61d3b9170017..c12d312eed728 100644 --- a/docs/scripts/generateProptypes.ts +++ b/docs/scripts/generateProptypes.ts @@ -24,6 +24,7 @@ async function generateProptypes(project: XTypeScriptProject, sourceFile: string 'referenceDate', 'day', 'currentMonth', + 'month', ]; if (T_DATE_PROPS.includes(name)) { diff --git a/docs/translations/api-docs/date-pickers/pickers-range-calendar-header/pickers-range-calendar-header.json b/docs/translations/api-docs/date-pickers/pickers-range-calendar-header/pickers-range-calendar-header.json new file mode 100644 index 0000000000000..a152174c7d953 --- /dev/null +++ b/docs/translations/api-docs/date-pickers/pickers-range-calendar-header/pickers-range-calendar-header.json @@ -0,0 +1,31 @@ +{ + "componentDescription": "", + "propDescriptions": { + "calendars": { "description": "The number of calendars rendered." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "format": { "description": "Format used to display the date." }, + "month": { "description": "Month used for this header." }, + "monthIndex": { "description": "Index of the month used for this header." }, + "slotProps": { "description": "The props used for each component slot." }, + "slots": { "description": "Overridable component slots." }, + "sx": { + "description": "The system prop that allows defining system overrides as well as additional CSS styles." + } + }, + "classDescriptions": { + "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the label element" }, + "labelContainer": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the label container element" + }, + "root": { "description": "Styles applied to the root element." }, + "switchViewButton": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the switch view button element" + }, + "switchViewIcon": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the switch view icon element" + } + } +} diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 713f67a2412ca..5657e124d0397 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -7,10 +7,6 @@ import { resolveComponentProps, useSlotProps } from '@mui/base/utils'; import { styled, useThemeProps } from '@mui/material/styles'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { Watermark } from '@mui/x-license'; -import { - PickersCalendarHeader, - PickersCalendarHeaderProps, -} from '@mui/x-date-pickers/PickersCalendarHeader'; import { applyDefaultDate, BaseDateValidationProps, @@ -18,12 +14,8 @@ import { DayCalendarSlots, DayCalendarSlotProps, useDefaultReduceAnimations, - PickersArrowSwitcher, useCalendarState, useDefaultDates, - useLocaleText, - useNextMonthDisabled, - usePreviousMonthDisabled, useUtils, PickerSelectionState, useNow, @@ -57,6 +49,10 @@ import { rangeValueManager } from '../internals/utils/valueManagers'; import { useDragRange } from './useDragRange'; import { useRangePosition } from '../internals/hooks/useRangePosition'; import { DAY_RANGE_SIZE, DAY_MARGIN } from '../internals/constants/dimensions'; +import { + PickersRangeCalendarHeader, + PickersRangeCalendarHeaderProps, +} from '../PickersRangeCalendarHeader'; const releaseInfo = getReleaseInfo(); @@ -79,13 +75,6 @@ const DateRangeCalendarMonthContainer = styled('div', { }, })); -const DateRangeCalendarArrowSwitcher = styled(PickersArrowSwitcher)({ - padding: '12px 16px 4px 16px', - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', -}); - const weeksContainerHeight = (DAY_RANGE_SIZE + DAY_MARGIN * 2) * 6; const warnInvalidCurrentMonthCalendarPosition = buildWarning([ @@ -242,7 +231,6 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar< }); const utils = useUtils(); - const localeText = useLocaleText(); const now = useNow(timezone); const { rangePosition, onRangePositionChange } = useRangePosition({ @@ -359,28 +347,28 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar< timezone, }); - // When disabled, limit the view to the selected date - const minDateWithDisabled = (disabled && value[0]) || minDate; - const maxDateWithDisabled = (disabled && value[1]) || maxDate; - - const CalendarHeader = slots?.calendarHeader ?? PickersCalendarHeader; - const calendarHeaderProps: PickersCalendarHeaderProps = useSlotProps({ + const CalendarHeader = slots?.calendarHeader ?? PickersRangeCalendarHeader; + const calendarHeaderProps: Omit< + PickersRangeCalendarHeaderProps, + 'month' | 'monthIndex' + > = useSlotProps({ elementType: CalendarHeader, externalSlotProps: slotProps?.calendarHeader, additionalProps: { + calendars, views: ['day'], view: 'day', currentMonth: calendarState.currentMonth, onMonthChange: (newMonth, direction) => handleChangeMonth({ newMonth, direction }), - minDate: minDateWithDisabled, - maxDate: maxDateWithDisabled, + minDate, + maxDate, disabled, disablePast, disableFuture, reduceAnimations, + timezone, slots, slotProps, - timezone, }, ownerState: props, }); @@ -420,26 +408,6 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar< } }, [rangePosition, value]); // eslint-disable-line - const selectNextMonth = React.useCallback(() => { - changeMonth(utils.addMonths(calendarState.currentMonth, 1)); - }, [changeMonth, calendarState.currentMonth, utils]); - - const selectPreviousMonth = React.useCallback(() => { - changeMonth(utils.addMonths(calendarState.currentMonth, -1)); - }, [changeMonth, calendarState.currentMonth, utils]); - - const isNextMonthDisabled = useNextMonthDisabled(calendarState.currentMonth, { - disableFuture, - maxDate, - timezone, - }); - - const isPreviousMonthDisabled = usePreviousMonthDisabled(calendarState.currentMonth, { - disablePast, - minDate, - timezone, - }); - const baseDateValidationProps: Required> = { disablePast, disableFuture, @@ -579,57 +547,39 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar< {...other} > - {calendarMonths.map((month, index) => ( - - {calendars === 1 ? ( - - ) : ( - - {utils.formatByString( - visibleMonths[month], - calendarHeaderProps.format ?? `${utils.formats.month} ${utils.formats.year}`, - )} - - )} - - - key={index} - className={classes.dayCalendar} - {...calendarState} - {...baseDateValidationProps} - {...commonViewProps} - onMonthSwitchingAnimationEnd={onMonthSwitchingAnimationEnd} - onFocusedDayChange={changeFocusedDay} - reduceAnimations={reduceAnimations} - selectedDays={value} - onSelectedDaysChange={handleSelectedDayChange} - currentMonth={visibleMonths[month]} - TransitionProps={CalendarTransitionProps} - shouldDisableDate={wrappedShouldDisableDate} - showDaysOutsideCurrentMonth={calendars === 1 && showDaysOutsideCurrentMonth} - dayOfWeekFormatter={dayOfWeekFormatter} - loading={loading} - renderLoading={renderLoading} - slots={slotsForDayCalendar} - slotProps={slotPropsForDayCalendar} - autoFocus={visibleMonths[month] === focusedMonth} - fixedWeekNumber={fixedWeekNumber} - displayWeekNumber={displayWeekNumber} - timezone={timezone} - /> - - ))} + {calendarMonths.map((monthIndex) => { + const month = visibleMonths[monthIndex]; + + return ( + + {...calendarHeaderProps} month={month} monthIndex={monthIndex} /> + + className={classes.dayCalendar} + {...calendarState} + {...baseDateValidationProps} + {...commonViewProps} + onMonthSwitchingAnimationEnd={onMonthSwitchingAnimationEnd} + onFocusedDayChange={changeFocusedDay} + reduceAnimations={reduceAnimations} + selectedDays={value} + onSelectedDaysChange={handleSelectedDayChange} + currentMonth={month} + TransitionProps={CalendarTransitionProps} + shouldDisableDate={wrappedShouldDisableDate} + showDaysOutsideCurrentMonth={calendars === 1 && showDaysOutsideCurrentMonth} + dayOfWeekFormatter={dayOfWeekFormatter} + loading={loading} + renderLoading={renderLoading} + slots={slotsForDayCalendar} + slotProps={slotPropsForDayCalendar} + autoFocus={month === focusedMonth} + fixedWeekNumber={fixedWeekNumber} + displayWeekNumber={displayWeekNumber} + timezone={timezone} + /> + + ); + })} ); }) as DateRangeCalendarComponent; diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts index 61aeb650da085..c588be3ea87e3 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts @@ -5,7 +5,6 @@ import { Theme } from '@mui/material/styles'; import { PickerValidDate, TimezoneProps } from '@mui/x-date-pickers/models'; import { PickersCalendarHeader, - PickersCalendarHeaderProps, PickersCalendarHeaderSlots, PickersCalendarHeaderSlotProps, } from '@mui/x-date-pickers/PickersCalendarHeader'; @@ -25,6 +24,7 @@ import { DateRange } from '../models'; import { DateRangeCalendarClasses } from './dateRangeCalendarClasses'; import { DateRangePickerDay, DateRangePickerDayProps } from '../DateRangePickerDay'; import { UseRangePositionProps } from '../internals/hooks/useRangePosition'; +import { PickersRangeCalendarHeaderProps } from '../PickersRangeCalendarHeader'; export type DateRangePosition = 'start' | 'end'; @@ -37,7 +37,7 @@ export interface DateRangeCalendarSlots * Check the [PickersCalendarHeader](https://mui.com/x/api/date-pickers/pickers-calendar-header/) component. * @default PickersCalendarHeader */ - calendarHeader?: React.ElementType>; + calendarHeader?: React.ElementType>; /** * Custom component for day in range pickers. * Check the [DateRangePickersDay](https://mui.com/x/api/date-pickers/date-range-picker-day/) component. diff --git a/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.tsx b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.tsx new file mode 100644 index 0000000000000..8bc3001c8cac9 --- /dev/null +++ b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.tsx @@ -0,0 +1,145 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { styled } from '@mui/material/styles'; +import { PickersCalendarHeader } from '@mui/x-date-pickers/PickersCalendarHeader'; +import { PickerValidDate } from '@mui/x-date-pickers/models'; +import { + PickersArrowSwitcher, + useLocaleText, + useNextMonthDisabled, + usePreviousMonthDisabled, + useUtils, +} from '@mui/x-date-pickers/internals'; +import { PickersRangeCalendarHeaderProps } from './PickersRangeCalendarHeader.types'; + +type PickersRangeCalendarHeaderComponent = (( + props: PickersRangeCalendarHeaderProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const PickersRangeCalendarHeaderContentMultipleCalendars = styled(PickersArrowSwitcher)({ + padding: '12px 16px 4px 16px', + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', +}); + +const PickersRangeCalendarHeader = React.forwardRef(function PickersRangeCalendarHeader< + TDate extends PickerValidDate, +>(props: PickersRangeCalendarHeaderProps, ref: React.Ref) { + const utils = useUtils(); + const localeText = useLocaleText(); + + const { calendars, month, monthIndex, ...other } = props; + const { + format, + slots, + slotProps, + currentMonth, + onMonthChange, + disableFuture, + disablePast, + minDate, + maxDate, + timezone, + } = props; + + const isNextMonthDisabled = useNextMonthDisabled(currentMonth, { + disableFuture, + maxDate, + timezone, + }); + + const isPreviousMonthDisabled = usePreviousMonthDisabled(currentMonth, { + disablePast, + minDate, + timezone, + }); + + if (calendars === 1) { + return ; + } + + const selectNextMonth = () => onMonthChange(utils.addMonths(currentMonth, 1), 'left'); + + const selectPreviousMonth = () => onMonthChange(utils.addMonths(currentMonth, -1), 'right'); + + return ( + + {utils.formatByString(month, format ?? `${utils.formats.month} ${utils.formats.year}`)} + + ); +}) as PickersRangeCalendarHeaderComponent; + +PickersRangeCalendarHeader.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The number of calendars rendered. + */ + calendars: PropTypes.oneOf([1, 2, 3]).isRequired, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + className: PropTypes.string, + currentMonth: PropTypes.object.isRequired, + disabled: PropTypes.bool, + disableFuture: PropTypes.bool, + disablePast: PropTypes.bool, + /** + * Format used to display the date. + * @default `${adapter.formats.month} ${adapter.formats.year}` + */ + format: PropTypes.string, + labelId: PropTypes.string, + maxDate: PropTypes.object.isRequired, + minDate: PropTypes.object.isRequired, + /** + * Month used for this header. + */ + month: PropTypes.object.isRequired, + /** + * Index of the month used for this header. + */ + monthIndex: PropTypes.number.isRequired, + onMonthChange: PropTypes.func.isRequired, + onViewChange: PropTypes.func, + reduceAnimations: PropTypes.bool.isRequired, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), + timezone: PropTypes.string.isRequired, + view: PropTypes.oneOf(['day', 'month', 'year']).isRequired, + views: PropTypes.arrayOf(PropTypes.oneOf(['day', 'month', 'year']).isRequired).isRequired, +} as any; + +export { PickersRangeCalendarHeader }; diff --git a/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.types.ts b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.types.ts new file mode 100644 index 0000000000000..aa758a4827376 --- /dev/null +++ b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/PickersRangeCalendarHeader.types.ts @@ -0,0 +1,24 @@ +import { PickerValidDate } from '@mui/x-date-pickers/models'; +import { + ExportedPickersCalendarHeaderProps, + PickersCalendarHeaderProps, +} from '@mui/x-date-pickers/PickersCalendarHeader'; + +export interface PickersRangeCalendarHeaderProps + extends PickersCalendarHeaderProps { + /** + * The number of calendars rendered. + */ + calendars: 1 | 2 | 3; + /** + * Month used for this header. + */ + month: TDate; + /** + * Index of the month used for this header. + */ + monthIndex: number; +} + +export interface ExportedPickersRangeCalendarHeaderProps + extends ExportedPickersCalendarHeaderProps {} diff --git a/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/index.ts b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/index.ts new file mode 100644 index 0000000000000..3af46294c9546 --- /dev/null +++ b/packages/x-date-pickers-pro/src/PickersRangeCalendarHeader/index.ts @@ -0,0 +1,5 @@ +export { PickersRangeCalendarHeader } from './PickersRangeCalendarHeader'; +export type { + PickersRangeCalendarHeaderProps, + ExportedPickersRangeCalendarHeaderProps, +} from './PickersRangeCalendarHeader.types'; diff --git a/packages/x-date-pickers-pro/src/index.ts b/packages/x-date-pickers-pro/src/index.ts index c87ecd38be82a..3e539df6c582d 100644 --- a/packages/x-date-pickers-pro/src/index.ts +++ b/packages/x-date-pickers-pro/src/index.ts @@ -19,6 +19,7 @@ export * from './SingleInputDateTimeRangeField'; // Calendars export * from './DateRangeCalendar'; +export * from './PickersRangeCalendarHeader'; // New pickers export * from './DateRangePicker'; diff --git a/packages/x-date-pickers-pro/src/themeAugmentation/components.d.ts b/packages/x-date-pickers-pro/src/themeAugmentation/components.d.ts index b3c6e171a2747..881d1d066a354 100644 --- a/packages/x-date-pickers-pro/src/themeAugmentation/components.d.ts +++ b/packages/x-date-pickers-pro/src/themeAugmentation/components.d.ts @@ -21,6 +21,9 @@ export interface PickersProComponents { defaultProps?: ComponentsProps['MuiDateTimeRangePickerToolbar']; styleOverrides?: ComponentsOverrides['MuiDateTimeRangePickerToolbar']; }; + MuiPickersRangeCalendarHeader?: { + defaultProps?: ComponentsProps['MuiPickersRangeCalendarHeader']; + }; // Multi input range fields MuiMultiInputDateRangeField?: { diff --git a/packages/x-date-pickers-pro/src/themeAugmentation/props.d.ts b/packages/x-date-pickers-pro/src/themeAugmentation/props.d.ts index 770bb81342265..6536f000c4944 100644 --- a/packages/x-date-pickers-pro/src/themeAugmentation/props.d.ts +++ b/packages/x-date-pickers-pro/src/themeAugmentation/props.d.ts @@ -17,6 +17,7 @@ import { DesktopDateTimeRangePickerProps } from '../DesktopDateTimeRangePicker'; import { MobileDateTimeRangePickerProps } from '../MobileDateTimeRangePicker'; import { ExportedDateTimeRangePickerTabsProps } from '../DateTimeRangePicker/DateTimeRangePickerTabs'; import { ExportedDateTimeRangePickerToolbarProps } from '../DateTimeRangePicker/DateTimeRangePickerToolbar'; +import { ExportedPickersRangeCalendarHeaderProps } from '../PickersRangeCalendarHeader'; export interface PickersProComponentsPropsList { MuiDateRangeCalendar: DateRangeCalendarProps; @@ -24,6 +25,7 @@ export interface PickersProComponentsPropsList { MuiDateTimeRangePickerTabs: ExportedDateTimeRangePickerTabsProps; MuiDateRangePickerToolbar: ExportedDateRangePickerToolbarProps; MuiDateTimeRangePickerToolbar: ExportedDateTimeRangePickerToolbarProps; + MuiPickersRangeCalendarHeader: ExportedPickersRangeCalendarHeaderProps; // Multi input range fields MuiMultiInputDateRangeField: MultiInputDateRangeFieldProps; diff --git a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx index 7f30921e99bbf..a16854f0e499b 100644 --- a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx @@ -491,13 +491,14 @@ export function DayCalendar(inProps: DayCalendarP }); const currentMonthNumber = utils.getMonth(currentMonth); + const currentYearNumber = utils.getYear(currentMonth); const validSelectedDays = React.useMemo( () => selectedDays.filter((day): day is TDate => !!day).map((day) => utils.startOfDay(day)), [utils, selectedDays], ); // need a new ref whenever the `key` of the transition changes: https://reactcommunity.org/react-transition-group/transition/#Transition-prop-nodeRef. - const transitionKey = currentMonthNumber; + const transitionKey = `${currentYearNumber}-${currentMonthNumber}`; // eslint-disable-next-line react-hooks/exhaustive-deps const slideNodeRef = React.useMemo(() => React.createRef(), [transitionKey]); const startOfCurrentWeek = utils.startOfWeek(now); diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx index bea491298d7ef..c26f5a2278d2f 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx @@ -12,7 +12,7 @@ import { } from 'test/utils/pickers'; describe('', () => { - const { render } = createPickerRenderer({ clock: 'fake' }); + const { render, clock } = createPickerRenderer({ clock: 'fake' }); it('should render date and time by default', () => { render( @@ -137,6 +137,8 @@ describe('', () => { expect(onChange.callCount).to.equal(1); expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2010, 0, 1)); + clock.runToLast(); + // Change the date userEvent.mousePress(screen.getByRole('gridcell', { name: '15' })); expect(onChange.callCount).to.equal(2); diff --git a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx index 4edc2507e7390..a18d69b4540f5 100644 --- a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx +++ b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx @@ -107,7 +107,7 @@ const PickersCalendarHeaderSwitchViewIcon = styled(ArrowDropDownIcon, { })); type PickersCalendarHeaderComponent = (( - props: PickersCalendarHeaderProps & React.RefAttributes, + props: PickersCalendarHeaderProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; /** diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index eb08c40047352..2601b8337e8ed 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -148,6 +148,7 @@ { "name": "ExportedPickersLayoutSlotProps", "kind": "Interface" }, { "name": "ExportedPickersLayoutSlots", "kind": "Interface" }, { "name": "ExportedPickersMonthProps", "kind": "Interface" }, + { "name": "ExportedPickersRangeCalendarHeaderProps", "kind": "Interface" }, { "name": "ExportedPickersSectionListProps", "kind": "Interface" }, { "name": "ExportedPickersYearProps", "kind": "Interface" }, { "name": "ExportedSlideTransitionProps", "kind": "Interface" }, @@ -295,6 +296,8 @@ { "name": "PickersOutlinedInputClasses", "kind": "Interface" }, { "name": "PickersOutlinedInputClassKey", "kind": "TypeAlias" }, { "name": "PickersOutlinedInputProps", "kind": "Interface" }, + { "name": "PickersRangeCalendarHeader", "kind": "Variable" }, + { "name": "PickersRangeCalendarHeaderProps", "kind": "Interface" }, { "name": "PickersSectionElement", "kind": "Interface" }, { "name": "pickersSectionListClasses", "kind": "Variable" }, { "name": "PickersSectionListClasses", "kind": "Interface" }, From 095d8745dd210f7424d066a712bb2ecd96005ca2 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sat, 9 Mar 2024 21:49:57 +0100 Subject: [PATCH 12/49] [docs] Polish What's new in MUI X blog titles (#12309) --- docs/src/modules/components/WhatsNewLayout.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/modules/components/WhatsNewLayout.js b/docs/src/modules/components/WhatsNewLayout.js index f77d437456ac6..9ed6e778bc59f 100644 --- a/docs/src/modules/components/WhatsNewLayout.js +++ b/docs/src/modules/components/WhatsNewLayout.js @@ -18,7 +18,7 @@ import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent'; const entries = [ { - title: 'MUI X v7.0.0-beta.0', + title: 'MUI X v7.0.0-beta.0', description: 'Featuring new components and multiple enhancements for both developers and end-users.', date: new Date(2024, 0, 29), @@ -47,7 +47,7 @@ const entries = [ ], }, { - title: 'MUI X v6.18.x', + title: 'MUI X v6.18.0', description: 'New stable components, polished features, better performance, and more.', date: new Date(2023, 10, 13), url: 'https://mui.com/blog/mui-x-end-v6-features/', @@ -79,7 +79,7 @@ const entries = [ ], }, { - title: 'MUI X v6.11.0', + title: 'MUI X v6.11.0', description: 'A roundup of all new features since v6.0.0.', date: new Date(2023, 7, 14), url: 'https://mui.com/blog/mui-x-mid-v6-features/', @@ -111,7 +111,7 @@ const entries = [ ], }, { - title: 'MUI X v6.0.0', + title: 'MUI X v6.0.0', description: 'A new major is available, with many new features and improvements.', date: new Date(2023, 2, 6), url: 'https://mui.com/blog/mui-x-v6/', @@ -147,7 +147,7 @@ const entries = [ ], }, { - title: 'Date Pickers v5.0.0', + title: 'MUI X Date Pickers v5.0.0', description: 'After some months of polishing in pre-releases, the Date Pickers finally get a stable.', date: new Date(2022, 8, 22), @@ -168,7 +168,7 @@ const entries = [ ], }, { - title: 'Data Grid v5.15', + title: 'MUI X Data Grid v5.15.0', description: 'This version brings an amazing set of new supported use cases with the Data Grid Premium.', date: new Date(2022, 7, 12), @@ -196,7 +196,7 @@ const entries = [ ], }, { - title: 'MUI X v5.0.0', + title: 'MUI X v5.0.0', description: 'A new Data Grid virtualization engine, and improvements in several APIs.', date: new Date(2021, 10, 22), url: 'https://mui.com/blog/mui-x-v5/', From 0d5c20e798f87770f1da9a57ee36f01e48268d50 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 10 Mar 2024 03:05:42 +0100 Subject: [PATCH 13/49] [docs] Replace noreferrer See https://github.com/mui/material-ui/pull/40447 for why. --- .../data-grid/column-menu/ColumnMenuGridReferencesNoSnap.js | 2 +- .../data-grid/column-menu/ColumnMenuGridReferencesNoSnap.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.js b/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.js index 8a6b0adb3e03b..be2e0a466ac8f 100644 --- a/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.js +++ b/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.js @@ -34,7 +34,7 @@ function PlanIcon(props) { diff --git a/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.tsx b/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.tsx index fcb48c1c4527e..d3ac326a2a298 100644 --- a/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.tsx +++ b/docs/data/data-grid/column-menu/ColumnMenuGridReferencesNoSnap.tsx @@ -38,7 +38,7 @@ function PlanIcon(props: { plan?: string }) { From 7c4ba741beeec7ba92b0d4686b6dda73594d58b6 Mon Sep 17 00:00:00 2001 From: Bilal Shafi Date: Sun, 10 Mar 2024 11:51:20 +0500 Subject: [PATCH 14/49] [DataGrid] Make `rowCount` part of the state (#12381) --- docs/data/data-grid/events/events.json | 7 + .../x/api/data-grid/data-grid-premium.json | 4 + docs/pages/x/api/data-grid/data-grid-pro.json | 4 + docs/pages/x/api/data-grid/data-grid.json | 4 + docs/pages/x/api/data-grid/grid-api.md | 1 + .../x/api/data-grid/grid-pagination-api.json | 5 + docs/pages/x/api/data-grid/selectors.json | 7 + .../data-grid-premium/data-grid-premium.json | 4 + .../data-grid-pro/data-grid-pro.json | 4 + .../data-grid/data-grid/data-grid.json | 4 + .../src/DataGridPremium/DataGridPremium.tsx | 5 + .../src/DataGridPro/DataGridPro.tsx | 5 + .../infiniteLoader/useGridInfiniteLoader.tsx | 4 +- .../x-data-grid-pro/src/models/gridApiPro.ts | 4 +- .../statePersistence.DataGridPro.test.tsx | 10 +- .../x-data-grid/src/DataGrid/DataGrid.tsx | 5 + .../src/components/GridPagination.tsx | 14 +- .../features/export/useGridPrintExport.tsx | 19 +- .../pagination/gridPaginationInterfaces.ts | 22 +- .../pagination/gridPaginationSelector.ts | 17 +- .../features/pagination/useGridPagination.ts | 263 +---------------- .../pagination/useGridPaginationModel.ts | 267 ++++++++++++++++++ .../features/pagination/useGridRowCount.ts | 130 +++++++++ .../virtualization/useGridVirtualScroller.tsx | 7 +- .../src/models/api/gridApiCommon.ts | 4 +- .../src/models/events/gridEventLookup.ts | 4 + .../src/models/props/DataGridProps.ts | 5 + .../src/tests/pagination.DataGrid.test.tsx | 3 +- scripts/x-data-grid-premium.exports.json | 1 + scripts/x-data-grid-pro.exports.json | 1 + scripts/x-data-grid.exports.json | 1 + 31 files changed, 547 insertions(+), 288 deletions(-) create mode 100644 packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts create mode 100644 packages/x-data-grid/src/hooks/features/pagination/useGridRowCount.ts diff --git a/docs/data/data-grid/events/events.json b/docs/data/data-grid/events/events.json index a4277ebbd834a..6bb6029718fa7 100644 --- a/docs/data/data-grid/events/events.json +++ b/docs/data/data-grid/events/events.json @@ -292,6 +292,13 @@ "event": "MuiEvent>", "componentProp": "onRowClick" }, + { + "projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"], + "name": "rowCountChange", + "description": "Fired when the row count change.", + "params": "number", + "event": "MuiEvent<{}>" + }, { "projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"], "name": "rowDoubleClick", diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index f6aab9649ea01..6d3fc43fb7317 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -448,6 +448,10 @@ "describedArgs": ["params", "event", "details"] } }, + "onRowCountChange": { + "type": { "name": "func" }, + "signature": { "type": "function(count: number) => void", "describedArgs": ["count"] } + }, "onRowDoubleClick": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 0ae775c41b774..44bfabd9a4a79 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -402,6 +402,10 @@ "describedArgs": ["params", "event", "details"] } }, + "onRowCountChange": { + "type": { "name": "func" }, + "signature": { "type": "function(count: number) => void", "describedArgs": ["count"] } + }, "onRowDoubleClick": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 6daecb51bd8aa..33d8011739096 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -315,6 +315,10 @@ "describedArgs": ["params", "event", "details"] } }, + "onRowCountChange": { + "type": { "name": "func" }, + "signature": { "type": "function(count: number) => void", "describedArgs": ["count"] } + }, "onRowDoubleClick": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/grid-api.md b/docs/pages/x/api/data-grid/grid-api.md index 67b125ccb392e..629601cd72985 100644 --- a/docs/pages/x/api/data-grid/grid-api.md +++ b/docs/pages/x/api/data-grid/grid-api.md @@ -116,6 +116,7 @@ import { GridApi } from '@mui/x-data-grid'; | setPinnedColumns [](/x/introduction/licensing/#pro-plan) | (pinnedColumns: GridPinnedColumnFields) => void | Changes the pinned columns. | | setQuickFilterValues | (values: any[]) => void | Set the quick filter values to the one given by `values` | | setRowChildrenExpansion [](/x/introduction/licensing/#pro-plan) | (id: GridRowId, isExpanded: boolean) => void | Expand or collapse a row children. | +| setRowCount | (rowCount: number) => void | Sets the `rowCount` to a new value. | | setRowGroupingCriteriaIndex [](/x/introduction/licensing/#premium-plan) | (groupingCriteriaField: string, groupingIndex: number) => void | Sets the grouping index of a grouping criteria. | | setRowGroupingModel [](/x/introduction/licensing/#premium-plan) | (model: GridRowGroupingModel) => void | Sets the columns to use as grouping criteria. | | setRowIndex [](/x/introduction/licensing/#pro-plan) | (rowId: GridRowId, targetIndex: number) => void | Moves a row from its original position to the position given by `targetIndex`. | diff --git a/docs/pages/x/api/data-grid/grid-pagination-api.json b/docs/pages/x/api/data-grid/grid-pagination-api.json index e756435855c12..b782ce762515a 100644 --- a/docs/pages/x/api/data-grid/grid-pagination-api.json +++ b/docs/pages/x/api/data-grid/grid-pagination-api.json @@ -16,6 +16,11 @@ "name": "setPaginationModel", "description": "Sets the paginationModel to a new value.", "type": "(model: GridPaginationModel) => void" + }, + { + "name": "setRowCount", + "description": "Sets the rowCount to a new value.", + "type": "(rowCount: number) => void" } ] } diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index f3fd67789100d..c381b10af9ae1 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -318,6 +318,13 @@ "description": "Get the pagination model", "supportsApiRef": true }, + { + "name": "gridPaginationRowCountSelector", + "returnType": "number", + "category": "Pagination", + "description": "Get the row count", + "supportsApiRef": true + }, { "name": "gridPaginationRowRangeSelector", "returnType": "{ firstRowIndex: number; lastRowIndex: number } | null", diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index c7931fb45627c..521fe49f8b734 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -482,6 +482,10 @@ "details": "Additional details for this callback." } }, + "onRowCountChange": { + "description": "Callback fired when the row count has changed.", + "typeDescriptions": { "count": "Updated row count." } + }, "onRowDoubleClick": { "description": "Callback fired when a double click event comes from a row container element.", "typeDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index 7e08ead8fa2d5..ae15af72b8042 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -439,6 +439,10 @@ "details": "Additional details for this callback." } }, + "onRowCountChange": { + "description": "Callback fired when the row count has changed.", + "typeDescriptions": { "count": "Updated row count." } + }, "onRowDoubleClick": { "description": "Callback fired when a double click event comes from a row container element.", "typeDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index a805b4dea7ea2..7abcdced7d428 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -337,6 +337,10 @@ "details": "Additional details for this callback." } }, + "onRowCountChange": { + "description": "Callback fired when the row count has changed.", + "typeDescriptions": { "count": "Updated row count." } + }, "onRowDoubleClick": { "description": "Callback fired when a double click event comes from a row container element.", "typeDescriptions": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 6bbdd2ae94e3e..96b00e33c81c0 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -755,6 +755,11 @@ DataGridPremiumRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onRowClick: PropTypes.func, + /** + * Callback fired when the row count has changed. + * @param {number} count Updated row count. + */ + onRowCountChange: PropTypes.func, /** * Callback fired when a double click event comes from a row container element. * @param {GridRowParams} params With all properties from [[RowParams]]. diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index d28392f9721e6..2950617d77b2d 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -671,6 +671,11 @@ DataGridProRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onRowClick: PropTypes.func, + /** + * Callback fired when the row count has changed. + * @param {number} count Updated row count. + */ + onRowCountChange: PropTypes.func, /** * Callback fired when a double click event comes from a row container element. * @param {GridRowParams} params With all properties from [[RowParams]]. diff --git a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx index 048dfb47a4166..6b8372a7393aa 100644 --- a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx +++ b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx @@ -121,10 +121,10 @@ export const useGridInfiniteLoader = ( [isEnabled, triggerRef], ); - const infiteLoaderPrivateApi: GridInfiniteLoaderPrivateApi = { + const infiniteLoaderPrivateApi: GridInfiniteLoaderPrivateApi = { getInfiniteLoadingTriggerElement, }; - useGridApiMethod(apiRef, infiteLoaderPrivateApi, 'private'); + useGridApiMethod(apiRef, infiniteLoaderPrivateApi, 'private'); useGridApiOptionHandler(apiRef, 'rowsScrollEnd', props.onRowsScrollEnd); }; diff --git a/packages/x-data-grid-pro/src/models/gridApiPro.ts b/packages/x-data-grid-pro/src/models/gridApiPro.ts index 1ad793c49c50e..a47b985e891ea 100644 --- a/packages/x-data-grid-pro/src/models/gridApiPro.ts +++ b/packages/x-data-grid-pro/src/models/gridApiPro.ts @@ -5,6 +5,7 @@ import { GridRowProApi, } from '@mui/x-data-grid'; import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals'; +import type { GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/models/api/gridInfiniteLoaderApi'; import { GridInitialStatePro, GridStatePro } from './gridStatePro'; import type { GridColumnPinningApi, @@ -32,4 +33,5 @@ export interface GridApiPro export interface GridPrivateApiPro extends GridApiPro, GridPrivateOnlyApiCommon, - GridDetailPanelPrivateApi {} + GridDetailPanelPrivateApi, + GridInfiniteLoaderPrivateApi {} diff --git a/packages/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index 69068db8f6f25..434c23c56c1cc 100644 --- a/packages/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -59,11 +59,12 @@ const FULL_INITIAL_STATE: GridInitialState = { }, filter: { filterModel: { - items: [{ field: 'id', operator: '<', value: '5' }], + items: [{ field: 'id', operator: '>=', value: '0' }], }, }, pagination: { paginationModel: { page: 1, pageSize: 2 }, + rowCount: 6, }, pinnedColumns: { left: ['id'], @@ -125,6 +126,7 @@ describe(' - State persistence', () => { }, pagination: { paginationModel: { page: 0, pageSize: 100 }, + rowCount: 6, }, pinnedColumns: {}, preferencePanel: { @@ -160,6 +162,7 @@ describe(' - State persistence', () => { page: FULL_INITIAL_STATE.pagination?.paginationModel?.page!, pageSize: FULL_INITIAL_STATE.pagination?.paginationModel?.pageSize!, }} + rowCount={FULL_INITIAL_STATE.pagination?.rowCount} pinnedColumns={FULL_INITIAL_STATE.pinnedColumns} // Some portable states don't have a controllable model initialState={{ @@ -184,6 +187,7 @@ describe(' - State persistence', () => { page: FULL_INITIAL_STATE.pagination?.paginationModel?.page!, pageSize: FULL_INITIAL_STATE.pagination?.paginationModel?.pageSize!, }} + rowCount={FULL_INITIAL_STATE.pagination?.rowCount} pinnedColumns={FULL_INITIAL_STATE.pinnedColumns} // Some portable states don't have a controllable model initialState={{ @@ -215,7 +219,7 @@ describe(' - State persistence', () => { apiRef.current.showPreferences(GridPreferencePanelsValue.filters); apiRef.current.setSortModel([{ field: 'id', sort: 'desc' }]); apiRef.current.setFilterModel({ - items: [{ field: 'id', operator: '<', value: '5' }], + items: [{ field: 'id', operator: '>=', value: '0' }], }); apiRef.current.setColumnIndex('category', 1); apiRef.current.setColumnWidth('category', 75); @@ -232,7 +236,7 @@ describe(' - State persistence', () => { act(() => apiRef.current.restoreState(FULL_INITIAL_STATE)); // Pinning, pagination, sorting and filtering - expect(getColumnValues(0)).to.deep.equal(['2', '1']); + expect(getColumnValues(0)).to.deep.equal(['3', '2']); // Preference panel expect(screen.getByRole('button', { name: /Add Filter/i })).to.not.equal(null); diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index 1d57912613e9e..dc5e5c27a5cb6 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -527,6 +527,11 @@ DataGridRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onRowClick: PropTypes.func, + /** + * Callback fired when the row count has changed. + * @param {number} count Updated row count. + */ + onRowCountChange: PropTypes.func, /** * Callback fired when a double click event comes from a row container element. * @param {GridRowParams} params With all properties from [[RowParams]]. diff --git a/packages/x-data-grid/src/components/GridPagination.tsx b/packages/x-data-grid/src/components/GridPagination.tsx index 5b6e3f184ae8e..03be488965cad 100644 --- a/packages/x-data-grid/src/components/GridPagination.tsx +++ b/packages/x-data-grid/src/components/GridPagination.tsx @@ -7,9 +7,10 @@ import { styled } from '@mui/material/styles'; import { useGridSelector } from '../hooks/utils/useGridSelector'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -import { gridFilteredTopLevelRowCountSelector } from '../hooks/features/filter'; - -import { gridPaginationModelSelector } from '../hooks/features/pagination/gridPaginationSelector'; +import { + gridPaginationModelSelector, + gridPaginationRowCountSelector, +} from '../hooks/features/pagination/gridPaginationSelector'; const GridPaginationRoot = styled(TablePagination)(({ theme }) => ({ [`& .${tablePaginationClasses.selectLabel}`]: { @@ -35,12 +36,7 @@ export const GridPagination = React.forwardRef rootProps.rowCount ?? visibleTopLevelRowCount ?? 0, - [rootProps.rowCount, visibleTopLevelRowCount], - ); + const rowCount = useGridSelector(apiRef, gridPaginationRowCountSelector); const lastPage = React.useMemo(() => { const calculatedValue = Math.ceil(rowCount / (paginationModel.pageSize || 1)) - 1; diff --git a/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx b/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx index 2c0ce7b9c4337..c24e98a1efd35 100644 --- a/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx +++ b/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx @@ -16,7 +16,7 @@ import { gridClasses } from '../../../constants/gridClasses'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { defaultGetRowsToExport, getColumnsToExport } from './utils'; -import { mergeStateWithPaginationModel } from '../pagination/useGridPagination'; +import { getDerivedPaginationModel } from '../pagination/useGridPaginationModel'; import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; import { GridExportDisplayOptions, @@ -307,11 +307,18 @@ export const useGridPrintExport = ( page: 0, pageSize: visibleRowCount, }; - apiRef.current.updateControlState( - 'pagination', - // Using signature `DataGridPro` to allow more than 100 rows in the print export - mergeStateWithPaginationModel(visibleRowCount, 'DataGridPro', paginationModel), - ); + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + paginationModel: getDerivedPaginationModel( + state.pagination, + // Using signature `DataGridPro` to allow more than 100 rows in the print export + 'DataGridPro', + paginationModel, + ), + }, + })); apiRef.current.forceUpdate(); } diff --git a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts index 2506e67ba3e8d..310c659870fae 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationInterfaces.ts @@ -2,16 +2,18 @@ import { GridPaginationModel } from '../../../models/gridPaginationProps'; export interface GridPaginationState { paginationModel: GridPaginationModel; + rowCount: number; } export interface GridPaginationInitialState { paginationModel?: Partial; + rowCount?: number; } /** - * The pagination API interface that is available in the grid `apiRef`. + * The pagination model API interface that is available in the grid `apiRef`. */ -export interface GridPaginationApi { +export interface GridPaginationModelApi { /** * Sets the displayed page to the value given by `page`. * @param {number} page The new page number. @@ -28,3 +30,19 @@ export interface GridPaginationApi { */ setPaginationModel: (model: GridPaginationModel) => void; } + +/** + * The pagination row count API interface that is available in the grid `apiRef`. + */ +export interface GridPaginationRowCountApi { + /** + * Sets the `rowCount` to a new value. + * @param {number} rowCount The new row count value. + */ + setRowCount: (rowCount: number) => void; +} + +/** + * The pagination API interface that is available in the grid `apiRef`. + */ +export interface GridPaginationApi extends GridPaginationModelApi, GridPaginationRowCountApi {} diff --git a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts index 5bd931769e0e6..bc0b8ef20d2e3 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/gridPaginationSelector.ts @@ -1,7 +1,6 @@ import { createSelector, createSelectorMemoized } from '../../../utils/createSelector'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; import { - gridFilteredTopLevelRowCountSelector, gridExpandedSortedRowEntriesSelector, gridExpandedSortedRowIdsSelector, gridFilteredSortedTopLevelRowEntriesSelector, @@ -24,6 +23,15 @@ export const gridPaginationModelSelector = createSelector( (pagination) => pagination.paginationModel, ); +/** + * Get the row count + * @category Pagination + */ +export const gridPaginationRowCountSelector = createSelector( + gridPaginationSelector, + (pagination) => pagination.rowCount, +); + /** * Get the index of the page to render if the pagination is enabled * @category Pagination @@ -47,10 +55,9 @@ export const gridPageSizeSelector = createSelector( * @category Pagination */ export const gridPageCountSelector = createSelector( - gridPaginationModelSelector, - gridFilteredTopLevelRowCountSelector, - (paginationModel, visibleTopLevelRowCount) => - getPageCount(visibleTopLevelRowCount, paginationModel.pageSize), + gridPageSizeSelector, + gridPaginationRowCountSelector, + (pageSize, rowCount) => getPageCount(rowCount, pageSize), ); /** diff --git a/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts b/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts index 1f310902229e2..9abbeab5301c4 100644 --- a/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts +++ b/packages/x-data-grid/src/hooks/features/pagination/useGridPagination.ts @@ -2,27 +2,13 @@ import * as React from 'react'; import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { GridStateInitializer } from '../../utils/useGridInitializeState'; -import { GridPaginationApi, GridPaginationState } from './gridPaginationInterfaces'; -import { GridEventListener } from '../../../models/events'; -import { GridPaginationModel } from '../../../models/gridPaginationProps'; -import { gridFilteredTopLevelRowCountSelector } from '../filter'; -import { gridDensityFactorSelector } from '../density'; -import { - useGridLogger, - useGridSelector, - useGridApiMethod, - useGridApiEventHandler, -} from '../../utils'; -import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; -import { gridPaginationModelSelector } from './gridPaginationSelector'; + import { - getPageCount, - noRowCountInServerMode, - defaultPageSize, throwIfPageSizeExceedsTheLimit, getDefaultGridPaginationModel, - getValidPage, } from './gridPaginationUtils'; +import { useGridPaginationModel } from './useGridPaginationModel'; +import { useGridRowCount } from './useGridRowCount'; export const paginationStateInitializer: GridStateInitializer< Pick< @@ -37,257 +23,24 @@ export const paginationStateInitializer: GridStateInitializer< throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, props.signature); + const rowCount = props.rowCount ?? props.initialState?.pagination?.rowCount; return { ...state, pagination: { paginationModel, + rowCount, }, }; }; -export const mergeStateWithPaginationModel = - ( - rowCount: number, - signature: DataGridProcessedProps['signature'], - paginationModelProp?: GridPaginationModel, - ) => - (paginationState: GridPaginationState) => { - let paginationModel = paginationState.paginationModel; - const pageSize = paginationModelProp?.pageSize ?? paginationModel.pageSize; - const pageCount = getPageCount(rowCount, pageSize); - - if ( - paginationModelProp && - (paginationModelProp?.page !== paginationModel.page || - paginationModelProp?.pageSize !== paginationModel.pageSize) - ) { - paginationModel = paginationModelProp; - } - - const validPage = getValidPage(paginationModel.page, pageCount); - if (validPage !== paginationModel.page) { - paginationModel = { ...paginationModel, page: validPage }; - } - - throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, signature); - - return { paginationModel }; - }; - /** * @requires useGridFilter (state) * @requires useGridDimensions (event) - can be after */ export const useGridPagination = ( apiRef: React.MutableRefObject, - props: Pick< - DataGridProcessedProps, - | 'paginationModel' - | 'onPaginationModelChange' - | 'autoPageSize' - | 'rowCount' - | 'initialState' - | 'paginationMode' - | 'signature' - | 'rowHeight' - >, + props: DataGridProcessedProps, ) => { - const logger = useGridLogger(apiRef, 'useGridPagination'); - - const visibleTopLevelRowCount = useGridSelector(apiRef, gridFilteredTopLevelRowCountSelector); - const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector); - const rowHeight = Math.floor(props.rowHeight * densityFactor); - - apiRef.current.registerControlState({ - stateId: 'pagination', - propModel: props.paginationModel, - propOnChange: props.onPaginationModelChange, - stateSelector: gridPaginationModelSelector, - changeEvent: 'paginationModelChange', - }); - - /** - * API METHODS - */ - const setPage = React.useCallback( - (page) => { - const currentModel = gridPaginationModelSelector(apiRef); - if (page === currentModel.page) { - return; - } - logger.debug(`Setting page to ${page}`); - apiRef.current.setPaginationModel({ page, pageSize: currentModel.pageSize }); - }, - [apiRef, logger], - ); - - const setPageSize = React.useCallback( - (pageSize) => { - const currentModel = gridPaginationModelSelector(apiRef); - if (pageSize === currentModel.pageSize) { - return; - } - - logger.debug(`Setting page size to ${pageSize}`); - apiRef.current.setPaginationModel({ pageSize, page: currentModel.page }); - }, - [apiRef, logger], - ); - - const setPaginationModel = React.useCallback( - (paginationModel) => { - const currentModel = gridPaginationModelSelector(apiRef); - if (paginationModel === currentModel) { - return; - } - logger.debug("Setting 'paginationModel' to", paginationModel); - - apiRef.current.updateControlState( - 'pagination', - mergeStateWithPaginationModel( - props.rowCount ?? visibleTopLevelRowCount, - props.signature, - paginationModel, - ), - 'setPaginationModel', - ); - apiRef.current.forceUpdate(); - }, - [apiRef, logger, props.rowCount, props.signature, visibleTopLevelRowCount], - ); - - const pageApi: GridPaginationApi = { - setPage, - setPageSize, - setPaginationModel, - }; - - useGridApiMethod(apiRef, pageApi, 'public'); - - /** - * PRE-PROCESSING - */ - const stateExportPreProcessing = React.useCallback>( - (prevState, context) => { - const paginationModel = gridPaginationModelSelector(apiRef); - - const shouldExportPaginationModel = - // Always export if the `exportOnlyDirtyModels` property is not activated - !context.exportOnlyDirtyModels || - // Always export if the `paginationModel` is controlled - props.paginationModel != null || - // Always export if the `paginationModel` has been initialized - props.initialState?.pagination?.paginationModel != null || - // Export if `page` or `pageSize` is not equal to the default value - (paginationModel.page !== 0 && - paginationModel.pageSize !== defaultPageSize(props.autoPageSize)); - - if (!shouldExportPaginationModel) { - return prevState; - } - - return { - ...prevState, - pagination: { - ...prevState.pagination, - paginationModel, - }, - }; - }, - [ - apiRef, - props.paginationModel, - props.initialState?.pagination?.paginationModel, - props.autoPageSize, - ], - ); - - const stateRestorePreProcessing = React.useCallback>( - (params, context) => { - const paginationModel = context.stateToRestore.pagination?.paginationModel - ? { - ...getDefaultGridPaginationModel(props.autoPageSize), - ...context.stateToRestore.pagination?.paginationModel, - } - : gridPaginationModelSelector(apiRef); - apiRef.current.updateControlState( - 'pagination', - mergeStateWithPaginationModel( - props.rowCount ?? visibleTopLevelRowCount, - props.signature, - paginationModel, - ), - 'stateRestorePreProcessing', - ); - return params; - }, - [apiRef, props.autoPageSize, props.rowCount, props.signature, visibleTopLevelRowCount], - ); - - useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing); - useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing); - - /** - * EVENTS - */ - const handlePaginationModelChange: GridEventListener<'paginationModelChange'> = () => { - const paginationModel = gridPaginationModelSelector(apiRef); - if (apiRef.current.virtualScrollerRef?.current) { - apiRef.current.scrollToIndexes({ - rowIndex: paginationModel.page * paginationModel.pageSize, - }); - } - - apiRef.current.forceUpdate(); - }; - - const handleUpdateAutoPageSize = React.useCallback(() => { - if (!props.autoPageSize) { - return; - } - - const dimensions = apiRef.current.getRootDimensions(); - - const maximumPageSizeWithoutScrollBar = Math.floor( - dimensions.viewportInnerSize.height / rowHeight, - ); - - apiRef.current.setPageSize(maximumPageSizeWithoutScrollBar); - }, [apiRef, props.autoPageSize, rowHeight]); - - useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleUpdateAutoPageSize); - useGridApiEventHandler(apiRef, 'paginationModelChange', handlePaginationModelChange); - - /** - * EFFECTS - */ - React.useEffect(() => { - if (process.env.NODE_ENV !== 'production') { - if (props.paginationMode === 'server' && props.rowCount == null) { - noRowCountInServerMode(); - } - } - }, [props.rowCount, props.paginationMode]); - - React.useEffect(() => { - apiRef.current.updateControlState( - 'pagination', - mergeStateWithPaginationModel( - props.rowCount ?? visibleTopLevelRowCount, - props.signature, - props.paginationModel, - ), - ); - }, [ - apiRef, - props.paginationModel, - props.rowCount, - props.paginationMode, - visibleTopLevelRowCount, - props.signature, - ]); - - React.useEffect(() => { - handleUpdateAutoPageSize(); - }, [handleUpdateAutoPageSize]); + useGridPaginationModel(apiRef, props); + useGridRowCount(apiRef, props); }; diff --git a/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts b/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts new file mode 100644 index 0000000000000..579eacf8cfd89 --- /dev/null +++ b/packages/x-data-grid/src/hooks/features/pagination/useGridPaginationModel.ts @@ -0,0 +1,267 @@ +import * as React from 'react'; +import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { GridPaginationModelApi, GridPaginationState } from './gridPaginationInterfaces'; +import { GridEventListener } from '../../../models/events'; +import { GridPaginationModel } from '../../../models/gridPaginationProps'; +import { gridDensityFactorSelector } from '../density'; +import { + useGridLogger, + useGridSelector, + useGridApiMethod, + useGridApiEventHandler, +} from '../../utils'; +import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; +import { gridPageCountSelector, gridPaginationModelSelector } from './gridPaginationSelector'; +import { + getPageCount, + defaultPageSize, + throwIfPageSizeExceedsTheLimit, + getDefaultGridPaginationModel, + getValidPage, +} from './gridPaginationUtils'; + +export const getDerivedPaginationModel = ( + paginationState: GridPaginationState, + signature: DataGridProcessedProps['signature'], + paginationModelProp?: GridPaginationModel, +) => { + let paginationModel = paginationState.paginationModel; + const rowCount = paginationState.rowCount; + const pageSize = paginationModelProp?.pageSize ?? paginationModel.pageSize; + const pageCount = getPageCount(rowCount, pageSize); + + if ( + paginationModelProp && + (paginationModelProp?.page !== paginationModel.page || + paginationModelProp?.pageSize !== paginationModel.pageSize) + ) { + paginationModel = paginationModelProp; + } + + const validPage = getValidPage(paginationModel.page, pageCount); + if (validPage !== paginationModel.page) { + paginationModel = { ...paginationModel, page: validPage }; + } + + throwIfPageSizeExceedsTheLimit(paginationModel.pageSize, signature); + + return paginationModel; +}; + +/** + * @requires useGridFilter (state) + * @requires useGridDimensions (event) - can be after + */ +export const useGridPaginationModel = ( + apiRef: React.MutableRefObject, + props: Pick< + DataGridProcessedProps, + | 'paginationModel' + | 'onPaginationModelChange' + | 'autoPageSize' + | 'initialState' + | 'paginationMode' + | 'signature' + | 'rowHeight' + >, +) => { + const logger = useGridLogger(apiRef, 'useGridPaginationModel'); + + const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector); + const rowHeight = Math.floor(props.rowHeight * densityFactor); + apiRef.current.registerControlState({ + stateId: 'paginationModel', + propModel: props.paginationModel, + propOnChange: props.onPaginationModelChange, + stateSelector: gridPaginationModelSelector, + changeEvent: 'paginationModelChange', + }); + + /** + * API METHODS + */ + const setPage = React.useCallback( + (page) => { + const currentModel = gridPaginationModelSelector(apiRef); + if (page === currentModel.page) { + return; + } + logger.debug(`Setting page to ${page}`); + apiRef.current.setPaginationModel({ page, pageSize: currentModel.pageSize }); + }, + [apiRef, logger], + ); + + const setPageSize = React.useCallback( + (pageSize) => { + const currentModel = gridPaginationModelSelector(apiRef); + if (pageSize === currentModel.pageSize) { + return; + } + + logger.debug(`Setting page size to ${pageSize}`); + apiRef.current.setPaginationModel({ pageSize, page: currentModel.page }); + }, + [apiRef, logger], + ); + + const setPaginationModel = React.useCallback( + (paginationModel) => { + const currentModel = gridPaginationModelSelector(apiRef); + if (paginationModel === currentModel) { + return; + } + logger.debug("Setting 'paginationModel' to", paginationModel); + + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + paginationModel: getDerivedPaginationModel( + state.pagination, + props.signature, + paginationModel, + ), + }, + })); + }, + [apiRef, logger, props.signature], + ); + + const paginationModelApi: GridPaginationModelApi = { + setPage, + setPageSize, + setPaginationModel, + }; + + useGridApiMethod(apiRef, paginationModelApi, 'public'); + + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback>( + (prevState, context) => { + const paginationModel = gridPaginationModelSelector(apiRef); + + const shouldExportPaginationModel = + // Always export if the `exportOnlyDirtyModels` property is not activated + !context.exportOnlyDirtyModels || + // Always export if the `paginationModel` is controlled + props.paginationModel != null || + // Always export if the `paginationModel` has been initialized + props.initialState?.pagination?.paginationModel != null || + // Export if `page` or `pageSize` is not equal to the default value + (paginationModel.page !== 0 && + paginationModel.pageSize !== defaultPageSize(props.autoPageSize)); + + if (!shouldExportPaginationModel) { + return prevState; + } + + return { + ...prevState, + pagination: { + ...prevState.pagination, + paginationModel, + }, + }; + }, + [ + apiRef, + props.paginationModel, + props.initialState?.pagination?.paginationModel, + props.autoPageSize, + ], + ); + + const stateRestorePreProcessing = React.useCallback>( + (params, context) => { + const paginationModel = context.stateToRestore.pagination?.paginationModel + ? { + ...getDefaultGridPaginationModel(props.autoPageSize), + ...context.stateToRestore.pagination?.paginationModel, + } + : gridPaginationModelSelector(apiRef); + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + paginationModel: getDerivedPaginationModel( + state.pagination, + props.signature, + paginationModel, + ), + }, + })); + return params; + }, + [apiRef, props.autoPageSize, props.signature], + ); + + useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing); + useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing); + + /** + * EVENTS + */ + const handlePaginationModelChange: GridEventListener<'paginationModelChange'> = () => { + const paginationModel = gridPaginationModelSelector(apiRef); + if (apiRef.current.virtualScrollerRef?.current) { + apiRef.current.scrollToIndexes({ + rowIndex: paginationModel.page * paginationModel.pageSize, + }); + } + }; + + const handleUpdateAutoPageSize = React.useCallback(() => { + if (!props.autoPageSize) { + return; + } + + const dimensions = apiRef.current.getRootDimensions(); + + const maximumPageSizeWithoutScrollBar = Math.floor( + dimensions.viewportInnerSize.height / rowHeight, + ); + + apiRef.current.setPageSize(maximumPageSizeWithoutScrollBar); + }, [apiRef, props.autoPageSize, rowHeight]); + + const handleRowCountChange = React.useCallback( + (newRowCount: GridPaginationState['rowCount']) => { + if (newRowCount == null) { + return; + } + const paginationModel = gridPaginationModelSelector(apiRef); + const pageCount = gridPageCountSelector(apiRef); + if (paginationModel.page > pageCount - 1) { + apiRef.current.setPage(Math.max(0, pageCount - 1)); + } + }, + [apiRef], + ); + + useGridApiEventHandler(apiRef, 'viewportInnerSizeChange', handleUpdateAutoPageSize); + useGridApiEventHandler(apiRef, 'paginationModelChange', handlePaginationModelChange); + useGridApiEventHandler(apiRef, 'rowCountChange', handleRowCountChange); + + /** + * EFFECTS + */ + React.useEffect(() => { + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + paginationModel: getDerivedPaginationModel( + state.pagination, + props.signature, + props.paginationModel, + ), + }, + })); + }, [apiRef, props.paginationModel, props.paginationMode, props.signature]); + + React.useEffect(handleUpdateAutoPageSize, [handleUpdateAutoPageSize]); +}; diff --git a/packages/x-data-grid/src/hooks/features/pagination/useGridRowCount.ts b/packages/x-data-grid/src/hooks/features/pagination/useGridRowCount.ts new file mode 100644 index 0000000000000..0e8e76533dbce --- /dev/null +++ b/packages/x-data-grid/src/hooks/features/pagination/useGridRowCount.ts @@ -0,0 +1,130 @@ +import * as React from 'react'; +import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { GridPaginationRowCountApi } from './gridPaginationInterfaces'; +import { gridFilteredTopLevelRowCountSelector } from '../filter'; +import { useGridLogger, useGridSelector, useGridApiMethod } from '../../utils'; +import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; +import { gridPaginationRowCountSelector } from './gridPaginationSelector'; +import { noRowCountInServerMode } from './gridPaginationUtils'; + +/** + * @requires useGridFilter (state) + * @requires useGridDimensions (event) - can be after + */ +export const useGridRowCount = ( + apiRef: React.MutableRefObject, + props: Pick< + DataGridProcessedProps, + 'rowCount' | 'initialState' | 'paginationMode' | 'onRowCountChange' + >, +) => { + const logger = useGridLogger(apiRef, 'useGridRowCount'); + + const visibleTopLevelRowCount = useGridSelector(apiRef, gridFilteredTopLevelRowCountSelector); + const rowCount = useGridSelector(apiRef, gridPaginationRowCountSelector); + + apiRef.current.registerControlState({ + stateId: 'paginationRowCount', + propModel: props.rowCount, + propOnChange: props.onRowCountChange, + stateSelector: gridPaginationRowCountSelector, + changeEvent: 'rowCountChange', + }); + + /** + * API METHODS + */ + const setRowCount = React.useCallback( + (newRowCount) => { + if (rowCount === newRowCount) { + return; + } + logger.debug("Setting 'rowCount' to", newRowCount); + + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + rowCount: newRowCount, + }, + })); + }, + [apiRef, logger, rowCount], + ); + + const paginationRowCountApi: GridPaginationRowCountApi = { + setRowCount, + }; + + useGridApiMethod(apiRef, paginationRowCountApi, 'public'); + + /** + * PRE-PROCESSING + */ + const stateExportPreProcessing = React.useCallback>( + (prevState, context) => { + const exportedRowCount = gridPaginationRowCountSelector(apiRef); + + const shouldExportRowCount = + // Always export if the `exportOnlyDirtyModels` property is not activated + !context.exportOnlyDirtyModels || + // Always export if the `rowCount` is controlled + props.rowCount != null || + // Always export if the `rowCount` has been initialized + props.initialState?.pagination?.rowCount != null; + + if (!shouldExportRowCount) { + return prevState; + } + + return { + ...prevState, + pagination: { + ...prevState.pagination, + rowCount: exportedRowCount, + }, + }; + }, + [apiRef, props.rowCount, props.initialState?.pagination?.rowCount], + ); + + const stateRestorePreProcessing = React.useCallback>( + (params, context) => { + const restoredRowCount = context.stateToRestore.pagination?.rowCount + ? context.stateToRestore.pagination.rowCount + : gridPaginationRowCountSelector(apiRef); + apiRef.current.setState((state) => ({ + ...state, + pagination: { + ...state.pagination, + rowCount: restoredRowCount, + }, + })); + return params; + }, + [apiRef], + ); + + useGridRegisterPipeProcessor(apiRef, 'exportState', stateExportPreProcessing); + useGridRegisterPipeProcessor(apiRef, 'restoreState', stateRestorePreProcessing); + + /** + * EFFECTS + */ + React.useEffect(() => { + if (process.env.NODE_ENV !== 'production') { + if (props.paginationMode === 'server' && props.rowCount == null) { + noRowCountInServerMode(); + } + } + }, [props.rowCount, props.paginationMode]); + + React.useEffect(() => { + if (props.paginationMode === 'client') { + apiRef.current.setRowCount(visibleTopLevelRowCount); + } else if (props.rowCount != null) { + apiRef.current.setRowCount(props.rowCount); + } + }, [apiRef, visibleTopLevelRowCount, props.paginationMode, props.rowCount]); +}; diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index ec45b0a581b62..01803fc493758 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -30,6 +30,7 @@ import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; import { getMinimalContentHeight } from '../rows/gridRowsUtils'; import { GridRowProps } from '../../../components/GridRow'; +import { GridInfiniteLoaderPrivateApi } from '../../../models/api/gridInfiniteLoaderApi'; import { gridRenderContextSelector, gridVirtualizationEnabledSelector, @@ -41,8 +42,12 @@ export const EMPTY_DETAIL_PANELS = Object.freeze(new Map; +interface PrivateApiWithInfiniteLoader + extends GridPrivateApiCommunity, + GridInfiniteLoaderPrivateApi {} + export const useGridVirtualScroller = () => { - const apiRef = useGridPrivateApiContext(); + const apiRef = useGridPrivateApiContext() as React.MutableRefObject; const rootProps = useGridRootProps(); const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); const enabled = useGridSelector(apiRef, gridVirtualizationEnabledSelector); diff --git a/packages/x-data-grid/src/models/api/gridApiCommon.ts b/packages/x-data-grid/src/models/api/gridApiCommon.ts index 8338cd42d2c7d..fc87a36a4d8b6 100644 --- a/packages/x-data-grid/src/models/api/gridApiCommon.ts +++ b/packages/x-data-grid/src/models/api/gridApiCommon.ts @@ -34,7 +34,6 @@ import { GridColumnGroupingApi } from './gridColumnGroupingApi'; import type { GridInitialStateCommunity, GridStateCommunity } from '../gridStateCommunity'; import { GridHeaderFilteringApi, GridHeaderFilteringPrivateApi } from './gridHeaderFilteringApi'; import type { DataGridProcessedProps } from '../props/DataGridProps'; -import type { GridInfiniteLoaderPrivateApi } from './gridInfiniteLoaderApi'; export interface GridApiCommon< GridState extends GridStateCommunity = any, @@ -81,8 +80,7 @@ export interface GridPrivateOnlyApiCommon< GridLoggerApi, GridFocusPrivateApi, GridHeaderFilteringPrivateApi, - GridVirtualizationPrivateApi, - GridInfiniteLoaderPrivateApi {} + GridVirtualizationPrivateApi {} export interface GridPrivateApiCommon extends GridApiCommon, diff --git a/packages/x-data-grid/src/models/events/gridEventLookup.ts b/packages/x-data-grid/src/models/events/gridEventLookup.ts index 31ed5a43554a1..451988f596a1d 100644 --- a/packages/x-data-grid/src/models/events/gridEventLookup.ts +++ b/packages/x-data-grid/src/models/events/gridEventLookup.ts @@ -358,6 +358,10 @@ export interface GridControlledStateEventLookup { * Fired when the column visibility model changes. */ columnVisibilityModelChange: { params: GridColumnVisibilityModel }; + /** + * Fired when the row count change. + */ + rowCountChange: { params: number }; } export interface GridControlledStateReasonLookup { diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index cab7a732b01e4..142892968f099 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -591,6 +591,11 @@ export interface DataGridPropsWithoutDefaultValue void; + /** + * Callback fired when the row count has changed. + * @param {number} count Updated row count. + */ + onRowCountChange?: (count: number) => void; /** * Callback fired when the preferences panel is closed. * @param {GridPreferencePanelParams} params With all properties from [[GridPreferencePanelParams]]. diff --git a/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx b/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx index 5b1d400a09503..fa636c43f2424 100644 --- a/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/pagination.DataGrid.test.tsx @@ -524,12 +524,13 @@ describe(' - Pagination', () => { }); }); - it('should react to an update of rowCount', () => { + it('should react to an update of rowCount when `paginationMode = server`', () => { const { setProps } = render( , ); expect(document.querySelector('.MuiTablePagination-root')).to.have.text('1–1 of 5'); // "–" is not a hyphen, it's an "en dash" diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 41fbd341ce7e4..96ed01c5e2011 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -408,6 +408,7 @@ { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationModel", "kind": "Interface" }, { "name": "gridPaginationModelSelector", "kind": "Variable" }, + { "name": "gridPaginationRowCountSelector", "kind": "Variable" }, { "name": "gridPaginationRowRangeSelector", "kind": "Variable" }, { "name": "gridPaginationSelector", "kind": "Variable" }, { "name": "GridPaginationState", "kind": "Interface" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index 4b31200d35d6b..7bb972eb7e633 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -372,6 +372,7 @@ { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationModel", "kind": "Interface" }, { "name": "gridPaginationModelSelector", "kind": "Variable" }, + { "name": "gridPaginationRowCountSelector", "kind": "Variable" }, { "name": "gridPaginationRowRangeSelector", "kind": "Variable" }, { "name": "gridPaginationSelector", "kind": "Variable" }, { "name": "GridPaginationState", "kind": "Interface" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 8ecdfb3222270..89ddee633343a 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -338,6 +338,7 @@ { "name": "GridPaginationInitialState", "kind": "Interface" }, { "name": "GridPaginationModel", "kind": "Interface" }, { "name": "gridPaginationModelSelector", "kind": "Variable" }, + { "name": "gridPaginationRowCountSelector", "kind": "Variable" }, { "name": "gridPaginationRowRangeSelector", "kind": "Variable" }, { "name": "gridPaginationSelector", "kind": "Variable" }, { "name": "GridPaginationState", "kind": "Interface" }, From 7d803fa9bb62f6f86ebdad66c70f97011f6c126a Mon Sep 17 00:00:00 2001 From: Makoto M <61872424+makoto14@users.noreply.github.com> Date: Mon, 11 Mar 2024 01:35:11 +0900 Subject: [PATCH 15/49] [l10n] Improve Japanese (ja-JP) locale (#12398) --- docs/data/data-grid/localization/data.json | 2 +- docs/data/date-pickers/localization/data.json | 2 +- packages/x-data-grid/src/locales/jaJP.ts | 6 ++-- packages/x-date-pickers/src/locales/jaJP.ts | 28 +++++++++---------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/data/data-grid/localization/data.json b/docs/data/data-grid/localization/data.json index 65b342c980adc..2d51946c58760 100644 --- a/docs/data/data-grid/localization/data.json +++ b/docs/data/data-grid/localization/data.json @@ -139,7 +139,7 @@ "languageTag": "ja-JP", "importName": "jaJP", "localeName": "Japanese", - "missingKeysCount": 3, + "missingKeysCount": 0, "totalKeysCount": 117, "githubLink": "https://github.com/mui/mui-x/blob/next/packages/x-data-grid/src/locales/jaJP.ts" }, diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index 0a76129d0673b..1ba2f8bb68bbe 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -131,7 +131,7 @@ "languageTag": "ja-JP", "importName": "jaJP", "localeName": "Japanese", - "missingKeysCount": 14, + "missingKeysCount": 0, "totalKeysCount": 50, "githubLink": "https://github.com/mui/mui-x/blob/next/packages/x-date-pickers/src/locales/jaJP.ts" }, diff --git a/packages/x-data-grid/src/locales/jaJP.ts b/packages/x-data-grid/src/locales/jaJP.ts index 03604e992d98e..2f98e640ffdba 100644 --- a/packages/x-data-grid/src/locales/jaJP.ts +++ b/packages/x-data-grid/src/locales/jaJP.ts @@ -38,9 +38,9 @@ const jaJPGrid: Partial = { toolbarExportExcel: 'Excelダウンロード', // Columns management text - // columnsManagementSearchTitle: 'Search', - // columnsManagementNoColumns: 'No columns', - // columnsManagementShowHideAllText: 'Show/Hide All', + columnsManagementSearchTitle: '検索', + columnsManagementNoColumns: 'カラムなし', + columnsManagementShowHideAllText: 'すべて表示/非表示', // Filter panel text filterPanelAddFilter: 'フィルター追加', diff --git a/packages/x-date-pickers/src/locales/jaJP.ts b/packages/x-date-pickers/src/locales/jaJP.ts index b75376e3059a8..6220e261c23ba 100644 --- a/packages/x-date-pickers/src/locales/jaJP.ts +++ b/packages/x-date-pickers/src/locales/jaJP.ts @@ -26,10 +26,10 @@ const jaJPPickers: Partial> = { // DateRange labels start: '開始', end: '終了', - // startDate: 'Start date', - // startTime: 'Start time', - // endDate: 'End date', - // endTime: 'End time', + startDate: '開始日', + startTime: '開始時間', + endDate: '終了日', + endTime: '終了時間', // Action bar cancelButtonLabel: 'キャンセル', @@ -68,7 +68,7 @@ const jaJPPickers: Partial> = { value !== null && utils.isValid(value) ? `時間を選択してください。選択した時間は ${utils.format(value, 'fullTime')} です` : '時間を選択してください', - // fieldClearLabel: 'Clear value', + fieldClearLabel: 'クリア', // Table labels timeTableLabel: '時間を選択', @@ -85,17 +85,17 @@ const jaJPPickers: Partial> = { fieldMeridiemPlaceholder: () => 'aa', // View names - // year: 'Year', - // month: 'Month', - // day: 'Day', - // weekDay: 'Week day', - // hours: 'Hours', - // minutes: 'Minutes', - // seconds: 'Seconds', - // meridiem: 'Meridiem', + year: '年', + month: '月', + day: '日', + weekDay: '平日', + hours: '時間', + minutes: '分', + seconds: '秒', + meridiem: 'メリディム', // Common - // empty: 'Empty', + empty: '空', }; export const jaJP = getPickersLocalization(jaJPPickers); From 994f739ca2b7516ca9df67aa616ff58c749f07b9 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Mon, 11 Mar 2024 00:54:12 +0100 Subject: [PATCH 16/49] [data grid] Fix focus visible style on scrollbar (#12402) --- .../src/components/virtualization/GridVirtualScrollbar.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx index aacc4c1dd0a2b..5be8dfaf89145 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollbar.tsx @@ -47,6 +47,8 @@ const ScrollbarVertical = styled(Scrollbar)({ 'calc(var(--DataGrid-hasScrollY) * (100% - var(--DataGrid-topContainerHeight) - var(--DataGrid-bottomContainerHeight) - var(--DataGrid-hasScrollX) * var(--DataGrid-scrollbarSize)))', overflowY: 'auto', overflowX: 'hidden', + // Disable focus-visible style, it's a scrollbar. + outline: 0, '& > div': { width: 'var(--size)', }, @@ -59,6 +61,8 @@ const ScrollbarHorizontal = styled(Scrollbar)({ height: 'var(--size)', overflowY: 'hidden', overflowX: 'auto', + // Disable focus-visible style, it's a scrollbar. + outline: 0, '& > div': { height: 'var(--size)', }, From b8566a0cf82bc7633a783820d3b19d05f19b8cee Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Mon, 11 Mar 2024 09:10:33 +0100 Subject: [PATCH 17/49] [TreeView] Introduce a new `TreeItem2` component and a new `useTreeItem2` hook (#11721) --- docs/data/tree-view-component-api-pages.ts | 4 + docs/data/tree-view/overview/overview.md | 72 ++++ .../customization/CustomContentTreeView.js | 109 +++--- .../customization/CustomContentTreeView.tsx | 119 +++---- .../CustomContentTreeView.tsx.preview | 2 +- .../customization/LabelSlotProps.js | 47 +++ .../customization/LabelSlotProps.tsx | 50 +++ .../customization/LabelSlotProps.tsx.preview | 7 + .../customization/LabelSlots.js | 156 +++++++++ .../customization/LabelSlots.tsx | 175 ++++++++++ .../customization/LabelSlots.tsx.preview | 9 + .../customization/customization.md | 31 +- .../rich-tree-view/headless/headless.md | 14 +- .../selection/DisableSelection.js | 2 +- .../selection/DisableSelection.tsx | 3 +- .../selection/MultiSelectTreeView.js | 2 +- .../selection/MultiSelectTreeView.tsx | 3 +- .../customization/CustomContentTreeView.js | 110 +++--- .../customization/CustomContentTreeView.tsx | 127 +++---- .../customization/CustomizedTreeView.js | 4 +- .../customization/CustomizedTreeView.tsx | 6 +- .../customization/GmailTreeView.js | 174 ++++++---- .../customization/GmailTreeView.tsx | 184 ++++++---- .../customization/IconExpansionTreeView.js | 77 ++--- .../customization/IconExpansionTreeView.tsx | 99 ++---- .../customization/LabelSlotProps.js | 35 ++ .../customization/LabelSlotProps.tsx | 37 ++ .../customization/LabelSlotProps.tsx.preview | 15 + .../customization/LabelSlots.js | 66 ++++ .../customization/LabelSlots.tsx | 81 +++++ .../customization/customization.md | 45 ++- docs/pages/x/api/tree-view/tree-item-2.js | 23 ++ docs/pages/x/api/tree-view/tree-item-2.json | 93 ++++++ .../tree-view/tree-item-2/tree-item-2.json | 49 +++ .../src/RichTreeView/RichTreeView.types.ts | 3 +- .../src/TreeItem/TreeItem.test.tsx | 15 +- .../x-tree-view/src/TreeItem/TreeItem.tsx | 95 +++--- .../src/TreeItem/TreeItem.types.ts | 2 +- .../x-tree-view/src/TreeItem2/TreeItem2.tsx | 316 ++++++++++++++++++ .../src/TreeItem2/TreeItem2.types.ts | 69 ++++ packages/x-tree-view/src/TreeItem2/index.ts | 9 + .../src/TreeItem2Icon/TreeItem2Icon.tsx | 80 +++++ .../src/TreeItem2Icon/TreeItem2Icon.types.ts | 43 +++ .../x-tree-view/src/TreeItem2Icon/index.ts | 6 + .../TreeItem2Provider/TreeItem2Provider.tsx | 21 ++ .../TreeItem2Provider.types.ts | 7 + .../src/TreeItem2Provider/index.ts | 2 + packages/x-tree-view/src/hooks/index.ts | 1 + .../src/hooks/useTreeItem2Utils/index.ts | 1 + .../useTreeItem2Utils/useTreeItem2Utils.tsx | 78 +++++ packages/x-tree-view/src/index.ts | 11 +- .../TreeViewProvider.types.ts | 7 +- .../src/internals/models/plugin.ts | 38 ++- .../useTreeViewJSXNodes.tsx | 33 +- .../useTreeViewKeyboardNavigation.ts | 7 +- .../useTreeViewNodes/useTreeViewNodes.ts | 4 +- .../src/internals/useTreeView/useTreeView.ts | 50 +-- .../src/themeAugmentation/components.d.ts | 5 + .../src/themeAugmentation/overrides.d.ts | 1 + .../src/themeAugmentation/props.d.ts | 2 + .../themeAugmentation.spec.ts | 19 ++ .../x-tree-view/src/useTreeItem2/index.ts | 7 + .../src/useTreeItem2/useTreeItem2.ts | 192 +++++++++++ .../src/useTreeItem2/useTreeItem2.types.ts | 140 ++++++++ scripts/x-tree-view.exports.json | 21 ++ 65 files changed, 2637 insertions(+), 678 deletions(-) create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlots.js create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx create mode 100644 docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview create mode 100644 docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js create mode 100644 docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx create mode 100644 docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview create mode 100644 docs/data/tree-view/simple-tree-view/customization/LabelSlots.js create mode 100644 docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx create mode 100644 docs/pages/x/api/tree-view/tree-item-2.js create mode 100644 docs/pages/x/api/tree-view/tree-item-2.json create mode 100644 docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json create mode 100644 packages/x-tree-view/src/TreeItem2/TreeItem2.tsx create mode 100644 packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts create mode 100644 packages/x-tree-view/src/TreeItem2/index.ts create mode 100644 packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.tsx create mode 100644 packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.types.ts create mode 100644 packages/x-tree-view/src/TreeItem2Icon/index.ts create mode 100644 packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx create mode 100644 packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts create mode 100644 packages/x-tree-view/src/TreeItem2Provider/index.ts create mode 100644 packages/x-tree-view/src/hooks/useTreeItem2Utils/index.ts create mode 100644 packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx create mode 100644 packages/x-tree-view/src/useTreeItem2/index.ts create mode 100644 packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts create mode 100644 packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts diff --git a/docs/data/tree-view-component-api-pages.ts b/docs/data/tree-view-component-api-pages.ts index b65570c58e476..39fcd6ae89333 100644 --- a/docs/data/tree-view-component-api-pages.ts +++ b/docs/data/tree-view-component-api-pages.ts @@ -13,6 +13,10 @@ const apiPages: MuiPage[] = [ pathname: '/x/api/tree-view/tree-item', title: 'TreeItem', }, + { + pathname: '/x/api/tree-view/tree-item-2', + title: 'TreeItem2', + }, { pathname: '/x/api/tree-view/tree-view', title: 'TreeView', diff --git a/docs/data/tree-view/overview/overview.md b/docs/data/tree-view/overview/overview.md index 8c820383e66c5..7c0145f2a6dd6 100644 --- a/docs/data/tree-view/overview/overview.md +++ b/docs/data/tree-view/overview/overview.md @@ -41,3 +41,75 @@ This is the recommended version for larger trees, as well as those that require :::info At the moment, the Simple and Rich Tree Views are similar in terms of feature support. But as the component grows, you can expect to see the more advanced ones appear primarily on the Rich Tree View. ::: + +### Tree Item components + +The `@mui/x-tree-view` package exposes two different components to define your tree items: + +- `TreeItem` +- `TreeItem2` + +#### `TreeItem` + +This is the long-standing component that is very similar to the one used in previous versions (`@mui/x-tree-view@6` and `@mui/lab`). + +When using `SimpleTreeView`, +you can import it from `@mui/x-tree-view/TreeItem` and use it as a child of the `SimpleTreeView` component: + +```tsx +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { TreeItem } from '@mui/x-tree-view/TreeItem'; + +export default function App() { + return ( + + + + + ); +} +``` + +When using `RichTreeView`, +you don't have to import anything; it's the default component used to render the items: + +```tsx +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +export default function App() { + return ; +} +``` + +#### `TreeItem2` + +This is a new component that provides a more powerful customization API, and will eventually replace `TreeItem`. + +When using `SimpleTreeView`, +you can import it from `@mui/x-tree-view/TreeItem2` and use it as a child of the `SimpleTreeView` component: + +```tsx +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; + +export default function App() { + return ( + + + + + ); +} +``` + +When using `RichTreeView`, +you can import it from `@mui/x-tree-view/TreeItem2` and pass it as a slot of the `RichTreeView` component: + +```tsx +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; + +export default function App() { + return ; +} +``` diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js index 4643d3aa8f88a..111a3009a3d3d 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js @@ -1,11 +1,19 @@ import * as React from 'react'; -import clsx from 'clsx'; import { styled } from '@mui/material/styles'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Avatar from '@mui/material/Avatar'; import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; -import { useTreeItemState } from '@mui/x-tree-view/TreeItem'; + +import { unstable_useTreeItem2 as useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; const ITEMS = [ { @@ -27,71 +35,46 @@ const ITEMS = [ }, ]; -const CustomContentRoot = styled('div')(({ theme }) => ({ - '&': { padding: theme.spacing(0.5, 1) }, +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + padding: theme.spacing(0.5, 1), })); -const CustomContent = React.forwardRef(function CustomContent(props, ref) { - const { - className, - classes, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { id, nodeId, label, disabled, children, ...other } = props; const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); - - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event) => { - preventSelection(event); - }; - - const handleClick = (event) => { - handleExpansion(event); - handleSelection(event); - }; + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); return ( - -
{icon}
- - ({ - background: theme.palette.primary.main, - width: 24, - height: 24, - fontSize: '0.8rem', - })} - > - {label[0]} - - - {label} - - -
+ + + + + + + + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {label[0]} + + + + + {children && } + + ); }); @@ -103,7 +86,7 @@ export default function CustomContentTreeView() { sx={{ position: 'relative' }} defaultExpandedNodes={['3']} items={ITEMS} - slotProps={{ item: { ContentComponent: CustomContent } }} + slots={{ item: CustomTreeItem }} /> ); diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx index 1d351739796a5..9d2333b5b6517 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx @@ -1,12 +1,22 @@ import * as React from 'react'; -import clsx from 'clsx'; import { styled } from '@mui/material/styles'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Avatar from '@mui/material/Avatar'; import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; -import { useTreeItemState, TreeItemContentProps } from '@mui/x-tree-view/TreeItem'; import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { + unstable_useTreeItem2 as useTreeItem2, + UseTreeItem2Parameters, +} from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; const ITEMS: TreeViewBaseItem[] = [ { @@ -28,74 +38,53 @@ const ITEMS: TreeViewBaseItem[] = [ }, ]; -const CustomContentRoot = styled('div')(({ theme }) => ({ - '&': { padding: theme.spacing(0.5, 1) }, +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + padding: theme.spacing(0.5, 1), })); -const CustomContent = React.forwardRef(function CustomContent( - props: TreeItemContentProps, - ref, +interface CustomTreeItemProps + extends Omit, + Omit, 'onFocus'> {} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: CustomTreeItemProps, + ref: React.Ref, ) { - const { - className, - classes, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; + const { id, nodeId, label, disabled, children, ...other } = props; const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); - - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event: React.MouseEvent) => { - preventSelection(event); - }; - - const handleClick = (event: React.MouseEvent) => { - handleExpansion(event); - handleSelection(event); - }; + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); return ( - } - > -
{icon}
- - ({ - background: theme.palette.primary.main, - width: 24, - height: 24, - fontSize: '0.8rem', - })} - > - {(label as string)[0]} - - - {label} - - -
+ + + + + + + + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {(label as string)[0]} + + + + + {children && } + + ); }); @@ -107,7 +96,7 @@ export default function CustomContentTreeView() { sx={{ position: 'relative' }} defaultExpandedNodes={['3']} items={ITEMS} - slotProps={{ item: { ContentComponent: CustomContent } }} + slots={{ item: CustomTreeItem }} /> ); diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview index 163c474b4f991..39ab54a65d5df 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview @@ -3,5 +3,5 @@ sx={{ position: 'relative' }} defaultExpandedNodes={['3']} items={ITEMS} - slotProps={{ item: { ContentComponent: CustomContent } }} + slots={{ item: CustomTreeItem }} /> \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js new file mode 100644 index 0000000000000..656ba20ef9f10 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; + +const MUI_X_PRODUCTS = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +const CustomTreeItem = React.forwardRef((props, ref) => ( + +)); + +export default function LabelSlotProps() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx new file mode 100644 index 0000000000000..f66b49a6951a2 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx @@ -0,0 +1,50 @@ +import * as React from 'react'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; + +const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +const CustomTreeItem = React.forwardRef( + (props: TreeItem2Props, ref: React.Ref) => ( + + ), +); + +export default function LabelSlotProps() { + return ( + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview new file mode 100644 index 0000000000000..a7f9d42cc158b --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js new file mode 100644 index 0000000000000..190a40da5ec89 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js @@ -0,0 +1,156 @@ +import * as React from 'react'; +import { TreeItem2, TreeItem2Label } from '@mui/x-tree-view/TreeItem2'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; + +import { useTreeItem2Utils } from '@mui/x-tree-view'; + +function CustomLabel(props) { + const { children, onChange, ...other } = props; + + const [isEditing, setIsEditing] = React.useState(false); + const [value, setValue] = React.useState(''); + const editingLabelRef = React.useRef(null); + + const handleLabelDoubleClick = () => { + setIsEditing(true); + setValue(children); + }; + + const handleEditingLabelChange = (event) => { + setValue(event.target.value); + }; + + const handleEditingLabelKeyDown = (event) => { + if (event.key === 'Enter') { + event.stopPropagation(); + setIsEditing(false); + onChange(value); + } else if (event.key === 'Escape') { + event.stopPropagation(); + setIsEditing(false); + } + }; + + React.useEffect(() => { + if (isEditing) { + editingLabelRef.current?.focus(); + } + }, [isEditing]); + + if (isEditing) { + return ( + + ); + } + + return ( + + {children} + + ); +} + +const TreeItemContext = React.createContext({ onLabelValueChange: () => {} }); + +const CustomTreeItem = React.forwardRef((props, ref) => { + const { interactions } = useTreeItem2Utils({ + nodeId: props.nodeId, + children: props.children, + }); + + const { onLabelValueChange } = React.useContext(TreeItemContext); + + const handleLabelValueChange = (newLabel) => { + onLabelValueChange(props.nodeId, newLabel); + }; + + const handleContentClick = (event) => { + event.defaultMuiPrevented = true; + interactions.handleSelection(event); + }; + + const handleIconContainerClick = (event) => { + interactions.handleExpansion(event); + }; + + return ( + + ); +}); + +const DEFAULT_MUI_X_PRODUCTS = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +const DEFAULT_EXPANDED_NODES = ['pickers']; + +export default function LabelSlots() { + const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); + + const context = React.useMemo( + () => ({ + onLabelValueChange: (nodeId, label) => + setProducts((prev) => { + const walkTree = (item) => { + if (item.id === nodeId) { + return { ...item, label }; + } + if (item.children) { + return { ...item, children: item.children.map(walkTree) }; + } + + return item; + }; + + return prev.map(walkTree); + }), + }), + [], + ); + + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx new file mode 100644 index 0000000000000..1fde72d18f234 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx @@ -0,0 +1,175 @@ +import * as React from 'react'; +import { + TreeItem2, + TreeItem2Label, + TreeItem2Props, +} from '@mui/x-tree-view/TreeItem2'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; +import { + UseTreeItem2ContentSlotOwnProps, + useTreeItem2Utils, +} from '@mui/x-tree-view'; + +interface CustomLabelProps { + children: string; + className: string; + onChange: (value: string) => void; +} + +function CustomLabel(props: CustomLabelProps) { + const { children, onChange, ...other } = props; + + const [isEditing, setIsEditing] = React.useState(false); + const [value, setValue] = React.useState(''); + const editingLabelRef = React.useRef(null); + + const handleLabelDoubleClick = () => { + setIsEditing(true); + setValue(children); + }; + + const handleEditingLabelChange = (event: React.ChangeEvent) => { + setValue(event.target.value); + }; + + const handleEditingLabelKeyDown = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + event.stopPropagation(); + setIsEditing(false); + onChange(value); + } else if (event.key === 'Escape') { + event.stopPropagation(); + setIsEditing(false); + } + }; + + React.useEffect(() => { + if (isEditing) { + editingLabelRef.current?.focus(); + } + }, [isEditing]); + + if (isEditing) { + return ( + + ); + } + + return ( + + {children} + + ); +} + +const TreeItemContext = React.createContext<{ + onLabelValueChange: (nodeId: string, label: string) => void; +}>({ onLabelValueChange: () => {} }); + +const CustomTreeItem = React.forwardRef( + (props: TreeItem2Props, ref: React.Ref) => { + const { interactions } = useTreeItem2Utils({ + nodeId: props.nodeId, + children: props.children, + }); + + const { onLabelValueChange } = React.useContext(TreeItemContext); + + const handleLabelValueChange = (newLabel: string) => { + onLabelValueChange(props.nodeId, newLabel); + }; + + const handleContentClick: UseTreeItem2ContentSlotOwnProps['onClick'] = ( + event, + ) => { + event.defaultMuiPrevented = true; + interactions.handleSelection(event); + }; + + const handleIconContainerClick = (event: React.MouseEvent) => { + interactions.handleExpansion(event); + }; + + return ( + + ); + }, +); + +const DEFAULT_MUI_X_PRODUCTS: TreeViewBaseItem[] = [ + { + id: 'grid', + label: 'Data Grid', + children: [ + { id: 'grid-community', label: '@mui/x-data-grid' }, + { id: 'grid-pro', label: '@mui/x-data-grid-pro' }, + { id: 'grid-premium', label: '@mui/x-data-grid-premium' }, + ], + }, + { + id: 'pickers', + label: 'Date and Time Pickers', + children: [ + { id: 'pickers-community', label: '@mui/x-date-pickers' }, + { id: 'pickers-pro', label: '@mui/x-date-pickers-pro' }, + ], + }, +]; + +const DEFAULT_EXPANDED_NODES = ['pickers']; + +export default function LabelSlots() { + const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); + + const context = React.useMemo( + () => ({ + onLabelValueChange: (nodeId: string, label: string) => + setProducts((prev) => { + const walkTree = (item: TreeViewBaseItem): TreeViewBaseItem => { + if (item.id === nodeId) { + return { ...item, label }; + } + if (item.children) { + return { ...item, children: item.children.map(walkTree) }; + } + + return item; + }; + + return prev.map(walkTree); + }), + }), + [], + ); + + return ( + + + + ); +} diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview new file mode 100644 index 0000000000000..ab6c46ba22496 --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/customization.md b/docs/data/tree-view/rich-tree-view/customization/customization.md index 771dbd8aefaaf..5d33efde72b5a 100644 --- a/docs/data/tree-view/rich-tree-view/customization/customization.md +++ b/docs/data/tree-view/rich-tree-view/customization/customization.md @@ -1,7 +1,7 @@ --- productId: x-tree-view title: Rich Tree View - Customization -components: RichTreeView, TreeItem +components: RichTreeView, TreeItem, TreeItem2 packageName: '@mui/x-tree-view' githubLabel: 'component: tree view' waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ @@ -9,7 +9,7 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ # Rich Tree View - Customization -

Learn how to customize the rich version of the Tree View component.

+

Learn how to customize the Rich Tree View component.

## Basics @@ -34,12 +34,33 @@ Use `treeItemClasses` to target internal elements of the Tree Item component and {{"demo": "CustomStyling.js"}} -### Adding custom content +### Custom label -Use the `ContentComponent` prop and the `useTreeItemState` hook to replace the Tree Item content with an entirely custom component. +:::warning +This example is built using the new `TreeItem2` component +which adds several slots to modify the content of the Tree Item or change its behavior. + +You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). +::: + +Use the `label` slot to customize the Tree Item label or to replace it with a custom component. + +The `slotProps` prop allows you to pass props to the label component. +The demo below shows how to pass an `id` attribute to the Tree Item label: + +{{"demo": "LabelSlotProps.js", "defaultCodeOpen": false }} + +The `slots` prop allows you to replace the default label with your own component: +The demo below shows how to add a tooltip on the Tree Item label: + +{{"demo": "LabelSlots.js", "defaultCodeOpen": false}} + +### Headless API + +Use the `useTreeItem2` hook to create your own component. The demo below shows how to add an avatar and custom typography elements. -{{"demo": "CustomContentTreeView.js"}} +{{"demo": "CustomContentTreeView.js", "defaultCodeOpen": false}} ## Common examples diff --git a/docs/data/tree-view/rich-tree-view/headless/headless.md b/docs/data/tree-view/rich-tree-view/headless/headless.md index c1248e4cffcff..285d47156a297 100644 --- a/docs/data/tree-view/rich-tree-view/headless/headless.md +++ b/docs/data/tree-view/rich-tree-view/headless/headless.md @@ -206,8 +206,8 @@ const useCustomPlugin = ({ params }) => { You can use the `contextValue` property in the returned object to pass elements to the Tree Item: :::warning -The context is private for now and cannot be accessed outside of our own plugins. -You need to modify the `useTreeItem` hook to return the new value returned by your plugin. +The context is private for now and cannot be accessed outside the provided plugins. +You need to modify the `useTreeItemState` hook to return the new value returned by your plugin. ::: ```tsx @@ -219,25 +219,25 @@ const useCustomPlugin = ({ params }) => { }; }; -function useTreeItem(nodeId: string) { +function useTreeItemState(nodeId: string) { const { customPlugin, // ...other elements returned by the context } = useTreeViewContext(); - // ...rest of the `useTreeItem` hook content + // ...rest of the `useTreeItemState` hook content return { customPlugin, - // ...other elements returned by `useTreeItem` + // ...other elements returned by `useTreeItemState` }; } function TreeItemContent() { const { customPlugin, - // ...other elements returned by `useTreeItem` - } = useTreeItem(props.nodeId); + // ...other elements returned by `useTreeItemState` + } = useTreeItemState(props.nodeId); // Do something with customPlugin.enabled } diff --git a/docs/data/tree-view/rich-tree-view/selection/DisableSelection.js b/docs/data/tree-view/rich-tree-view/selection/DisableSelection.js index 0f9b7cec775a5..45f155a4b3e4e 100644 --- a/docs/data/tree-view/rich-tree-view/selection/DisableSelection.js +++ b/docs/data/tree-view/rich-tree-view/selection/DisableSelection.js @@ -1,6 +1,6 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { RichTreeView } from '@mui/x-tree-view'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; const MUI_X_PRODUCTS = [ { diff --git a/docs/data/tree-view/rich-tree-view/selection/DisableSelection.tsx b/docs/data/tree-view/rich-tree-view/selection/DisableSelection.tsx index 1d5186c765421..7937123a72e34 100644 --- a/docs/data/tree-view/rich-tree-view/selection/DisableSelection.tsx +++ b/docs/data/tree-view/rich-tree-view/selection/DisableSelection.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { RichTreeView, TreeViewBaseItem } from '@mui/x-tree-view'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ { diff --git a/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.js b/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.js index a873643c428db..ea21e6cb404b8 100644 --- a/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.js +++ b/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.js @@ -1,6 +1,6 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { RichTreeView } from '@mui/x-tree-view'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; const MUI_X_PRODUCTS = [ { diff --git a/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.tsx b/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.tsx index e66daa1db6456..deea7256e615e 100644 --- a/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/selection/MultiSelectTreeView.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { RichTreeView, TreeViewBaseItem } from '@mui/x-tree-view'; +import { RichTreeView } from '@mui/x-tree-view/RichTreeView'; +import { TreeViewBaseItem } from '@mui/x-tree-view/models'; const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ { diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js index c0286f0f82529..feaf4582c8d17 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js @@ -1,84 +1,62 @@ import * as React from 'react'; -import clsx from 'clsx'; import { styled } from '@mui/material/styles'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Avatar from '@mui/material/Avatar'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem, useTreeItemState } from '@mui/x-tree-view/TreeItem'; +import { unstable_useTreeItem2 as useTreeItem2 } from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; -const CustomContentRoot = styled('div')(({ theme }) => ({ - '&': { padding: theme.spacing(0.5, 1) }, +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + padding: theme.spacing(0.5, 1), })); -const CustomContent = React.forwardRef(function CustomContent(props, ref) { - const { - className, - classes, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + const { id, nodeId, label, disabled, children, ...other } = props; const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); - - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event) => { - preventSelection(event); - }; - - const handleClick = (event) => { - handleExpansion(event); - handleSelection(event); - }; + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); return ( - -
{icon}
- - ({ - background: theme.palette.primary.main, - width: 24, - height: 24, - fontSize: '0.8rem', - })} - > - {label[0]} - - - {label} - - -
+ + + + + + + + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {label[0]} + + + + + {children && } + + ); }); -const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { - return ; -}); - export default function CustomContentTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx index 25406d06419f6..5e4bf36f6798a 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx @@ -1,95 +1,72 @@ import * as React from 'react'; -import clsx from 'clsx'; import { styled } from '@mui/material/styles'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import Avatar from '@mui/material/Avatar'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { - TreeItem, - useTreeItemState, - TreeItemProps, - TreeItemContentProps, -} from '@mui/x-tree-view/TreeItem'; + unstable_useTreeItem2 as useTreeItem2, + UseTreeItem2Parameters, +} from '@mui/x-tree-view/useTreeItem2'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, + TreeItem2Root, +} from '@mui/x-tree-view/TreeItem2'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; -const CustomContentRoot = styled('div')(({ theme }) => ({ - '&': { padding: theme.spacing(0.5, 1) }, +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + padding: theme.spacing(0.5, 1), })); -const CustomContent = React.forwardRef(function CustomContent( - props: TreeItemContentProps, - ref, +interface CustomTreeItemProps + extends Omit, + Omit, 'onFocus'> {} + +const CustomTreeItem = React.forwardRef(function CustomTreeItem( + props: CustomTreeItemProps, + ref: React.Ref, ) { - const { - className, - classes, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; + const { id, nodeId, label, disabled, children, ...other } = props; const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); - - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event: React.MouseEvent) => { - preventSelection(event); - }; - - const handleClick = (event: React.MouseEvent) => { - handleExpansion(event); - handleSelection(event); - }; + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); return ( - } - > -
{icon}
- - ({ - background: theme.palette.primary.main, - width: 24, - height: 24, - fontSize: '0.8rem', - })} - > - {(label as string)[0]} - - - {label} - - -
+ + + + + + + + ({ + background: theme.palette.primary.main, + width: 24, + height: 24, + fontSize: '0.8rem', + })} + > + {(label as string)[0]} + + + + + {children && } + + ); }); -const CustomTreeItem = React.forwardRef(function CustomTreeItem( - props: TreeItemProps & { subtitle?: string }, - ref: React.Ref, -) { - return ; -}); - export default function CustomContentTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js index b659b9223137e..1abd841cf7cd6 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js @@ -115,6 +115,9 @@ const StyledTreeItem = React.forwardRef(function StyledTreeItem(props, ref) { return ( } {...other} - slots={{ groupTransition: TransitionComponent }} ref={ref} /> ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx index 68654be8a2f31..f3e80c99b95b3 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx @@ -36,7 +36,7 @@ declare module 'react' { } } -type StyledTreeItemProps = TreeItemProps & { +type StyledTreeItemProps = Omit & { labelIcon: React.ElementType; labelText: string; }; @@ -130,6 +130,9 @@ const StyledTreeItem = React.forwardRef(function StyledTreeItem( return ( } {...other} - slots={{ groupTransition: TransitionComponent }} ref={ref} /> ); diff --git a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js index b889d47e79f12..f1831034f7227 100644 --- a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js @@ -1,4 +1,5 @@ import * as React from 'react'; +import clsx from 'clsx'; import { styled, useTheme, alpha } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -13,85 +14,124 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem, treeItemClasses } from '@mui/x-tree-view/TreeItem'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2Root, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { unstable_useTreeItem2 as useTreeItem } from '@mui/x-tree-view/useTreeItem2'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; -const StyledTreeItemRoot = styled(TreeItem)(({ theme }) => ({ +const CustomTreeItemRoot = styled(TreeItem2Root)(({ theme }) => ({ color: theme.palette.text.secondary, - [`& .${treeItemClasses.content}`]: { - marginBottom: theme.spacing(0.3), - color: theme.palette.text.secondary, - borderRadius: theme.spacing(2), - paddingRight: theme.spacing(1), - fontWeight: theme.typography.fontWeightMedium, - '&.Mui-expanded': { - fontWeight: theme.typography.fontWeightRegular, - }, - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - '&.Mui-focused, &.Mui-selected, &.Mui-selected.Mui-focused': { - backgroundColor: `var(--tree-view-bg-color, ${theme.palette.action.selected})`, - color: 'var(--tree-view-color)', - }, - [`& .${treeItemClasses.label}`]: { - fontWeight: 'inherit', - color: 'inherit', - }, - [`& .${treeItemClasses.iconContainer}`]: { - marginRight: theme.spacing(1), - }, +})); + +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + marginBottom: theme.spacing(0.3), + color: theme.palette.text.secondary, + borderRadius: theme.spacing(2), + paddingRight: theme.spacing(1), + fontWeight: theme.typography.fontWeightMedium, + '&.expanded': { + fontWeight: theme.typography.fontWeightRegular, + }, + '&:hover': { + backgroundColor: theme.palette.action.hover, }, - [`& .${treeItemClasses.groupTransition}`]: { + '&.focused, &.selected, &.selected.focused': { + backgroundColor: `var(--tree-view-bg-color, ${theme.palette.action.selected})`, + color: 'var(--tree-view-color)', + }, +})); + +const CustomTreeItemIconContainer = styled(TreeItem2IconContainer)(({ theme }) => ({ + marginRight: theme.spacing(1), +})); + +const CustomTreeItemGroupTransition = styled(TreeItem2GroupTransition)( + ({ theme }) => ({ marginLeft: 0, - [`& .${treeItemClasses.content}`]: { + [`& .content`]: { paddingLeft: theme.spacing(2), }, - }, -})); + }), +); -const StyledTreeItem = React.forwardRef(function StyledTreeItem(props, ref) { +const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { const theme = useTheme(); const { + id, + nodeId, + label, + disabled, + children, bgColor, color, labelIcon: LabelIcon, labelInfo, - labelText, colorForDarkMode, bgColorForDarkMode, ...other } = props; - const styleProps = { + const { + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem({ id, nodeId, children, label, disabled, rootRef: ref }); + + const style = { '--tree-view-color': theme.palette.mode !== 'dark' ? color : colorForDarkMode, '--tree-view-bg-color': theme.palette.mode !== 'dark' ? bgColor : bgColorForDarkMode, }; return ( - + + - - - {labelText} - - - {labelInfo} - - - } - style={styleProps} - {...other} - ref={ref} - /> + + + + + + + + {labelInfo} + + + + {children && ( + + )} + + ); }); @@ -112,12 +152,12 @@ export default function GmailTreeView() { }} sx={{ flexGrow: 1, maxWidth: 400 }} > - - - - + + + - - - - - + + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx index ebee8bbe12af3..a449d7af90752 100644 --- a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import clsx from 'clsx'; import { styled, useTheme, alpha } from '@mui/material/styles'; import Box from '@mui/material/Box'; import Typography from '@mui/material/Typography'; @@ -13,7 +14,18 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowRightIcon from '@mui/icons-material/ArrowRight'; import { SvgIconProps } from '@mui/material/SvgIcon'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem, TreeItemProps, treeItemClasses } from '@mui/x-tree-view/TreeItem'; +import { + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2Root, + TreeItem2GroupTransition, +} from '@mui/x-tree-view/TreeItem2'; +import { + unstable_useTreeItem2 as useTreeItem, + UseTreeItem2Parameters, +} from '@mui/x-tree-view/useTreeItem2'; +import { TreeItem2Provider } from '@mui/x-tree-view/TreeItem2Provider'; +import { TreeItem2Icon } from '@mui/x-tree-view/TreeItem2Icon'; declare module 'react' { interface CSSProperties { @@ -22,96 +34,128 @@ declare module 'react' { } } -type StyledTreeItemProps = TreeItemProps & { +interface StyledTreeItemProps + extends Omit, + React.HTMLAttributes { bgColor?: string; bgColorForDarkMode?: string; color?: string; colorForDarkMode?: string; labelIcon: React.ElementType; labelInfo?: string; - labelText: string; -}; +} -const StyledTreeItemRoot = styled(TreeItem)(({ theme }) => ({ +const CustomTreeItemRoot = styled(TreeItem2Root)(({ theme }) => ({ color: theme.palette.text.secondary, - [`& .${treeItemClasses.content}`]: { - marginBottom: theme.spacing(0.3), - color: theme.palette.text.secondary, - borderRadius: theme.spacing(2), - paddingRight: theme.spacing(1), - fontWeight: theme.typography.fontWeightMedium, - '&.Mui-expanded': { - fontWeight: theme.typography.fontWeightRegular, - }, - '&:hover': { - backgroundColor: theme.palette.action.hover, - }, - '&.Mui-focused, &.Mui-selected, &.Mui-selected.Mui-focused': { - backgroundColor: `var(--tree-view-bg-color, ${theme.palette.action.selected})`, - color: 'var(--tree-view-color)', - }, - [`& .${treeItemClasses.label}`]: { - fontWeight: 'inherit', - color: 'inherit', - }, - [`& .${treeItemClasses.iconContainer}`]: { - marginRight: theme.spacing(1), - }, +})); + +const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ + marginBottom: theme.spacing(0.3), + color: theme.palette.text.secondary, + borderRadius: theme.spacing(2), + paddingRight: theme.spacing(1), + fontWeight: theme.typography.fontWeightMedium, + '&.expanded': { + fontWeight: theme.typography.fontWeightRegular, }, - [`& .${treeItemClasses.groupTransition}`]: { + '&:hover': { + backgroundColor: theme.palette.action.hover, + }, + '&.focused, &.selected, &.selected.focused': { + backgroundColor: `var(--tree-view-bg-color, ${theme.palette.action.selected})`, + color: 'var(--tree-view-color)', + }, +})); + +const CustomTreeItemIconContainer = styled(TreeItem2IconContainer)(({ theme }) => ({ + marginRight: theme.spacing(1), +})); + +const CustomTreeItemGroupTransition = styled(TreeItem2GroupTransition)( + ({ theme }) => ({ marginLeft: 0, - [`& .${treeItemClasses.content}`]: { + [`& .content`]: { paddingLeft: theme.spacing(2), }, - }, -})) as unknown as typeof TreeItem; + }), +); -const StyledTreeItem = React.forwardRef(function StyledTreeItem( +const CustomTreeItem = React.forwardRef(function CustomTreeItem( props: StyledTreeItemProps, ref: React.Ref, ) { const theme = useTheme(); const { + id, + nodeId, + label, + disabled, + children, bgColor, color, labelIcon: LabelIcon, labelInfo, - labelText, colorForDarkMode, bgColorForDarkMode, ...other } = props; - const styleProps = { + const { + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem({ id, nodeId, children, label, disabled, rootRef: ref }); + + const style = { '--tree-view-color': theme.palette.mode !== 'dark' ? color : colorForDarkMode, '--tree-view-bg-color': theme.palette.mode !== 'dark' ? bgColor : bgColorForDarkMode, }; return ( - + + - - - {labelText} - - - {labelInfo} - - - } - style={styleProps} - {...other} - ref={ref} - /> + + + + + + + + {labelInfo} + + + + {children && ( + + )} + + ); }); @@ -132,12 +176,12 @@ export default function GmailTreeView() { }} sx={{ flexGrow: 1, maxWidth: 400 }} > - - - - + + + - - - - - + + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js index b435a9946a4b9..0037ee1c64f1e 100644 --- a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js @@ -1,76 +1,37 @@ import * as React from 'react'; -import clsx from 'clsx'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { TreeItem, useTreeItemState } from '@mui/x-tree-view/TreeItem'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks/useTreeItem2Utils'; -const CustomContent = React.forwardRef(function CustomContent(props, ref) { - const { - classes, - className, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; - const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); +const CustomTreeItem = React.forwardRef(function MyTreeItem(props, ref) { + const { interactions } = useTreeItem2Utils({ + nodeId: props.nodeId, + children: props.children, + }); - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event) => { - preventSelection(event); - }; - - const handleExpansionClick = (event) => { - handleExpansion(event); + const handleContentClick = (event) => { + event.defaultMuiPrevented = true; + interactions.handleSelection(event); }; - const handleSelectionClick = (event) => { - handleSelection(event); + const handleIconContainerClick = (event) => { + interactions.handleExpansion(event); }; return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
- {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} -
- {icon} -
- - {label} - -
+ slotProps={{ + content: { onClick: handleContentClick }, + iconContainer: { onClick: handleIconContainerClick }, + }} + /> ); }); -const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { - return ; -}); - export default function IconExpansionTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx index 947a4a6b15c34..8e55c8a1626c7 100644 --- a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx @@ -1,91 +1,40 @@ import * as React from 'react'; -import clsx from 'clsx'; -import Typography from '@mui/material/Typography'; import Box from '@mui/material/Box'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; -import { - TreeItem, - TreeItemProps, - useTreeItemState, - TreeItemContentProps, -} from '@mui/x-tree-view/TreeItem'; +import { useTreeItem2Utils } from '@mui/x-tree-view/hooks/useTreeItem2Utils'; +import { UseTreeItem2ContentSlotOwnProps } from '@mui/x-tree-view/useTreeItem2'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; -const CustomContent = React.forwardRef(function CustomContent( - props: TreeItemContentProps, - ref, +const CustomTreeItem = React.forwardRef(function MyTreeItem( + props: TreeItem2Props, + ref: React.Ref, ) { - const { - classes, - className, - label, - nodeId, - icon: iconProp, - expansionIcon, - displayIcon, - } = props; - - const { - disabled, - expanded, - selected, - focused, - handleExpansion, - handleSelection, - preventSelection, - } = useTreeItemState(nodeId); - - const icon = iconProp || expansionIcon || displayIcon; - - const handleMouseDown = (event: React.MouseEvent) => { - preventSelection(event); - }; - - const handleExpansionClick = ( - event: React.MouseEvent, - ) => { - handleExpansion(event); + const { interactions } = useTreeItem2Utils({ + nodeId: props.nodeId, + children: props.children, + }); + + const handleContentClick: UseTreeItem2ContentSlotOwnProps['onClick'] = (event) => { + event.defaultMuiPrevented = true; + interactions.handleSelection(event); }; - const handleSelectionClick = ( - event: React.MouseEvent, - ) => { - handleSelection(event); + const handleIconContainerClick = (event: React.MouseEvent) => { + interactions.handleExpansion(event); }; return ( - // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
} - > - {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} -
- {icon} -
- - {label} - -
+ ); }); -const CustomTreeItem = React.forwardRef(function CustomTreeItem( - props: TreeItemProps, - ref: React.Ref, -) { - return ; -}); - export default function IconExpansionTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js new file mode 100644 index 0000000000000..3304521382ad2 --- /dev/null +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js @@ -0,0 +1,35 @@ +import * as React from 'react'; +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; + +const CustomTreeItem = React.forwardRef((props, ref) => ( + +)); + +export default function LabelSlotProps() { + return ( + + + + + + + + + + + + ); +} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx new file mode 100644 index 0000000000000..75bde63ccb430 --- /dev/null +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { TreeItem2, TreeItem2Props } from '@mui/x-tree-view/TreeItem2'; + +const CustomTreeItem = React.forwardRef( + (props: TreeItem2Props, ref: React.Ref) => ( + + ), +); + +export default function LabelSlotProps() { + return ( + + + + + + + + + + + + ); +} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview new file mode 100644 index 0000000000000..0d56f866e88ca --- /dev/null +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js new file mode 100644 index 0000000000000..db0483b0b8634 --- /dev/null +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Tooltip from '@mui/material/Tooltip'; +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { TreeItem2, TreeItem2Label } from '@mui/x-tree-view/TreeItem2'; + +function CustomLabel(props) { + const { tooltip, ...other } = props; + + return ( + + + + ); +} + +const CustomTreeItem = React.forwardRef((props, ref) => { + const { labelTooltip, ...other } = props; + + return ( + + ); +}); + +export default function LabelSlots() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx new file mode 100644 index 0000000000000..b33454a2973e6 --- /dev/null +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Tooltip from '@mui/material/Tooltip'; +import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { + TreeItem2, + TreeItem2Label, + TreeItem2Props, +} from '@mui/x-tree-view/TreeItem2'; + +interface CustomLabelProps { + children: React.ReactNode; + tooltip?: string; +} + +function CustomLabel(props: CustomLabelProps) { + const { tooltip, ...other } = props; + + return ( + + + + ); +} + +interface CustomTreeItemProps extends TreeItem2Props { + labelTooltip?: string; +} + +const CustomTreeItem = React.forwardRef( + (props: CustomTreeItemProps, ref: React.Ref) => { + const { labelTooltip, ...other } = props; + + return ( + + ); + }, +); + +export default function LabelSlots() { + return ( + + + + + + + + + + + + + + ); +} diff --git a/docs/data/tree-view/simple-tree-view/customization/customization.md b/docs/data/tree-view/simple-tree-view/customization/customization.md index 319ebcba5b271..819205b2d8155 100644 --- a/docs/data/tree-view/simple-tree-view/customization/customization.md +++ b/docs/data/tree-view/simple-tree-view/customization/customization.md @@ -1,7 +1,7 @@ --- productId: x-tree-view title: Simple Tree View - Customization -components: SimpleTreeView, TreeItem +components: SimpleTreeView, TreeItem, TreeItem2 packageName: '@mui/x-tree-view' githubLabel: 'component: tree view' waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ @@ -9,7 +9,7 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ # Simple Tree View - Customization -

Learn how to customize the simple version of the Tree View component.

+

Learn how to customize the Simple Tree View component.

## Basics @@ -34,12 +34,33 @@ Use `treeItemClasses` to target internal elements of the Tree Item component and {{"demo": "CustomStyling.js"}} -### Adding custom content +### Custom label -Use the `ContentComponent` prop and the `useTreeItemState` hook to replace the Tree Item content with an entirely custom component. +:::warning +This example is built using the new `TreeItem2` component +which adds several slots to modify the content of the Tree Item or change its behavior. + +You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). +::: + +Use the `label` slot to customize the Tree Item label or to replace it with a custom component. + +The `slotProps` prop allows you to pass props to the label component. +The demo below shows how to pass an `id` attribute to the Tree Item label: + +{{"demo": "LabelSlotProps.js", "defaultCodeOpen": false }} + +The `slots` prop allows you to replace the default label with your own component: +The demo below shows how to add a tooltip on the Tree Item label: + +{{"demo": "LabelSlots.js", "defaultCodeOpen": false}} + +### Headless API + +Use the `useTreeItem2` hook to create your own component. The demo below shows how to add an avatar and custom typography elements. -{{"demo": "CustomContentTreeView.js"}} +{{"demo": "CustomContentTreeView.js", "defaultCodeOpen": false}} ## Common examples @@ -51,6 +72,13 @@ Target the `treeItemClasses.groupTransition` class to add connection borders bet ### Limit expansion to icon container +:::warning +This example is built using the new `TreeItem2` component +which adds several slots to modify the content of the Tree Item or change its behavior. + +You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). +::: + The demo below shows how to trigger the expansion interaction just by clicking on the icon container instead of the whole Tree Item surface. {{"demo": "IconExpansionTreeView.js", "defaultCodeOpen": false}} @@ -63,6 +91,13 @@ The demo below shows many of the previous customization examples brought togethe ### Gmail clone +:::warning +This example is built using the new `TreeItem2` component +which adds several slots to modify the content of the Tree Item or change its behavior. + +You can learn more about this new component in the [Overview page](/x/react-tree-view/#tree-item-components). +::: + Google's Gmail side nav is potentially one of the web's most famous tree view components. The demo below shows how to replicate it. diff --git a/docs/pages/x/api/tree-view/tree-item-2.js b/docs/pages/x/api/tree-view/tree-item-2.js new file mode 100644 index 0000000000000..157c5febd2e14 --- /dev/null +++ b/docs/pages/x/api/tree-view/tree-item-2.js @@ -0,0 +1,23 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './tree-item-2.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/tree-view/tree-item-2', + false, + /\.\/tree-item-2.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/tree-view/tree-item-2.json b/docs/pages/x/api/tree-view/tree-item-2.json new file mode 100644 index 0000000000000..d75d891d15282 --- /dev/null +++ b/docs/pages/x/api/tree-view/tree-item-2.json @@ -0,0 +1,93 @@ +{ + "props": { + "nodeId": { "type": { "name": "string" }, "required": true }, + "children": { "type": { "name": "node" } }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "disabled": { "type": { "name": "bool" }, "default": "false" }, + "id": { "type": { "name": "string" } }, + "label": { "type": { "name": "node" } }, + "onFocus": { "type": { "name": "custom", "description": "unsupportedProp" } }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { + "type": { "name": "object" }, + "default": "{}", + "additionalInfo": { "slotsApi": true } + } + }, + "name": "TreeItem2", + "imports": [ + "import { TreeItem2 } from '@mui/x-tree-view/TreeItem2';", + "import { TreeItem2 } from '@mui/x-tree-view';" + ], + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "TreeItem2Root", + "class": "MuiTreeItem2-root" + }, + { + "name": "content", + "description": "The component that renders the content of the item.\n(e.g.: everything related to this item, not to its children).", + "default": "TreeItem2Content", + "class": "MuiTreeItem2-content" + }, + { + "name": "groupTransition", + "description": "The component that renders the children of the item.", + "default": "TreeItem2GroupTransition", + "class": "MuiTreeItem2-groupTransition" + }, + { + "name": "iconContainer", + "description": "The component that renders the icon.", + "default": "TreeItem2IconContainer", + "class": "MuiTreeItem2-iconContainer" + }, + { + "name": "label", + "description": "The component that renders the item label.", + "default": "TreeItem2Label", + "class": "MuiTreeItem2-label" + }, + { "name": "collapseIcon", "description": "The icon used to collapse the node.", "class": null }, + { "name": "expandIcon", "description": "The icon used to expand the node.", "class": null }, + { "name": "endIcon", "description": "The icon displayed next to an end node.", "class": null }, + { + "name": "icon", + "description": "The icon to display next to the tree node's label.", + "class": null + } + ], + "classes": [ + { + "key": "disabled", + "className": "Mui-disabled", + "description": "State class applied to the element when disabled.", + "isGlobal": true + }, + { + "key": "expanded", + "className": "Mui-expanded", + "description": "State class applied to the content element when expanded.", + "isGlobal": true + }, + { + "key": "focused", + "className": "Mui-focused", + "description": "State class applied to the content element when focused.", + "isGlobal": true + }, + { + "key": "selected", + "className": "Mui-selected", + "description": "State class applied to the content element when selected.", + "isGlobal": true + } + ], + "muiName": "MuiTreeItem2", + "filename": "/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx", + "inheritance": null, + "demos": "
", + "cssComponent": false +} diff --git a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json new file mode 100644 index 0000000000000..95a635bd5a516 --- /dev/null +++ b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json @@ -0,0 +1,49 @@ +{ + "componentDescription": "", + "propDescriptions": { + "children": { "description": "The content of the component." }, + "classes": { "description": "Override or extend the styles applied to the component." }, + "disabled": { "description": "If true, the node is disabled." }, + "id": { "description": "The id attribute of the node. If not provided, it will be generated." }, + "label": { "description": "The label of the node." }, + "nodeId": { "description": "The id of the node. Must be unique." }, + "onFocus": { + "description": "This prop isn't supported. Use the onNodeFocus callback on the tree if you need to monitor a node's focus." + }, + "slotProps": { "description": "The props used for each component slot." }, + "slots": { "description": "Overridable component slots." } + }, + "classDescriptions": { + "disabled": { + "description": "State class applied to {{nodeName}} when {{conditions}}.", + "nodeName": "the element", + "conditions": "disabled" + }, + "expanded": { + "description": "State class applied to {{nodeName}} when {{conditions}}.", + "nodeName": "the content element", + "conditions": "expanded" + }, + "focused": { + "description": "State class applied to {{nodeName}} when {{conditions}}.", + "nodeName": "the content element", + "conditions": "focused" + }, + "selected": { + "description": "State class applied to {{nodeName}} when {{conditions}}.", + "nodeName": "the content element", + "conditions": "selected" + } + }, + "slotDescriptions": { + "collapseIcon": "The icon used to collapse the node.", + "content": "The component that renders the content of the item. (e.g.: everything related to this item, not to its children).", + "endIcon": "The icon displayed next to an end node.", + "expandIcon": "The icon used to expand the node.", + "groupTransition": "The component that renders the children of the item.", + "icon": "The icon to display next to the tree node's label.", + "iconContainer": "The component that renders the icon.", + "label": "The component that renders the item label.", + "root": "The component that renders the root." + } +} diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts index 124821c8aaf15..5403d392a7fe3 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts @@ -10,6 +10,7 @@ import { DefaultTreeViewPlugins, } from '../internals/plugins/defaultPlugins'; import { TreeItem, TreeItemProps } from '../TreeItem'; +import { TreeItem2Props } from '../TreeItem2'; import { TreeViewItemId } from '../models'; import { TreeViewPublicAPI } from '../internals/models'; @@ -28,7 +29,7 @@ export interface RichTreeViewSlots extends DefaultTreeViewPluginSlots { * Custom component for the item. * @default TreeItem. */ - item?: React.JSXElementConstructor; + item?: React.JSXElementConstructor | React.JSXElementConstructor; } export interface RichTreeViewSlotProps diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index 7fe15bbfeffac..b9b4c8e57e9ae 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -4,23 +4,28 @@ import PropTypes from 'prop-types'; import { spy } from 'sinon'; import { act, createEvent, createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; +import { SimpleTreeViewPlugins } from '@mui/x-tree-view/SimpleTreeView/SimpleTreeView.plugins'; import { TreeItem, treeItemClasses as classes } from '@mui/x-tree-view/TreeItem'; import { TreeViewContextValue } from '@mui/x-tree-view/internals/TreeViewProvider'; import { TreeViewContext } from '@mui/x-tree-view/internals/TreeViewProvider/TreeViewContext'; -import { DefaultTreeViewPlugins } from '@mui/x-tree-view/internals'; import { describeConformance } from 'test/utils/describeConformance'; -const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue = { +const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue = { instance: { isNodeExpandable: () => false, isNodeExpanded: () => false, isNodeFocused: () => false, isNodeSelected: () => false, - isNodeDisabled: () => false, + isNodeDisabled: (nodeId: string | null): nodeId is string => !!nodeId, getTreeItemId: () => '', - mapFirstCharFromJSX: () => {}, + mapFirstCharFromJSX: () => () => {}, } as any, - runItemPlugins: ({ props, ref }) => ({ props, ref, wrapItem: (children) => children }), + publicAPI: { + focusNode: () => {}, + getItem: () => ({}), + }, + runItemPlugins: () => ({ rootRef: null, contentRef: null }), + wrapItem: ({ children }) => children, disabledItemsFocusable: false, icons: { slots: {}, diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx index fe77d2d636810..d2b2db6741e91 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import clsx from 'clsx'; import Collapse from '@mui/material/Collapse'; import { resolveComponentProps, useSlotProps } from '@mui/base/utils'; +import useForkRef from '@mui/utils/useForkRef'; import { alpha, styled, useThemeProps } from '@mui/material/styles'; import { TransitionProps } from '@mui/material/transitions'; import unsupportedProp from '@mui/utils/unsupportedProp'; @@ -14,6 +15,7 @@ import { TreeItemOwnerState, TreeItemProps } from './TreeItem.types'; import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; import { DefaultTreeViewPlugins } from '../internals/plugins'; import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons'; +import { TreeItem2Provider } from '../TreeItem2Provider'; const useUtilityClasses = (ownerState: TreeItemOwnerState) => { const { classes } = ownerState; @@ -160,9 +162,7 @@ export const TreeItem = React.forwardRef(function TreeItem( instance, } = useTreeViewContext(); - const inPropsWithTheme = useThemeProps({ props: inProps, name: 'MuiTreeItem' }); - - const { props, ref, wrapItem } = runItemPlugins({ props: inPropsWithTheme, ref: inRef }); + const props = useThemeProps({ props: inProps, name: 'MuiTreeItem' }); const { children, @@ -179,6 +179,10 @@ export const TreeItem = React.forwardRef(function TreeItem( ...other } = props; + const { contentRef, rootRef } = runItemPlugins(props); + const handleRootRef = useForkRef(inRef, rootRef); + const handleContentRef = useForkRef(ContentProps?.ref, contentRef); + const slots = { expandIcon: inSlots?.expandIcon ?? contextIcons.slots.expandIcon ?? TreeViewExpandIcon, collapseIcon: inSlots?.collapseIcon ?? contextIcons.slots.collapseIcon ?? TreeViewCollapseIcon, @@ -296,50 +300,51 @@ export const TreeItem = React.forwardRef(function TreeItem( const idAttribute = instance.getTreeItemId(nodeId, id); - const item = ( - - + - {children && ( - - {children} - - )} - + onFocus={handleFocus} + ref={handleRootRef} + > + + {children && ( + + {children} + + )} + + ); - - return wrapItem(item); }); TreeItem.propTypes = { diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts index e38bc7d26b7a3..dbbc8b968b13d 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts +++ b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts @@ -68,7 +68,7 @@ export interface TreeItemProps extends Omit, /** * Props applied to ContentComponent. */ - ContentProps?: React.HTMLAttributes; + ContentProps?: React.HTMLAttributes & { ref?: React.Ref }; /** * If `true`, the node is disabled. * @default false diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx new file mode 100644 index 0000000000000..9d9c4acd13a78 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx @@ -0,0 +1,316 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import clsx from 'clsx'; +import unsupportedProp from '@mui/utils/unsupportedProp'; +import { alpha, styled, useThemeProps } from '@mui/material/styles'; +import Collapse from '@mui/material/Collapse'; +import { useSlotProps } from '@mui/base/utils'; +import { shouldForwardProp } from '@mui/system'; +import composeClasses from '@mui/utils/composeClasses'; +import { TreeItem2Props, TreeItem2OwnerState } from './TreeItem2.types'; +import { + unstable_useTreeItem2 as useTreeItem2, + UseTreeItem2ContentSlotOwnProps, +} from '../useTreeItem2'; +import { getTreeItemUtilityClass, treeItemClasses } from '../TreeItem'; +import { TreeItem2Icon } from '../TreeItem2Icon'; +import { TreeItem2Provider } from '../TreeItem2Provider'; + +export const TreeItem2Root = styled('li', { + name: 'MuiTreeItem2', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, +})({ + listStyle: 'none', + margin: 0, + padding: 0, + outline: 0, +}); + +export const TreeItem2Content = styled('div', { + name: 'MuiTreeItem2', + slot: 'Content', + overridesResolver: (props, styles) => styles.content, + shouldForwardProp: (prop) => shouldForwardProp(prop) && prop !== 'status', +})(({ theme }) => ({ + padding: theme.spacing(0.5, 1), + borderRadius: theme.shape.borderRadius, + width: '100%', + boxSizing: 'border-box', // prevent width + padding to overflow + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1), + cursor: 'pointer', + WebkitTapHighlightColor: 'transparent', + '&:hover': { + backgroundColor: (theme.vars || theme).palette.action.hover, + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + [`& .${treeItemClasses.groupTransition}`]: { + margin: 0, + padding: 0, + paddingLeft: 12, + }, + variants: [ + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.disabled, + style: { + opacity: (theme.vars || theme).palette.action.disabledOpacity, + backgroundColor: 'transparent', + }, + }, + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.focused, + style: { backgroundColor: (theme.vars || theme).palette.action.focus }, + }, + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.selected, + style: { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), + '&:hover': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` + : alpha( + theme.palette.primary.main, + theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` + : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity), + }, + }, + }, + }, + { + props: ({ status }: UseTreeItem2ContentSlotOwnProps) => status.selected && status.focused, + style: { + backgroundColor: theme.vars + ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` + : alpha( + theme.palette.primary.main, + theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity, + ), + }, + }, + ], +})); + +export const TreeItem2Label = styled('div', { + name: 'MuiTreeItem2', + slot: 'Label', + overridesResolver: (props, styles) => styles.label, +})(({ theme }) => ({ + width: '100%', + boxSizing: 'border-box', // prevent width + padding to overflow + // fixes overflow - see https://github.com/mui/material-ui/issues/27372 + minWidth: 0, + position: 'relative', + ...theme.typography.body1, +})); + +export const TreeItem2IconContainer = styled('div', { + name: 'MuiTreeItem2', + slot: 'IconContainer', + overridesResolver: (props, styles) => styles.iconContainer, +})({ + width: 16, + display: 'flex', + flexShrink: 0, + justifyContent: 'center', + '& svg': { + fontSize: 18, + }, +}); + +export const TreeItem2GroupTransition = styled(Collapse, { + name: 'MuiTreeItem2GroupTransition', + slot: 'GroupTransition', + overridesResolver: (props, styles) => styles.groupTransition, +})({ + margin: 0, + padding: 0, + paddingLeft: 12, +}); + +const useUtilityClasses = (ownerState: TreeItem2OwnerState) => { + const { classes } = ownerState; + + const slots = { + root: ['root'], + content: ['content'], + expanded: ['expanded'], + selected: ['selected'], + focused: ['focused'], + disabled: ['disabled'], + iconContainer: ['iconContainer'], + label: ['label'], + groupTransition: ['groupTransition'], + }; + + return composeClasses(slots, getTreeItemUtilityClass, classes); +}; + +/** + * + * Demos: + * + * - [Tree View](https://mui.com/x/react-tree-view/) + * + * API: + * + * - [TreeItem2 API](https://mui.com/x/api/tree-view/tree-item-2/) + */ +export const TreeItem2 = React.forwardRef(function TreeItem2( + inProps: TreeItem2Props, + forwardedRef: React.Ref, +) { + const props = useThemeProps({ props: inProps, name: 'MuiTreeItem2' }); + + const { id, nodeId, label, disabled, children, slots = {}, slotProps = {}, ...other } = props; + + const { + getRootProps, + getContentProps, + getIconContainerProps, + getLabelProps, + getGroupTransitionProps, + status, + } = useTreeItem2({ + id, + nodeId, + children, + label, + disabled, + }); + + const ownerState: TreeItem2OwnerState = { + ...props, + ...status, + }; + + const classes = useUtilityClasses(ownerState); + + const Root: React.ElementType = slots.root ?? TreeItem2Root; + const rootProps = useSlotProps({ + elementType: Root, + getSlotProps: getRootProps, + externalForwardedProps: other, + externalSlotProps: slotProps.root, + additionalProps: { + ref: forwardedRef, + }, + ownerState: {}, + className: classes.root, + }); + + const Content: React.ElementType = slots.content ?? TreeItem2Content; + const contentProps = useSlotProps({ + elementType: Content, + getSlotProps: getContentProps, + externalSlotProps: slotProps.content, + ownerState: {}, + className: clsx(classes.content, { + [classes.expanded]: status.expanded, + [classes.selected]: status.selected, + [classes.focused]: status.focused, + [classes.disabled]: status.disabled, + }), + }); + + const IconContainer: React.ElementType = slots.iconContainer ?? TreeItem2IconContainer; + const iconContainerProps = useSlotProps({ + elementType: IconContainer, + getSlotProps: getIconContainerProps, + externalSlotProps: slotProps.iconContainer, + ownerState: {}, + className: classes.iconContainer, + }); + + const Label: React.ElementType = slots.label ?? TreeItem2Label; + const labelProps = useSlotProps({ + elementType: Label, + getSlotProps: getLabelProps, + externalSlotProps: slotProps.label, + ownerState: {}, + className: classes.label, + }); + + const GroupTransition: React.ElementType | undefined = slots.groupTransition ?? undefined; + const groupTransitionProps = useSlotProps({ + elementType: GroupTransition, + getSlotProps: getGroupTransitionProps, + externalSlotProps: slotProps.groupTransition, + ownerState: {}, + className: classes.groupTransition, + }); + + return ( + + + + + + + + {children && } + + + ); +}); + +TreeItem2.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The content of the component. + */ + children: PropTypes.node, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + className: PropTypes.string, + /** + * If `true`, the node is disabled. + * @default false + */ + disabled: PropTypes.bool, + /** + * The id attribute of the node. If not provided, it will be generated. + */ + id: PropTypes.string, + /** + * The label of the node. + */ + label: PropTypes.node, + /** + * The id of the node. + * Must be unique. + */ + nodeId: PropTypes.string.isRequired, + /** + * This prop isn't supported. + * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + */ + onFocus: unsupportedProp, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts new file mode 100644 index 0000000000000..5c6711d72133c --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { SlotComponentProps } from '@mui/base/utils'; +import { UseTreeItem2Parameters, UseTreeItem2Status } from '../useTreeItem2'; +import { TreeItemClasses } from '../TreeItem'; +import { TreeItem2IconSlotProps, TreeItem2IconSlots } from '../TreeItem2Icon'; + +export interface TreeItem2Slots extends TreeItem2IconSlots { + /** + * The component that renders the root. + * @default TreeItem2Root + */ + root?: React.ElementType; + /** + * The component that renders the content of the item. + * (e.g.: everything related to this item, not to its children). + * @default TreeItem2Content + */ + content?: React.ElementType; + /** + * The component that renders the children of the item. + * @default TreeItem2GroupTransition + */ + groupTransition?: React.ElementType; + /** + * The component that renders the icon. + * @default TreeItem2IconContainer + */ + iconContainer?: React.ElementType; + /** + * The component that renders the item label. + * @default TreeItem2Label + */ + label?: React.ElementType; +} + +export interface TreeItem2SlotProps extends TreeItem2IconSlotProps { + root?: SlotComponentProps<'li', {}, {}>; + content?: SlotComponentProps<'div', {}, {}>; + groupTransition?: SlotComponentProps<'div', {}, {}>; + iconContainer?: SlotComponentProps<'div', {}, {}>; + label?: SlotComponentProps<'div', {}, {}>; +} + +export interface TreeItem2Props + extends Omit, + Omit, 'onFocus'> { + className?: string; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * Overridable component slots. + * @default {} + */ + slots?: TreeItem2Slots; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: TreeItem2SlotProps; + /** + * This prop isn't supported. + * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + */ + onFocus?: null; +} + +export interface TreeItem2OwnerState extends Omit, UseTreeItem2Status {} diff --git a/packages/x-tree-view/src/TreeItem2/index.ts b/packages/x-tree-view/src/TreeItem2/index.ts new file mode 100644 index 0000000000000..f8fddaaa0c83e --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2/index.ts @@ -0,0 +1,9 @@ +export { + TreeItem2, + TreeItem2Root, + TreeItem2Content, + TreeItem2IconContainer, + TreeItem2GroupTransition, + TreeItem2Label, +} from './TreeItem2'; +export type { TreeItem2Props, TreeItem2Slots, TreeItem2SlotProps } from './TreeItem2.types'; diff --git a/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.tsx b/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.tsx new file mode 100644 index 0000000000000..adce86de3c113 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { resolveComponentProps, useSlotProps } from '@mui/base/utils'; +import { TreeItem2IconProps } from './TreeItem2Icon.types'; +import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; +import { UseTreeViewIconsSignature } from '../internals/plugins/useTreeViewIcons'; +import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons'; + +function TreeItem2Icon(props: TreeItem2IconProps) { + const { slots, slotProps, status } = props; + + const context = useTreeViewContext<[UseTreeViewIconsSignature]>(); + + const contextIcons = { + ...context.icons.slots, + expandIcon: context.icons.slots.expandIcon ?? TreeViewExpandIcon, + collapseIcon: context.icons.slots.collapseIcon ?? TreeViewCollapseIcon, + }; + + const contextIconProps = context.icons.slotProps; + + let iconName: 'collapseIcon' | 'expandIcon' | 'endIcon' | 'icon'; + if (slots?.icon) { + iconName = 'icon'; + } else if (status.expandable) { + if (status.expanded) { + iconName = 'collapseIcon'; + } else { + iconName = 'expandIcon'; + } + } else { + iconName = 'endIcon'; + } + + const Icon = slots?.[iconName] ?? contextIcons[iconName as keyof typeof contextIcons]; + const iconProps = useSlotProps({ + elementType: Icon, + externalSlotProps: (tempOwnerState: any) => ({ + ...resolveComponentProps( + contextIconProps[iconName as keyof typeof contextIconProps], + tempOwnerState, + ), + ...resolveComponentProps(slotProps?.[iconName], tempOwnerState), + }), + // TODO: Add proper ownerState + ownerState: {}, + }); + + if (!Icon) { + return null; + } + + return ; +} + +TreeItem2Icon.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, + status: PropTypes.shape({ + disabled: PropTypes.bool.isRequired, + expandable: PropTypes.bool.isRequired, + expanded: PropTypes.bool.isRequired, + focused: PropTypes.bool.isRequired, + selected: PropTypes.bool.isRequired, + }).isRequired, +} as any; + +export { TreeItem2Icon }; diff --git a/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.types.ts b/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.types.ts new file mode 100644 index 0000000000000..e3af78d0820d9 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Icon/TreeItem2Icon.types.ts @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { SlotComponentProps } from '@mui/base/utils'; +import { UseTreeItem2Status } from '../useTreeItem2'; + +export interface TreeItem2IconSlots { + /** + * The icon used to collapse the node. + */ + collapseIcon?: React.ElementType; + /** + * The icon used to expand the node. + */ + expandIcon?: React.ElementType; + /** + * The icon displayed next to an end node. + */ + endIcon?: React.ElementType; + /** + * The icon to display next to the tree node's label. + */ + icon?: React.ElementType; +} + +export interface TreeItem2IconSlotProps { + collapseIcon?: SlotComponentProps<'svg', {}, {}>; + expandIcon?: SlotComponentProps<'svg', {}, {}>; + endIcon?: SlotComponentProps<'svg', {}, {}>; + icon?: SlotComponentProps<'svg', {}, {}>; +} + +export interface TreeItem2IconProps { + status: UseTreeItem2Status; + /** + * Overridable component slots. + * @default {} + */ + slots?: TreeItem2IconSlots; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: TreeItem2IconSlotProps; +} diff --git a/packages/x-tree-view/src/TreeItem2Icon/index.ts b/packages/x-tree-view/src/TreeItem2Icon/index.ts new file mode 100644 index 0000000000000..d24d4ccc89638 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Icon/index.ts @@ -0,0 +1,6 @@ +export { TreeItem2Icon } from './TreeItem2Icon'; +export type { + TreeItem2IconProps, + TreeItem2IconSlots, + TreeItem2IconSlotProps, +} from './TreeItem2Icon.types'; diff --git a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx new file mode 100644 index 0000000000000..636641d35d75b --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx @@ -0,0 +1,21 @@ +import PropTypes from 'prop-types'; +import { TreeItem2ProviderProps } from './TreeItem2Provider.types'; +import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; + +function TreeItem2Provider(props: TreeItem2ProviderProps) { + const { children, nodeId } = props; + const { wrapItem } = useTreeViewContext<[]>(); + + return wrapItem({ children, nodeId }); +} + +TreeItem2Provider.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + children: PropTypes.node, + nodeId: PropTypes.string.isRequired, +} as any; + +export { TreeItem2Provider }; diff --git a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts new file mode 100644 index 0000000000000..29cab33d51648 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts @@ -0,0 +1,7 @@ +import * as React from 'react'; +import { TreeViewItemId } from '../models'; + +export interface TreeItem2ProviderProps { + children: React.ReactNode; + nodeId: TreeViewItemId; +} diff --git a/packages/x-tree-view/src/TreeItem2Provider/index.ts b/packages/x-tree-view/src/TreeItem2Provider/index.ts new file mode 100644 index 0000000000000..d569e8cd15169 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2Provider/index.ts @@ -0,0 +1,2 @@ +export { TreeItem2Provider } from './TreeItem2Provider'; +export type { TreeItem2ProviderProps } from './TreeItem2Provider.types'; diff --git a/packages/x-tree-view/src/hooks/index.ts b/packages/x-tree-view/src/hooks/index.ts index e07d07a202b5a..222c64e360562 100644 --- a/packages/x-tree-view/src/hooks/index.ts +++ b/packages/x-tree-view/src/hooks/index.ts @@ -1 +1,2 @@ export { useTreeViewApiRef } from './useTreeViewApiRef'; +export { useTreeItem2Utils } from './useTreeItem2Utils'; diff --git a/packages/x-tree-view/src/hooks/useTreeItem2Utils/index.ts b/packages/x-tree-view/src/hooks/useTreeItem2Utils/index.ts new file mode 100644 index 0000000000000..71c065769d1f9 --- /dev/null +++ b/packages/x-tree-view/src/hooks/useTreeItem2Utils/index.ts @@ -0,0 +1 @@ +export { useTreeItem2Utils } from './useTreeItem2Utils'; diff --git a/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx new file mode 100644 index 0000000000000..2c73a697b1ac3 --- /dev/null +++ b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx @@ -0,0 +1,78 @@ +import * as React from 'react'; +import { useTreeViewContext } from '../../internals/TreeViewProvider/useTreeViewContext'; +import { DefaultTreeViewPlugins } from '../../internals/plugins'; +import type { UseTreeItem2Status } from '../../useTreeItem2'; + +interface UseTreeItem2Interactions { + handleExpansion: (event: React.MouseEvent) => void; + handleSelection: (event: React.MouseEvent) => void; +} + +interface UseTreeItem2UtilsReturnValue { + interactions: UseTreeItem2Interactions; + status: UseTreeItem2Status; +} + +export const useTreeItem2Utils = ({ + nodeId, + children, +}: { + nodeId: string; + children: React.ReactNode; +}): UseTreeItem2UtilsReturnValue => { + const { + instance, + selection: { multiSelect }, + } = useTreeViewContext(); + + const status: UseTreeItem2Status = { + expandable: Boolean(Array.isArray(children) ? children.length : children), + expanded: instance.isNodeExpanded(nodeId), + focused: instance.isNodeFocused(nodeId), + selected: instance.isNodeSelected(nodeId), + disabled: instance.isNodeDisabled(nodeId), + }; + + const handleExpansion = (event: React.MouseEvent) => { + if (status.disabled) { + return; + } + + if (!status.focused) { + instance.focusNode(event, nodeId); + } + + const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); + + // If already expanded and trying to toggle selection don't close + if (status.expandable && !(multiple && instance.isNodeExpanded(nodeId))) { + instance.toggleNodeExpansion(event, nodeId); + } + }; + + const handleSelection = (event: React.MouseEvent) => { + if (status.disabled) { + return; + } + + if (!status.focused) { + instance.focusNode(event, nodeId); + } + + const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); + + if (multiple) { + if (event.shiftKey) { + instance.selectRange(event, { end: nodeId }); + } else { + instance.selectNode(event, nodeId, true); + } + } else { + instance.selectNode(event, nodeId); + } + }; + + const interactions: UseTreeItem2Interactions = { handleExpansion, handleSelection }; + + return { interactions, status }; +}; diff --git a/packages/x-tree-view/src/index.ts b/packages/x-tree-view/src/index.ts index 15599f35a6aba..de34fea127931 100644 --- a/packages/x-tree-view/src/index.ts +++ b/packages/x-tree-view/src/index.ts @@ -1,10 +1,17 @@ -export * from './TreeItem'; +// Tree View export * from './TreeView'; export * from './SimpleTreeView'; export * from './RichTreeView'; + +// Tree Item +export * from './TreeItem'; +export * from './TreeItem2'; +export * from './useTreeItem2'; +export * from './TreeItem2Icon'; +export * from './TreeItem2Provider'; + export { unstable_resetCleanupTracking } from './internals/hooks/useInstanceEventHandler'; export * from './models'; export * from './icons'; - export * from './hooks'; diff --git a/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts b/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts index 0ef0c989b4ba4..99a00d68c5362 100644 --- a/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts +++ b/packages/x-tree-view/src/internals/TreeViewProvider/TreeViewProvider.types.ts @@ -1,16 +1,19 @@ import * as React from 'react'; import { MergePluginsProperty, + TreeItemWrapper, TreeViewAnyPluginSignature, TreeViewInstance, - TreeViewItemPluginOptions, TreeViewItemPluginResponse, + TreeViewPublicAPI, } from '../models'; export type TreeViewContextValue = MergePluginsProperty & { instance: TreeViewInstance; - runItemPlugins: (options: TreeViewItemPluginOptions) => Required; + publicAPI: TreeViewPublicAPI; + wrapItem: TreeItemWrapper; + runItemPlugins: (props: TProps) => Required; }; export interface TreeViewProviderProps { diff --git a/packages/x-tree-view/src/internals/models/plugin.ts b/packages/x-tree-view/src/internals/models/plugin.ts index 9ffae62154798..aeacf008d090e 100644 --- a/packages/x-tree-view/src/internals/models/plugin.ts +++ b/packages/x-tree-view/src/internals/models/plugin.ts @@ -4,7 +4,7 @@ import { TreeViewModel } from './treeView'; import type { MergePluginsProperty, OptionalIfEmpty } from './helpers'; import { TreeViewEventLookupElement } from './events'; import type { TreeViewCorePluginsSignature } from '../corePlugins'; -import type { TreeItemProps } from '../../TreeItem'; +import { TreeViewItemId } from '../../models'; export interface TreeViewPluginOptions { instance: TreeViewUsedInstance; @@ -124,32 +124,30 @@ export type TreeViewUsedModels = export type TreeViewUsedEvents = TSignature['events'] & MergePluginsProperty, 'events'>; -export interface TreeViewItemPluginOptions { - props: TreeItemProps; - ref: React.Ref; +export interface TreeViewItemPluginOptions extends TreeViewItemPluginResponse { + props: TProps; } export interface TreeViewItemPluginResponse { /** - * Props enriched by the plugin. + * Root of the `content` slot enriched by the plugin. */ - props?: TreeItemProps; + contentRef?: React.RefCallback | null; /** - * Ref enriched by the plugin + * Ref of the `root` slot enriched by the plugin */ - ref?: React.Ref; - /** - * Render function used to add React wrappers around the TreeItem. - * @param {React.ReactNode} children The TreeItem node before this plugin execution. - * @returns {React.ReactNode} The wrapped TreeItem. - */ - wrapItem?: (children: React.ReactNode) => React.ReactNode; + rootRef?: React.RefCallback | null; } -export type TreeViewItemPlugin = ( - options: TreeViewItemPluginOptions, +export type TreeViewItemPlugin = ( + options: TreeViewItemPluginOptions, ) => void | TreeViewItemPluginResponse; +export type TreeItemWrapper = (params: { + nodeId: TreeViewItemId; + children: React.ReactNode; +}) => React.ReactNode; + export type TreeViewPlugin = { (options: TreeViewPluginOptions): void | TreeViewResponse; getDefaultizedParams?: ( @@ -158,5 +156,11 @@ export type TreeViewPlugin = { getInitialState?: (params: TreeViewUsedDefaultizedParams) => TSignature['state']; models?: TreeViewModelsInitializer; params: Record; - itemPlugin?: TreeViewItemPlugin; + itemPlugin?: TreeViewItemPlugin; + /** + * Render function used to add React wrappers around the TreeItem. + * @param {TreeItemWrapperParams} params The params of the item. + * @returns {React.ReactNode} The wrapped items. + */ + wrapItem?: TreeItemWrapper; }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx index fda563de2b3f1..51f8ca2acf193 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx @@ -11,6 +11,8 @@ import { TreeItemDescendant, useDescendant, } from '../../TreeViewProvider/DescendantProvider'; +import type { TreeItemProps } from '../../../TreeItem'; +import type { TreeItem2Props } from '../../../TreeItem2'; export const useTreeViewJSXNodes: TreeViewPlugin = ({ instance, @@ -80,8 +82,12 @@ export const useTreeViewJSXNodes: TreeViewPlugin = }); }; -const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin = ({ props, ref }) => { - const { children, disabled = false, label, nodeId, id, ContentProps: inContentProps } = props; +const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin = ({ + props, + rootRef, + contentRef, +}) => { + const { children, disabled = false, label, nodeId, id } = props; const { instance } = useTreeViewContext<[UseTreeViewJSXNodesSignature]>(); @@ -95,8 +101,10 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin = ({ props, ref }) => { const expandable = isExpandable(children); const [treeItemElement, setTreeItemElement] = React.useState(null); - const contentRef = React.useRef(null); - const handleRef = useForkRef(setTreeItemElement, ref); + const pluginContentRef = React.useRef(null); + + const handleRootRef = useForkRef(setTreeItemElement, rootRef); + const handleContentRef = useForkRef(pluginContentRef, contentRef); const descendant = React.useMemo( () => ({ @@ -130,25 +138,22 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin = ({ props, ref }) => { if (label) { return instance.mapFirstCharFromJSX( nodeId, - (contentRef.current?.textContent ?? '').substring(0, 1).toLowerCase(), + (pluginContentRef.current?.textContent ?? '').substring(0, 1).toLowerCase(), ); } return undefined; }, [instance, nodeId, label]); return { - props: { - ...props, - ContentProps: { - ...inContentProps, - ref: contentRef, - }, - }, - ref: handleRef, - wrapItem: (item) => {item}, + contentRef: handleContentRef, + rootRef: handleRootRef, }; }; useTreeViewJSXNodes.itemPlugin = useTreeViewJSXNodesItemPlugin; +useTreeViewJSXNodes.wrapItem = ({ children, nodeId }) => ( + {children} +); + useTreeViewJSXNodes.params = {}; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts index 544ed0a911f40..deeb8cd5bb6f2 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts @@ -113,8 +113,9 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< const canToggleNodeSelection = (nodeId: string) => !params.disableSelection && !instance.isNodeDisabled(nodeId); - const canToggleNodeExpansion = (nodeId: string) => - !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId); + const canToggleNodeExpansion = (nodeId: string) => { + return !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId); + }; // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction const createHandleKeyDown = @@ -126,7 +127,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< return; } - // If the tree is empty there will be no focused node + // If the tree is empty, there will be no focused node if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) { return; } diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts index ecbd95aa5eb3a..54ca801486d61 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts @@ -180,7 +180,7 @@ export const useTreeViewNodes: TreeViewPlugin = ({ params.getItemLabel, ]); - const getNodesToRender = useEventCallback(() => { + const getNodesToRender = () => { const getPropsFromNodeId = ({ id, children, @@ -195,7 +195,7 @@ export const useTreeViewNodes: TreeViewPlugin = ({ }; return state.nodes.nodeTree.map(getPropsFromNodeId); - }); + }; populateInstance(instance, { getNode, diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts index 3584f61115f8a..0fd10e4d56879 100644 --- a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts +++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts @@ -7,6 +7,7 @@ import { TreeViewPlugin, ConvertPluginsIntoSignatures, MergePluginsProperty, + TreeItemWrapper, TreeViewPublicAPI, } from '../models'; import { @@ -80,6 +81,7 @@ export const useTreeView = React.HTMLAttributes)[] = []; const contextValue = { + publicAPI, instance: instance as TreeViewInstance, } as TreeViewContextValue; @@ -108,42 +110,46 @@ export const useTreeView = { - let finalProps = props; - let finalRef = ref; - const itemWrappers: ((children: React.ReactNode) => React.ReactNode)[] = []; + contextValue.runItemPlugins = (itemPluginProps) => { + let finalRootRef: React.RefCallback | null = null; + let finalContentRef: React.RefCallback | null = null; plugins.forEach((plugin) => { if (!plugin.itemPlugin) { return; } - const itemPluginResponse = plugin.itemPlugin({ props: finalProps, ref: finalRef }); - if (itemPluginResponse?.props) { - finalProps = itemPluginResponse.props; + const itemPluginResponse = plugin.itemPlugin({ + props: itemPluginProps, + rootRef: finalRootRef, + contentRef: finalContentRef, + }); + if (itemPluginResponse?.rootRef) { + finalRootRef = itemPluginResponse.rootRef; } - if (itemPluginResponse?.ref) { - finalRef = itemPluginResponse.ref; - } - if (itemPluginResponse?.wrapItem) { - itemWrappers.push(itemPluginResponse.wrapItem); + if (itemPluginResponse?.contentRef) { + finalContentRef = itemPluginResponse.contentRef; } }); return { - props: finalProps, - ref: finalRef, - wrapItem: (children) => { - let finalChildren: React.ReactNode = children; - itemWrappers.forEach((itemWrapper) => { - finalChildren = itemWrapper(finalChildren); - }); - - return finalChildren; - }, + contentRef: finalContentRef, + rootRef: finalRootRef, }; }; + const itemWrappers = plugins + .map((plugin) => plugin.wrapItem) + .filter((wrapItem): wrapItem is TreeItemWrapper => !!wrapItem); + contextValue.wrapItem = ({ nodeId, children }) => { + let finalChildren: React.ReactNode = children; + itemWrappers.forEach((itemWrapper) => { + finalChildren = itemWrapper({ nodeId, children: finalChildren }); + }); + + return finalChildren; + }; + const getRootProps = ( otherHandlers: TOther = {} as TOther, ) => { diff --git a/packages/x-tree-view/src/themeAugmentation/components.d.ts b/packages/x-tree-view/src/themeAugmentation/components.d.ts index 3c953271789f2..8dbd4cff0aa8b 100644 --- a/packages/x-tree-view/src/themeAugmentation/components.d.ts +++ b/packages/x-tree-view/src/themeAugmentation/components.d.ts @@ -21,6 +21,11 @@ export interface TreeViewComponents { styleOverrides?: ComponentsOverrides['MuiTreeItem']; variants?: ComponentsVariants['MuiTreeItem']; }; + MuiTreeItem2?: { + defaultProps?: ComponentsProps['MuiTreeItem2']; + styleOverrides?: ComponentsOverrides['MuiTreeItem2']; + variants?: ComponentsVariants['MuiTreeItem2']; + }; } declare module '@mui/material/styles' { diff --git a/packages/x-tree-view/src/themeAugmentation/overrides.d.ts b/packages/x-tree-view/src/themeAugmentation/overrides.d.ts index 8cedc3a2308c3..6402fef94e922 100644 --- a/packages/x-tree-view/src/themeAugmentation/overrides.d.ts +++ b/packages/x-tree-view/src/themeAugmentation/overrides.d.ts @@ -9,6 +9,7 @@ export interface TreeViewComponentNameToClassKey { MuiRichTreeView: RichTreeViewClassKey; MuiTreeView: TreeViewClassKey; MuiTreeItem: TreeItemClassKey; + MuiTreeItem2: TreeItemClassKey; } declare module '@mui/material/styles' { diff --git a/packages/x-tree-view/src/themeAugmentation/props.d.ts b/packages/x-tree-view/src/themeAugmentation/props.d.ts index 163c60a7e789b..da5ae41f5bd46 100644 --- a/packages/x-tree-view/src/themeAugmentation/props.d.ts +++ b/packages/x-tree-view/src/themeAugmentation/props.d.ts @@ -2,12 +2,14 @@ import { TreeItemProps } from '../TreeItem'; import { TreeViewProps } from '../TreeView'; import { SimpleTreeViewProps } from '../SimpleTreeView'; import { RichTreeViewProps } from '../RichTreeView'; +import { TreeItem2Props } from '../TreeItem2'; export interface TreeViewComponentsPropsList { MuiSimpleTreeView: SimpleTreeViewProps; MuiRichTreeView: RichTreeViewProps; MuiTreeView: TreeViewProps; MuiTreeItem: TreeItemProps; + MuiTreeItem2: TreeItem2Props; } declare module '@mui/material/styles' { diff --git a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts index 77dae4729aba5..8d8b47a5147b1 100644 --- a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts +++ b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts @@ -82,5 +82,24 @@ createTheme({ }, }, }, + MuiTreeItem2: { + defaultProps: { + nodeId: '1', + // @ts-expect-error invalid MuiTreeItem2 prop + someRandomProp: true, + }, + styleOverrides: { + root: { + backgroundColor: 'red', + [`.${treeItemClasses.content}`]: { + backgroundColor: 'green', + }, + }, + // @ts-expect-error invalid MuiTreeItem2 class key + main: { + backgroundColor: 'blue', + }, + }, + }, }, }); diff --git a/packages/x-tree-view/src/useTreeItem2/index.ts b/packages/x-tree-view/src/useTreeItem2/index.ts new file mode 100644 index 0000000000000..dc1a81e7f114c --- /dev/null +++ b/packages/x-tree-view/src/useTreeItem2/index.ts @@ -0,0 +1,7 @@ +export { useTreeItem2 as unstable_useTreeItem2 } from './useTreeItem2'; +export type { + UseTreeItem2Parameters, + UseTreeItem2ReturnValue, + UseTreeItem2Status, + UseTreeItem2ContentSlotOwnProps, +} from './useTreeItem2.types'; diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts new file mode 100644 index 0000000000000..8acd94e62edd9 --- /dev/null +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts @@ -0,0 +1,192 @@ +import * as React from 'react'; +import { EventHandlers, extractEventHandlers } from '@mui/base/utils'; +import useForkRef from '@mui/utils/useForkRef'; +import { + UseTreeItem2Parameters, + UseTreeItem2ReturnValue, + UseTreeItem2RootSlotProps, + UseTreeItem2ContentSlotProps, + UseTreeItem2GroupTransitionSlotProps, + UseTreeItem2LabelSlotProps, + UseTreeItemIconContainerSlotProps, +} from './useTreeItem2.types'; +import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; +import { DefaultTreeViewPlugins } from '../internals/plugins/defaultPlugins'; +import { MuiCancellableEvent } from '../internals/models/MuiCancellableEvent'; +import { useTreeItem2Utils } from '../hooks/useTreeItem2Utils'; + +export const useTreeItem2 = ( + parameters: UseTreeItem2Parameters, +): UseTreeItem2ReturnValue => { + const { + runItemPlugins, + selection: { multiSelect }, + disabledItemsFocusable, + instance, + publicAPI, + } = useTreeViewContext(); + + const { id, nodeId, label, children, rootRef } = parameters; + + const { rootRef: pluginRootRef, contentRef } = runItemPlugins(parameters); + const { interactions, status } = useTreeItem2Utils({ nodeId, children }); + const idAttribute = instance.getTreeItemId(nodeId, id); + const handleRootRef = useForkRef(rootRef, pluginRootRef)!; + + const createRootHandleFocus = + (otherHandlers: EventHandlers) => (event: React.FocusEvent & MuiCancellableEvent) => { + otherHandlers.onFocus?.(event); + + if (event.defaultMuiPrevented) { + return; + } + + // DOM focus stays on the tree which manages focus with aria-activedescendant + if (event.target === event.currentTarget) { + instance.focusRoot(); + } + + const canBeFocused = !status.disabled || disabledItemsFocusable; + if (!status.focused && canBeFocused && event.currentTarget === event.target) { + instance.focusNode(event, nodeId); + } + }; + + const createContentHandleClick = + (otherHandlers: EventHandlers) => (event: React.MouseEvent & MuiCancellableEvent) => { + otherHandlers.onClick?.(event); + + if (event.defaultMuiPrevented) { + return; + } + + interactions.handleExpansion(event); + interactions.handleSelection(event); + }; + + const createContentHandleMouseDown = + (otherHandlers: EventHandlers) => (event: React.MouseEvent & MuiCancellableEvent) => { + otherHandlers.onMouseDown?.(event); + + if (event.defaultMuiPrevented) { + return; + } + + // Prevent text selection + if (event.shiftKey || event.ctrlKey || event.metaKey || status.disabled) { + event.preventDefault(); + } + }; + + const getRootProps = = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ): UseTreeItem2RootSlotProps => { + const externalEventHandlers = { + ...extractEventHandlers(parameters), + ...extractEventHandlers(externalProps), + }; + + let ariaSelected: boolean | undefined; + if (multiSelect) { + ariaSelected = status.selected; + } else if (status.selected) { + /* single-selection trees unset aria-selected on un-selected items. + * + * If the tree does not support multiple selection, aria-selected + * is set to true for the selected node and it is not present on any other node in the tree. + * Source: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ + */ + ariaSelected = true; + } + + return { + ...externalEventHandlers, + ref: handleRootRef, + role: 'treeitem', + tabIndex: -1, + id: idAttribute, + 'aria-expanded': status.expandable ? status.expanded : undefined, + 'aria-selected': ariaSelected, + 'aria-disabled': status.disabled || undefined, + ...externalProps, + onFocus: createRootHandleFocus(externalEventHandlers), + }; + }; + + const getContentProps = = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ): UseTreeItem2ContentSlotProps => { + const externalEventHandlers = { + ...extractEventHandlers(parameters), + ...extractEventHandlers(externalProps), + }; + + return { + ...externalEventHandlers, + ...externalProps, + ref: contentRef, + onClick: createContentHandleClick(externalEventHandlers), + onMouseDown: createContentHandleMouseDown(externalEventHandlers), + status, + }; + }; + + const getLabelProps = = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ): UseTreeItem2LabelSlotProps => { + const externalEventHandlers = { + ...extractEventHandlers(parameters), + ...extractEventHandlers(externalProps), + }; + + return { + ...externalEventHandlers, + children: label, + ...externalProps, + }; + }; + + const getIconContainerProps = = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ): UseTreeItemIconContainerSlotProps => { + const externalEventHandlers = { + ...extractEventHandlers(parameters), + ...extractEventHandlers(externalProps), + }; + + return { + ...externalEventHandlers, + ...externalProps, + }; + }; + + const getGroupTransitionProps = = {}>( + externalProps: ExternalProps = {} as ExternalProps, + ): UseTreeItem2GroupTransitionSlotProps => { + const externalEventHandlers = { + ...extractEventHandlers(parameters), + ...extractEventHandlers(externalProps), + }; + + return { + ...externalEventHandlers, + unmountOnExit: true, + component: 'ul', + role: 'group', + in: status.expanded, + children, + ...externalProps, + }; + }; + + return { + getRootProps, + getContentProps, + getGroupTransitionProps, + getIconContainerProps, + getLabelProps, + rootRef: handleRootRef, + status, + publicAPI, + }; +}; diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts new file mode 100644 index 0000000000000..d8bb00157c494 --- /dev/null +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.types.ts @@ -0,0 +1,140 @@ +import * as React from 'react'; +import { TreeViewItemId } from '../models'; +import { MuiCancellableEventHandler } from '../internals/models/MuiCancellableEvent'; +import { TreeViewAnyPluginSignature, TreeViewPublicAPI } from '../internals/models'; + +export interface UseTreeItem2Parameters { + /** + * The id attribute of the node. If not provided, it will be generated. + */ + id?: string; + /** + * If `true`, the node is disabled. + * @default false + */ + disabled?: boolean; + /** + * The id of the node. + * Must be unique. + */ + nodeId: TreeViewItemId; + /** + * The label of the node. + */ + label?: React.ReactNode; + rootRef?: React.Ref; + /** + * The content of the component. + */ + children?: React.ReactNode; +} + +export interface UseTreeItem2RootSlotOwnProps { + role: 'treeitem'; + tabIndex: -1; + id: string; + 'aria-expanded': React.AriaAttributes['aria-expanded']; + 'aria-selected': React.AriaAttributes['aria-selected']; + 'aria-disabled': React.AriaAttributes['aria-disabled']; + onFocus: MuiCancellableEventHandler; + ref: React.RefCallback; +} + +export type UseTreeItem2RootSlotProps = ExternalProps & + UseTreeItem2RootSlotOwnProps; + +export interface UseTreeItem2ContentSlotOwnProps { + onClick: MuiCancellableEventHandler; + onMouseDown: MuiCancellableEventHandler; + ref: React.RefCallback | null; + status: UseTreeItem2Status; +} + +export type UseTreeItem2ContentSlotProps = ExternalProps & + UseTreeItem2ContentSlotOwnProps; + +export interface UseTreeItem2IconContainerSlotOwnProps {} + +export type UseTreeItemIconContainerSlotProps = ExternalProps & + UseTreeItem2IconContainerSlotOwnProps; + +export interface UseTreeItem2LabelSlotOwnProps { + children: React.ReactNode; +} + +export type UseTreeItem2LabelSlotProps = ExternalProps & + UseTreeItem2LabelSlotOwnProps; + +export interface UseTreeItem2GroupTransitionSlotOwnProps { + unmountOnExit: boolean; + in: boolean; + component: 'ul'; + role: 'group'; + children: React.ReactNode; +} + +export type UseTreeItem2GroupTransitionSlotProps = ExternalProps & + UseTreeItem2GroupTransitionSlotOwnProps; + +export interface UseTreeItem2Status { + expandable: boolean; + expanded: boolean; + focused: boolean; + selected: boolean; + disabled: boolean; +} + +export interface UseTreeItem2ReturnValue { + /** + * Resolver for the root slot's props. + * @param {ExternalProps} externalProps Additional props for the root slot + * @returns {UseTreeItem2RootSlotProps} Props that should be spread on the root slot + */ + getRootProps: = {}>( + externalProps?: ExternalProps, + ) => UseTreeItem2RootSlotProps; + /** + * Resolver for the content slot's props. + * @param {ExternalProps} externalProps Additional props for the content slot + * @returns {UseTreeItem2ContentSlotProps} Props that should be spread on the content slot + */ + getContentProps: = {}>( + externalProps?: ExternalProps, + ) => UseTreeItem2ContentSlotProps; + /** + * Resolver for the label slot's props. + * @param {ExternalProps} externalProps Additional props for the label slot + * @returns {UseTreeItem2LabelSlotProps} Props that should be spread on the label slot + */ + getLabelProps: = {}>( + externalProps?: ExternalProps, + ) => UseTreeItem2LabelSlotProps; + /** + * Resolver for the iconContainer slot's props. + * @param {ExternalProps} externalProps Additional props for the iconContainer slot + * @returns {UseTreeItemIconContainerSlotProps} Props that should be spread on the iconContainer slot + */ + getIconContainerProps: = {}>( + externalProps?: ExternalProps, + ) => UseTreeItemIconContainerSlotProps; + /** + * Resolver for the GroupTransition slot's props. + * @param {ExternalProps} externalProps Additional props for the GroupTransition slot + * @returns {UseTreeItem2GroupTransitionSlotProps} Props that should be spread on the GroupTransition slot + */ + getGroupTransitionProps: = {}>( + externalProps?: ExternalProps, + ) => UseTreeItem2GroupTransitionSlotProps; + /** + * A ref to the component's root DOM element. + */ + rootRef: React.RefCallback | null; + /** + * Current status of the item. + */ + status: UseTreeItem2Status; + /** + * The object the allows Tree View manipulation. + */ + publicAPI: TreeViewPublicAPI; +} diff --git a/scripts/x-tree-view.exports.json b/scripts/x-tree-view.exports.json index 19663f9de2115..d976b52c03da6 100644 --- a/scripts/x-tree-view.exports.json +++ b/scripts/x-tree-view.exports.json @@ -23,6 +23,21 @@ { "name": "SimpleTreeViewSlots", "kind": "Interface" }, { "name": "SingleSelectTreeViewProps", "kind": "TypeAlias" }, { "name": "TreeItem", "kind": "Variable" }, + { "name": "TreeItem2", "kind": "Variable" }, + { "name": "TreeItem2Content", "kind": "Variable" }, + { "name": "TreeItem2GroupTransition", "kind": "Variable" }, + { "name": "TreeItem2Icon", "kind": "Function" }, + { "name": "TreeItem2IconContainer", "kind": "Variable" }, + { "name": "TreeItem2IconProps", "kind": "Interface" }, + { "name": "TreeItem2IconSlotProps", "kind": "Interface" }, + { "name": "TreeItem2IconSlots", "kind": "Interface" }, + { "name": "TreeItem2Label", "kind": "Variable" }, + { "name": "TreeItem2Props", "kind": "Interface" }, + { "name": "TreeItem2Provider", "kind": "Function" }, + { "name": "TreeItem2ProviderProps", "kind": "Interface" }, + { "name": "TreeItem2Root", "kind": "Variable" }, + { "name": "TreeItem2SlotProps", "kind": "Interface" }, + { "name": "TreeItem2Slots", "kind": "Interface" }, { "name": "treeItemClasses", "kind": "Variable" }, { "name": "TreeItemClasses", "kind": "Interface" }, { "name": "TreeItemClassKey", "kind": "TypeAlias" }, @@ -44,6 +59,12 @@ { "name": "TreeViewSlotProps", "kind": "Interface" }, { "name": "TreeViewSlots", "kind": "Interface" }, { "name": "unstable_resetCleanupTracking", "kind": "Variable" }, + { "name": "unstable_useTreeItem2", "kind": "Variable" }, + { "name": "UseTreeItem2ContentSlotOwnProps", "kind": "Interface" }, + { "name": "UseTreeItem2Parameters", "kind": "Interface" }, + { "name": "UseTreeItem2ReturnValue", "kind": "Interface" }, + { "name": "UseTreeItem2Status", "kind": "Interface" }, + { "name": "useTreeItem2Utils", "kind": "Variable" }, { "name": "useTreeItemState", "kind": "Function" }, { "name": "useTreeViewApiRef", "kind": "Variable" } ] From 719fa0a0ef879b56a322a3b82da512c589ca8fe0 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 11 Mar 2024 12:41:38 +0200 Subject: [PATCH 18/49] [pickers] Keep the existing time when looking for closest enabled date (#12377) --- .../DateCalendar/tests/DateCalendar.test.tsx | 25 +++++++++++++++- .../src/internals/utils/date-utils.test.ts | 20 +++++++++++++ .../src/internals/utils/date-utils.ts | 29 +++++++++---------- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx index d29b083bca93e..18e200761ded5 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx @@ -9,7 +9,10 @@ import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe('', () => { - const { render, clock } = createPickerRenderer({ clock: 'fake' }); + const { render, clock } = createPickerRenderer({ + clock: 'fake', + clockConfig: new Date('2019-01-02'), + }); it('switches between views uncontrolled', () => { const handleViewChange = spy(); @@ -127,6 +130,26 @@ describe('', () => { expect(screen.getAllByRole('rowheader').length).to.equal(5); }); + // test: https://github.com/mui/mui-x/issues/12373 + it('should not reset day to `startOfDay` if value already exists when finding the closest enabled date', () => { + const onChange = spy(); + const defaultDate = adapterToUse.date('2019-01-02T11:12:13'); + render(); + + userEvent.mousePress( + screen.getByRole('button', { name: 'calendar view is open, switch to year view' }), + ); + userEvent.mousePress(screen.getByRole('radio', { name: '2020' })); + userEvent.mousePress(screen.getByRole('gridcell', { name: '1' })); + userEvent.mousePress( + screen.getByRole('button', { name: 'calendar view is open, switch to year view' }), + ); + // select the current year with a date in the past to trigger "findClosestEnabledDate" + userEvent.mousePress(screen.getByRole('radio', { name: '2019' })); + + expect(onChange.lastCall.firstArg).toEqualDateTime(defaultDate); + }); + describe('Slot: calendarHeader', () => { it('should allow to override the format', () => { render( diff --git a/packages/x-date-pickers/src/internals/utils/date-utils.test.ts b/packages/x-date-pickers/src/internals/utils/date-utils.test.ts index b887e6d406365..6f7e2c855e308 100644 --- a/packages/x-date-pickers/src/internals/utils/date-utils.test.ts +++ b/packages/x-date-pickers/src/internals/utils/date-utils.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { adapterToUse } from 'test/utils/pickers'; +import { useFakeTimers } from 'sinon'; import { findClosestEnabledDate } from './date-utils'; describe('findClosestEnabledDate', () => { @@ -99,6 +100,25 @@ describe('findClosestEnabledDate', () => { expect(adapterToUse.isSameDay(result, today)).to.equal(true); }); + it('should return now with given time part if disablePast and now is valid', () => { + const clock = useFakeTimers({ now: new Date('2000-01-02') }); + + const tryDate = adapterToUse.date('2000-01-01T11:12:13'); + const result = findClosestEnabledDate({ + date: tryDate, + minDate: adapterToUse.date('1900-01-01'), + maxDate: adapterToUse.date('2100-01-01'), + utils: adapterToUse, + isDateDisabled: () => false, + disableFuture: false, + disablePast: true, + timezone: 'default', + })!; + + expect(result).toEqualDateTime(adapterToUse.addDays(tryDate, 1)); + clock.reset(); + }); + it('should fallback to today if disablePast+disableFuture and now is invalid', () => { const today = adapterToUse.date(); const result = findClosestEnabledDate({ diff --git a/packages/x-date-pickers/src/internals/utils/date-utils.ts b/packages/x-date-pickers/src/internals/utils/date-utils.ts index 639332f7dc49e..80e086cfc3cf2 100644 --- a/packages/x-date-pickers/src/internals/utils/date-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/date-utils.ts @@ -8,6 +8,19 @@ import { import { DateOrTimeViewWithMeridiem } from '../models'; import { areViewsEqual } from './views'; +export const mergeDateAndTime = ( + utils: MuiPickersAdapter, + dateParam: TDate, + timeParam: TDate, +) => { + let mergedDate = dateParam; + mergedDate = utils.setHours(mergedDate, utils.getHours(timeParam)); + mergedDate = utils.setMinutes(mergedDate, utils.getMinutes(timeParam)); + mergedDate = utils.setSeconds(mergedDate, utils.getSeconds(timeParam)); + + return mergedDate; +}; + interface FindClosestDateParams { date: TDate; disableFuture?: boolean; @@ -29,8 +42,7 @@ export const findClosestEnabledDate = ({ utils, timezone, }: FindClosestDateParams) => { - const today = utils.startOfDay(utils.date(undefined, timezone)); - + const today = mergeDateAndTime(utils, utils.date(undefined, timezone), date); if (disablePast && utils.isBefore(minDate!, today)) { minDate = today; } @@ -124,19 +136,6 @@ export const getMonthsInYear = ( return months; }; -export const mergeDateAndTime = ( - utils: MuiPickersAdapter, - dateParam: TDate, - timeParam: TDate, -) => { - let mergedDate = dateParam; - mergedDate = utils.setHours(mergedDate, utils.getHours(timeParam)); - mergedDate = utils.setMinutes(mergedDate, utils.getMinutes(timeParam)); - mergedDate = utils.setSeconds(mergedDate, utils.getSeconds(timeParam)); - - return mergedDate; -}; - export const getTodayDate = ( utils: MuiPickersAdapter, timezone: PickersTimezone, From 848f2a1d3c40a84aecec4d31e09b3b49cd47903c Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Mon, 11 Mar 2024 11:57:44 +0100 Subject: [PATCH 19/49] [core] Fix PR deploy link for Tree View doc pages (#12411) --- dangerfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/dangerfile.js b/dangerfile.js index a3726af581bf7..479d7aaa0a73a 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -12,6 +12,7 @@ function addDeployPreviewUrls() { .replace('data-grid/', 'react-data-grid/') .replace('date-pickers/', 'react-date-pickers/') .replace('charts/', 'react-charts/') + .replace('tree-view/', 'react-tree-view/') .replace(/\/[^/]+\.md$/, '/'); } From f881048bc7f73c3e970200eb92c69d9473df3bce Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Tue, 12 Mar 2024 09:47:01 +0100 Subject: [PATCH 20/49] [core] Fix CI (#12414) Signed-off-by: Flavien DELANGLE Co-authored-by: Lukas --- .../src/DateCalendar/tests/DateCalendar.test.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx index 18e200761ded5..7bb1e9c0380c8 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx @@ -133,13 +133,17 @@ describe('', () => { // test: https://github.com/mui/mui-x/issues/12373 it('should not reset day to `startOfDay` if value already exists when finding the closest enabled date', () => { const onChange = spy(); - const defaultDate = adapterToUse.date('2019-01-02T11:12:13'); + const defaultDate = adapterToUse.date('2019-01-02T11:12:13.550Z'); render(); userEvent.mousePress( screen.getByRole('button', { name: 'calendar view is open, switch to year view' }), ); userEvent.mousePress(screen.getByRole('radio', { name: '2020' })); + + // Finish the transition to the day view + clock.runToLast(); + userEvent.mousePress(screen.getByRole('gridcell', { name: '1' })); userEvent.mousePress( screen.getByRole('button', { name: 'calendar view is open, switch to year view' }), From 3a90c251e0c67f546605fec213b2b47c7211e36e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 11:03:35 +0200 Subject: [PATCH 21/49] Bump github/codeql-action action to v3.24.6 (#12299) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/codeql.yml | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b83d736e55bf8..93bcb42103847 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,7 +19,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 with: languages: typescript # If you wish to specify custom queries, you can do so here or in a config file. @@ -29,4 +29,4 @@ jobs: # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 21b47b161d200..c32bf3cf5b8ed 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -44,6 +44,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@47b3d888fe66b639e431abf22ebca059152f1eea # v3.24.5 + uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6 with: sarif_file: results.sarif From 3dd2afe818abdb1f7b801c55ff6a22a8d273dcd6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:01:12 +0200 Subject: [PATCH 22/49] Bump @types/node to ^18.19.23 (#12387) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Lukas --- package.json | 4 ++-- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f72a674494976..3de081a76fa1c 100644 --- a/package.json +++ b/package.json @@ -98,7 +98,7 @@ "@types/chai-dom": "^1.11.3", "@types/enzyme": "3.10.12", "@types/mocha": "^10.0.6", - "@types/node": "^18.19.21", + "@types/node": "^18.19.23", "@types/prettier": "^2.7.3", "@types/react": "^18.2.60", "@types/react-dom": "^18.2.19", @@ -191,6 +191,6 @@ }, "resolutions": { "**/react-is": "^18.2.0", - "**/@types/node": "^18.19.21" + "**/@types/node": "^18.19.23" } } diff --git a/yarn.lock b/yarn.lock index 151fe00fe4f5a..5c9fec500da07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3336,10 +3336,10 @@ dependencies: moment ">=2.14.0" -"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12", "@types/node@>=12.0.0", "@types/node@>=18.0.0", "@types/node@^14.0.1", "@types/node@^18.19.21": - version "18.19.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.21.tgz#f4ca1ac8ffb05ee4b89163c2d6fac9a1a59ee149" - integrity sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw== +"@types/node@*", "@types/node@>=10.0.0", "@types/node@>=12", "@types/node@>=12.0.0", "@types/node@>=18.0.0", "@types/node@^14.0.1", "@types/node@^18.19.23": + version "18.19.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.23.tgz#e02c759218bc9957423a3f7d585d511b17be2351" + integrity sha512-wtE3d0OUfNKtZYAqZb8HAWGxxXsImJcPUAgZNw+dWFxO6s5tIwIjyKnY76tsTatsNCLJPkVYwUpq15D38ng9Aw== dependencies: undici-types "~5.26.4" From 1c328501203f6e6eab5a0f86011506aa3ae53df3 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 12 Mar 2024 12:01:31 +0200 Subject: [PATCH 23/49] [docs] Update `date-fns` `weekStarsOn` overriding example (#12416) Signed-off-by: Lukas Co-authored-by: Flavien DELANGLE --- .../adapters-locale/adapters-locale.md | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/data/date-pickers/adapters-locale/adapters-locale.md b/docs/data/date-pickers/adapters-locale/adapters-locale.md index 8d08042c202ba..c8fd2ed683dda 100644 --- a/docs/data/date-pickers/adapters-locale/adapters-locale.md +++ b/docs/data/date-pickers/adapters-locale/adapters-locale.md @@ -291,18 +291,25 @@ dayjs.updateLocale('en', { ### With `date-fns` -For `date-fns`, use the `setDefaultOptions` utility: +For `date-fns`, override the `options.weekStartsOn` of the used locale: ```ts +import { Locale } from 'date-fns'; // with date-fns v2.x -import setDefaultOptions from 'date-fns/setDefaultOptions'; +import enUS from 'date-fns/locale/en-US'; // with date-fns v3.x -import { setDefaultOptions } from 'date-fns/setDefaultOptions'; +import { enUS } from 'date-fns/locale/en-US'; -setDefaultOptions({ - // Sunday = 0, Monday = 1. - weekStartsOn: 1, -}); +const customEnLocale: Locale = { + ...enUS, + options: { + ...enUS.options, + // Sunday = 0, Monday = 1. + weekStartsOn: 1, + }, +}; + + ``` ### With `luxon` From ed9073da1e2849bf2f7b9358e0a1156bbb62bfc0 Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Tue, 12 Mar 2024 11:10:12 +0100 Subject: [PATCH 24/49] [fields] Fix items alignment on multi input range fields (#12312) --- .../MultiInputDateRangeField/MultiInputDateRangeField.tsx | 6 ++++-- .../MultiInputDateTimeRangeField.tsx | 6 ++++-- .../MultiInputTimeRangeField/MultiInputTimeRangeField.tsx | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx index 640f42f6036b9..4a4e615be45df 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx @@ -45,7 +45,7 @@ const useUtilityClasses = (ownerState: MultiInputDateRangeFieldProps) const MultiInputDateRangeFieldRoot = styled( React.forwardRef((props: StackProps, ref: React.Ref) => ( - + )), { name: 'MuiMultiInputDateRangeField', @@ -61,7 +61,9 @@ const MultiInputDateRangeFieldSeparator = styled( slot: 'Separator', overridesResolver: (props, styles) => styles.separator, }, -)({}); +)({ + lineHeight: '1.4375em', // 23px +}); type MultiInputDateRangeFieldComponent = (< TDate extends PickerValidDate, diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx index 28ace8cfa6ad2..cd0a2bc5e8f0e 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx @@ -43,7 +43,7 @@ const useUtilityClasses = (ownerState: MultiInputDateTimeRangeFieldProps) => ( - + )), { name: 'MuiMultiInputDateTimeRangeField', @@ -59,7 +59,9 @@ const MultiInputDateTimeRangeFieldSeparator = styled( slot: 'Separator', overridesResolver: (props, styles) => styles.separator, }, -)({}); +)({ + lineHeight: '1.4375em', // 23px +}); type MultiInputDateTimeRangeFieldComponent = (< TDate extends PickerValidDate, diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx index 593a224064a80..c57fd8c6e1b0e 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx @@ -45,7 +45,7 @@ const useUtilityClasses = (ownerState: MultiInputTimeRangeFieldProps) const MultiInputTimeRangeFieldRoot = styled( React.forwardRef((props: StackProps, ref: React.Ref) => ( - + )), { name: 'MuiMultiInputTimeRangeField', @@ -61,7 +61,9 @@ const MultiInputTimeRangeFieldSeparator = styled( slot: 'Separator', overridesResolver: (props, styles) => styles.separator, }, -)({}); +)({ + lineHeight: '1.4375em', // 23px +}); type MultiInputTimeRangeFieldComponent = (< TDate extends PickerValidDate, From eae5943217654f17140224af869f96a2e790452e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 14:16:05 +0200 Subject: [PATCH 25/49] Bump React router to ^6.22.3 (#12388) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Lukas --- docs/package.json | 4 ++-- yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/package.json b/docs/package.json index 3ea7635d66f2a..75a7b5f3ee778 100644 --- a/docs/package.json +++ b/docs/package.json @@ -78,8 +78,8 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.51.0", "react-is": "^18.2.0", - "react-router": "^6.22.2", - "react-router-dom": "^6.22.2", + "react-router": "^6.22.3", + "react-router-dom": "^6.22.3", "react-runner": "^1.0.3", "react-simple-code-editor": "^0.13.1", "recast": "^0.23.5", diff --git a/yarn.lock b/yarn.lock index 5c9fec500da07..f6bb4b7a86e37 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2714,10 +2714,10 @@ "@react-spring/shared" "~9.7.3" "@react-spring/types" "~9.7.3" -"@remix-run/router@1.15.2": - version "1.15.2" - resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.2.tgz#35726510d332ba5349c6398d13259d5da184553d" - integrity sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q== +"@remix-run/router@1.15.3": + version "1.15.3" + resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.15.3.tgz#d2509048d69dbb72d5389a14945339f1430b2d3c" + integrity sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w== "@sigstore/bundle@^1.1.0": version "1.1.0" @@ -12609,20 +12609,20 @@ react-reconciler@^0.29.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-router-dom@^6.22.2: - version "6.22.2" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.2.tgz#8233968a8a576f3006e5549c80f3527d2598fc9c" - integrity sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ== +react-router-dom@^6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.22.3.tgz#9781415667fd1361a475146c5826d9f16752a691" + integrity sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw== dependencies: - "@remix-run/router" "1.15.2" - react-router "6.22.2" + "@remix-run/router" "1.15.3" + react-router "6.22.3" -react-router@6.22.2, react-router@^6.22.2: - version "6.22.2" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.2.tgz#27e77e4c635a5697693b922d131d773451c98a5b" - integrity sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw== +react-router@6.22.3, react-router@^6.22.3: + version "6.22.3" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.22.3.tgz#9d9142f35e08be08c736a2082db5f0c9540a885e" + integrity sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ== dependencies: - "@remix-run/router" "1.15.2" + "@remix-run/router" "1.15.3" react-runner@^1.0.3: version "1.0.3" From d4b5ce9326cb762e785b0b956d966a5aa8bba9c6 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 12 Mar 2024 13:24:07 +0100 Subject: [PATCH 26/49] [DataGridPro] Make lazy loading feature stable (#12421) --- .../data-grid/row-updates/LazyLoadingGrid.js | 3 -- .../data-grid/row-updates/LazyLoadingGrid.tsx | 3 -- .../row-updates/LazyLoadingGrid.tsx.preview | 3 -- .../data/data-grid/row-updates/row-updates.md | 9 ------ .../migration-data-grid-v6.md | 8 +++-- .../x/api/data-grid/data-grid-premium.json | 5 +--- docs/pages/x/api/data-grid/data-grid-pro.json | 5 +--- .../src/DataGridPremium/DataGridPremium.tsx | 1 - .../src/DataGridPro/DataGridPro.tsx | 1 - .../features/lazyLoader/useGridLazyLoader.ts | 30 ++----------------- .../useGridLazyLoaderPreProcessors.tsx | 10 ++----- .../src/models/dataGridProProps.ts | 7 +---- .../src/tests/events.DataGridPro.test.tsx | 3 -- .../src/tests/lazyLoader.DataGridPro.test.tsx | 3 -- 14 files changed, 12 insertions(+), 79 deletions(-) diff --git a/docs/data/data-grid/row-updates/LazyLoadingGrid.js b/docs/data/data-grid/row-updates/LazyLoadingGrid.js index 6a53f34caf2f5..7acc1a654e547 100644 --- a/docs/data/data-grid/row-updates/LazyLoadingGrid.js +++ b/docs/data/data-grid/row-updates/LazyLoadingGrid.js @@ -97,9 +97,6 @@ export default function LazyLoadingGrid() { filterMode="server" rowsLoadingMode="server" onFetchRows={debouncedHandleFetchRows} - experimentalFeatures={{ - lazyLoading: true, - }} />
); diff --git a/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx b/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx index f228e1ff87115..16d46719cb03a 100644 --- a/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx +++ b/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx @@ -105,9 +105,6 @@ export default function LazyLoadingGrid() { filterMode="server" rowsLoadingMode="server" onFetchRows={debouncedHandleFetchRows} - experimentalFeatures={{ - lazyLoading: true, - }} /> ); diff --git a/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx.preview b/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx.preview index 76d26a1e110bf..5b46e03ab75d6 100644 --- a/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx.preview +++ b/docs/data/data-grid/row-updates/LazyLoadingGrid.tsx.preview @@ -8,7 +8,4 @@ filterMode="server" rowsLoadingMode="server" onFetchRows={debouncedHandleFetchRows} - experimentalFeatures={{ - lazyLoading: true, - }} /> \ No newline at end of file diff --git a/docs/data/data-grid/row-updates/row-updates.md b/docs/data/data-grid/row-updates/row-updates.md index 3bbb173476c3f..02d6e165843fa 100644 --- a/docs/data/data-grid/row-updates/row-updates.md +++ b/docs/data/data-grid/row-updates/row-updates.md @@ -47,15 +47,6 @@ Otherwise, the sorting and filtering will only be applied to the subset of rows ## Lazy loading [](/x/introduction/licensing/#pro-plan 'Pro plan') -:::warning -This feature is experimental and must be explicitly activated using the `lazyLoading` experimental feature flag: - -```tsx - -``` - -::: - Lazy Loading works like a pagination system, but instead of loading new rows based on pages, it loads them based on the viewport. It loads new rows in chunks, as the user scrolls through the data grid and reveals empty rows. diff --git a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md index 3ce5734605a16..86da76954e934 100644 --- a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md +++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md @@ -135,6 +135,11 @@ As a result, the following changes have been made: }; ``` +- Some feature flags were removed from the `experimentalFeatures` prop. These features are now stable and enabled by default: + - [`columnGrouping`](/x/react-data-grid/column-groups/) + - [`clipboardPaste`](/x/react-data-grid/clipboard/#clipboard-paste) + - [`lazyLoading`](/x/react-data-grid/row-updates/#lazy-loading) + ### Behavioral changes The disabled column specific features like `hiding`, `sorting`, `filtering`, `pinning`, `row grouping`, etc., can now be controlled programmatically using `initialState`, respective controlled models, or the [API object](/x/react-data-grid/api-object/). @@ -187,8 +192,6 @@ See the [Direct state access](/x/react-data-grid/state/#direct-selector-access) - The type `GridPinnedPosition` has been renamed to `GridPinnedColumnPosition`. -- Column grouping is now enabled by default. The flag `columnGrouping` is no longer needed to be passed to the `experimentalFeatures` prop to enable it. - - The column grouping API methods `getColumnGroupPath` and `getAllGroupDetails` are not anymore prefixed with `unstable_`. - The column grouping selectors `gridFocusColumnGroupHeaderSelector` and `gridTabIndexColumnGroupHeaderSelector` are not anymore prefixed with `unstable_`. @@ -301,7 +304,6 @@ See the [Direct state access](/x/react-data-grid/state/#direct-selector-access) ### Clipboard -- Clipboard paste is now enabled by default. The flag `clipboardPaste` is no longer needed to be passed to the `experimentalFeatures` prop to enable it. - The clipboard related exports `ignoreValueFormatterDuringExport` and `splitClipboardPastedText` are not anymore prefixed with `unstable_`. ### Print export diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 6d3fc43fb7317..5305710f3f4ea 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -79,10 +79,7 @@ "default": "\"cell\"" }, "experimentalFeatures": { - "type": { - "name": "shape", - "description": "{ lazyLoading?: bool, warnIfFocusStateIsNotSynced?: bool }" - } + "type": { "name": "shape", "description": "{ warnIfFocusStateIsNotSynced?: bool }" } }, "filterDebounceMs": { "type": { "name": "number" }, "default": "150" }, "filterMode": { diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 44bfabd9a4a79..30bfeb697b268 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -65,10 +65,7 @@ "default": "\"cell\"" }, "experimentalFeatures": { - "type": { - "name": "shape", - "description": "{ lazyLoading?: bool, warnIfFocusStateIsNotSynced?: bool }" - } + "type": { "name": "shape", "description": "{ warnIfFocusStateIsNotSynced?: bool }" } }, "filterDebounceMs": { "type": { "name": "number" }, "default": "150" }, "filterMode": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 96b00e33c81c0..ad6815061d5c1 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -323,7 +323,6 @@ DataGridPremiumRaw.propTypes = { * For each feature, if the flag is not explicitly set to `true`, then the feature is fully disabled, and neither property nor method calls will have any effect. */ experimentalFeatures: PropTypes.shape({ - lazyLoading: PropTypes.bool, warnIfFocusStateIsNotSynced: PropTypes.bool, }), /** diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 2950617d77b2d..55bae26091799 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -271,7 +271,6 @@ DataGridProRaw.propTypes = { * For each feature, if the flag is not explicitly set to `true`, the feature will be fully disabled and any property / method call will not have any effect. */ experimentalFeatures: PropTypes.shape({ - lazyLoading: PropTypes.bool, warnIfFocusStateIsNotSynced: PropTypes.bool, }), /** diff --git a/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts b/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts index ae61a9f02f080..745a4fb63d5d5 100644 --- a/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts +++ b/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts @@ -8,14 +8,10 @@ import { useGridApiOptionHandler, GridEventListener, GridRowEntry, - GridFeatureMode, } from '@mui/x-data-grid'; import { getVisibleRows } from '@mui/x-data-grid/internals'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; -import { - DataGridProProcessedProps, - GridExperimentalProFeatures, -} from '../../../models/dataGridProProps'; +import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; import { GridFetchRowsParams } from '../../../models/gridFetchRowsParams'; function findSkeletonRowsSection({ @@ -62,24 +58,6 @@ function findSkeletonRowsSection({ : undefined; } -function isLazyLoadingDisabled({ - lazyLoadingFeatureFlag, - rowsLoadingMode, -}: { - lazyLoadingFeatureFlag: boolean; - rowsLoadingMode: GridFeatureMode; -}) { - if (!lazyLoadingFeatureFlag) { - return true; - } - - if (rowsLoadingMode !== 'server') { - return true; - } - - return false; -} - /** * @requires useGridRows (state) * @requires useGridPagination (state) @@ -104,11 +82,7 @@ export const useGridLazyLoader = ( firstRowToRender: 0, lastRowToRender: 0, }); - const { lazyLoading } = (props.experimentalFeatures ?? {}) as GridExperimentalProFeatures; - const isDisabled = isLazyLoadingDisabled({ - lazyLoadingFeatureFlag: lazyLoading, - rowsLoadingMode: props.rowsLoadingMode, - }); + const isDisabled = props.rowsLoadingMode !== 'server'; const handleRenderedRowsIntervalChange = React.useCallback< GridEventListener<'renderedRowsIntervalChange'> diff --git a/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.tsx b/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.tsx index 25502a356bf5f..b6b5bf3359d81 100644 --- a/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.tsx +++ b/packages/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoaderPreProcessors.tsx @@ -2,10 +2,7 @@ import * as React from 'react'; import { GridPipeProcessor, useGridRegisterPipeProcessor } from '@mui/x-data-grid/internals'; import { GRID_ROOT_GROUP_ID, GridGroupNode, GridSkeletonRowNode } from '@mui/x-data-grid'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; -import { - DataGridProProcessedProps, - GridExperimentalProFeatures, -} from '../../../models/dataGridProProps'; +import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; export const GRID_SKELETON_ROW_ROOT_ID = 'auto-generated-skeleton-row-root'; @@ -15,14 +12,11 @@ export const useGridLazyLoaderPreProcessors = ( privateApiRef: React.MutableRefObject, props: Pick, ) => { - const { lazyLoading } = (props.experimentalFeatures ?? {}) as GridExperimentalProFeatures; - const addSkeletonRows = React.useCallback>( (groupingParams) => { const rootGroup = groupingParams.tree[GRID_ROOT_GROUP_ID] as GridGroupNode; if ( - !lazyLoading || props.rowsLoadingMode !== 'server' || !props.rowCount || rootGroup.children.length >= props.rowCount @@ -55,7 +49,7 @@ export const useGridLazyLoaderPreProcessors = ( tree, }; }, - [props.rowCount, props.rowsLoadingMode, lazyLoading], + [props.rowCount, props.rowsLoadingMode], ); useGridRegisterPipeProcessor(privateApiRef, 'hydrateRows', addSkeletonRows); diff --git a/packages/x-data-grid-pro/src/models/dataGridProProps.ts b/packages/x-data-grid-pro/src/models/dataGridProProps.ts index ee279741b9ce0..f6d9c4ed9d7b5 100644 --- a/packages/x-data-grid-pro/src/models/dataGridProProps.ts +++ b/packages/x-data-grid-pro/src/models/dataGridProProps.ts @@ -27,12 +27,7 @@ import { GridProSlotsComponent } from './gridProSlotsComponent'; import type { GridProSlotProps } from './gridProSlotProps'; import type { GridAutosizeOptions } from '../hooks'; -export interface GridExperimentalProFeatures extends GridExperimentalFeatures { - /** - * Enables the data grid to lazy load rows while scrolling. - */ - lazyLoading: boolean; -} +export interface GridExperimentalProFeatures extends GridExperimentalFeatures {} interface DataGridProPropsWithComplexDefaultValueBeforeProcessing extends Omit { diff --git a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx index 81275e69b7858..0dc92e4724841 100644 --- a/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx @@ -336,9 +336,6 @@ describe(' - Events params', () => { const handleFetchRows = spy(); render( - Lazy loader', () => { return (
Date: Tue, 12 Mar 2024 14:28:29 +0200 Subject: [PATCH 27/49] [docs] Add `legacy` bundle drop mention in migration pages (#12424) Signed-off-by: Lukas Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> --- .../migration/migration-charts-v6/migration-charts-v6.md | 9 +++++++++ .../migration-data-grid-v6/migration-data-grid-v6.md | 9 +++++++++ .../migration-pickers-v6/migration-pickers-v6.md | 9 +++++++++ .../migration-tree-view-v6/migration-tree-view-v6.md | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/docs/data/migration/migration-charts-v6/migration-charts-v6.md b/docs/data/migration/migration-charts-v6/migration-charts-v6.md index 4586825ccc825..ca9b8da21fce2 100644 --- a/docs/data/migration/migration-charts-v6/migration-charts-v6.md +++ b/docs/data/migration/migration-charts-v6/migration-charts-v6.md @@ -32,6 +32,15 @@ Please update your `@mui/material` package to this or a newer version. Since `v7` is a major release, it contains changes that affect the public API. These changes were done for consistency, improved stability and to make room for new features. +### Drop the legacy bundle + +The support for IE11 has been removed from all MUI X packages. +The `legacy` bundle that used to support old browsers like IE11 is no longer included. + +:::info +If you need support for IE11, you will need to keep using the latest version of the `v6` release. +::: + ### Renaming #### Types diff --git a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md index 86da76954e934..5d26059f9ef83 100644 --- a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md +++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md @@ -96,6 +96,15 @@ Since v7 is a major release, it contains some changes that affect the public API These changes were done for consistency, improve stability and make room for new features. Below are described the steps you need to make to migrate from v6 to v7. +### Drop the legacy bundle + +The support for IE11 has been removed from all MUI X packages. +The `legacy` bundle that used to support old browsers like IE11 is no longer included. + +:::info +If you need support for IE11, you will need to keep using the latest version of the `v6` release. +::: + ### DOM changes The Data Grid's layout has been substantially altered to use CSS sticky positioned elements. diff --git a/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md index f9d09cc46fafa..5189f68a1a484 100644 --- a/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md +++ b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md @@ -85,6 +85,15 @@ After running the codemods, make sure to test your application and that you don' Feel free to [open an issue](https://github.com/mui/mui-x/issues/new/choose) for support if you need help to proceed with your migration. ::: +## Drop the legacy bundle + +The support for IE11 has been removed from all MUI X packages. +The `legacy` bundle that used to support old browsers like IE11 is no longer included. + +:::info +If you need support for IE11, you will need to keep using the latest version of the `v6` release. +::: + ## Component slots ### Rename `components` to `slots` diff --git a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md index 5c4a6aa36e91e..e2f439e8a29b8 100644 --- a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md +++ b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md @@ -32,6 +32,15 @@ Please update your `@mui/material` package to this or a newer version. Since `v7` is a major release, it contains changes that affect the public API. These changes were done for consistency, improved stability and to make room for new features. +### Drop the legacy bundle + +The support for IE11 has been removed from all MUI X packages. +The `legacy` bundle that used to support old browsers like IE11 is no longer included. + +:::info +If you need support for IE11, you will need to keep using the latest version of the `v6` release. +::: + ### ✅ Use `SimpleTreeView` instead of `TreeView` The `TreeView` component has been deprecated and will be removed in the next major. From ec4e7d9498b6dc643494248d8ece4f2bf503eb1f Mon Sep 17 00:00:00 2001 From: Nora <72460825+noraleonte@users.noreply.github.com> Date: Tue, 12 Mar 2024 15:06:14 +0200 Subject: [PATCH 28/49] [TreeView] Rename `onNodeFocus` to `onItemFocus` (#12419) Signed-off-by: Nora <72460825+noraleonte@users.noreply.github.com> Co-authored-by: Flavien DELANGLE --- .../migration-tree-view-v6.md | 11 +++++ .../pages/x/api/tree-view/rich-tree-view.json | 12 ++--- .../x/api/tree-view/simple-tree-view.json | 12 ++--- docs/pages/x/api/tree-view/tree-view.json | 12 ++--- .../rich-tree-view/rich-tree-view.json | 16 +++---- .../simple-tree-view/simple-tree-view.json | 16 +++---- .../tree-view/tree-item-2/tree-item-2.json | 2 +- .../tree-view/tree-item/tree-item.json | 2 +- .../tree-view/tree-view/tree-view.json | 16 +++---- packages/x-codemod/README.md | 12 +++++ .../src/v7.0.0/tree-view/preset-safe/index.ts | 2 + .../rename-focus-callback/actual.spec.js | 1 + .../rename-focus-callback/expected.spec.js | 1 + .../tree-view/rename-focus-callback/index.ts | 18 ++++++++ .../rename-focus-callback.test.ts | 27 +++++++++++ .../src/RichTreeView/RichTreeView.tsx | 14 +++--- .../SimpleTreeView/SimpleTreeView.test.tsx | 46 +++++++++---------- .../src/SimpleTreeView/SimpleTreeView.tsx | 14 +++--- .../src/TreeItem/TreeItem.test.tsx | 4 +- .../x-tree-view/src/TreeItem/TreeItem.tsx | 2 +- .../src/TreeItem/TreeItem.types.ts | 2 +- .../x-tree-view/src/TreeItem2/TreeItem2.tsx | 2 +- .../src/TreeItem2/TreeItem2.types.ts | 2 +- .../x-tree-view/src/TreeView/TreeView.tsx | 14 +++--- .../useTreeViewFocus/useTreeViewFocus.ts | 10 ++-- .../useTreeViewFocus.types.ts | 6 +-- 26 files changed, 174 insertions(+), 102 deletions(-) create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/actual.spec.js create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/expected.spec.js create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/index.ts create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/rename-focus-callback.test.ts diff --git a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md index e2f439e8a29b8..442089ff038e6 100644 --- a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md +++ b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md @@ -396,3 +396,14 @@ This will help create a new headless version of the `TreeItem` component based o ) } ``` + +### ✅ Rename `onNodeFocus` + +The `onNodeFocus` callback has been renamed to `onItemFocus` for consistency: + +```diff + +``` diff --git a/docs/pages/x/api/tree-view/rich-tree-view.json b/docs/pages/x/api/tree-view/rich-tree-view.json index 663ab19ef16a4..10a0d28dfcc3e 100644 --- a/docs/pages/x/api/tree-view/rich-tree-view.json +++ b/docs/pages/x/api/tree-view/rich-tree-view.json @@ -47,18 +47,18 @@ "describedArgs": ["event", "nodeIds"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeFocus": { + "onNodeExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: string, value: string) => void", - "describedArgs": ["event", "nodeId", "value"] + "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", + "describedArgs": ["event", "nodeId", "isExpanded"] } }, "onNodeSelectionToggle": { diff --git a/docs/pages/x/api/tree-view/simple-tree-view.json b/docs/pages/x/api/tree-view/simple-tree-view.json index 2f6d68a24e5fa..06fb7d3d0280d 100644 --- a/docs/pages/x/api/tree-view/simple-tree-view.json +++ b/docs/pages/x/api/tree-view/simple-tree-view.json @@ -22,18 +22,18 @@ "describedArgs": ["event", "nodeIds"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeFocus": { + "onNodeExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: string, value: string) => void", - "describedArgs": ["event", "nodeId", "value"] + "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", + "describedArgs": ["event", "nodeId", "isExpanded"] } }, "onNodeSelectionToggle": { diff --git a/docs/pages/x/api/tree-view/tree-view.json b/docs/pages/x/api/tree-view/tree-view.json index 40d3c60828130..e36e618ba7f14 100644 --- a/docs/pages/x/api/tree-view/tree-view.json +++ b/docs/pages/x/api/tree-view/tree-view.json @@ -22,18 +22,18 @@ "describedArgs": ["event", "nodeIds"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeFocus": { + "onNodeExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: string, value: string) => void", - "describedArgs": ["event", "nodeId", "value"] + "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", + "describedArgs": ["event", "nodeId", "isExpanded"] } }, "onNodeSelectionToggle": { diff --git a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json index 883829d9ab0d7..cabd27145a140 100644 --- a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json +++ b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json @@ -46,6 +46,14 @@ "nodeIds": "The ids of the expanded nodes." } }, + "onItemFocus": { + "description": "Callback fired when tree items are focused.", + "typeDescriptions": { + "event": "The event source of the callback Warning: This is a generic event not a focus event.", + "itemId": "The id of the focused item.", + "value": "of the focused item." + } + }, "onNodeExpansionToggle": { "description": "Callback fired when a tree item is expanded or collapsed.", "typeDescriptions": { @@ -54,14 +62,6 @@ "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." } }, - "onNodeFocus": { - "description": "Callback fired when tree items are focused.", - "typeDescriptions": { - "event": "The event source of the callback Warning: This is a generic event not a focus event.", - "nodeId": "The id of the node focused.", - "value": "of the focused node." - } - }, "onNodeSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { diff --git a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json index 2cb82f5e46440..a220d49a0adc6 100644 --- a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json +++ b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json @@ -32,6 +32,14 @@ "nodeIds": "The ids of the expanded nodes." } }, + "onItemFocus": { + "description": "Callback fired when tree items are focused.", + "typeDescriptions": { + "event": "The event source of the callback Warning: This is a generic event not a focus event.", + "itemId": "The id of the focused item.", + "value": "of the focused item." + } + }, "onNodeExpansionToggle": { "description": "Callback fired when a tree item is expanded or collapsed.", "typeDescriptions": { @@ -40,14 +48,6 @@ "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." } }, - "onNodeFocus": { - "description": "Callback fired when tree items are focused.", - "typeDescriptions": { - "event": "The event source of the callback Warning: This is a generic event not a focus event.", - "nodeId": "The id of the node focused.", - "value": "of the focused node." - } - }, "onNodeSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { diff --git a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json index 95a635bd5a516..560c99a58071c 100644 --- a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json +++ b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json @@ -8,7 +8,7 @@ "label": { "description": "The label of the node." }, "nodeId": { "description": "The id of the node. Must be unique." }, "onFocus": { - "description": "This prop isn't supported. Use the onNodeFocus callback on the tree if you need to monitor a node's focus." + "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a node's focus." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." } diff --git a/docs/translations/api-docs/tree-view/tree-item/tree-item.json b/docs/translations/api-docs/tree-view/tree-item/tree-item.json index 7427881edcdbc..bf8989332a330 100644 --- a/docs/translations/api-docs/tree-view/tree-item/tree-item.json +++ b/docs/translations/api-docs/tree-view/tree-item/tree-item.json @@ -12,7 +12,7 @@ "label": { "description": "The tree node label." }, "nodeId": { "description": "The id of the node." }, "onFocus": { - "description": "This prop isn't supported. Use the onNodeFocus callback on the tree if you need to monitor a node's focus." + "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a node's focus." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." }, diff --git a/docs/translations/api-docs/tree-view/tree-view/tree-view.json b/docs/translations/api-docs/tree-view/tree-view/tree-view.json index 739dc69511641..fc049580836cf 100644 --- a/docs/translations/api-docs/tree-view/tree-view/tree-view.json +++ b/docs/translations/api-docs/tree-view/tree-view/tree-view.json @@ -32,6 +32,14 @@ "nodeIds": "The ids of the expanded nodes." } }, + "onItemFocus": { + "description": "Callback fired when tree items are focused.", + "typeDescriptions": { + "event": "The event source of the callback Warning: This is a generic event not a focus event.", + "itemId": "The id of the focused item.", + "value": "of the focused item." + } + }, "onNodeExpansionToggle": { "description": "Callback fired when a tree item is expanded or collapsed.", "typeDescriptions": { @@ -40,14 +48,6 @@ "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." } }, - "onNodeFocus": { - "description": "Callback fired when tree items are focused.", - "typeDescriptions": { - "event": "The event source of the callback Warning: This is a generic event not a focus event.", - "nodeId": "The id of the node focused.", - "value": "of the focused node." - } - }, "onNodeSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { diff --git a/packages/x-codemod/README.md b/packages/x-codemod/README.md index 021b7f57f9dbb..e3114b264f616 100644 --- a/packages/x-codemod/README.md +++ b/packages/x-codemod/README.md @@ -222,6 +222,7 @@ The list includes these transformers - [`rename-expansion-props`](#rename-expansion-props) - [`rename-selection-props`](#rename-selection-props) - [`replace-transition-props-by-slot`](#replace-transition-props-by-slot) +- [`rename-focus-callback`](#rename-focus-callback) #### `rename-tree-view-simple-tree-view` @@ -315,6 +316,17 @@ Replace the `TransitionComponent` and `TransitionProps` components with the `gro /> ``` +#### `rename-focus-callback` + +Replace the `onNodeFocus` callback with `onItemFocus`: + +```diff + +``` + ## v6.0.0 ### 🚀 `preset-safe` for v6.0.0 diff --git a/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/index.ts index 2490df48027e9..2158c78410d12 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/index.ts +++ b/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/index.ts @@ -1,5 +1,6 @@ import transformRenameExpansionProps from '../rename-expansion-props'; import transformRenameSelectionProps from '../rename-selection-props'; +import transformOnNodeFocus from '../rename-focus-callback'; import transformReplaceTransitionPropsBySlot from '../replace-transition-props-by-slot'; import transformRenameUseTreeItem from '../rename-use-tree-item'; import transformRenameTreeViewSimpleTreeView from '../rename-tree-view-simple-tree-view'; @@ -9,6 +10,7 @@ import { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { file.source = transformRenameExpansionProps(file, api, options); file.source = transformRenameSelectionProps(file, api, options); + file.source = transformOnNodeFocus(file, api, options); file.source = transformReplaceTransitionPropsBySlot(file, api, options); file.source = transformRenameUseTreeItem(file, api, options); file.source = transformRenameTreeViewSimpleTreeView(file, api, options); diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/actual.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/actual.spec.js new file mode 100644 index 0000000000000..45ac52567183d --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/actual.spec.js @@ -0,0 +1 @@ +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/expected.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/expected.spec.js new file mode 100644 index 0000000000000..3b20279d762de --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/expected.spec.js @@ -0,0 +1 @@ +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/index.ts new file mode 100644 index 0000000000000..27d578775c4f8 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/index.ts @@ -0,0 +1,18 @@ +import renameProps from '../../../util/renameProps'; +import type { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; + +export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { + const j = api.jscodeshift; + const root = j(file.source); + + const printOptions = options.printOptions; + + return renameProps({ + root, + componentNames: ['TreeView', 'SimpleTreeView'], + props: { + onNodeFocus: 'onItemFocus', + }, + j, + }).toSource(printOptions); +} diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/rename-focus-callback.test.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/rename-focus-callback.test.ts new file mode 100644 index 0000000000000..8fc3f69465835 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-focus-callback/rename-focus-callback.test.ts @@ -0,0 +1,27 @@ +import path from 'path'; +import { expect } from 'chai'; +import jscodeshift from 'jscodeshift'; +import transform from '.'; +import readFile from '../../../util/readFile'; + +function read(fileName) { + return readFile(path.join(__dirname, fileName)); +} + +describe('v7.0.0/tree-view', () => { + describe('rename-focus-callback', () => { + it('transforms props as needed', () => { + const actual = transform({ source: read('./actual.spec.js') }, { jscodeshift }, {}); + + const expected = read('./expected.spec.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + + it('should be idempotent', () => { + const actual = transform({ source: read('./expected.spec.js') }, { jscodeshift }, {}); + + const expected = read('./expected.spec.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + }); +}); diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx index d8fb2697b558a..84d8f504d4a20 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx @@ -229,6 +229,13 @@ RichTreeView.propTypes = { * @param {array} nodeIds The ids of the expanded nodes. */ onExpandedNodesChange: PropTypes.func, + /** + * Callback fired when tree items are focused. + * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. + * @param {string} itemId The id of the focused item. + * @param {string} value of the focused item. + */ + onItemFocus: PropTypes.func, /** * Callback fired when a tree item is expanded or collapsed. * @param {React.SyntheticEvent} event The event source of the callback. @@ -236,13 +243,6 @@ RichTreeView.propTypes = { * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. */ onNodeExpansionToggle: PropTypes.func, - /** - * Callback fired when tree items are focused. - * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. - * @param {string} nodeId The id of the node focused. - * @param {string} value of the focused node. - */ - onNodeFocus: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx index e0d149a6d5e71..dcc29f123cc9e 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx @@ -387,16 +387,16 @@ describe('', () => { expect(getByTestId('four')).toHaveVirtualFocus(); }); - describe('onNodeFocus', () => { + describe('onItemFocus', () => { it('should be called when node is focused', () => { const focusSpy = spy(); const { getByRole } = render( - + , ); - // First node receives focus when tree focused + // First item receives focus when tree focused act(() => { getByRole('tree').focus(); }); @@ -444,10 +444,10 @@ describe('', () => { describe('useTreeViewFocus', () => { it('should focus the selected item when the tree is focused', () => { - const onNodeFocus = spy(); + const onItemFocus = spy(); const { getByRole } = render( - + , @@ -457,14 +457,14 @@ describe('', () => { getByRole('tree').focus(); }); - expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + expect(onItemFocus.lastCall.lastArg).to.equal('2'); }); it('should focus the selected item when the tree is focused (multi select)', () => { - const onNodeFocus = spy(); + const onItemFocus = spy(); const { getByRole } = render( - + , @@ -474,14 +474,14 @@ describe('', () => { getByRole('tree').focus(); }); - expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + expect(onItemFocus.lastCall.lastArg).to.equal('2'); }); it('should focus the first visible selected item when the tree is focused (multi select)', () => { - const onNodeFocus = spy(); + const onItemFocus = spy(); const { getByRole } = render( - + @@ -493,14 +493,14 @@ describe('', () => { getByRole('tree').focus(); }); - expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + expect(onItemFocus.lastCall.lastArg).to.equal('2'); }); it('should focus the first item if the selected item is not visible', () => { - const onNodeFocus = spy(); + const onItemFocus = spy(); const { getByRole } = render( - + @@ -512,14 +512,14 @@ describe('', () => { getByRole('tree').focus(); }); - expect(onNodeFocus.lastCall.lastArg).to.equal('1'); + expect(onItemFocus.lastCall.lastArg).to.equal('1'); }); it('should focus the first item if no selected item is visible (multi select)', () => { - const onNodeFocus = spy(); + const onItemFocus = spy(); const { getByRole } = render( - + @@ -531,17 +531,17 @@ describe('', () => { getByRole('tree').focus(); }); - expect(onNodeFocus.lastCall.lastArg).to.equal('1'); + expect(onItemFocus.lastCall.lastArg).to.equal('1'); }); it('should focus specific node using `apiRef`', () => { let apiRef: SimpleTreeViewApiRef; - const onNodeFocus = spy(); + const onItemFocus = spy(); function TestCase() { apiRef = useTreeViewApiRef(); return ( - + @@ -557,17 +557,17 @@ describe('', () => { }); expect(getByRole('tree')).toHaveFocus(); - expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + expect(onItemFocus.lastCall.lastArg).to.equal('2'); }); it('should not focus node if parent is collapsed', () => { let apiRef: SimpleTreeViewApiRef; - const onNodeFocus = spy(); + const onItemFocus = spy(); function TestCase() { apiRef = useTreeViewApiRef(); return ( - + diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx index 55c1be134b82a..c72e1f7ac3e00 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx @@ -170,6 +170,13 @@ SimpleTreeView.propTypes = { * @param {array} nodeIds The ids of the expanded nodes. */ onExpandedNodesChange: PropTypes.func, + /** + * Callback fired when tree items are focused. + * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. + * @param {string} itemId The id of the focused item. + * @param {string} value of the focused item. + */ + onItemFocus: PropTypes.func, /** * Callback fired when a tree item is expanded or collapsed. * @param {React.SyntheticEvent} event The event source of the callback. @@ -177,13 +184,6 @@ SimpleTreeView.propTypes = { * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. */ onNodeExpansionToggle: PropTypes.func, - /** - * Callback fired when tree items are focused. - * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. - * @param {string} nodeId The id of the node focused. - * @param {string} value of the focused node. - */ - onNodeFocus: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index b9b4c8e57e9ae..14b442ca9446d 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -2086,7 +2086,7 @@ describe('', () => { it('should prevent focus by mouse', () => { const focusSpy = spy(); const { getByText } = render( - + , @@ -2164,7 +2164,7 @@ describe('', () => { it('should prevent focus by mouse', () => { const focusSpy = spy(); const { getByText } = render( - + , diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx index d2b2db6741e91..8f9c3bb622334 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx @@ -385,7 +385,7 @@ TreeItem.propTypes = { nodeId: PropTypes.string.isRequired, /** * This prop isn't supported. - * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. */ onFocus: unsupportedProp, /** diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts index dbbc8b968b13d..1e7307d200e48 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts +++ b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts @@ -76,7 +76,7 @@ export interface TreeItemProps extends Omit, disabled?: boolean; /** * This prop isn't supported. - * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. */ onFocus?: null; /** diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx index 9d9c4acd13a78..92b0ba9f4d9bc 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx @@ -300,7 +300,7 @@ TreeItem2.propTypes = { nodeId: PropTypes.string.isRequired, /** * This prop isn't supported. - * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. */ onFocus: unsupportedProp, /** diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts index 5c6711d72133c..ddf3d38d3b4e7 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts @@ -61,7 +61,7 @@ export interface TreeItem2Props slotProps?: TreeItem2SlotProps; /** * This prop isn't supported. - * Use the `onNodeFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. */ onFocus?: null; } diff --git a/packages/x-tree-view/src/TreeView/TreeView.tsx b/packages/x-tree-view/src/TreeView/TreeView.tsx index c6d7982da8b64..a5c3f5350afe7 100644 --- a/packages/x-tree-view/src/TreeView/TreeView.tsx +++ b/packages/x-tree-view/src/TreeView/TreeView.tsx @@ -147,6 +147,13 @@ TreeView.propTypes = { * @param {array} nodeIds The ids of the expanded nodes. */ onExpandedNodesChange: PropTypes.func, + /** + * Callback fired when tree items are focused. + * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. + * @param {string} itemId The id of the focused item. + * @param {string} value of the focused item. + */ + onItemFocus: PropTypes.func, /** * Callback fired when a tree item is expanded or collapsed. * @param {React.SyntheticEvent} event The event source of the callback. @@ -154,13 +161,6 @@ TreeView.propTypes = { * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. */ onNodeExpansionToggle: PropTypes.func, - /** - * Callback fired when tree items are focused. - * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. - * @param {string} nodeId The id of the node focused. - * @param {string} value of the focused node. - */ - onNodeFocus: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts index 7dda6e8cd4b8d..c238b4d457275 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts @@ -46,8 +46,8 @@ export const useTreeViewFocus: TreeViewPlugin = ({ instance.focusRoot(); } setFocusedNodeId(nodeId); - if (params.onNodeFocus) { - params.onNodeFocus(event, nodeId); + if (params.onItemFocus) { + params.onItemFocus(event, nodeId); } } }); @@ -65,8 +65,8 @@ export const useTreeViewFocus: TreeViewPlugin = ({ } setFocusedNodeId(nodeToFocusId); - if (params.onNodeFocus) { - params.onNodeFocus(event, nodeToFocusId); + if (params.onItemFocus) { + params.onItemFocus(event, nodeToFocusId); } }); @@ -129,5 +129,5 @@ export const useTreeViewFocus: TreeViewPlugin = ({ useTreeViewFocus.getInitialState = () => ({ focusedNodeId: null }); useTreeViewFocus.params = { - onNodeFocus: true, + onItemFocus: true, }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts index eaa26c0b3585b..b4ed569f1520f 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts @@ -18,10 +18,10 @@ export interface UseTreeViewFocusParameters { /** * Callback fired when tree items are focused. * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. - * @param {string} nodeId The id of the node focused. - * @param {string} value of the focused node. + * @param {string} itemId The id of the focused item. + * @param {string} value of the focused item. */ - onNodeFocus?: (event: React.SyntheticEvent, nodeId: string) => void; + onItemFocus?: (event: React.SyntheticEvent, itemId: string) => void; } export type UseTreeViewFocusDefaultizedParameters = UseTreeViewFocusParameters; From f5b4294626743e1aa87dc824f0c1f79bbb61bc42 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 12 Mar 2024 15:14:11 +0200 Subject: [PATCH 29/49] [docs] Add missing luxon `Info` import (#12427) --- CHANGELOG.md | 2 +- docs/data/date-pickers/adapters-locale/adapters-locale.md | 2 +- .../data/migration/migration-pickers-v6/migration-pickers-v6.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db25637ddd28d..3fd14a3acb4f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1731,7 +1731,7 @@ Same changes as in `@mui/x-data-grid-pro@7.0.0-alpha.3`, plus: The Firefox browser currently does not support this behavior because the [getWeekInfo](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getWeekInfo) API is not yet implemented. ```ts - import { Settings } from 'luxon'; + import { Settings, Info } from 'luxon'; Settings.defaultWeekSettings = { firstDay: 1, diff --git a/docs/data/date-pickers/adapters-locale/adapters-locale.md b/docs/data/date-pickers/adapters-locale/adapters-locale.md index c8fd2ed683dda..6bf74e5efefaf 100644 --- a/docs/data/date-pickers/adapters-locale/adapters-locale.md +++ b/docs/data/date-pickers/adapters-locale/adapters-locale.md @@ -317,7 +317,7 @@ const customEnLocale: Locale = { For `luxon`, use the `Settings.defaultWeekSettings` object: ```ts -import { Settings } from 'luxon'; +import { Settings, Info } from 'luxon'; Settings.defaultWeekSettings = { // Sunday = 7, Monday = 1. diff --git a/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md index 5189f68a1a484..4358620ff85ec 100644 --- a/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md +++ b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md @@ -430,7 +430,7 @@ If you want to keep the start of the week on Monday even if your locale says oth You can hardcode the week settings as follows: ```ts -import { Settings } from 'luxon'; +import { Settings, Info } from 'luxon'; Settings.defaultWeekSettings = { firstDay: 1, From 88d5387e4d47665d53afd9d7a92c3b3ee049561b Mon Sep 17 00:00:00 2001 From: Rudomanenko Volodymyr Date: Tue, 12 Mar 2024 16:26:23 +0200 Subject: [PATCH 30/49] [DataGridPro] Add `inputRef` to the props passed to `colDef.renderHeaderFilter` (#12328) Co-authored-by: Bilal Shafi --- .../filtering/CustomHeaderFilterSingleDataGridPro.tsx | 6 +++--- docs/pages/x/api/data-grid/grid-actions-col-def.md | 2 +- docs/pages/x/api/data-grid/grid-col-def.md | 2 +- docs/pages/x/api/data-grid/grid-single-select-col-def.md | 2 +- .../src/components/headerFiltering/GridHeaderFilterCell.tsx | 6 +++++- packages/x-data-grid-pro/src/typeOverloads/modules.ts | 6 +++--- scripts/x-data-grid-premium.exports.json | 1 + scripts/x-data-grid-pro.exports.json | 1 + 8 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/data/data-grid/filtering/CustomHeaderFilterSingleDataGridPro.tsx b/docs/data/data-grid/filtering/CustomHeaderFilterSingleDataGridPro.tsx index 6c737e3f19537..3f591c64efe94 100644 --- a/docs/data/data-grid/filtering/CustomHeaderFilterSingleDataGridPro.tsx +++ b/docs/data/data-grid/filtering/CustomHeaderFilterSingleDataGridPro.tsx @@ -5,7 +5,7 @@ import Select, { SelectChangeEvent } from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import { DataGridPro, - GridHeaderFilterCellProps, + GridRenderHeaderFilterProps, gridFilterModelSelector, useGridSelector, useGridApiContext, @@ -14,7 +14,7 @@ import { useDemoData } from '@mui/x-data-grid-generator'; const getDefaultFilter = (field: string) => ({ field, operator: 'is' }); -function AdminFilter(props: GridHeaderFilterCellProps) { +function AdminFilter(props: GridRenderHeaderFilterProps) { const { colDef } = props; const apiRef = useGridApiContext(); const filterModel = useGridSelector(apiRef, gridFilterModelSelector); @@ -76,7 +76,7 @@ export default function CustomHeaderFilterSingleDataGridPro() { return { ...colDef, width: 200, - renderHeaderFilter: (params: GridHeaderFilterCellProps) => ( + renderHeaderFilter: (params: GridRenderHeaderFilterProps) => ( ), }; diff --git a/docs/pages/x/api/data-grid/grid-actions-col-def.md b/docs/pages/x/api/data-grid/grid-actions-col-def.md index dfe50212922c2..e1f9daf908f4f 100644 --- a/docs/pages/x/api/data-grid/grid-actions-col-def.md +++ b/docs/pages/x/api/data-grid/grid-actions-col-def.md @@ -58,7 +58,7 @@ import { GridActionsColDef } from '@mui/x-data-grid'; | renderCell? | (params: GridRenderCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered as cell for this column. | | renderEditCell? | (params: GridRenderEditCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams<R, V, F>) => React.ReactNode | | Allows to render a component in the column header cell. | -| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridHeaderFilterCellProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | +| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridRenderHeaderFilterProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | | resizable? | boolean | true | If `true`, the column is resizable. | | sortable? | boolean | true | If `true`, the column is sortable. | | sortComparator? | GridComparatorFn<V> | | A comparator function used to sort rows. | diff --git a/docs/pages/x/api/data-grid/grid-col-def.md b/docs/pages/x/api/data-grid/grid-col-def.md index 544eb9d138f78..b8927910ff2e4 100644 --- a/docs/pages/x/api/data-grid/grid-col-def.md +++ b/docs/pages/x/api/data-grid/grid-col-def.md @@ -57,7 +57,7 @@ import { GridColDef } from '@mui/x-data-grid'; | renderCell? | (params: GridRenderCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered as cell for this column. | | renderEditCell? | (params: GridRenderEditCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams<R, V, F>) => React.ReactNode | | Allows to render a component in the column header cell. | -| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridHeaderFilterCellProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | +| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridRenderHeaderFilterProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | | resizable? | boolean | true | If `true`, the column is resizable. | | sortable? | boolean | true | If `true`, the column is sortable. | | sortComparator? | GridComparatorFn<V> | | A comparator function used to sort rows. | diff --git a/docs/pages/x/api/data-grid/grid-single-select-col-def.md b/docs/pages/x/api/data-grid/grid-single-select-col-def.md index 0ca01bd1af9ab..b42076119a95d 100644 --- a/docs/pages/x/api/data-grid/grid-single-select-col-def.md +++ b/docs/pages/x/api/data-grid/grid-single-select-col-def.md @@ -59,7 +59,7 @@ import { GridSingleSelectColDef } from '@mui/x-data-grid'; | renderCell? | (params: GridRenderCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered as cell for this column. | | renderEditCell? | (params: GridRenderEditCellParams<R, V, F>) => React.ReactNode | | Allows to override the component rendered in edit cell mode for this column. | | renderHeader? | (params: GridColumnHeaderParams<R, V, F>) => React.ReactNode | | Allows to render a component in the column header cell. | -| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridHeaderFilterCellProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | +| renderHeaderFilter? [](/x/introduction/licensing/#pro-plan) | (params: GridRenderHeaderFilterProps) => React.ReactNode | | Allows to render a component in the column header filter cell. | | resizable? | boolean | true | If `true`, the column is resizable. | | sortable? | boolean | true | If `true`, the column is sortable. | | sortComparator? | GridComparatorFn<V> | | A comparator function used to sort rows. | diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index e2d8d8de81c8c..9f7d6e6c3d884 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -30,6 +30,10 @@ import { DataGridProProcessedProps } from '../../models/dataGridProProps'; import { GridHeaderFilterMenuContainer } from './GridHeaderFilterMenuContainer'; import { GridHeaderFilterClearButton } from './GridHeaderFilterClearButton'; +export interface GridRenderHeaderFilterProps extends GridHeaderFilterCellProps { + inputRef: React.RefObject; +} + export interface GridHeaderFilterCellProps extends Pick { colIndex: number; height: number; @@ -133,7 +137,7 @@ const GridHeaderFilterCell = React.forwardRef { diff --git a/packages/x-data-grid-pro/src/typeOverloads/modules.ts b/packages/x-data-grid-pro/src/typeOverloads/modules.ts index a2fcde4f2366b..1d0fe7a306e60 100644 --- a/packages/x-data-grid-pro/src/typeOverloads/modules.ts +++ b/packages/x-data-grid-pro/src/typeOverloads/modules.ts @@ -4,7 +4,7 @@ import type { GridRowOrderChangeParams, GridFetchRowsParams, } from '../models'; -import type { GridHeaderFilterCellProps } from '../components/headerFiltering/GridHeaderFilterCell'; +import type { GridRenderHeaderFilterProps } from '../components/headerFiltering/GridHeaderFilterCell'; import type { GridColumnPinningInternalCache } from '../hooks/features/columnPinning/gridColumnPinningInterface'; import type { GridCanBeReorderedPreProcessingContext } from '../hooks/features/columnReorder/columnReorderInterfaces'; import { GridRowPinningInternalCache } from '../hooks/features/rowPinning/gridRowPinningInterface'; @@ -12,10 +12,10 @@ import { GridRowPinningInternalCache } from '../hooks/features/rowPinning/gridRo export interface GridColDefPro { /** * Allows to render a component in the column header filter cell. - * @param {GridHeaderFilterCellProps} params Object containing parameters for the renderer. + * @param {GridRenderHeaderFilterProps} params Object containing parameters for the renderer and `inputRef`. * @returns {React.ReactNode} The element to be rendered. */ - renderHeaderFilter?: (params: GridHeaderFilterCellProps) => React.ReactNode; + renderHeaderFilter?: (params: GridRenderHeaderFilterProps) => React.ReactNode; } export interface GridControlledStateEventLookupPro { diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 96ed01c5e2011..e2297f6f75672 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -458,6 +458,7 @@ { "name": "GridRenderContextProps", "kind": "TypeAlias" }, { "name": "gridRenderContextSelector", "kind": "Variable" }, { "name": "GridRenderEditCellParams", "kind": "Interface" }, + { "name": "GridRenderHeaderFilterProps", "kind": "Interface" }, { "name": "GridRenderPaginationProps", "kind": "Interface" }, { "name": "GridRenderRowProps", "kind": "Interface" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index 7bb972eb7e633..ab58d533526b5 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -419,6 +419,7 @@ { "name": "GridRenderContextProps", "kind": "TypeAlias" }, { "name": "gridRenderContextSelector", "kind": "Variable" }, { "name": "GridRenderEditCellParams", "kind": "Interface" }, + { "name": "GridRenderHeaderFilterProps", "kind": "Interface" }, { "name": "GridRenderPaginationProps", "kind": "Interface" }, { "name": "GridRenderRowProps", "kind": "Interface" }, { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, From b721c3d4c0e16a12e9c2bc68a9542b142a7b9ee0 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Tue, 12 Mar 2024 16:30:49 +0100 Subject: [PATCH 31/49] [DataGrid] Make column resizing and autosizing available in Community plan (#12420) --- .../column-dimensions/ColumnAutosizing.js | 9 +- .../column-dimensions/ColumnAutosizing.tsx | 11 +- .../ColumnAutosizingAsync.js | 3 +- .../ColumnAutosizingAsync.tsx | 5 +- .../ColumnAutosizingDynamicRowHeight.js | 4 +- .../ColumnAutosizingDynamicRowHeight.tsx | 6 +- .../column-dimensions/ColumnSizingGrid.js | 4 +- .../column-dimensions/ColumnSizingGrid.tsx | 4 +- .../ColumnSizingGrid.tsx.preview | 2 +- .../column-dimensions/column-dimensions.md | 14 +- docs/data/data-grid/overview/overview.md | 3 +- docs/pages/x/api/data-grid/data-grid.json | 23 +++ docs/pages/x/api/data-grid/grid-api.md | 2 +- .../data-grid/data-grid/data-grid.json | 24 +++ .../src/models/gridApiPremium.ts | 2 - .../DataGridPro/useDataGridProComponent.tsx | 6 +- .../useGridColumnPinningPreProcessors.ts | 6 + .../columnResize/columnResizeSelector.ts | 9 -- .../src/hooks/features/index.ts | 1 - .../x-data-grid-pro/src/internals/index.ts | 4 - .../src/models/dataGridProProps.ts | 29 ---- .../x-data-grid-pro/src/models/gridApiPro.ts | 2 - .../src/models/gridStatePro.ts | 2 - .../x-data-grid/src/DataGrid/DataGrid.tsx | 39 +++++ .../src/DataGrid/useDataGridComponent.tsx | 6 + .../src/DataGrid/useDataGridProps.ts | 3 +- .../columnHeaders/GridColumnGroupHeader.tsx | 2 +- .../pipeProcessing/gridPipeProcessingApi.ts | 6 +- .../columnResize/columnResizeSelector.ts | 9 ++ .../columnResize/columnResizeState.ts | 0 .../columnResize/gridColumnResizeApi.ts | 2 +- .../src/hooks/features/columnResize/index.ts | 0 .../columnResize/useGridColumnResize.tsx | 76 +++++----- .../x-data-grid/src/hooks/features/index.ts | 1 + packages/x-data-grid/src/internals/index.ts | 4 + .../src/models/api/gridApiCommon.ts | 4 +- .../src/models/gridStateCommunity.ts | 2 + .../src/models/props/DataGridProps.ts | 30 +++- packages/x-data-grid/src/utils/domUtils.ts | 139 +++++++++++++++++- scripts/x-data-grid.exports.json | 6 + 40 files changed, 377 insertions(+), 127 deletions(-) delete mode 100644 packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeSelector.ts create mode 100644 packages/x-data-grid/src/hooks/features/columnResize/columnResizeSelector.ts rename packages/{x-data-grid-pro => x-data-grid}/src/hooks/features/columnResize/columnResizeState.ts (100%) rename packages/{x-data-grid-pro => x-data-grid}/src/hooks/features/columnResize/gridColumnResizeApi.ts (94%) rename packages/{x-data-grid-pro => x-data-grid}/src/hooks/features/columnResize/index.ts (100%) rename packages/{x-data-grid-pro => x-data-grid}/src/hooks/features/columnResize/useGridColumnResize.tsx (93%) diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizing.js b/docs/data/data-grid/column-dimensions/ColumnAutosizing.js index 3d46d27bbd9b0..05f0c627be1f0 100644 --- a/docs/data/data-grid/column-dimensions/ColumnAutosizing.js +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizing.js @@ -5,8 +5,11 @@ import FormControlLabel from '@mui/material/FormControlLabel'; import Rating from '@mui/material/Rating'; import Stack from '@mui/material/Stack'; import TextField from '@mui/material/TextField'; -import { useGridApiRef } from '@mui/x-data-grid'; -import { DataGridPro, DEFAULT_GRID_AUTOSIZE_OPTIONS } from '@mui/x-data-grid-pro'; +import { + DataGrid, + useGridApiRef, + DEFAULT_GRID_AUTOSIZE_OPTIONS, +} from '@mui/x-data-grid'; import { randomRating, randomTraderName } from '@mui/x-data-grid-generator'; function renderRating(params) { @@ -127,7 +130,7 @@ export default function ColumnAutosizing() { />
- (); + const apiRef = useGridApiRef(); const data = useData(100); const [includeHeaders, setIncludeHeaders] = React.useState( @@ -130,7 +129,7 @@ export default function ColumnAutosizing() { />
- { } export default function ColumnAutosizingAsync() { - const apiRef = useGridApiRef(); + const apiRef = useGridApiRef(); const [isLoading, setIsLoading] = React.useState(false); const [rows] = React.useState([]); diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js index 4101807c03e92..fd239b3db5a2e 100644 --- a/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js @@ -1,6 +1,6 @@ import * as React from 'react'; import Button from '@mui/material/Button'; -import { DataGridPro, gridClasses, useGridApiRef } from '@mui/x-data-grid-pro'; +import { DataGrid, gridClasses, useGridApiRef } from '@mui/x-data-grid'; import { randomInt, randomArrayItem } from '@mui/x-data-grid-generator'; const lines = [ @@ -46,7 +46,7 @@ export default function ColumnAutosizingDynamicRowHeight() { Autosize Columns
-
- - - ](/x/introduction/licensing/#pro-plan 'Pro plan') +## Resizing -By default, `DataGridPro` allows all columns to be resized by dragging the right portion of the column separator. +By default, Data Grid allows all columns to be resized by dragging the right portion of the column separator. To prevent the resizing of a column, set `resizable: false` in the `GridColDef`. Alternatively, to disable all columns resize, set the prop `disableColumnResize={true}`. @@ -58,9 +58,9 @@ To capture changes in the width of a column there are two callbacks that are cal - `onColumnResize`: Called while a column is being resized. - `onColumnWidthChange`: Called after the width of a column is changed, but not during resizing. -## Autosizing [](/x/introduction/licensing/#pro-plan 'Pro plan') +## Autosizing -`DataGridPro` allows to autosize the columns' dimensions based on their content. Autosizing is enabled by default. To turn it off, pass the `disableAutosize` prop to the datagrid. +Data Grid allows to autosize the columns' dimensions based on their content. Autosizing is enabled by default. To turn it off, pass the `disableAutosize` prop to the Data Grid. Autosizing can be used by one of the following methods: @@ -75,7 +75,7 @@ Note that for the separator double-click method, the `autosizeOptions.columns` w In all the cases, the `colDef.minWidth` and `colDef.maxWidth` options will be respected. ```tsx -](/x/introduction/licensing/#pro-plan 'Pro plan') +### Autosizing asynchronously The `autosizeColumns` method from the `apiRef` can be used as well to adjust the column size on specified events, for example when receiving row data from the server. diff --git a/docs/data/data-grid/overview/overview.md b/docs/data/data-grid/overview/overview.md index fbee25d014715..1d573d2e0e0d6 100644 --- a/docs/data/data-grid/overview/overview.md +++ b/docs/data/data-grid/overview/overview.md @@ -74,6 +74,8 @@ Please see [the Licensing page](/x/introduction/licensing/) for details. - Built with and exclusively for React ⚛️ - High performance 🚀 - [Column groups](/x/react-data-grid/column-groups/) +- [Column resizing](/x/react-data-grid/column-dimensions/#resizing) +- [Column autosizing](/x/react-data-grid/column-dimensions/#autosizing) - [Filtering](/x/react-data-grid/filtering/), [multi-filters](/x/react-data-grid/filtering/multi-filters/) , and [header filters](/x/react-data-grid/filtering/header-filters/) - [Pagination](/x/react-data-grid/pagination/) - [Row & Cell editing](/x/react-data-grid/editing/) @@ -86,7 +88,6 @@ Please see [the Licensing page](/x/introduction/licensing/) for details. - [Excel export](/x/react-data-grid/export/#excel-export) - [Tree data](/x/react-data-grid/tree-data/) - [Master detail](/x/react-data-grid/master-detail/) -- [Resizable columns](/x/react-data-grid/column-dimensions/#resizing) - [100% customizable](/x/react-data-grid/style/) - Server-side data - [Column hiding](/x/react-data-grid/column-visibility/) diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 33d8011739096..338574d351b13 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -13,6 +13,13 @@ "aria-labelledby": { "type": { "name": "string" } }, "autoHeight": { "type": { "name": "bool" }, "default": "false" }, "autoPageSize": { "type": { "name": "bool" }, "default": "false" }, + "autosizeOnMount": { "type": { "name": "bool" }, "default": "false" }, + "autosizeOptions": { + "type": { + "name": "shape", + "description": "{ columns?: Array<string>, expand?: bool, includeHeaders?: bool, includeOutliers?: bool, outliersFactor?: number }" + } + }, "cellModesModel": { "type": { "name": "object" } }, "checkboxSelection": { "type": { "name": "bool" }, "default": "false" }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, @@ -28,8 +35,10 @@ }, "default": "\"standard\"" }, + "disableAutosize": { "type": { "name": "bool" }, "default": "false" }, "disableColumnFilter": { "type": { "name": "bool" }, "default": "false" }, "disableColumnMenu": { "type": { "name": "bool" }, "default": "false" }, + "disableColumnResize": { "type": { "name": "bool" }, "default": "false" }, "disableColumnSelector": { "type": { "name": "bool" }, "default": "false" }, "disableColumnSorting": { "type": { "name": "bool" }, "default": "false" }, "disableDensitySelector": { "type": { "name": "bool" }, "default": "false" }, @@ -248,6 +257,13 @@ "describedArgs": ["params", "event", "details"] } }, + "onColumnResize": { + "type": { "name": "func" }, + "signature": { + "type": "function(params: GridColumnResizeParams, event: MuiEvent, details: GridCallbackDetails) => void", + "describedArgs": ["params", "event", "details"] + } + }, "onColumnVisibilityModelChange": { "type": { "name": "func" }, "signature": { @@ -255,6 +271,13 @@ "describedArgs": ["model", "details"] } }, + "onColumnWidthChange": { + "type": { "name": "func" }, + "signature": { + "type": "function(params: GridColumnResizeParams, event: MuiEvent, details: GridCallbackDetails) => void", + "describedArgs": ["params", "event", "details"] + } + }, "onFilterModelChange": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/grid-api.md b/docs/pages/x/api/data-grid/grid-api.md index 629601cd72985..cf6a2bdbf7de8 100644 --- a/docs/pages/x/api/data-grid/grid-api.md +++ b/docs/pages/x/api/data-grid/grid-api.md @@ -27,7 +27,7 @@ import { GridApi } from '@mui/x-data-grid'; | :------------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | addRowGroupingCriteria [](/x/introduction/licensing/#premium-plan) | (groupingCriteriaField: string, groupingIndex?: number) => void | Adds the field to the row grouping model. | | applySorting | () => void | Applies the current sort model to the rows. | -| autosizeColumns [](/x/introduction/licensing/#pro-plan) | (options?: GridAutosizeOptions) => Promise<void> | Auto-size the columns of the grid based on the cells' content and the space available. | +| autosizeColumns | (options?: GridAutosizeOptions) => Promise<void> | Auto-size the columns of the grid based on the cells' content and the space available. | | deleteFilterItem | (item: GridFilterItem) => void | Deletes a [GridFilterItem](/x/api/data-grid/grid-filter-item/). | | exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. | | exportDataAsExcel [](/x/introduction/licensing/#premium-plan) | (options?: GridExcelExportOptions) => Promise<void> | Downloads and exports an Excel file of the grid's data. | diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index 7abcdced7d428..cdbd61fc7c128 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -14,6 +14,10 @@ "autoPageSize": { "description": "If true, the pageSize is calculated according to the container size and the max number of rows to avoid rendering a vertical scroll bar." }, + "autosizeOnMount": { + "description": "If true, columns are autosized after the datagrid is mounted." + }, + "autosizeOptions": { "description": "The options for autosize when user-initiated." }, "cellModesModel": { "description": "Controls the modes of the cells." }, "checkboxSelection": { "description": "If true, the Data Grid will display an extra column with checkboxes for selecting rows." @@ -38,8 +42,12 @@ "description": "Set the column visibility model of the Data Grid. If defined, the Data Grid will ignore the hide property in GridColDef." }, "density": { "description": "Set the density of the Data Grid." }, + "disableAutosize": { + "description": "If true, column autosizing on header separator double-click is disabled." + }, "disableColumnFilter": { "description": "If true, column filters are disabled." }, "disableColumnMenu": { "description": "If true, the column menu is disabled." }, + "disableColumnResize": { "description": "If true, resizing columns is disabled." }, "disableColumnSelector": { "description": "If true, hiding/showing columns is disabled." }, @@ -264,6 +272,14 @@ "details": "Additional details for this callback." } }, + "onColumnResize": { + "description": "Callback fired while a column is being resized.", + "typeDescriptions": { + "params": "With all properties from GridColumnResizeParams.", + "event": "The event object.", + "details": "Additional details for this callback." + } + }, "onColumnVisibilityModelChange": { "description": "Callback fired when the column visibility model changes.", "typeDescriptions": { @@ -271,6 +287,14 @@ "details": "Additional details for this callback." } }, + "onColumnWidthChange": { + "description": "Callback fired when the width of a column is changed.", + "typeDescriptions": { + "params": "With all properties from GridColumnResizeParams.", + "event": "The event object.", + "details": "Additional details for this callback." + } + }, "onFilterModelChange": { "description": "Callback fired when the Filter model changes before the filters are applied.", "typeDescriptions": { diff --git a/packages/x-data-grid-premium/src/models/gridApiPremium.ts b/packages/x-data-grid-premium/src/models/gridApiPremium.ts index e79561d733b99..c9a1c7f911a98 100644 --- a/packages/x-data-grid-premium/src/models/gridApiPremium.ts +++ b/packages/x-data-grid-premium/src/models/gridApiPremium.ts @@ -2,7 +2,6 @@ import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals'; import { GridApiCommon, GridColumnPinningApi, - GridColumnResizeApi, GridDetailPanelApi, GridDetailPanelPrivateApi, GridRowPinningApi, @@ -23,7 +22,6 @@ export interface GridApiPremium extends GridApiCommon, GridRowProApi, GridColumnPinningApi, - GridColumnResizeApi, GridDetailPanelApi, GridRowGroupingApi, GridExcelExportApi, diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index b89f14923492f..3357ebbaf7ebd 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -45,6 +45,8 @@ import { useGridHeaderFiltering, virtualizationStateInitializer, useGridVirtualization, + useGridColumnResize, + columnResizeStateInitializer, } from '@mui/x-data-grid/internals'; import { GridApiPro, GridPrivateApiPro } from '../models/gridApiPro'; import { DataGridProProcessedProps } from '../models/dataGridProProps'; @@ -54,10 +56,6 @@ import { useGridColumnReorder, columnReorderStateInitializer, } from '../hooks/features/columnReorder/useGridColumnReorder'; -import { - useGridColumnResize, - columnResizeStateInitializer, -} from '../hooks/features/columnResize/useGridColumnResize'; import { useGridTreeData } from '../hooks/features/treeData/useGridTreeData'; import { useGridTreeDataPreProcessors } from '../hooks/features/treeData/useGridTreeDataPreProcessors'; import { diff --git a/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinningPreProcessors.ts b/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinningPreProcessors.ts index 6982ac82e130b..310644dcd678f 100644 --- a/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinningPreProcessors.ts +++ b/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinningPreProcessors.ts @@ -135,4 +135,10 @@ export const useGridColumnPinningPreProcessors = ( ); useGridRegisterPipeProcessor(apiRef, 'hydrateColumns', reorderPinnedColumns); + + const isColumnPinned = React.useCallback>( + (initialValue, field) => apiRef.current.isColumnPinned(field), + [apiRef], + ); + useGridRegisterPipeProcessor(apiRef, 'isColumnPinned', isColumnPinned); }; diff --git a/packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeSelector.ts b/packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeSelector.ts deleted file mode 100644 index c5978d6ddd81e..0000000000000 --- a/packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeSelector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createSelector } from '@mui/x-data-grid/internals'; -import { GridStatePro } from '../../../models/gridStatePro'; - -export const gridColumnResizeSelector = (state: GridStatePro) => state.columnResize; - -export const gridResizingColumnFieldSelector = createSelector( - gridColumnResizeSelector, - (columnResize) => columnResize.resizingColumnField, -); diff --git a/packages/x-data-grid-pro/src/hooks/features/index.ts b/packages/x-data-grid-pro/src/hooks/features/index.ts index 2ad8b18e908e2..ea390a65dc61d 100644 --- a/packages/x-data-grid-pro/src/hooks/features/index.ts +++ b/packages/x-data-grid-pro/src/hooks/features/index.ts @@ -1,7 +1,6 @@ // Only export the variable and types that should be publicly exposed and re-exported from `@mui/x-data-grid-pro` export * from './columnPinning'; export * from './columnReorder'; -export * from './columnResize'; export * from './rowReorder'; export * from './treeData'; export * from './detailPanel'; diff --git a/packages/x-data-grid-pro/src/internals/index.ts b/packages/x-data-grid-pro/src/internals/index.ts index 3f8352dd07ed3..83f507d992b1c 100644 --- a/packages/x-data-grid-pro/src/internals/index.ts +++ b/packages/x-data-grid-pro/src/internals/index.ts @@ -6,10 +6,6 @@ export { DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS } from '../constants/dataGridPro // eslint-disable-next-line import/export export { useGridColumnHeaders } from '../hooks/features/columnHeaders/useGridColumnHeaders'; -export { - useGridColumnResize, - columnResizeStateInitializer, -} from '../hooks/features/columnResize/useGridColumnResize'; export { useGridColumnPinning, columnPinningStateInitializer, diff --git a/packages/x-data-grid-pro/src/models/dataGridProProps.ts b/packages/x-data-grid-pro/src/models/dataGridProProps.ts index f6d9c4ed9d7b5..de56c3be465ec 100644 --- a/packages/x-data-grid-pro/src/models/dataGridProProps.ts +++ b/packages/x-data-grid-pro/src/models/dataGridProProps.ts @@ -25,7 +25,6 @@ import { import { GridInitialStatePro } from './gridStatePro'; import { GridProSlotsComponent } from './gridProSlotsComponent'; import type { GridProSlotProps } from './gridProSlotProps'; -import type { GridAutosizeOptions } from '../hooks'; export interface GridExperimentalProFeatures extends GridExperimentalFeatures {} @@ -92,16 +91,6 @@ export interface DataGridProPropsWithDefaultValue extends DataGridPropsWithDefau * @returns {boolean} A boolean indicating if the group is expanded. */ isGroupExpandedByDefault?: (node: GridGroupNode) => boolean; - /** - * If `true`, columns are autosized after the datagrid is mounted. - * @default false - */ - autosizeOnMount: boolean; - /** - * If `true`, column autosizing on header separator double-click is disabled. - * @default false - */ - disableAutosize: boolean; /** * If `true`, the column pinning is disabled. * @default false @@ -158,10 +147,6 @@ export interface DataGridProPropsWithoutDefaultValue; - /** - * The options for autosize when user-initiated. - */ - autosizeOptions?: GridAutosizeOptions; /** * The initial state of the DataGridPro. * The data in it will be set in the state on initialization but will not be controlled. @@ -182,20 +167,6 @@ export interface DataGridProPropsWithoutDefaultValue string[]; - /** - * Callback fired while a column is being resized. - * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. - * @param {MuiEvent} event The event object. - * @param {GridCallbackDetails} details Additional details for this callback. - */ - onColumnResize?: GridEventListener<'columnResize'>; - /** - * Callback fired when the width of a column is changed. - * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. - * @param {MuiEvent} event The event object. - * @param {GridCallbackDetails} details Additional details for this callback. - */ - onColumnWidthChange?: GridEventListener<'columnWidthChange'>; /** * Callback fired when scrolling to the bottom of the grid viewport. * @param {GridRowScrollEndParams} params With all properties from [[GridRowScrollEndParams]]. diff --git a/packages/x-data-grid-pro/src/models/gridApiPro.ts b/packages/x-data-grid-pro/src/models/gridApiPro.ts index a47b985e891ea..d70437eeb15a2 100644 --- a/packages/x-data-grid-pro/src/models/gridApiPro.ts +++ b/packages/x-data-grid-pro/src/models/gridApiPro.ts @@ -9,7 +9,6 @@ import type { GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/models/api/g import { GridInitialStatePro, GridStatePro } from './gridStatePro'; import type { GridColumnPinningApi, - GridColumnResizeApi, GridDetailPanelApi, GridRowPinningApi, GridDetailPanelPrivateApi, @@ -23,7 +22,6 @@ export interface GridApiPro extends GridApiCommon, GridRowProApi, GridColumnPinningApi, - GridColumnResizeApi, GridDetailPanelApi, GridRowPinningApi, // APIs that are private in Community plan, but public in Pro and Premium plans diff --git a/packages/x-data-grid-pro/src/models/gridStatePro.ts b/packages/x-data-grid-pro/src/models/gridStatePro.ts index bad4a22adbdf2..662a9bed10b09 100644 --- a/packages/x-data-grid-pro/src/models/gridStatePro.ts +++ b/packages/x-data-grid-pro/src/models/gridStatePro.ts @@ -8,7 +8,6 @@ import type { GridDetailPanelState, GridDetailPanelInitialState, GridColumnReorderState, - GridColumnResizeState, } from '../hooks'; /** @@ -16,7 +15,6 @@ import type { */ export interface GridStatePro extends GridStateCommunity { columnReorder: GridColumnReorderState; - columnResize: GridColumnResizeState; pinnedColumns: GridColumnPinningState; detailPanel: GridDetailPanelState; } diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index dc5e5c27a5cb6..475dc646e981f 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -101,6 +101,21 @@ DataGridRaw.propTypes = { * @default false */ autoPageSize: PropTypes.bool, + /** + * If `true`, columns are autosized after the datagrid is mounted. + * @default false + */ + autosizeOnMount: PropTypes.bool, + /** + * The options for autosize when user-initiated. + */ + autosizeOptions: PropTypes.shape({ + columns: PropTypes.arrayOf(PropTypes.string), + expand: PropTypes.bool, + includeHeaders: PropTypes.bool, + includeOutliers: PropTypes.bool, + outliersFactor: PropTypes.number, + }), /** * Controls the modes of the cells. */ @@ -149,6 +164,11 @@ DataGridRaw.propTypes = { * @default "standard" */ density: PropTypes.oneOf(['comfortable', 'compact', 'standard']), + /** + * If `true`, column autosizing on header separator double-click is disabled. + * @default false + */ + disableAutosize: PropTypes.bool, /** * If `true`, column filters are disabled. * @default false @@ -159,6 +179,11 @@ DataGridRaw.propTypes = { * @default false */ disableColumnMenu: PropTypes.bool, + /** + * If `true`, resizing columns is disabled. + * @default false + */ + disableColumnResize: PropTypes.bool, /** * If `true`, hiding/showing columns is disabled. * @default false @@ -461,12 +486,26 @@ DataGridRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onColumnOrderChange: PropTypes.func, + /** + * Callback fired while a column is being resized. + * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. + * @param {MuiEvent} event The event object. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnResize: PropTypes.func, /** * Callback fired when the column visibility model changes. * @param {GridColumnVisibilityModel} model The new model. * @param {GridCallbackDetails} details Additional details for this callback. */ onColumnVisibilityModelChange: PropTypes.func, + /** + * Callback fired when the width of a column is changed. + * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. + * @param {MuiEvent} event The event object. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnWidthChange: PropTypes.func, /** * Callback fired when the Filter model changes before the filters are applied. * @param {GridFilterModel} model With all properties from [[GridFilterModel]]. diff --git a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx index 7de0ea67c93c6..42b9d2dde827e 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx +++ b/packages/x-data-grid/src/DataGrid/useDataGridComponent.tsx @@ -49,6 +49,10 @@ import { useGridVirtualization, virtualizationStateInitializer, } from '../hooks/features/virtualization'; +import { + columnResizeStateInitializer, + useGridColumnResize, +} from '../hooks/features/columnResize/useGridColumnResize'; export const useDataGridComponent = ( inputApiRef: React.MutableRefObject | undefined, @@ -78,6 +82,7 @@ export const useDataGridComponent = ( useGridInitializeState(preferencePanelStateInitializer, apiRef, props); useGridInitializeState(filterStateInitializer, apiRef, props); useGridInitializeState(densityStateInitializer, apiRef, props); + useGridInitializeState(columnResizeStateInitializer, apiRef, props); useGridInitializeState(paginationStateInitializer, apiRef, props); useGridInitializeState(rowsMetaStateInitializer, apiRef, props); useGridInitializeState(columnMenuStateInitializer, apiRef, props); @@ -97,6 +102,7 @@ export const useDataGridComponent = ( useGridFilter(apiRef, props); useGridSorting(apiRef, props); useGridDensity(apiRef, props); + useGridColumnResize(apiRef, props); useGridPagination(apiRef, props); useGridRowsMeta(apiRef, props); useGridScroll(apiRef, props); diff --git a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts index 139c6151f83f4..b0f91d49cf291 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts +++ b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts @@ -19,7 +19,6 @@ const DATA_GRID_FORCED_PROPS: { [key in DataGridForcedPropsKey]?: DataGridProces pagination: true, checkboxSelectionVisibleOnly: false, disableColumnReorder: true, - disableColumnResize: true, keepColumnPositionIfDraggedOutside: false, signature: 'DataGrid', }; @@ -77,6 +76,8 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { ignoreValueFormatterDuringExport: false, clipboardCopyCellDelimiter: '\t', rowPositionsDebounceMs: 166, + autosizeOnMount: false, + disableAutosize: false, }; const defaultSlots = DATA_GRID_DEFAULT_SLOTS_COMPONENTS; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index 485f53efc9b35..b9f7cc2beb460 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -173,7 +173,7 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { width={width} columnMenuIconButton={null} columnTitleIconButtons={null} - resizable={false} + resizable label={label} aria-colspan={fields.length} // The fields are wrapped between |-...-| to avoid confusion between fields "id" and "id2" when using selector data-fields~= diff --git a/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts b/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts index bcc94a7589d43..5e065f1a572ab 100644 --- a/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts +++ b/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts @@ -13,7 +13,10 @@ import { GridRestoreStatePreProcessingContext, GridRestoreStatePreProcessingValue, } from '../../features/statePersistence/gridStatePersistenceInterface'; -import { GridHydrateColumnsValue } from '../../features/columns/gridColumnsInterfaces'; +import { + GridHydrateColumnsValue, + GridPinnedColumnPosition, +} from '../../features/columns/gridColumnsInterfaces'; import { GridRowEntry, GridRowId } from '../../../models/gridRows'; import { GridHydrateRowsValue } from '../../features/rows/gridRowsInterfaces'; import { GridPreferencePanelsValue } from '../../features/preferencesPanel'; @@ -58,6 +61,7 @@ export interface GridPipeProcessingLookup { value: boolean; context: { event: React.KeyboardEvent; cellParams: GridCellParams; editMode: GridEditMode }; }; + isColumnPinned: { value: GridPinnedColumnPosition | false; context: string }; } export type GridPipeProcessor

= ( diff --git a/packages/x-data-grid/src/hooks/features/columnResize/columnResizeSelector.ts b/packages/x-data-grid/src/hooks/features/columnResize/columnResizeSelector.ts new file mode 100644 index 0000000000000..879acf456a15a --- /dev/null +++ b/packages/x-data-grid/src/hooks/features/columnResize/columnResizeSelector.ts @@ -0,0 +1,9 @@ +import { createSelector } from '../../../utils/createSelector'; +import { GridStateCommunity } from '../../../models/gridStateCommunity'; + +export const gridColumnResizeSelector = (state: GridStateCommunity) => state.columnResize; + +export const gridResizingColumnFieldSelector = createSelector( + gridColumnResizeSelector, + (columnResize) => columnResize.resizingColumnField, +); diff --git a/packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeState.ts b/packages/x-data-grid/src/hooks/features/columnResize/columnResizeState.ts similarity index 100% rename from packages/x-data-grid-pro/src/hooks/features/columnResize/columnResizeState.ts rename to packages/x-data-grid/src/hooks/features/columnResize/columnResizeState.ts diff --git a/packages/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts b/packages/x-data-grid/src/hooks/features/columnResize/gridColumnResizeApi.ts similarity index 94% rename from packages/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts rename to packages/x-data-grid/src/hooks/features/columnResize/gridColumnResizeApi.ts index c19883f4368aa..3b0e62969221f 100644 --- a/packages/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts +++ b/packages/x-data-grid/src/hooks/features/columnResize/gridColumnResizeApi.ts @@ -1,4 +1,4 @@ -import { GridColDef } from '@mui/x-data-grid'; +import type { GridColDef } from '../../../models/colDef/gridColDef'; export const DEFAULT_GRID_AUTOSIZE_OPTIONS = { includeHeaders: true, diff --git a/packages/x-data-grid-pro/src/hooks/features/columnResize/index.ts b/packages/x-data-grid/src/hooks/features/columnResize/index.ts similarity index 100% rename from packages/x-data-grid-pro/src/hooks/features/columnResize/index.ts rename to packages/x-data-grid/src/hooks/features/columnResize/index.ts diff --git a/packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx similarity index 93% rename from packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx rename to packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx index a49fc3166cb3c..c54292496cdba 100644 --- a/packages/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx @@ -3,32 +3,6 @@ import { unstable_ownerDocument as ownerDocument, unstable_useEventCallback as useEventCallback, } from '@mui/utils'; -import { - GridEventListener, - gridClasses, - CursorCoordinates, - GridColumnHeaderSeparatorSides, - GridColumnResizeParams, - GridPinnedColumnPosition, - useGridApiEventHandler, - useGridApiOptionHandler, - useGridApiMethod, - useGridNativeEventListener, - useGridLogger, - useGridSelector, - gridVirtualizationColumnEnabledSelector, -} from '@mui/x-data-grid'; -import { - clamp, - findParentElementFromClassName, - gridColumnsStateSelector, - useOnMount, - useTimeout, - createControllablePromise, - ControllablePromise, - GridStateColDef, - GridStateInitializer, -} from '@mui/x-data-grid/internals'; import { useTheme, Direction } from '@mui/material/styles'; import { findGridCellElementsFromCol, @@ -40,14 +14,40 @@ import { findGroupHeaderElementsFromField, findGridHeader, findGridCells, + findParentElementFromClassName, } from '../../../utils/domUtils'; -import { GridPrivateApiPro } from '../../../models/gridApiPro'; -import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; import { GridAutosizeOptions, GridColumnResizeApi, DEFAULT_GRID_AUTOSIZE_OPTIONS, } from './gridColumnResizeApi'; +import { CursorCoordinates } from '../../../models/cursorCoordinates'; +import { GridColumnHeaderSeparatorSides } from '../../../components/columnHeaders/GridColumnHeaderSeparator'; +import { gridClasses } from '../../../constants/gridClasses'; +import { + useGridApiEventHandler, + useGridApiMethod, + useGridApiOptionHandler, + useGridLogger, + useGridNativeEventListener, + useGridSelector, + useOnMount, +} from '../../utils'; +import { gridVirtualizationColumnEnabledSelector } from '../virtualization'; +import { + ControllablePromise, + createControllablePromise, +} from '../../../utils/createControllablePromise'; +import { GridStateInitializer } from '../../utils/useGridInitializeState'; +import { clamp } from '../../../utils/utils'; +import { useTimeout } from '../../utils/useTimeout'; +import { GridPinnedColumnPosition } from '../columns/gridColumnsInterfaces'; +import { gridColumnsStateSelector } from '../columns'; +import type { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import type { GridColumnResizeParams } from '../../../models/params/gridColumnResizeParams'; +import type { GridStateColDef } from '../../../models/colDef/gridColDef'; +import type { GridEventListener } from '../../../models/events/gridEventListener'; +import type { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; type AutosizeOptionsRequired = Required; @@ -149,7 +149,7 @@ function preventClick(event: MouseEvent) { * Checker that returns a promise that resolves when the column virtualization * is disabled. */ -function useColumnVirtualizationDisabled(apiRef: React.MutableRefObject) { +function useColumnVirtualizationDisabled(apiRef: React.MutableRefObject) { const promise = React.useRef(); const selector = () => gridVirtualizationColumnEnabledSelector(apiRef); const value = useGridSelector(apiRef, selector); @@ -201,7 +201,7 @@ function excludeOutliers(inputValues: number[], factor: number) { } function extractColumnWidths( - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, options: AutosizeOptionsRequired, columns: GridStateColDef[], ) { @@ -267,9 +267,9 @@ export const columnResizeStateInitializer: GridStateInitializer = (state) => ({ * TODO: improve experience for last column */ export const useGridColumnResize = ( - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, props: Pick< - DataGridProProcessedProps, + DataGridProcessedProps, | 'autosizeOptions' | 'autosizeOnMount' | 'disableAutosize' @@ -353,7 +353,11 @@ export const useGridColumnResize = ( div.style.setProperty('--width', finalWidth); }); - const pinnedPosition = apiRef.current.isColumnPinned(colDefRef.current!.field); + const pinnedPosition = apiRef.current.unstable_applyPipeProcessors( + 'isColumnPinned', + false, + colDefRef.current!.field, + ); if (pinnedPosition === GridPinnedColumnPosition.LEFT) { updateProperty(fillerLeftRef.current, 'width', widthDiff); @@ -436,7 +440,11 @@ export const useGridColumnResize = ( fillerLeftRef.current = findGridElement(apiRef.current, 'filler--pinnedLeft'); fillerRightRef.current = findGridElement(apiRef.current, 'filler--pinnedRight'); - const pinnedPosition = apiRef.current.isColumnPinned(colDef.field); + const pinnedPosition = apiRef.current.unstable_applyPipeProcessors( + 'isColumnPinned', + false, + colDefRef.current!.field, + ); leftPinnedCellsAfterRef.current = pinnedPosition !== GridPinnedColumnPosition.LEFT diff --git a/packages/x-data-grid/src/hooks/features/index.ts b/packages/x-data-grid/src/hooks/features/index.ts index b5d62c7b80d5b..88459db607a09 100644 --- a/packages/x-data-grid/src/hooks/features/index.ts +++ b/packages/x-data-grid/src/hooks/features/index.ts @@ -2,6 +2,7 @@ export * from './columnMenu'; export * from './columns'; export * from './columnGrouping'; +export * from './columnResize'; export * from './density'; export * from './filter'; export * from './focus'; diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 8cb5686021445..3ef2cfab4a039 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -120,6 +120,10 @@ export { EMPTY_DETAIL_PANELS, } from '../hooks/features/virtualization/useGridVirtualScroller'; export * from '../hooks/features/virtualization'; +export { + useGridColumnResize, + columnResizeStateInitializer, +} from '../hooks/features/columnResize/useGridColumnResize'; export { useTimeout } from '../hooks/utils/useTimeout'; export { useGridVisibleRows, getVisibleRows } from '../hooks/utils/useGridVisibleRows'; diff --git a/packages/x-data-grid/src/models/api/gridApiCommon.ts b/packages/x-data-grid/src/models/api/gridApiCommon.ts index fc87a36a4d8b6..05cf1bd6bda7e 100644 --- a/packages/x-data-grid/src/models/api/gridApiCommon.ts +++ b/packages/x-data-grid/src/models/api/gridApiCommon.ts @@ -34,6 +34,7 @@ import { GridColumnGroupingApi } from './gridColumnGroupingApi'; import type { GridInitialStateCommunity, GridStateCommunity } from '../gridStateCommunity'; import { GridHeaderFilteringApi, GridHeaderFilteringPrivateApi } from './gridHeaderFilteringApi'; import type { DataGridProcessedProps } from '../props/DataGridProps'; +import type { GridColumnResizeApi } from '../../hooks/features/columnResize'; export interface GridApiCommon< GridState extends GridStateCommunity = any, @@ -63,7 +64,8 @@ export interface GridApiCommon< GridStateApi, GridStatePersistenceApi, GridColumnGroupingApi, - GridHeaderFilteringApi {} + GridHeaderFilteringApi, + GridColumnResizeApi {} export interface GridPrivateOnlyApiCommon< Api extends GridApiCommon, diff --git a/packages/x-data-grid/src/models/gridStateCommunity.ts b/packages/x-data-grid/src/models/gridStateCommunity.ts index f5877ecf6222d..95015838ddc1b 100644 --- a/packages/x-data-grid/src/models/gridStateCommunity.ts +++ b/packages/x-data-grid/src/models/gridStateCommunity.ts @@ -25,6 +25,7 @@ import type { GridEditingState } from './gridEditRowModel'; import { GridHeaderFilteringState } from './gridHeaderFilteringModel'; import type { GridRowSelectionModel } from './gridRowSelectionModel'; import type { GridVisibleRowsLookupState } from '../hooks/features/filter/gridFilterState'; +import type { GridColumnResizeState } from '../hooks/features/columnResize'; /** * The state of `DataGrid`. @@ -50,6 +51,7 @@ export interface GridStateCommunity { preferencePanel: GridPreferencePanelState; density: GridDensityState; virtualization: GridVirtualizationState; + columnResize: GridColumnResizeState; } /** diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index 142892968f099..90c2b031ccbea 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -31,6 +31,7 @@ import { GridColumnVisibilityModel } from '../../hooks/features/columns/gridColu import { GridCellModesModel, GridRowModesModel } from '../api/gridEditingApi'; import { GridColumnGroupingModel } from '../gridColumnGrouping'; import { GridPaginationModel } from '../gridPaginationProps'; +import type { GridAutosizeOptions } from '../../hooks/features/columnResize'; export interface GridExperimentalFeatures { /** @@ -69,7 +70,6 @@ export type DataGridForcedPropsKey = | 'disableMultipleColumnsFiltering' | 'disableMultipleColumnsSorting' | 'disableColumnReorder' - | 'disableColumnResize' | 'keepColumnPositionIfDraggedOutside' | 'throttleRowsMs' | 'hideFooterRowCount' @@ -372,6 +372,16 @@ export interface DataGridPropsWithDefaultValues { * @default 166 */ rowPositionsDebounceMs: number; + /** + * If `true`, columns are autosized after the datagrid is mounted. + * @default false + */ + autosizeOnMount: boolean; + /** + * If `true`, column autosizing on header separator double-click is disabled. + * @default false + */ + disableAutosize: boolean; } /** @@ -760,4 +770,22 @@ export interface DataGridPropsWithoutDefaultValue; + /** + * The options for autosize when user-initiated. + */ + autosizeOptions?: GridAutosizeOptions; + /** + * Callback fired while a column is being resized. + * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. + * @param {MuiEvent} event The event object. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnResize?: GridEventListener<'columnResize'>; + /** + * Callback fired when the width of a column is changed. + * @param {GridColumnResizeParams} params With all properties from [[GridColumnResizeParams]]. + * @param {MuiEvent} event The event object. + * @param {GridCallbackDetails} details Additional details for this callback. + */ + onColumnWidthChange?: GridEventListener<'columnWidthChange'>; } diff --git a/packages/x-data-grid/src/utils/domUtils.ts b/packages/x-data-grid/src/utils/domUtils.ts index fdd8044d1a4f6..8f6b61bb93923 100644 --- a/packages/x-data-grid/src/utils/domUtils.ts +++ b/packages/x-data-grid/src/utils/domUtils.ts @@ -1,5 +1,6 @@ import { gridClasses } from '../constants/gridClasses'; -import { GridRowId } from '../models/gridRows'; +import type { GridPrivateApiCommunity } from '../models/api/gridApiCommunity'; +import type { GridRowId } from '../models/gridRows'; export function isOverflown(element: Element): boolean { return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth; @@ -61,3 +62,139 @@ export function isEventTargetInPortal(event: React.SyntheticEvent) { } return false; } + +export function getFieldFromHeaderElem(colCellEl: Element): string { + return colCellEl.getAttribute('data-field')!; +} + +export function findHeaderElementFromField(elem: Element, field: string): HTMLDivElement { + return elem.querySelector(`[data-field="${field}"]`)!; +} + +export function findGroupHeaderElementsFromField(elem: Element, field: string): Element[] { + return Array.from(elem.querySelectorAll(`[data-fields*="|-${field}-|"]`) ?? []); +} + +export function findGridCellElementsFromCol(col: HTMLElement, api: GridPrivateApiCommunity) { + const root = findParentElementFromClassName(col, gridClasses.root); + if (!root) { + throw new Error('MUI X: The root element is not found.'); + } + + const ariaColIndex = col.getAttribute('aria-colindex'); + if (!ariaColIndex) { + return []; + } + + const colIndex = Number(ariaColIndex) - 1; + const cells: Element[] = []; + + if (!api.virtualScrollerRef?.current) { + return []; + } + + queryRows(api).forEach((rowElement) => { + const rowId = rowElement.getAttribute('data-id'); + if (!rowId) { + return; + } + + let columnIndex = colIndex; + + const cellColSpanInfo = api.unstable_getCellColSpanInfo(rowId, colIndex); + if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) { + columnIndex = cellColSpanInfo.leftVisibleCellIndex; + } + const cell = rowElement.querySelector(`[data-colindex="${columnIndex}"]`); + if (cell) { + cells.push(cell); + } + }); + + return cells; +} + +export function findGridElement(api: GridPrivateApiCommunity, klass: keyof typeof gridClasses) { + return api.rootElementRef.current!.querySelector(`.${gridClasses[klass]}`)! as HTMLElement; +} + +export function findLeftPinnedCellsAfterCol(api: GridPrivateApiCommunity, col: HTMLElement) { + const colIndex = parseCellColIndex(col); + if (colIndex === null) { + return []; + } + + const cells: HTMLElement[] = []; + + queryRows(api).forEach((rowElement) => { + const rowId = rowElement.getAttribute('data-id'); + if (!rowId) { + return; + } + + const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedLeft']}`); + rightPinnedCells.forEach((cell) => { + const currentColIndex = parseCellColIndex(cell); + if (currentColIndex !== null && currentColIndex > colIndex) { + cells.push(cell as HTMLElement); + } + }); + }); + + return cells; +} + +export function findRightPinnedCellsBeforeCol(api: GridPrivateApiCommunity, col: HTMLElement) { + const colIndex = parseCellColIndex(col); + if (colIndex === null) { + return []; + } + + const cells: HTMLElement[] = []; + + queryRows(api).forEach((rowElement) => { + const rowId = rowElement.getAttribute('data-id'); + if (!rowId) { + return; + } + + const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedRight']}`); + rightPinnedCells.forEach((cell) => { + const currentColIndex = parseCellColIndex(cell); + if (currentColIndex !== null && currentColIndex < colIndex) { + cells.push(cell as HTMLElement); + } + }); + }); + + return cells; +} + +export function findGridHeader(api: GridPrivateApiCommunity, field: string) { + const headers = api.columnHeadersContainerElementRef!.current!; + return headers.querySelector(`:scope > div > div > [data-field="${field}"][role="columnheader"]`); +} + +export function findGridCells(api: GridPrivateApiCommunity, field: string) { + const container = api.virtualScrollerRef!.current!; + return Array.from( + container.querySelectorAll( + `:scope > div > div > div > [data-field="${field}"][role="gridcell"]`, + ), + ); +} + +function queryRows(api: GridPrivateApiCommunity) { + return api.virtualScrollerRef.current!.querySelectorAll( + // Use > to ignore rows from nested data grids (e.g. in detail panel) + `:scope > div > div > .${gridClasses.row}`, + ); +} + +function parseCellColIndex(col: Element) { + const ariaColIndex = col.getAttribute('aria-colindex'); + if (!ariaColIndex) { + return null; + } + return Number(ariaColIndex) - 1; +} diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 89ddee633343a..08d4b5ebc605d 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -24,6 +24,7 @@ { "name": "DATA_GRID_PROPS_DEFAULT_VALUES", "kind": "Variable" }, { "name": "DataGrid", "kind": "Variable" }, { "name": "DataGridProps", "kind": "TypeAlias" }, + { "name": "DEFAULT_GRID_AUTOSIZE_OPTIONS", "kind": "Variable" }, { "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" }, { "name": "DetailPanelsPropsOverrides", "kind": "Interface" }, { "name": "ElementSize", "kind": "Interface" }, @@ -75,6 +76,7 @@ { "name": "GridArrowUpwardIcon", "kind": "Variable" }, { "name": "GridAutoGeneratedGroupNode", "kind": "Interface" }, { "name": "GridAutoGeneratedPinnedRowNode", "kind": "Interface" }, + { "name": "GridAutosizeOptions", "kind": "TypeAlias" }, { "name": "GridBasicGroupNode", "kind": "Interface" }, { "name": "GridBody", "kind": "ImportSpecifier" }, { "name": "GridBooleanCell", "kind": "Variable" }, @@ -166,7 +168,10 @@ { "name": "gridColumnPositionsSelector", "kind": "Variable" }, { "name": "GridColumnRawLookup", "kind": "TypeAlias" }, { "name": "GridColumnReorderApi", "kind": "Interface" }, + { "name": "GridColumnResizeApi", "kind": "Interface" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, + { "name": "gridColumnResizeSelector", "kind": "Variable" }, + { "name": "GridColumnResizeState", "kind": "Interface" }, { "name": "GridColumnsGroupingState", "kind": "Interface" }, { "name": "GridColumnsInitialState", "kind": "Interface" }, { "name": "GridColumnsManagement", "kind": "Function" }, @@ -381,6 +386,7 @@ { "name": "GridRenderEditCellParams", "kind": "Interface" }, { "name": "GridRenderPaginationProps", "kind": "Interface" }, { "name": "GridRenderRowProps", "kind": "Interface" }, + { "name": "gridResizingColumnFieldSelector", "kind": "Variable" }, { "name": "GridRoot", "kind": "Variable" }, { "name": "GridRootProps", "kind": "Interface" }, { "name": "GridRow", "kind": "Variable" }, From 3d604b01d5851d5804a8f572222ac6aba929a812 Mon Sep 17 00:00:00 2001 From: Nora <72460825+noraleonte@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:05:48 +0200 Subject: [PATCH 32/49] [TreeView] Clean the usage of the term "item" and "node" in API introduced during v7 (#12368) --- .../migration-tree-view-v6.md | 62 +++++------ .../customization/CustomAnimation.js | 2 +- .../customization/CustomAnimation.tsx | 2 +- .../customization/CustomAnimation.tsx.preview | 2 +- .../customization/CustomContentTreeView.js | 2 +- .../customization/CustomContentTreeView.tsx | 2 +- .../CustomContentTreeView.tsx.preview | 2 +- .../customization/CustomIcons.js | 2 +- .../customization/CustomIcons.tsx | 2 +- .../customization/CustomIcons.tsx.preview | 2 +- .../customization/CustomStyling.js | 2 +- .../customization/CustomStyling.tsx | 2 +- .../customization/CustomStyling.tsx.preview | 2 +- .../customization/LabelSlotProps.js | 2 +- .../customization/LabelSlotProps.tsx | 2 +- .../customization/LabelSlotProps.tsx.preview | 2 +- .../customization/LabelSlots.js | 4 +- .../customization/LabelSlots.tsx | 4 +- .../customization/LabelSlots.tsx.preview | 2 +- .../expansion/ControlledExpansion.js | 30 ++--- .../expansion/ControlledExpansion.tsx | 32 +++--- .../expansion/ControlledExpansion.tsx.preview | 6 +- ...nToggle.js => TrackItemExpansionToggle.js} | 10 +- ...oggle.tsx => TrackItemExpansionToggle.tsx} | 14 +-- ...w => TrackItemExpansionToggle.tsx.preview} | 4 +- .../rich-tree-view/expansion/expansion.md | 14 +-- .../focus/FocusedRichTreeView.js | 4 +- .../focus/FocusedRichTreeView.tsx | 4 +- .../focus/FocusedRichTreeView.tsx.preview | 2 +- .../tree-view/rich-tree-view/focus/focus.md | 10 +- ...ogExpandedNodes.js => LogExpandedItems.js} | 4 +- ...ExpandedNodes.tsx => LogExpandedItems.tsx} | 4 +- ...x.preview => LogExpandedItems.tsx.preview} | 0 .../rich-tree-view/headless/headless.md | 16 +-- .../selection/ControlledSelection.js | 24 ++-- .../selection/ControlledSelection.tsx | 24 ++-- .../selection/ControlledSelection.tsx.preview | 6 +- ...nToggle.js => TrackItemSelectionToggle.js} | 16 +-- ...oggle.tsx => TrackItemSelectionToggle.tsx} | 18 +-- .../TrackItemSelectionToggle.tsx.preview | 11 ++ .../TrackNodeSelectionToggle.tsx.preview | 11 -- .../rich-tree-view/selection/selection.md | 14 +-- .../customization/BorderedTreeView.js | 2 +- .../customization/BorderedTreeView.tsx | 2 +- .../customization/CustomAnimation.js | 2 +- .../customization/CustomAnimation.tsx | 2 +- .../customization/CustomContentTreeView.js | 2 +- .../customization/CustomContentTreeView.tsx | 2 +- .../CustomContentTreeView.tsx.preview | 2 +- .../customization/CustomIcons.js | 2 +- .../customization/CustomIcons.tsx | 2 +- .../customization/CustomStyling.js | 2 +- .../customization/CustomStyling.tsx | 2 +- .../customization/CustomizedTreeView.js | 4 +- .../customization/CustomizedTreeView.tsx | 4 +- .../customization/GmailTreeView.js | 4 +- .../customization/GmailTreeView.tsx | 4 +- .../customization/LabelSlotProps.js | 2 +- .../customization/LabelSlotProps.tsx | 2 +- .../customization/LabelSlotProps.tsx.preview | 2 +- .../customization/LabelSlots.js | 2 +- .../customization/LabelSlots.tsx | 2 +- .../expansion/ControlledExpansion.js | 14 +-- .../expansion/ControlledExpansion.tsx | 16 +-- ...nToggle.js => TrackItemExpansionToggle.js} | 10 +- ...oggle.tsx => TrackItemExpansionToggle.tsx} | 14 +-- .../simple-tree-view/expansion/expansion.md | 14 +-- .../focus/FocusedSimpleTreeView.js | 4 +- .../focus/FocusedSimpleTreeView.tsx | 4 +- .../tree-view/simple-tree-view/focus/focus.md | 10 +- .../selection/ControlledSelection.js | 14 +-- .../selection/ControlledSelection.tsx | 14 +-- ...nToggle.js => TrackItemSelectionToggle.js} | 16 +-- ...oggle.tsx => TrackItemSelectionToggle.tsx} | 18 +-- .../simple-tree-view/selection/selection.md | 14 +-- .../pages/x/api/tree-view/rich-tree-view.json | 40 +++---- .../x/api/tree-view/simple-tree-view.json | 40 +++---- docs/pages/x/api/tree-view/tree-item.json | 8 +- docs/pages/x/api/tree-view/tree-view.json | 40 +++---- .../rich-tree-view/rich-tree-view.json | 46 ++++---- .../simple-tree-view/simple-tree-view.json | 46 ++++---- .../tree-view/tree-item-2/tree-item-2.json | 2 +- .../tree-view/tree-item/tree-item.json | 14 +-- .../tree-view/tree-view/tree-view.json | 46 ++++---- packages/x-codemod/README.md | 20 ++-- .../tree-view/preset-safe/expected.spec.tsx | 12 +- .../rename-expansion-props/actual.spec.js | 7 +- .../rename-expansion-props/expected.spec.js | 7 +- .../tree-view/rename-expansion-props/index.ts | 6 +- .../rename-selection-props/actual.spec.js | 7 +- .../rename-selection-props/expected.spec.js | 7 +- .../tree-view/rename-selection-props/index.ts | 6 +- .../src/RichTreeView/RichTreeView.tsx | 46 ++++---- .../SimpleTreeView/SimpleTreeView.test.tsx | 82 +++++++------- .../src/SimpleTreeView/SimpleTreeView.tsx | 46 ++++---- .../src/TreeItem/TreeItem.test.tsx | 104 +++++++++--------- .../x-tree-view/src/TreeItem/TreeItem.tsx | 8 +- .../src/TreeItem/TreeItem.types.ts | 14 +-- .../src/TreeItem/useTreeItemState.ts | 4 +- .../x-tree-view/src/TreeItem2/TreeItem2.tsx | 2 +- .../src/TreeItem2/TreeItem2.types.ts | 2 +- .../x-tree-view/src/TreeView/TreeView.tsx | 46 ++++---- .../useTreeItem2Utils/useTreeItem2Utils.tsx | 4 +- .../useTreeViewExpansion.ts | 50 ++++----- .../useTreeViewExpansion.types.ts | 24 ++-- .../useTreeViewFocus/useTreeViewFocus.ts | 14 +-- .../useTreeViewFocus.types.ts | 4 +- .../useTreeViewKeyboardNavigation.ts | 14 +-- .../useTreeViewSelection.ts | 80 +++++++------- .../useTreeViewSelection.types.ts | 26 ++--- .../themeAugmentation.spec.ts | 6 +- .../src/useTreeItem2/useTreeItem2.ts | 2 +- 112 files changed, 751 insertions(+), 771 deletions(-) rename docs/data/tree-view/rich-tree-view/expansion/{TrackNodeExpansionToggle.js => TrackItemExpansionToggle.js} (85%) rename docs/data/tree-view/rich-tree-view/expansion/{TrackNodeExpansionToggle.tsx => TrackItemExpansionToggle.tsx} (86%) rename docs/data/tree-view/rich-tree-view/expansion/{TrackNodeExpansionToggle.tsx.preview => TrackItemExpansionToggle.tsx.preview} (79%) rename docs/data/tree-view/rich-tree-view/headless/{LogExpandedNodes.js => LogExpandedItems.js} (96%) rename docs/data/tree-view/rich-tree-view/headless/{LogExpandedNodes.tsx => LogExpandedItems.tsx} (97%) rename docs/data/tree-view/rich-tree-view/headless/{LogExpandedNodes.tsx.preview => LogExpandedItems.tsx.preview} (100%) rename docs/data/tree-view/rich-tree-view/selection/{TrackNodeSelectionToggle.js => TrackItemSelectionToggle.js} (75%) rename docs/data/tree-view/rich-tree-view/selection/{TrackNodeSelectionToggle.tsx => TrackItemSelectionToggle.tsx} (77%) create mode 100644 docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx.preview delete mode 100644 docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx.preview rename docs/data/tree-view/simple-tree-view/expansion/{TrackNodeExpansionToggle.js => TrackItemExpansionToggle.js} (86%) rename docs/data/tree-view/simple-tree-view/expansion/{TrackNodeExpansionToggle.tsx => TrackItemExpansionToggle.tsx} (86%) rename docs/data/tree-view/simple-tree-view/selection/{TrackNodeSelectionToggle.js => TrackItemSelectionToggle.js} (77%) rename docs/data/tree-view/simple-tree-view/selection/{TrackNodeSelectionToggle.tsx => TrackItemSelectionToggle.tsx} (78%) diff --git a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md index 442089ff038e6..851bd995d35ab 100644 --- a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md +++ b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md @@ -93,7 +93,7 @@ If you were using the `treeViewClasses` object, you can replace it with the new #### Define `expandIcon` -The icon used to expand the children of a node (rendered when this node is collapsed) +The icon used to expand the children of an item (rendered when this item is collapsed) is now defined as a slot both on the Tree View and the `TreeItem` components. If you were using the `ChevronRight` icon from `@mui/icons-material`, @@ -132,7 +132,7 @@ you need to use the new `expandIcon` slot on this component: } + slots={{ expandIcon: MyCustomExpandIcon }} /> @@ -141,7 +141,7 @@ you need to use the new `expandIcon` slot on this component: #### Define `collapseIcon` -The icon used to collapse the children of a node (rendered when this node is expanded) +The icon used to collapse the children of an item (rendered when this item is expanded) is now defined as a slot both on the Tree View and the `TreeItem` components. If you were using the `ExpandMore` icon from `@mui/icons-material`, @@ -180,7 +180,7 @@ you need to use the new `collapseIcon` slot on this component: } + slots={{ collapseIcon: MyCustomCollapseIcon }} /> @@ -232,7 +232,7 @@ you need to use the new `endIcon` slot on this component: } + slots={{ endIcon: MyCustomEndIcon }} /> @@ -251,7 +251,7 @@ you need to use the new `icon` slot on this component: } + slots={{ icon: MyCustomIcon }} /> @@ -274,7 +274,7 @@ you need to use the new `groupTransition` slot on this component: ``` :::info -If you were using the `onNodeToggle` prop to react to the expansion or collapse of a specific node, -you can use the new `onNodeExpansionToggle` prop which is called whenever a node is expanded or collapsed with its id and expansion status +If you were using the `onNodeToggle` prop to react to the expansion or collapse of a specific item, +you can use the new `onItemExpansionToggle` prop which is called whenever an item is expanded or collapsed with its id and expansion status ```tsx // It is also available on the deprecated `TreeView` component - console.log(nodeId, isExpanded) + onItemExpansionToggle={(event, itemId, isExpanded) => + console.log(itemId, isExpanded) } /> ``` @@ -340,32 +340,32 @@ The selection props have been renamed to better describe their behaviors: | Old name | New name | | :---------------- | :---------------------- | -| `onNodeSelect` | `onSelectedNodesChange` | -| `selected` | `selectedNodes` | -| `defaultSelected` | `defaultSelectedNodes` | +| `onNodeSelect` | `onSelectedItemsChange` | +| `selected` | `selectedItems` | +| `defaultSelected` | `defaultSelectedItems` | ```diff ``` :::info -If you were using the `onNodeSelect` prop to react to the selection or deselection of a specific node, -you can use the new `onNodeSelectionToggle` prop which is called whenever a node is selected or deselected with its id and selection status. +If you were using the `onNodeSelect` prop to react to the selection or deselection of a specific item, +you can use the new `onItemSelectionToggle` prop which is called whenever an item is selected or deselected with its id and selection status. ```tsx // It is also available on the deprecated `TreeView` component - console.log(nodeId, isSelected) + onItemSelectionToggle={(event, itemId, isSelected) => + console.log(itemId, isSelected) } /> ``` diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js index 6900116338403..53d9edc89c938 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js +++ b/docs/data/tree-view/rich-tree-view/customization/CustomAnimation.js @@ -53,7 +53,7 @@ export default function CustomAnimation() { return ( diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx index 9d2333b5b6517..c426440118e53 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx @@ -94,7 +94,7 @@ export default function CustomContentTreeView() { diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview index 39ab54a65d5df..e37df6849fe22 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx.preview @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js index a955b32be421b..9b6787b5c825a 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js +++ b/docs/data/tree-view/rich-tree-view/customization/CustomIcons.js @@ -61,7 +61,7 @@ export default function CustomIcons() { return ( diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx index f66b49a6951a2..7bac158b75a7c 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx @@ -42,7 +42,7 @@ export default function LabelSlotProps() { diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview index a7f9d42cc158b..511debc05ff2a 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx.preview @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js index 190a40da5ec89..655627dc07947 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js @@ -116,7 +116,7 @@ const DEFAULT_MUI_X_PRODUCTS = [ }, ]; -const DEFAULT_EXPANDED_NODES = ['pickers']; +const DEFAULT_EXPANDED_ITEMS = ['pickers']; export default function LabelSlots() { const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); @@ -147,7 +147,7 @@ export default function LabelSlots() { diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx index 1fde72d18f234..fab56741721b0 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx @@ -135,7 +135,7 @@ const DEFAULT_MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, ]; -const DEFAULT_EXPANDED_NODES = ['pickers']; +const DEFAULT_EXPANDED_ITEMS = ['pickers']; export default function LabelSlots() { const [products, setProducts] = React.useState(DEFAULT_MUI_X_PRODUCTS); @@ -166,7 +166,7 @@ export default function LabelSlots() { diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview index ab6c46ba22496..36cf606d1cb17 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx.preview @@ -2,7 +2,7 @@ diff --git a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.js b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.js index a85a4005de935..bcc49a4c33c45 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.js +++ b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.js @@ -33,30 +33,30 @@ const MUI_X_PRODUCTS = [ }, ]; -const getAllItemWithChildrenNodeIds = () => { - const nodeIds = []; - const registerNodeId = (item) => { +const getAllItemsWithChildrenItemIds = () => { + const itemIds = []; + const registerItemId = (item) => { if (item.children?.length) { - nodeIds.push(item.id); - item.children.forEach(registerNodeId); + itemIds.push(item.id); + item.children.forEach(registerItemId); } }; - MUI_X_PRODUCTS.forEach(registerNodeId); + MUI_X_PRODUCTS.forEach(registerItemId); - return nodeIds; + return itemIds; }; export default function ControlledExpansion() { - const [expandedNodes, setExpandedNodes] = React.useState([]); + const [expandedItems, setExpandedItems] = React.useState([]); - const handleExpandedNodesChange = (event, nodeIds) => { - setExpandedNodes(nodeIds); + const handleExpandedItemsChange = (event, itemIds) => { + setExpandedItems(itemIds); }; const handleExpandClick = () => { - setExpandedNodes((oldExpanded) => - oldExpanded.length === 0 ? getAllItemWithChildrenNodeIds() : [], + setExpandedItems((oldExpanded) => + oldExpanded.length === 0 ? getAllItemsWithChildrenItemIds() : [], ); }; @@ -64,14 +64,14 @@ export default function ControlledExpansion() { diff --git a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx index 3ab5ceda3eee7..83d8260914785 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx +++ b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx @@ -34,33 +34,33 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, ]; -const getAllItemWithChildrenNodeIds = () => { - const nodeIds: TreeViewItemId[] = []; - const registerNodeId = (item: TreeViewBaseItem) => { +const getAllItemsWithChildrenItemIds = () => { + const itemIds: TreeViewItemId[] = []; + const registerItemId = (item: TreeViewBaseItem) => { if (item.children?.length) { - nodeIds.push(item.id); - item.children.forEach(registerNodeId); + itemIds.push(item.id); + item.children.forEach(registerItemId); } }; - MUI_X_PRODUCTS.forEach(registerNodeId); + MUI_X_PRODUCTS.forEach(registerItemId); - return nodeIds; + return itemIds; }; export default function ControlledExpansion() { - const [expandedNodes, setExpandedNodes] = React.useState([]); + const [expandedItems, setExpandedItems] = React.useState([]); - const handleExpandedNodesChange = ( + const handleExpandedItemsChange = ( event: React.SyntheticEvent, - nodeIds: string[], + itemIds: string[], ) => { - setExpandedNodes(nodeIds); + setExpandedItems(itemIds); }; const handleExpandClick = () => { - setExpandedNodes((oldExpanded) => - oldExpanded.length === 0 ? getAllItemWithChildrenNodeIds() : [], + setExpandedItems((oldExpanded) => + oldExpanded.length === 0 ? getAllItemsWithChildrenItemIds() : [], ); }; @@ -68,14 +68,14 @@ export default function ControlledExpansion() { diff --git a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx.preview b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx.preview index 23c1b30b2f8d3..8a83ec46e5411 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/expansion/ControlledExpansion.tsx.preview @@ -1,12 +1,12 @@ \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.js b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.js similarity index 85% rename from docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.js rename to docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.js index 5712834a0ada7..10e6f0dc3a5e6 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.js +++ b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.js @@ -35,11 +35,11 @@ const MUI_X_PRODUCTS = [ }, ]; -export default function TrackNodeExpansionToggle() { +export default function TrackItemExpansionToggle() { const [action, setAction] = React.useState(null); - const handleNodeExpansionToggle = (event, nodeId, isExpanded) => { - setAction({ nodeId, isExpanded }); + const handleItemExpansionToggle = (event, itemId, isExpanded) => { + setAction({ itemId, isExpanded }); }; return ( @@ -48,14 +48,14 @@ export default function TrackNodeExpansionToggle() { No action recorded ) : ( - Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.nodeId} + Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.itemId} )} diff --git a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx similarity index 86% rename from docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx rename to docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx index d0bfded863e44..7da11861cd8c7 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx +++ b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx @@ -35,18 +35,18 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, ]; -export default function TrackNodeExpansionToggle() { +export default function TrackItemExpansionToggle() { const [action, setAction] = React.useState<{ - nodeId: string; + itemId: string; isExpanded: boolean; } | null>(null); - const handleNodeExpansionToggle = ( + const handleItemExpansionToggle = ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isExpanded: boolean, ) => { - setAction({ nodeId, isExpanded }); + setAction({ itemId, isExpanded }); }; return ( @@ -55,13 +55,13 @@ export default function TrackNodeExpansionToggle() { No action recorded ) : ( - Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.nodeId} + Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.itemId} )} diff --git a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx.preview b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx.preview similarity index 79% rename from docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx.preview rename to docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx.preview index d4aa558e09aae..4699de8b8e112 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/TrackNodeExpansionToggle.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/expansion/TrackItemExpansionToggle.tsx.preview @@ -2,12 +2,12 @@ No action recorded ) : ( - Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.nodeId} + Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.itemId} )} \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/expansion/expansion.md b/docs/data/tree-view/rich-tree-view/expansion/expansion.md index d45ef4960fa6c..86a04e1b8c1f5 100644 --- a/docs/data/tree-view/rich-tree-view/expansion/expansion.md +++ b/docs/data/tree-view/rich-tree-view/expansion/expansion.md @@ -13,22 +13,22 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ ## Controlled expansion -Use the `expandedNodes` prop to control the expanded items. +Use the `expandedItems` prop to control the expanded items. -You can use the `onExpandedNodesChange` prop to listen to changes in the expanded items and update the prop accordingly. +You can use the `onExpandedItemsChange` prop to listen to changes in the expanded items and update the prop accordingly. {{"demo": "ControlledExpansion.js"}} :::info -- The expansion is **controlled** when its parent manages it by providing a `expandedNodes` prop. -- The expansion is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultExpandedNodes` prop. +- The expansion is **controlled** when its parent manages it by providing a `expandedItems` prop. +- The expansion is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultExpandedItems` prop. Learn more about the _Controlled and uncontrolled_ pattern in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). ::: -## Track node expansion change +## Track item expansion change -Use the `onNodeExpansionToggle` prop if you want to react to a node expansion change: +Use the `onItemExpansionToggle` prop if you want to react to an item expansion change: -{{"demo": "TrackNodeExpansionToggle.js"}} +{{"demo": "TrackItemExpansionToggle.js"}} diff --git a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.js b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.js index b55bbc89843cf..c2a96bffdf47e 100644 --- a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.js +++ b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.js @@ -38,13 +38,13 @@ const MUI_X_PRODUCTS = [ export default function FocusedRichTreeView() { const apiRef = useTreeViewApiRef(); const handleButtonClick = (event) => { - apiRef.current?.focusNode(event, 'pickers'); + apiRef.current?.focusItem(event, 'pickers'); }; return ( - + diff --git a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx index 5d83b6cec1162..04b1aad4d11cf 100644 --- a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx @@ -38,13 +38,13 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ export default function FocusedRichTreeView() { const apiRef = useTreeViewApiRef(); const handleButtonClick = (event: React.SyntheticEvent) => { - apiRef.current?.focusNode(event, 'pickers'); + apiRef.current?.focusItem(event, 'pickers'); }; return ( - + diff --git a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx.preview b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx.preview index bc63fee6e7fc3..144d8e4e8dd5d 100644 --- a/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/focus/FocusedRichTreeView.tsx.preview @@ -1,5 +1,5 @@ - + diff --git a/docs/data/tree-view/rich-tree-view/focus/focus.md b/docs/data/tree-view/rich-tree-view/focus/focus.md index 1f201af613a9a..4fa127cea557e 100644 --- a/docs/data/tree-view/rich-tree-view/focus/focus.md +++ b/docs/data/tree-view/rich-tree-view/focus/focus.md @@ -11,10 +11,10 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/

Learn how to focus Tree View items.

-## Focus a specific node +## Focus a specific item -You can use the the `apiRef.focusNode` method to focus a specific node. -This methods receives two parameters: `event` and `nodeId`. +You can use the the `apiRef.focusItem` method to focus a specific item. +This methods receives two parameters: `event` and `itemId`. :::success To use the `apiRef` object, you need to initialize it using the `useTreeViewApiRef` hook as follows: @@ -29,8 +29,8 @@ return ; ::: :::info -This method only works with nodes that are currently visible. -Calling `apiRef.focusNode` on a node whose parent is collapsed will do nothing. +This method only works with items that are currently visible. +Calling `apiRef.focusItem` on an item whose parent is collapsed will do nothing. ::: {{"demo": "FocusedRichTreeView.js"}} diff --git a/docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.js b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js similarity index 96% rename from docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.js rename to docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js index a59c50a630583..7105fc20cfb05 100644 --- a/docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.js +++ b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js @@ -14,7 +14,7 @@ import { } from '@mui/x-tree-view/internals'; const useTreeViewLogExpanded = ({ params, models }) => { - const expandedStr = JSON.stringify(models.expandedNodes.value); + const expandedStr = JSON.stringify(models.expandedItems.value); React.useEffect(() => { if (params.areLogsEnabled && params.logMessage) { @@ -90,7 +90,7 @@ const ITEMS = [ }, ]; -export default function LogExpandedNodes() { +export default function LogExpandedItems() { const [logs, setLogs] = React.useState([]); return ( diff --git a/docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.tsx b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx similarity index 97% rename from docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.tsx rename to docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx index bb7aa2140f48e..64197707f8f60 100644 --- a/docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.tsx +++ b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx @@ -45,7 +45,7 @@ const useTreeViewLogExpanded: TreeViewPlugin = ({ params, models, }) => { - const expandedStr = JSON.stringify(models.expandedNodes.value); + const expandedStr = JSON.stringify(models.expandedItems.value); React.useEffect(() => { if (params.areLogsEnabled && params.logMessage) { @@ -142,7 +142,7 @@ const ITEMS: TreeViewBaseItem[] = [ }, ]; -export default function LogExpandedNodes() { +export default function LogExpandedItems() { const [logs, setLogs] = React.useState([]); return ( diff --git a/docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.tsx.preview b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx.preview similarity index 100% rename from docs/data/tree-view/rich-tree-view/headless/LogExpandedNodes.tsx.preview rename to docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx.preview diff --git a/docs/data/tree-view/rich-tree-view/headless/headless.md b/docs/data/tree-view/rich-tree-view/headless/headless.md index 285d47156a297..98eff9f4b29ce 100644 --- a/docs/data/tree-view/rich-tree-view/headless/headless.md +++ b/docs/data/tree-view/rich-tree-view/headless/headless.md @@ -61,7 +61,7 @@ useCustomPlugin.getDefaultizedParams = (params) => ({ ### Controllable models A model is a value that can either be controlled or initialized using a prop. -The Tree View contains several models like the `expandedNodes` model which contains the ids of the nodes currently expanded. +The Tree View contains several models like the `expandedItems` model which contains the ids of the items currently expanded. You can create your own models using the `models` property of your plugin: @@ -110,14 +110,14 @@ Your plugin can access the instance methods, the params and the models of any ot ```ts const useCustomPlugin = ({ models }) => { const handleSomeAction = () => { - // Log the id of the nodes currently expanded - console.log(models.expandedNodes.value); + // Log the id of the items currently expanded + console.log(models.expandedItems.value); - // Collapse all the nodes - models.expandedNodes.setValue([]); + // Collapse all the items + models.expandedItems.setValue([]); - // Check if a node is expanded - const isExpanded = instance.isNodeExpanded('some-node-id'); + // Check if an item is expanded + const isExpanded = instance.isNodeExpanded('some-item-id'); }; }; ``` @@ -313,4 +313,4 @@ type UseCustomPluginSignature = TreeViewPluginSignature<{ Interact with the tree view to see the expanded items being logged: -{{"demo": "LogExpandedNodes.js"}} +{{"demo": "LogExpandedItems.js"}} diff --git a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.js b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.js index 392ad5ede8635..537b04fc53cb7 100644 --- a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.js +++ b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.js @@ -33,28 +33,28 @@ const MUI_X_PRODUCTS = [ }, ]; -const getAllItemNodeIds = () => { +const getAllItemItemIds = () => { const ids = []; - const registerNodeId = (item) => { + const registerItemId = (item) => { ids.push(item.id); - item.children?.forEach(registerNodeId); + item.children?.forEach(registerItemId); }; - MUI_X_PRODUCTS.forEach(registerNodeId); + MUI_X_PRODUCTS.forEach(registerItemId); return ids; }; export default function ControlledSelection() { - const [selectedNodes, setSelectedNodes] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); - const handleSelectedNodesChange = (event, ids) => { - setSelectedNodes(ids); + const handleSelectedItemsChange = (event, ids) => { + setSelectedItems(ids); }; const handleSelectClick = () => { - setSelectedNodes((oldSelected) => - oldSelected.length === 0 ? getAllItemNodeIds() : [], + setSelectedItems((oldSelected) => + oldSelected.length === 0 ? getAllItemItemIds() : [], ); }; @@ -62,14 +62,14 @@ export default function ControlledSelection() { diff --git a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx index a51fddb2f7b98..3f2967a50d4ae 100644 --- a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx +++ b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx @@ -34,28 +34,28 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, ]; -const getAllItemNodeIds = () => { +const getAllItemItemIds = () => { const ids: TreeViewItemId[] = []; - const registerNodeId = (item: TreeViewBaseItem) => { + const registerItemId = (item: TreeViewBaseItem) => { ids.push(item.id); - item.children?.forEach(registerNodeId); + item.children?.forEach(registerItemId); }; - MUI_X_PRODUCTS.forEach(registerNodeId); + MUI_X_PRODUCTS.forEach(registerItemId); return ids; }; export default function ControlledSelection() { - const [selectedNodes, setSelectedNodes] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); - const handleSelectedNodesChange = (event: React.SyntheticEvent, ids: string[]) => { - setSelectedNodes(ids); + const handleSelectedItemsChange = (event: React.SyntheticEvent, ids: string[]) => { + setSelectedItems(ids); }; const handleSelectClick = () => { - setSelectedNodes((oldSelected) => - oldSelected.length === 0 ? getAllItemNodeIds() : [], + setSelectedItems((oldSelected) => + oldSelected.length === 0 ? getAllItemItemIds() : [], ); }; @@ -63,14 +63,14 @@ export default function ControlledSelection() { diff --git a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx.preview b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx.preview index 05dbf38354c71..e1b7677fd7547 100644 --- a/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx.preview +++ b/docs/data/tree-view/rich-tree-view/selection/ControlledSelection.tsx.preview @@ -1,13 +1,13 @@ \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.js b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.js similarity index 75% rename from docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.js rename to docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.js index fdf2cb447ca6c..6e0bbc061cd2f 100644 --- a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.js +++ b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.js @@ -35,26 +35,26 @@ const MUI_X_PRODUCTS = [ }, ]; -export default function TrackNodeSelectionToggle() { - const [lastSelectedNode, setLastSelectedNode] = React.useState(null); +export default function TrackItemSelectionToggle() { + const [lastSelectedItem, setLastSelectedItem] = React.useState(null); - const handleNodeSelectionToggle = (event, nodeId, isSelected) => { + const handleItemSelectionToggle = (event, itemId, isSelected) => { if (isSelected) { - setLastSelectedNode(nodeId); + setLastSelectedItem(itemId); } }; return ( - {lastSelectedNode == null - ? 'No node selection recorded' - : `Last selected node: ${lastSelectedNode}`} + {lastSelectedItem == null + ? 'No item selection recorded' + : `Last selected item: ${lastSelectedItem}`} diff --git a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx similarity index 77% rename from docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx rename to docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx index dc49b176b84a6..bd1b41122a185 100644 --- a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx +++ b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx @@ -35,32 +35,32 @@ const MUI_X_PRODUCTS: TreeViewBaseItem[] = [ }, ]; -export default function TrackNodeSelectionToggle() { - const [lastSelectedNode, setLastSelectedNode] = React.useState( +export default function TrackItemSelectionToggle() { + const [lastSelectedItem, setLastSelectedItem] = React.useState( null, ); - const handleNodeSelectionToggle = ( + const handleItemSelectionToggle = ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isSelected: boolean, ) => { if (isSelected) { - setLastSelectedNode(nodeId); + setLastSelectedItem(itemId); } }; return ( - {lastSelectedNode == null - ? 'No node selection recorded' - : `Last selected node: ${lastSelectedNode}`} + {lastSelectedItem == null + ? 'No item selection recorded' + : `Last selected item: ${lastSelectedItem}`} diff --git a/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx.preview b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx.preview new file mode 100644 index 0000000000000..2b55fdb43b56d --- /dev/null +++ b/docs/data/tree-view/rich-tree-view/selection/TrackItemSelectionToggle.tsx.preview @@ -0,0 +1,11 @@ + + {lastSelectedItem == null + ? 'No item selection recorded' + : `Last selected item: ${lastSelectedItem}`} + + + + \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx.preview b/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx.preview deleted file mode 100644 index a7ad6e6419cec..0000000000000 --- a/docs/data/tree-view/rich-tree-view/selection/TrackNodeSelectionToggle.tsx.preview +++ /dev/null @@ -1,11 +0,0 @@ - - {lastSelectedNode == null - ? 'No node selection recorded' - : `Last selected node: ${lastSelectedNode}`} - - - - \ No newline at end of file diff --git a/docs/data/tree-view/rich-tree-view/selection/selection.md b/docs/data/tree-view/rich-tree-view/selection/selection.md index 472d654ea28b9..90e60c40dcc41 100644 --- a/docs/data/tree-view/rich-tree-view/selection/selection.md +++ b/docs/data/tree-view/rich-tree-view/selection/selection.md @@ -25,22 +25,22 @@ Use the `disableSelection` prop if you don't want your items to be selectable: ## Controlled selection -Use the `selectedNodes` prop to control the selected items. +Use the `selectedItems` prop to control the selected items. -You can use the `onSelectedNodesChange` prop to listen to changes in the selected items and update the prop accordingly. +You can use the `onSelectedItemsChange` prop to listen to changes in the selected items and update the prop accordingly. {{"demo": "ControlledSelection.js"}} :::info -- The selection is **controlled** when its parent manages it by providing a `selectedNodes` prop. -- The selection is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultSelectedNodes` prop. +- The selection is **controlled** when its parent manages it by providing a `selectedItems` prop. +- The selection is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultSelectedItems` prop. Learn more about the _Controlled and uncontrolled_ pattern in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). ::: -## Track node selection change +## Track item selection change -Use the `onNodeSelectionToggle` prop if you want to react to a node selection change: +Use the `onItemSelectionToggle` prop if you want to react to an item selection change: -{{"demo": "TrackNodeSelectionToggle.js"}} +{{"demo": "TrackItemSelectionToggle.js"}} diff --git a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js index 188b8b986bc05..64bdeca0c9c65 100644 --- a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js @@ -39,7 +39,7 @@ export default function BorderedTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx index 44441404f0513..4d9ec87c58ff3 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx @@ -34,7 +34,7 @@ export default function CustomAnimation() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js index feaf4582c8d17..93805a66ff4d2 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js @@ -63,7 +63,7 @@ export default function CustomContentTreeView() { diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx index 5e4bf36f6798a..4eed51b5755a5 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx @@ -73,7 +73,7 @@ export default function CustomContentTreeView() { diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview index 54f412d163174..bcceb2d033480 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview @@ -1,7 +1,7 @@ diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js index 844e3faea6f38..93ca0557c4cef 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js @@ -32,7 +32,7 @@ export default function CustomIcons() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx index 325ca55790f66..68512c179f178 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx @@ -33,7 +33,7 @@ export default function CustomStyling() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js index 1abd841cf7cd6..3eb0ebf98490a 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js @@ -144,8 +144,8 @@ export default function CustomizedTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx index f3e80c99b95b3..62cf7aeadacf6 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx @@ -159,8 +159,8 @@ export default function CustomizedTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js index f1831034f7227..540f159f5df1c 100644 --- a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js @@ -143,8 +143,8 @@ export default function GmailTreeView() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx index 75bde63ccb430..9f8a617aad0fb 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx @@ -20,7 +20,7 @@ export default function LabelSlotProps() { return ( diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview index 0d56f866e88ca..8c983099963e7 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview @@ -1,6 +1,6 @@ diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js index db0483b0b8634..28d3659e4a0bd 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js @@ -30,7 +30,7 @@ const CustomTreeItem = React.forwardRef((props, ref) => { export default function LabelSlots() { return ( - + - + { - setExpandedNodes(nodeIds); + const handleExpandedItemsChange = (event, itemIds) => { + setExpandedItems(itemIds); }; const handleExpandClick = () => { - setExpandedNodes((oldExpanded) => + setExpandedItems((oldExpanded) => oldExpanded.length === 0 ? [ 'grid', @@ -35,13 +35,13 @@ export default function ControlledExpansion() { diff --git a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx index 369e4a0439f22..6fcc35adf19b2 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx +++ b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx @@ -5,17 +5,17 @@ import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; export default function ControlledExpansion() { - const [expandedNodes, setExpandedNodes] = React.useState([]); + const [expandedItems, setExpandedItems] = React.useState([]); - const handleExpandedNodesChange = ( + const handleExpandedItemsChange = ( event: React.SyntheticEvent, - nodeIds: string[], + itemIds: string[], ) => { - setExpandedNodes(nodeIds); + setExpandedItems(itemIds); }; const handleExpandClick = () => { - setExpandedNodes((oldExpanded) => + setExpandedItems((oldExpanded) => oldExpanded.length === 0 ? [ 'grid', @@ -38,13 +38,13 @@ export default function ControlledExpansion() { diff --git a/docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.js b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js similarity index 86% rename from docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.js rename to docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js index 255c0efbb54b4..b50e0cf04c880 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.js +++ b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js @@ -5,11 +5,11 @@ import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Typography from '@mui/material/Typography'; -export default function TrackNodeExpansionToggle() { +export default function TrackItemExpansionToggle() { const [action, setAction] = React.useState(null); - const handleNodeExpansionToggle = (event, nodeId, isExpanded) => { - setAction({ nodeId, isExpanded }); + const handleItemExpansionToggle = (event, itemId, isExpanded) => { + setAction({ itemId, isExpanded }); }; return ( @@ -18,12 +18,12 @@ export default function TrackNodeExpansionToggle() { No action recorded ) : ( - Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.nodeId} + Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.itemId} )} - + diff --git a/docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.tsx b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx similarity index 86% rename from docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.tsx rename to docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx index 3cdf96e2b5fc5..db4da5fe2876e 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/TrackNodeExpansionToggle.tsx +++ b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx @@ -5,18 +5,18 @@ import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Typography from '@mui/material/Typography'; -export default function TrackNodeExpansionToggle() { +export default function TrackItemExpansionToggle() { const [action, setAction] = React.useState<{ - nodeId: string; + itemId: string; isExpanded: boolean; } | null>(null); - const handleNodeExpansionToggle = ( + const handleItemExpansionToggle = ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isExpanded: boolean, ) => { - setAction({ nodeId, isExpanded }); + setAction({ itemId, isExpanded }); }; return ( @@ -25,11 +25,11 @@ export default function TrackNodeExpansionToggle() { No action recorded ) : ( - Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.nodeId} + Last action: {action.isExpanded ? 'expand' : 'collapse'} {action.itemId} )} - + diff --git a/docs/data/tree-view/simple-tree-view/expansion/expansion.md b/docs/data/tree-view/simple-tree-view/expansion/expansion.md index f85949aa2b062..296736f0d16a9 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/expansion.md +++ b/docs/data/tree-view/simple-tree-view/expansion/expansion.md @@ -13,21 +13,21 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ ## Controlled expansion -Use the `expandedNodes` prop to control the expanded items. -You can also use the `onExpandedNodesChange` prop to listen to changes in the expanded items and update the prop accordingly. +Use the `expandedItems` prop to control the expanded items. +You can also use the `onExpandedItemsChange` prop to listen to changes in the expanded items and update the prop accordingly. {{"demo": "ControlledExpansion.js"}} :::info -- The expansion is **controlled** when its parent manages it by providing a `expandedNodes` prop. -- The expansion is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultExpandedNodes` prop. +- The expansion is **controlled** when its parent manages it by providing a `expandedItems` prop. +- The expansion is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultExpandedItems` prop. Learn more about the _Controlled and uncontrolled_ pattern in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). ::: -## Track node expansion change +## Track item expansion change -Use the `onNodeExpansionToggle` prop to trigger an action upon a node being expanded. +Use the `onItemExpansionToggle` prop to trigger an action upon an item being expanded. -{{"demo": "TrackNodeExpansionToggle.js"}} +{{"demo": "TrackItemExpansionToggle.js"}} diff --git a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js index ae3dc438a6970..e8b445182d595 100644 --- a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js +++ b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js @@ -8,13 +8,13 @@ import { useTreeViewApiRef } from '@mui/x-tree-view/hooks/useTreeViewApiRef'; export default function FocusedSimpleTreeView() { const apiRef = useTreeViewApiRef(); const handleButtonClick = (event) => { - apiRef.current?.focusNode(event, 'pickers'); + apiRef.current?.focusItem(event, 'pickers'); }; return ( - + diff --git a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx index 4f2e6eac445de..810cae2363a28 100644 --- a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx @@ -8,13 +8,13 @@ import { useTreeViewApiRef } from '@mui/x-tree-view/hooks/useTreeViewApiRef'; export default function FocusedSimpleTreeView() { const apiRef = useTreeViewApiRef(); const handleButtonClick = (event: React.SyntheticEvent) => { - apiRef.current?.focusNode(event, 'pickers'); + apiRef.current?.focusItem(event, 'pickers'); }; return ( - + diff --git a/docs/data/tree-view/simple-tree-view/focus/focus.md b/docs/data/tree-view/simple-tree-view/focus/focus.md index 9f85933666c05..b408b02c9f35d 100644 --- a/docs/data/tree-view/simple-tree-view/focus/focus.md +++ b/docs/data/tree-view/simple-tree-view/focus/focus.md @@ -11,10 +11,10 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/

Learn how to focus Tree View items.

-## Focus a specific node +## Focus a specific item -You can use the the `apiRef.focusNode` method to focus a specific node. -This methods receives two parameters: `event` and `nodeId`. +You can use the the `apiRef.focusItem` method to focus a specific item. +This methods receives two parameters: `event` and `itemId`. :::success To use the `apiRef` object, you need to initialize it using the `useTreeViewApiRef` hook as follows: @@ -29,8 +29,8 @@ return {children}; ::: :::info -This method only works with nodes that are currently visible. -Calling `apiRef.focusNode` on a node whose parent is collapsed will do nothing. +This method only works with items that are currently visible. +Calling `apiRef.focusItem` on an item whose parent is collapsed will do nothing. ::: {{"demo": "FocusedSimpleTreeView.js"}} diff --git a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js index 9a9e18844b029..1d47a26774c7f 100644 --- a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js +++ b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js @@ -5,14 +5,14 @@ import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Button from '@mui/material/Button'; export default function ControlledSelection() { - const [selectedNodes, setSelectedNodes] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); - const handleSelectedNodesChange = (event, ids) => { - setSelectedNodes(ids); + const handleSelectedItemsChange = (event, ids) => { + setSelectedItems(ids); }; const handleSelectClick = () => { - setSelectedNodes((oldSelected) => + setSelectedItems((oldSelected) => oldSelected.length === 0 ? [ 'grid', @@ -35,13 +35,13 @@ export default function ControlledSelection() { diff --git a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx index 3f5fe3e4619fc..1b5d5f01692b3 100644 --- a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx @@ -5,14 +5,14 @@ import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Button from '@mui/material/Button'; export default function ControlledSelection() { - const [selectedNodes, setSelectedNodes] = React.useState([]); + const [selectedItems, setSelectedItems] = React.useState([]); - const handleSelectedNodesChange = (event: React.SyntheticEvent, ids: string[]) => { - setSelectedNodes(ids); + const handleSelectedItemsChange = (event: React.SyntheticEvent, ids: string[]) => { + setSelectedItems(ids); }; const handleSelectClick = () => { - setSelectedNodes((oldSelected) => + setSelectedItems((oldSelected) => oldSelected.length === 0 ? [ 'grid', @@ -35,13 +35,13 @@ export default function ControlledSelection() { diff --git a/docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.js b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js similarity index 77% rename from docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.js rename to docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js index 4fd35b6b0b25e..ff2d73fb65c97 100644 --- a/docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.js +++ b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js @@ -5,24 +5,24 @@ import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Typography from '@mui/material/Typography'; -export default function TrackNodeSelectionToggle() { - const [lastSelectedNode, setLastSelectedNode] = React.useState(null); +export default function TrackItemSelectionToggle() { + const [lastSelectedItem, setLastSelectedItem] = React.useState(null); - const handleNodeSelectionToggle = (event, nodeId, isSelected) => { + const handleItemSelectionToggle = (event, itemId, isSelected) => { if (isSelected) { - setLastSelectedNode(nodeId); + setLastSelectedItem(itemId); } }; return ( - {lastSelectedNode == null - ? 'No node selection recorded' - : `Last selected node: ${lastSelectedNode}`} + {lastSelectedItem == null + ? 'No item selection recorded' + : `Last selected item: ${lastSelectedItem}`} - + diff --git a/docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.tsx b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx similarity index 78% rename from docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.tsx rename to docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx index f978231c95ad6..f1fd58c9cdeca 100644 --- a/docs/data/tree-view/simple-tree-view/selection/TrackNodeSelectionToggle.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx @@ -5,30 +5,30 @@ import { SimpleTreeView } from '@mui/x-tree-view/SimpleTreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; import Typography from '@mui/material/Typography'; -export default function TrackNodeSelectionToggle() { - const [lastSelectedNode, setLastSelectedNode] = React.useState( +export default function TrackItemSelectionToggle() { + const [lastSelectedItem, setLastSelectedItem] = React.useState( null, ); - const handleNodeSelectionToggle = ( + const handleItemSelectionToggle = ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isSelected: boolean, ) => { if (isSelected) { - setLastSelectedNode(nodeId); + setLastSelectedItem(itemId); } }; return ( - {lastSelectedNode == null - ? 'No node selection recorded' - : `Last selected node: ${lastSelectedNode}`} + {lastSelectedItem == null + ? 'No item selection recorded' + : `Last selected item: ${lastSelectedItem}`} - + diff --git a/docs/data/tree-view/simple-tree-view/selection/selection.md b/docs/data/tree-view/simple-tree-view/selection/selection.md index 62201e5bb6473..da1aabecfa40b 100644 --- a/docs/data/tree-view/simple-tree-view/selection/selection.md +++ b/docs/data/tree-view/simple-tree-view/selection/selection.md @@ -25,21 +25,21 @@ Use the `disableSelection` prop if you don't want your items to be selectable: ## Controlled selection -Use the `selectedNodes` prop to control selected Tree View items. -You can also use the `onSelectedNodesChange` prop to listen to changes in the selected items and update the prop accordingly. +Use the `selectedItems` prop to control selected Tree View items. +You can also use the `onSelectedItemsChange` prop to listen to changes in the selected items and update the prop accordingly. {{"demo": "ControlledSelection.js"}} :::info -- The selection is **controlled** when its parent manages it by providing a `selectedNodes` prop. -- The selection is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultSelectedNodes` prop. +- The selection is **controlled** when its parent manages it by providing a `selectedItems` prop. +- The selection is **uncontrolled** when it is managed by the component's own internal state. This state can be initialized using the `defaultSelectedItems` prop. Learn more about the _Controlled and uncontrolled_ pattern in the [React documentation](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components). ::: -## Track node selection change +## Track item selection change -Use the `onNodeSelectionToggle` prop if you want to react to a node selection change: +Use the `onItemSelectionToggle` prop if you want to react to an item selection change: -{{"demo": "TrackNodeSelectionToggle.js"}} +{{"demo": "TrackItemSelectionToggle.js"}} diff --git a/docs/pages/x/api/tree-view/rich-tree-view.json b/docs/pages/x/api/tree-view/rich-tree-view.json index 10a0d28dfcc3e..181ae591f91cd 100644 --- a/docs/pages/x/api/tree-view/rich-tree-view.json +++ b/docs/pages/x/api/tree-view/rich-tree-view.json @@ -1,17 +1,17 @@ { "props": { "apiRef": { - "type": { "name": "shape", "description": "{ current?: { focusNode: func, getItem: func } }" } + "type": { "name": "shape", "description": "{ current?: { focusItem: func, getItem: func } }" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "defaultExpandedNodes": { + "defaultExpandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" }, "default": "[]" }, - "defaultSelectedNodes": { "type": { "name": "any" }, "default": "[]" }, + "defaultSelectedItems": { "type": { "name": "any" }, "default": "[]" }, "disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" }, "disableSelection": { "type": { "name": "bool" }, "default": "false" }, - "expandedNodes": { "type": { "name": "arrayOf", "description": "Array<string>" } }, + "expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } }, "getItemId": { "type": { "name": "func" }, "default": "`(item) => item.id`", @@ -40,42 +40,42 @@ } }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: array) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: array) => void", + "describedArgs": ["event", "itemIds"] } }, - "onItemFocus": { + "onItemExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", - "describedArgs": ["event", "itemId", "value"] + "type": "function(event: React.SyntheticEvent, itemId: array, isExpanded: array) => void", + "describedArgs": ["event", "itemId", "isExpanded"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isSelected: array) => void", - "describedArgs": ["event", "nodeId", "isSelected"] + "type": "function(event: React.SyntheticEvent, itemId: array, isSelected: array) => void", + "describedArgs": ["event", "itemId", "isSelected"] } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: Array | string) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: Array | string) => void", + "describedArgs": ["event", "itemIds"] } }, - "selectedNodes": { "type": { "name": "any" } }, + "selectedItems": { "type": { "name": "any" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, diff --git a/docs/pages/x/api/tree-view/simple-tree-view.json b/docs/pages/x/api/tree-view/simple-tree-view.json index 06fb7d3d0280d..5957aad770567 100644 --- a/docs/pages/x/api/tree-view/simple-tree-view.json +++ b/docs/pages/x/api/tree-view/simple-tree-view.json @@ -1,56 +1,56 @@ { "props": { "apiRef": { - "type": { "name": "shape", "description": "{ current?: { focusNode: func, getItem: func } }" } + "type": { "name": "shape", "description": "{ current?: { focusItem: func, getItem: func } }" } }, "children": { "type": { "name": "node" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "defaultExpandedNodes": { + "defaultExpandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" }, "default": "[]" }, - "defaultSelectedNodes": { "type": { "name": "any" }, "default": "[]" }, + "defaultSelectedItems": { "type": { "name": "any" }, "default": "[]" }, "disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" }, "disableSelection": { "type": { "name": "bool" }, "default": "false" }, - "expandedNodes": { "type": { "name": "arrayOf", "description": "Array<string>" } }, + "expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } }, "id": { "type": { "name": "string" } }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: array) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: array) => void", + "describedArgs": ["event", "itemIds"] } }, - "onItemFocus": { + "onItemExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", - "describedArgs": ["event", "itemId", "value"] + "type": "function(event: React.SyntheticEvent, itemId: array, isExpanded: array) => void", + "describedArgs": ["event", "itemId", "isExpanded"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isSelected: array) => void", - "describedArgs": ["event", "nodeId", "isSelected"] + "type": "function(event: React.SyntheticEvent, itemId: array, isSelected: array) => void", + "describedArgs": ["event", "itemId", "isSelected"] } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: Array | string) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: Array | string) => void", + "describedArgs": ["event", "itemIds"] } }, - "selectedNodes": { "type": { "name": "any" } }, + "selectedItems": { "type": { "name": "any" } }, "slotProps": { "type": { "name": "object" } }, "slots": { "type": { "name": "object" }, "additionalInfo": { "slotsApi": true } }, "sx": { diff --git a/docs/pages/x/api/tree-view/tree-item.json b/docs/pages/x/api/tree-view/tree-item.json index 5835afabd5928..62886e2866d29 100644 --- a/docs/pages/x/api/tree-view/tree-item.json +++ b/docs/pages/x/api/tree-view/tree-item.json @@ -31,12 +31,12 @@ "import { TreeItem } from '@mui/x-tree-view';" ], "slots": [ - { "name": "collapseIcon", "description": "The icon used to collapse the node.", "class": null }, - { "name": "expandIcon", "description": "The icon used to expand the node.", "class": null }, - { "name": "endIcon", "description": "The icon displayed next to an end node.", "class": null }, + { "name": "collapseIcon", "description": "The icon used to collapse the item.", "class": null }, + { "name": "expandIcon", "description": "The icon used to expand the item.", "class": null }, + { "name": "endIcon", "description": "The icon displayed next to an end item.", "class": null }, { "name": "icon", - "description": "The icon to display next to the tree node's label.", + "description": "The icon to display next to the tree item's label.", "class": null }, { diff --git a/docs/pages/x/api/tree-view/tree-view.json b/docs/pages/x/api/tree-view/tree-view.json index e36e618ba7f14..fae8965650e99 100644 --- a/docs/pages/x/api/tree-view/tree-view.json +++ b/docs/pages/x/api/tree-view/tree-view.json @@ -1,56 +1,56 @@ { "props": { "apiRef": { - "type": { "name": "shape", "description": "{ current?: { focusNode: func, getItem: func } }" } + "type": { "name": "shape", "description": "{ current?: { focusItem: func, getItem: func } }" } }, "children": { "type": { "name": "node" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "defaultExpandedNodes": { + "defaultExpandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" }, "default": "[]" }, - "defaultSelectedNodes": { "type": { "name": "any" }, "default": "[]" }, + "defaultSelectedItems": { "type": { "name": "any" }, "default": "[]" }, "disabledItemsFocusable": { "type": { "name": "bool" }, "default": "false" }, "disableSelection": { "type": { "name": "bool" }, "default": "false" }, - "expandedNodes": { "type": { "name": "arrayOf", "description": "Array<string>" } }, + "expandedItems": { "type": { "name": "arrayOf", "description": "Array<string>" } }, "id": { "type": { "name": "string" } }, "multiSelect": { "type": { "name": "bool" }, "default": "false" }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: array) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: array) => void", + "describedArgs": ["event", "itemIds"] } }, - "onItemFocus": { + "onItemExpansionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", - "describedArgs": ["event", "itemId", "value"] + "type": "function(event: React.SyntheticEvent, itemId: array, isExpanded: array) => void", + "describedArgs": ["event", "itemId", "isExpanded"] } }, - "onNodeExpansionToggle": { + "onItemFocus": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isExpanded: array) => void", - "describedArgs": ["event", "nodeId", "isExpanded"] + "type": "function(event: React.SyntheticEvent, itemId: string, value: string) => void", + "describedArgs": ["event", "itemId", "value"] } }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeId: array, isSelected: array) => void", - "describedArgs": ["event", "nodeId", "isSelected"] + "type": "function(event: React.SyntheticEvent, itemId: array, isSelected: array) => void", + "describedArgs": ["event", "itemId", "isSelected"] } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "type": { "name": "func" }, "signature": { - "type": "function(event: React.SyntheticEvent, nodeIds: Array | string) => void", - "describedArgs": ["event", "nodeIds"] + "type": "function(event: React.SyntheticEvent, itemIds: Array | string) => void", + "describedArgs": ["event", "itemIds"] } }, - "selectedNodes": { "type": { "name": "any" } }, + "selectedItems": { "type": { "name": "any" } }, "slotProps": { "type": { "name": "object" } }, "slots": { "type": { "name": "object" }, "additionalInfo": { "slotsApi": true } }, "sx": { diff --git a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json index cabd27145a140..14b1089423573 100644 --- a/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json +++ b/docs/translations/api-docs/tree-view/rich-tree-view/rich-tree-view.json @@ -5,18 +5,18 @@ "description": "The ref object that allows Tree View manipulation. Can be instantiated with useTreeViewApiRef()." }, "classes": { "description": "Override or extend the styles applied to the component." }, - "defaultExpandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is not controlled." + "defaultExpandedItems": { + "description": "Expanded item ids. Used when the item's expansion is not controlled." }, - "defaultSelectedNodes": { - "description": "Selected node ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." + "defaultSelectedItems": { + "description": "Selected item ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "disabledItemsFocusable": { "description": "If true, will allow focus on disabled items." }, "disableSelection": { "description": "If true selection is disabled." }, - "expandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is controlled." + "expandedItems": { + "description": "Expanded item ids. Used when the item's expansion is controlled." }, "getItemId": { "description": "Used to determine the string label for a given item.", @@ -39,11 +39,19 @@ "multiSelect": { "description": "If true ctrl and shift will trigger multiselect." }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "description": "Callback fired when tree items are expanded/collapsed.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeIds": "The ids of the expanded nodes." + "itemIds": "The ids of the expanded items." + } + }, + "onItemExpansionToggle": { + "description": "Callback fired when a tree item is expanded or collapsed.", + "typeDescriptions": { + "event": "The event source of the callback.", + "itemId": "The itemId of the modified item.", + "isExpanded": "true if the item has just been expanded, false if it has just been collapsed." } }, "onItemFocus": { @@ -54,31 +62,23 @@ "value": "of the focused item." } }, - "onNodeExpansionToggle": { - "description": "Callback fired when a tree item is expanded or collapsed.", - "typeDescriptions": { - "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." - } - }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isSelected": "true if the node has just been selected, false if it has just been deselected." + "itemId": "The itemId of the modified item.", + "isSelected": "true if the item has just been selected, false if it has just been deselected." } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "description": "Callback fired when tree items are selected/deselected.", "typeDescriptions": { "event": "The event source of the callback", - "nodeIds": "The ids of the selected nodes. When multiSelect is true, this is an array of strings; when false (default) a string." + "itemIds": "The ids of the selected items. When multiSelect is true, this is an array of strings; when false (default) a string." } }, - "selectedNodes": { - "description": "Selected node ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." + "selectedItems": { + "description": "Selected item ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." }, diff --git a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json index a220d49a0adc6..24f8592c31a09 100644 --- a/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json +++ b/docs/translations/api-docs/tree-view/simple-tree-view/simple-tree-view.json @@ -6,18 +6,18 @@ }, "children": { "description": "The content of the component." }, "classes": { "description": "Override or extend the styles applied to the component." }, - "defaultExpandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is not controlled." + "defaultExpandedItems": { + "description": "Expanded item ids. Used when the item's expansion is not controlled." }, - "defaultSelectedNodes": { - "description": "Selected node ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." + "defaultSelectedItems": { + "description": "Selected item ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "disabledItemsFocusable": { "description": "If true, will allow focus on disabled items." }, "disableSelection": { "description": "If true selection is disabled." }, - "expandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is controlled." + "expandedItems": { + "description": "Expanded item ids. Used when the item's expansion is controlled." }, "id": { "description": "This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id." @@ -25,11 +25,19 @@ "multiSelect": { "description": "If true ctrl and shift will trigger multiselect." }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "description": "Callback fired when tree items are expanded/collapsed.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeIds": "The ids of the expanded nodes." + "itemIds": "The ids of the expanded items." + } + }, + "onItemExpansionToggle": { + "description": "Callback fired when a tree item is expanded or collapsed.", + "typeDescriptions": { + "event": "The event source of the callback.", + "itemId": "The itemId of the modified item.", + "isExpanded": "true if the item has just been expanded, false if it has just been collapsed." } }, "onItemFocus": { @@ -40,31 +48,23 @@ "value": "of the focused item." } }, - "onNodeExpansionToggle": { - "description": "Callback fired when a tree item is expanded or collapsed.", - "typeDescriptions": { - "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." - } - }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isSelected": "true if the node has just been selected, false if it has just been deselected." + "itemId": "The itemId of the modified item.", + "isSelected": "true if the item has just been selected, false if it has just been deselected." } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "description": "Callback fired when tree items are selected/deselected.", "typeDescriptions": { "event": "The event source of the callback", - "nodeIds": "The ids of the selected nodes. When multiSelect is true, this is an array of strings; when false (default) a string." + "itemIds": "The ids of the selected items. When multiSelect is true, this is an array of strings; when false (default) a string." } }, - "selectedNodes": { - "description": "Selected node ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." + "selectedItems": { + "description": "Selected item ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." }, diff --git a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json index 560c99a58071c..5fb4062e31c84 100644 --- a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json +++ b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json @@ -8,7 +8,7 @@ "label": { "description": "The label of the node." }, "nodeId": { "description": "The id of the node. Must be unique." }, "onFocus": { - "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a node's focus." + "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a item's focus." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." } diff --git a/docs/translations/api-docs/tree-view/tree-item/tree-item.json b/docs/translations/api-docs/tree-view/tree-item/tree-item.json index bf8989332a330..4144332744dc9 100644 --- a/docs/translations/api-docs/tree-view/tree-item/tree-item.json +++ b/docs/translations/api-docs/tree-view/tree-item/tree-item.json @@ -8,11 +8,11 @@ "requiresRef": true }, "ContentProps": { "description": "Props applied to ContentComponent." }, - "disabled": { "description": "If true, the node is disabled." }, - "label": { "description": "The tree node label." }, + "disabled": { "description": "If true, the item is disabled." }, + "label": { "description": "The tree item label." }, "nodeId": { "description": "The id of the node." }, "onFocus": { - "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a node's focus." + "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a item's focus." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." }, @@ -53,10 +53,10 @@ } }, "slotDescriptions": { - "collapseIcon": "The icon used to collapse the node.", - "endIcon": "The icon displayed next to an end node.", - "expandIcon": "The icon used to expand the node.", + "collapseIcon": "The icon used to collapse the item.", + "endIcon": "The icon displayed next to an end item.", + "expandIcon": "The icon used to expand the item.", "groupTransition": "The component that animates to appearance / disappearance of the item's children.", - "icon": "The icon to display next to the tree node's label." + "icon": "The icon to display next to the tree item's label." } } diff --git a/docs/translations/api-docs/tree-view/tree-view/tree-view.json b/docs/translations/api-docs/tree-view/tree-view/tree-view.json index fc049580836cf..a8a98ee8ae876 100644 --- a/docs/translations/api-docs/tree-view/tree-view/tree-view.json +++ b/docs/translations/api-docs/tree-view/tree-view/tree-view.json @@ -6,18 +6,18 @@ }, "children": { "description": "The content of the component." }, "classes": { "description": "Override or extend the styles applied to the component." }, - "defaultExpandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is not controlled." + "defaultExpandedItems": { + "description": "Expanded item ids. Used when the item's expansion is not controlled." }, - "defaultSelectedNodes": { - "description": "Selected node ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." + "defaultSelectedItems": { + "description": "Selected item ids. (Uncontrolled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "disabledItemsFocusable": { "description": "If true, will allow focus on disabled items." }, "disableSelection": { "description": "If true selection is disabled." }, - "expandedNodes": { - "description": "Expanded node ids. Used when the item's expansion is controlled." + "expandedItems": { + "description": "Expanded item ids. Used when the item's expansion is controlled." }, "id": { "description": "This prop is used to help implement the accessibility logic. If you don't provide this prop. It falls back to a randomly generated id." @@ -25,11 +25,19 @@ "multiSelect": { "description": "If true ctrl and shift will trigger multiselect." }, - "onExpandedNodesChange": { + "onExpandedItemsChange": { "description": "Callback fired when tree items are expanded/collapsed.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeIds": "The ids of the expanded nodes." + "itemIds": "The ids of the expanded items." + } + }, + "onItemExpansionToggle": { + "description": "Callback fired when a tree item is expanded or collapsed.", + "typeDescriptions": { + "event": "The event source of the callback.", + "itemId": "The itemId of the modified item.", + "isExpanded": "true if the item has just been expanded, false if it has just been collapsed." } }, "onItemFocus": { @@ -40,31 +48,23 @@ "value": "of the focused item." } }, - "onNodeExpansionToggle": { - "description": "Callback fired when a tree item is expanded or collapsed.", - "typeDescriptions": { - "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isExpanded": "true if the node has just been expanded, false if it has just been collapsed." - } - }, - "onNodeSelectionToggle": { + "onItemSelectionToggle": { "description": "Callback fired when a tree item is selected or deselected.", "typeDescriptions": { "event": "The event source of the callback.", - "nodeId": "The nodeId of the modified node.", - "isSelected": "true if the node has just been selected, false if it has just been deselected." + "itemId": "The itemId of the modified item.", + "isSelected": "true if the item has just been selected, false if it has just been deselected." } }, - "onSelectedNodesChange": { + "onSelectedItemsChange": { "description": "Callback fired when tree items are selected/deselected.", "typeDescriptions": { "event": "The event source of the callback", - "nodeIds": "The ids of the selected nodes. When multiSelect is true, this is an array of strings; when false (default) a string." + "itemIds": "The ids of the selected items. When multiSelect is true, this is an array of strings; when false (default) a string." } }, - "selectedNodes": { - "description": "Selected node ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." + "selectedItems": { + "description": "Selected item ids. (Controlled) When multiSelect is true this takes an array of strings; when false (default) a string." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." }, diff --git a/packages/x-codemod/README.md b/packages/x-codemod/README.md index e3114b264f616..f89dbad4e1f62 100644 --- a/packages/x-codemod/README.md +++ b/packages/x-codemod/README.md @@ -275,13 +275,13 @@ Rename the expansion props ```diff ``` @@ -292,13 +292,13 @@ Rename the selection props ```diff ``` diff --git a/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/expected.spec.tsx b/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/expected.spec.tsx index 69878814ec2b9..64a105690c142 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/expected.spec.tsx +++ b/packages/x-codemod/src/v7.0.0/tree-view/preset-safe/expected.spec.tsx @@ -7,12 +7,12 @@ const className = simpleTreeViewClasses.root; // prettier-ignore - \ No newline at end of file +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/expected.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/expected.spec.js index e91a5960989d3..f8cbf2610fd51 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/expected.spec.js +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/expected.spec.js @@ -1,6 +1 @@ - - \ No newline at end of file +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/index.ts index 54fe0ac3ff5c4..f216f1a1306e7 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/index.ts +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-expansion-props/index.ts @@ -11,9 +11,9 @@ export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftA root, componentNames: ['TreeView', 'SimpleTreeView'], props: { - expanded: 'expandedNodes', - defaultExpanded: 'defaultExpandedNodes', - onNodeToggle: 'onExpandedNodesChange', + expanded: 'expandedItems', + defaultExpanded: 'defaultExpandedItems', + onNodeToggle: 'onExpandedItemsChange', }, j, }).toSource(printOptions); diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/actual.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/actual.spec.js index 17f5819bfa58a..1d1a16b102f2c 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/actual.spec.js +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/actual.spec.js @@ -1,6 +1 @@ - - \ No newline at end of file +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/expected.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/expected.spec.js index f56dacd74f7c0..1aa16c26752c7 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/expected.spec.js +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/expected.spec.js @@ -1,6 +1 @@ - - \ No newline at end of file +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/index.ts index a06e66276d311..40100125ce38b 100644 --- a/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/index.ts +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-selection-props/index.ts @@ -11,9 +11,9 @@ export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftA root, componentNames: ['TreeView', 'SimpleTreeView'], props: { - selected: 'selectedNodes', - defaultSelected: 'defaultSelectedNodes', - onNodeSelect: 'onSelectedNodesChange', + selected: 'selectedItems', + defaultSelected: 'defaultSelectedItems', + onNodeSelect: 'onSelectedItemsChange', }, j, }).toSource(printOptions); diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx index 84d8f504d4a20..b1131dec923b4 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx @@ -151,7 +151,7 @@ RichTreeView.propTypes = { */ apiRef: PropTypes.shape({ current: PropTypes.shape({ - focusNode: PropTypes.func.isRequired, + focusItem: PropTypes.func.isRequired, getItem: PropTypes.func.isRequired, }), }), @@ -161,17 +161,17 @@ RichTreeView.propTypes = { classes: PropTypes.object, className: PropTypes.string, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is not controlled. * @default [] */ - defaultExpandedNodes: PropTypes.arrayOf(PropTypes.string), + defaultExpandedItems: PropTypes.arrayOf(PropTypes.string), /** - * Selected node ids. (Uncontrolled) + * Selected item ids. (Uncontrolled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. * @default [] */ - defaultSelectedNodes: PropTypes.any, + defaultSelectedItems: PropTypes.any, /** * If `true`, will allow focus on disabled items. * @default false @@ -183,10 +183,10 @@ RichTreeView.propTypes = { */ disableSelection: PropTypes.bool, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is controlled. */ - expandedNodes: PropTypes.arrayOf(PropTypes.string), + expandedItems: PropTypes.arrayOf(PropTypes.string), /** * Used to determine the string label for a given item. * @@ -226,9 +226,16 @@ RichTreeView.propTypes = { /** * Callback fired when tree items are expanded/collapsed. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeIds The ids of the expanded nodes. + * @param {array} itemIds The ids of the expanded items. */ - onExpandedNodesChange: PropTypes.func, + onExpandedItemsChange: PropTypes.func, + /** + * Callback fired when a tree item is expanded or collapsed. + * @param {React.SyntheticEvent} event The event source of the callback. + * @param {array} itemId The itemId of the modified item. + * @param {array} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed. + */ + onItemExpansionToggle: PropTypes.func, /** * Callback fired when tree items are focused. * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. @@ -236,32 +243,25 @@ RichTreeView.propTypes = { * @param {string} value of the focused item. */ onItemFocus: PropTypes.func, - /** - * Callback fired when a tree item is expanded or collapsed. - * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. - */ - onNodeExpansionToggle: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isSelected `true` if the node has just been selected, `false` if it has just been deselected. + * @param {array} itemId The itemId of the modified item. + * @param {array} isSelected `true` if the item has just been selected, `false` if it has just been deselected. */ - onNodeSelectionToggle: PropTypes.func, + onItemSelectionToggle: PropTypes.func, /** * Callback fired when tree items are selected/deselected. * @param {React.SyntheticEvent} event The event source of the callback - * @param {string[] | string} nodeIds The ids of the selected nodes. + * @param {string[] | string} itemIds The ids of the selected items. * When `multiSelect` is `true`, this is an array of strings; when false (default) a string. */ - onSelectedNodesChange: PropTypes.func, + onSelectedItemsChange: PropTypes.func, /** - * Selected node ids. (Controlled) + * Selected item ids. (Controlled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. */ - selectedNodes: PropTypes.any, + selectedItems: PropTypes.any, /** * The props used for each component slot. * @default {} diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx index dcc29f123cc9e..d60e248a558bd 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx @@ -22,31 +22,31 @@ describe('', () => { })); describe('warnings', () => { - it('should warn when switching from controlled to uncontrolled of the expandedNodes prop', () => { + it('should warn when switching from controlled to uncontrolled of the expandedItems prop', () => { const { setProps } = render( - + , ); expect(() => { - setProps({ expandedNodes: undefined }); + setProps({ expandedItems: undefined }); }).toErrorDev( - 'MUI X: A component is changing the controlled expandedNodes state of TreeView to be uncontrolled.', + 'MUI X: A component is changing the controlled expandedItems state of TreeView to be uncontrolled.', ); }); - it('should warn when switching from controlled to uncontrolled of the selectedNodes prop', () => { + it('should warn when switching from controlled to uncontrolled of the selectedItems prop', () => { const { setProps } = render( - + , ); expect(() => { - setProps({ selectedNodes: undefined }); + setProps({ selectedItems: undefined }); }).toErrorDev( - 'MUI X: A component is changing the controlled selectedNodes state of TreeView to be uncontrolled.', + 'MUI X: A component is changing the controlled selectedItems state of TreeView to be uncontrolled.', ); }); @@ -63,7 +63,7 @@ describe('', () => { it('should not crash when selecting multiple items in a deeply nested tree', () => { render( - + @@ -198,14 +198,14 @@ describe('', () => { expect(handleBlur.callCount).to.equal(1); }); - it('should be able to be controlled with the expandedNodes prop', () => { + it('should be able to be controlled with the expandedItems prop', () => { function MyComponent() { const [expandedState, setExpandedState] = React.useState([]); - const onExpandedNodesChange = (event, nodes) => { - setExpandedState(nodes); + const onExpandedItemsChange = (event, items) => { + setExpandedState(items); }; return ( - + @@ -233,14 +233,14 @@ describe('', () => { expect(getByTestId('one')).to.have.attribute('aria-expanded', 'true'); }); - it('should be able to be controlled with the selectedNodes prop and singleSelect', () => { + it('should be able to be controlled with the selectedItems prop and singleSelect', () => { function MyComponent() { const [selectedState, setSelectedState] = React.useState(null); - const onSelectedNodesChange = (event, nodes) => { - setSelectedState(nodes); + const onSelectedItemsChange = (event, items) => { + setSelectedState(items); }; return ( - + @@ -263,16 +263,16 @@ describe('', () => { expect(getByTestId('two')).to.have.attribute('aria-selected', 'true'); }); - it('should be able to be controlled with the selectedNodes prop and multiSelect', () => { + it('should be able to be controlled with the selectedItems prop and multiSelect', () => { function MyComponent() { const [selectedState, setSelectedState] = React.useState([]); - const onSelectedNodesChange = (event, nodes) => { - setSelectedState(nodes); + const onSelectedItemsChange = (event, items) => { + setSelectedState(items); }; return ( @@ -388,7 +388,7 @@ describe('', () => { }); describe('onItemFocus', () => { - it('should be called when node is focused', () => { + it('should be called when item is focused', () => { const focusSpy = spy(); const { getByRole } = render( @@ -406,12 +406,12 @@ describe('', () => { }); }); - describe('onNodeToggle', () => { - it('should be called when a parent node label is clicked', () => { - const onExpandedNodesChange = spy(); + describe('onExpandedItemsChange', () => { + it('should be called when a parent item label is clicked', () => { + const onExpandedItemsChange = spy(); const { getByText } = render( - + @@ -420,15 +420,15 @@ describe('', () => { fireEvent.click(getByText('outer')); - expect(onExpandedNodesChange.callCount).to.equal(1); - expect(onExpandedNodesChange.args[0][1]).to.deep.equal(['1']); + expect(onExpandedItemsChange.callCount).to.equal(1); + expect(onExpandedItemsChange.args[0][1]).to.deep.equal(['1']); }); - it('should be called when a parent node icon is clicked', () => { - const onExpandedNodesChange = spy(); + it('should be called when a parent item icon is clicked', () => { + const onExpandedItemsChange = spy(); const { getByTestId } = render( - +
}} nodeId="1" label="outer"> @@ -437,8 +437,8 @@ describe('', () => { fireEvent.click(getByTestId('icon')); - expect(onExpandedNodesChange.callCount).to.equal(1); - expect(onExpandedNodesChange.args[0][1]).to.deep.equal(['1']); + expect(onExpandedItemsChange.callCount).to.equal(1); + expect(onExpandedItemsChange.args[0][1]).to.deep.equal(['1']); }); }); @@ -447,7 +447,7 @@ describe('', () => { const onItemFocus = spy(); const { getByRole } = render( - + , @@ -464,7 +464,7 @@ describe('', () => { const onItemFocus = spy(); const { getByRole } = render( - + , @@ -481,7 +481,7 @@ describe('', () => { const onItemFocus = spy(); const { getByRole } = render( - + @@ -500,7 +500,7 @@ describe('', () => { const onItemFocus = spy(); const { getByRole } = render( - + @@ -519,7 +519,7 @@ describe('', () => { const onItemFocus = spy(); const { getByRole } = render( - + @@ -553,7 +553,7 @@ describe('', () => { const { getByRole } = render(); act(() => { - apiRef.current?.focusNode({} as React.SyntheticEvent, '2'); + apiRef.current?.focusItem({} as React.SyntheticEvent, '2'); }); expect(getByRole('tree')).toHaveFocus(); @@ -579,7 +579,7 @@ describe('', () => { const { getByRole } = render(); act(() => { - apiRef.current?.focusNode({} as React.SyntheticEvent, '1.1'); + apiRef.current?.focusItem({} as React.SyntheticEvent, '1.1'); }); expect(getByRole('tree')).not.toHaveFocus(); diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx index c72e1f7ac3e00..54c093ba1b5b6 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.tsx @@ -114,7 +114,7 @@ SimpleTreeView.propTypes = { */ apiRef: PropTypes.shape({ current: PropTypes.shape({ - focusNode: PropTypes.func.isRequired, + focusItem: PropTypes.func.isRequired, getItem: PropTypes.func.isRequired, }), }), @@ -128,17 +128,17 @@ SimpleTreeView.propTypes = { classes: PropTypes.object, className: PropTypes.string, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is not controlled. * @default [] */ - defaultExpandedNodes: PropTypes.arrayOf(PropTypes.string), + defaultExpandedItems: PropTypes.arrayOf(PropTypes.string), /** - * Selected node ids. (Uncontrolled) + * Selected item ids. (Uncontrolled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. * @default [] */ - defaultSelectedNodes: PropTypes.any, + defaultSelectedItems: PropTypes.any, /** * If `true`, will allow focus on disabled items. * @default false @@ -150,10 +150,10 @@ SimpleTreeView.propTypes = { */ disableSelection: PropTypes.bool, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is controlled. */ - expandedNodes: PropTypes.arrayOf(PropTypes.string), + expandedItems: PropTypes.arrayOf(PropTypes.string), /** * This prop is used to help implement the accessibility logic. * If you don't provide this prop. It falls back to a randomly generated id. @@ -167,9 +167,16 @@ SimpleTreeView.propTypes = { /** * Callback fired when tree items are expanded/collapsed. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeIds The ids of the expanded nodes. + * @param {array} itemIds The ids of the expanded items. */ - onExpandedNodesChange: PropTypes.func, + onExpandedItemsChange: PropTypes.func, + /** + * Callback fired when a tree item is expanded or collapsed. + * @param {React.SyntheticEvent} event The event source of the callback. + * @param {array} itemId The itemId of the modified item. + * @param {array} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed. + */ + onItemExpansionToggle: PropTypes.func, /** * Callback fired when tree items are focused. * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. @@ -177,32 +184,25 @@ SimpleTreeView.propTypes = { * @param {string} value of the focused item. */ onItemFocus: PropTypes.func, - /** - * Callback fired when a tree item is expanded or collapsed. - * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. - */ - onNodeExpansionToggle: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isSelected `true` if the node has just been selected, `false` if it has just been deselected. + * @param {array} itemId The itemId of the modified item. + * @param {array} isSelected `true` if the item has just been selected, `false` if it has just been deselected. */ - onNodeSelectionToggle: PropTypes.func, + onItemSelectionToggle: PropTypes.func, /** * Callback fired when tree items are selected/deselected. * @param {React.SyntheticEvent} event The event source of the callback - * @param {string[] | string} nodeIds The ids of the selected nodes. + * @param {string[] | string} itemIds The ids of the selected items. * When `multiSelect` is `true`, this is an array of strings; when false (default) a string. */ - onSelectedNodesChange: PropTypes.func, + onSelectedItemsChange: PropTypes.func, /** - * Selected node ids. (Controlled) + * Selected item ids. (Controlled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. */ - selectedNodes: PropTypes.any, + selectedItems: PropTypes.any, /** * The props used for each component slot. */ diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index 14b442ca9446d..4e0a49f2eda27 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -21,7 +21,7 @@ const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue mapFirstCharFromJSX: () => () => {}, } as any, publicAPI: { - focusNode: () => {}, + focusItem: () => {}, getItem: () => ({}), }, runItemPlugins: () => ({ rootRef: null, contentRef: null }), @@ -112,7 +112,7 @@ describe('', () => { collapseIcon: () =>
, endIcon: () =>
, }} - defaultExpandedNodes={['1']} + defaultExpandedItems={['1']} > @@ -153,7 +153,7 @@ describe('', () => { - + {!hide && } @@ -172,7 +172,7 @@ describe('', () => { it('should treat an empty array equally to no children', () => { const { getByTestId } = render( - + {[]} @@ -186,7 +186,7 @@ describe('', () => { it('should treat multiple empty conditional arrays as empty', () => { const { getByTestId } = render( - + {[].map((_, index) => ( @@ -205,7 +205,7 @@ describe('', () => { it('should treat one conditional empty and one conditional with results as expandable', () => { const { getByTestId } = render( - + {[]} @@ -222,7 +222,7 @@ describe('', () => { it('should handle edge case of nested array of array', () => { const { getByTestId } = render( - + {[[]]} @@ -238,7 +238,7 @@ describe('', () => { const handleClick = spy(); const { getByText } = render( - + @@ -277,7 +277,7 @@ describe('', () => { it('should add the role `group` to a component containing children', () => { const { getByRole, getByText } = render( - + @@ -302,7 +302,7 @@ describe('', () => { it('should have the attribute `aria-expanded={true}` if expanded', () => { const { getByTestId } = render( - + @@ -359,7 +359,7 @@ describe('', () => { it('should have the attribute `aria-selected={true}` if selected', () => { const { getByTestId } = render( - + , ); @@ -381,7 +381,7 @@ describe('', () => { it('should have the attribute `aria-selected={true}` if selected', () => { const { getByTestId } = render( - + , ); @@ -422,7 +422,7 @@ describe('', () => { it('should focus the selected node if a node is selected before the tree receives focus', () => { const { getByTestId, getByRole } = render( - + @@ -458,7 +458,7 @@ describe('', () => { expect(getByTestId('two')).toHaveVirtualFocus(); }); - it('should work when focused node is removed', () => { + it('should work when focused item is removed', () => { let removeActiveItem; // a TreeItem which can remove from the tree by calling `removeActiveItem` function ControlledTreeItem(props) { @@ -472,7 +472,7 @@ describe('', () => { } const { getByRole, getByTestId, getByText } = render( - + @@ -539,9 +539,9 @@ describe('', () => { expect(getByTestId('one')).toHaveVirtualFocus(); }); - it('should move focus to the first child if focus is on an open node', () => { + it('should move focus to the first child if focus is on an open item', () => { const { getByTestId, getByRole } = render( - + @@ -558,9 +558,9 @@ describe('', () => { expect(getByTestId('two')).toHaveVirtualFocus(); }); - it('should do nothing if focus is on an end node', () => { + it('should do nothing if focus is on an end item', () => { const { getByRole, getByTestId, getByText } = render( - + @@ -604,9 +604,9 @@ describe('', () => { expect(screen.getByTestId('one')).toHaveVirtualFocus(); }); - it("should move focus to the node's parent node if focus is on a child node that is an end node", () => { + it("should move focus to the item's parent item if focus is on a child that is an end node", () => { render( - + @@ -707,9 +707,9 @@ describe('', () => { expect(getByTestId('two')).toHaveVirtualFocus(); }); - it('moves focus to a child node', () => { + it('moves focus to a child item', () => { const { getByRole, getByTestId } = render( - + @@ -726,7 +726,7 @@ describe('', () => { expect(getByTestId('two')).toHaveVirtualFocus(); }); - it('moves focus to a child node works with a dynamic tree', () => { + it('moves focus to a child item works with a dynamic tree', () => { function TestComponent() { const [hide, setState] = React.useState(false); @@ -739,7 +739,7 @@ describe('', () => { > Toggle Hide - + {!hide && ( @@ -769,7 +769,7 @@ describe('', () => { it("moves focus to a parent's sibling", () => { const { getByRole, getByTestId, getByText } = render( - + @@ -815,7 +815,7 @@ describe('', () => { it('moves focus to a parent', () => { const { getByRole, getByTestId, getByText } = render( - + @@ -838,7 +838,7 @@ describe('', () => { it("moves focus to a sibling's child", () => { const { getByRole, getByTestId, getByText } = render( - + @@ -907,9 +907,9 @@ describe('', () => { expect(getByTestId('four')).toHaveVirtualFocus(); }); - it('moves focus to the last node in the tree with expanded items', () => { + it('moves focus to the last item in the tree with expanded items', () => { const { getByRole, getByTestId } = render( - + @@ -1057,11 +1057,11 @@ describe('', () => { }); describe('asterisk key interaction', () => { - it('expands all siblings that are at the same level as the current node', () => { - const onExpandedNodesChange = spy(); + it('expands all siblings that are at the same level as the current item', () => { + const onExpandedItemsChange = spy(); const { getByRole, getByTestId } = render( - + @@ -1087,7 +1087,7 @@ describe('', () => { fireEvent.keyDown(getByRole('tree'), { key: '*' }); - expect(onExpandedNodesChange.args[0][1]).to.have.length(3); + expect(onExpandedItemsChange.args[0][1]).to.have.length(3); expect(getByTestId('one')).to.have.attribute('aria-expanded', 'true'); expect(getByTestId('three')).to.have.attribute('aria-expanded', 'true'); @@ -1165,7 +1165,7 @@ describe('', () => { it('should not deselect a node when space is pressed on a selected node', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1214,7 +1214,7 @@ describe('', () => { it('should not un-select a node when Enter is pressed and the node is selected', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1243,7 +1243,7 @@ describe('', () => { it('should not deselect a node when clicking a selected node', () => { const { getByText, getByTestId } = render( - + , ); @@ -1271,7 +1271,7 @@ describe('', () => { describe('mouse behavior when multiple nodes are selected', () => { specify('clicking a selected node holding ctrl should deselect the node', () => { const { getByText, getByTestId } = render( - + , @@ -1286,7 +1286,7 @@ describe('', () => { specify('clicking a selected node holding meta should deselect the node', () => { const { getByText, getByTestId } = render( - + , @@ -1303,7 +1303,7 @@ describe('', () => { describe('mouse behavior when one node is selected', () => { it('clicking a selected node shout not deselect the node', () => { const { getByText, getByTestId } = render( - + , @@ -1317,9 +1317,9 @@ describe('', () => { }); }); - it('should deselect the node when pressing space on a selected node', () => { + it('should deselect the item when pressing space on a selected item', () => { const { getByTestId, getByRole } = render( - + , ); @@ -1338,7 +1338,7 @@ describe('', () => { describe('range selection', () => { specify('keyboard arrow', () => { const { getByRole, getByTestId, getByText, queryAllByRole } = render( - + @@ -1416,7 +1416,7 @@ describe('', () => { specify('keyboard arrow merge', () => { const { getByRole, getByTestId, getByText, queryAllByRole } = render( - + @@ -1450,7 +1450,7 @@ describe('', () => { specify('keyboard space', () => { const { getByRole, getByTestId, getByText } = render( - + @@ -1497,7 +1497,7 @@ describe('', () => { specify('keyboard home and end', () => { const { getByRole, getByTestId } = render( - + @@ -1547,7 +1547,7 @@ describe('', () => { specify('keyboard home and end do not select when selectionDisabled', () => { const { getByRole, getByText, queryAllByRole } = render( - + @@ -1587,7 +1587,7 @@ describe('', () => { specify('mouse', () => { const { getByTestId, getByText } = render( - + @@ -1652,7 +1652,7 @@ describe('', () => { specify('mouse does not range select when selectionDisabled', () => { const { getByText, queryAllByRole } = render( - + @@ -2282,7 +2282,7 @@ describe('', () => { it('should prevent collapse on left arrow', () => { const { getByRole, getByTestId } = render( - + @@ -2332,7 +2332,7 @@ describe('', () => { it('should disable child items when parent item is disabled', () => { const { getByTestId } = render( - + @@ -2378,7 +2378,7 @@ describe('', () => { it('should be able to type in an child input', () => { const { getByRole } = render( - + , */ ContentProps?: React.HTMLAttributes & { ref?: React.Ref }; /** - * If `true`, the node is disabled. + * If `true`, the item is disabled. * @default false */ disabled?: boolean; /** * This prop isn't supported. - * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a item's focus. */ onFocus?: null; /** - * The tree node label. + * The tree item label. */ label?: React.ReactNode; /** diff --git a/packages/x-tree-view/src/TreeItem/useTreeItemState.ts b/packages/x-tree-view/src/TreeItem/useTreeItemState.ts index 8fc755f6bff1e..ea80469e22b32 100644 --- a/packages/x-tree-view/src/TreeItem/useTreeItemState.ts +++ b/packages/x-tree-view/src/TreeItem/useTreeItemState.ts @@ -17,7 +17,7 @@ export function useTreeItemState(nodeId: string) { const handleExpansion = (event: React.MouseEvent) => { if (!disabled) { if (!focused) { - instance.focusNode(event, nodeId); + instance.focusItem(event, nodeId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); @@ -32,7 +32,7 @@ export function useTreeItemState(nodeId: string) { const handleSelection = (event: React.MouseEvent) => { if (!disabled) { if (!focused) { - instance.focusNode(event, nodeId); + instance.focusItem(event, nodeId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx index 92b0ba9f4d9bc..45cddfc5efd6e 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx @@ -300,7 +300,7 @@ TreeItem2.propTypes = { nodeId: PropTypes.string.isRequired, /** * This prop isn't supported. - * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a item's focus. */ onFocus: unsupportedProp, /** diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts index ddf3d38d3b4e7..a2bb8a88b2437 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.types.ts @@ -61,7 +61,7 @@ export interface TreeItem2Props slotProps?: TreeItem2SlotProps; /** * This prop isn't supported. - * Use the `onItemFocus` callback on the tree if you need to monitor a node's focus. + * Use the `onItemFocus` callback on the tree if you need to monitor a item's focus. */ onFocus?: null; } diff --git a/packages/x-tree-view/src/TreeView/TreeView.tsx b/packages/x-tree-view/src/TreeView/TreeView.tsx index a5c3f5350afe7..6cc59d4ff1623 100644 --- a/packages/x-tree-view/src/TreeView/TreeView.tsx +++ b/packages/x-tree-view/src/TreeView/TreeView.tsx @@ -91,7 +91,7 @@ TreeView.propTypes = { */ apiRef: PropTypes.shape({ current: PropTypes.shape({ - focusNode: PropTypes.func.isRequired, + focusItem: PropTypes.func.isRequired, getItem: PropTypes.func.isRequired, }), }), @@ -105,17 +105,17 @@ TreeView.propTypes = { classes: PropTypes.object, className: PropTypes.string, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is not controlled. * @default [] */ - defaultExpandedNodes: PropTypes.arrayOf(PropTypes.string), + defaultExpandedItems: PropTypes.arrayOf(PropTypes.string), /** - * Selected node ids. (Uncontrolled) + * Selected item ids. (Uncontrolled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. * @default [] */ - defaultSelectedNodes: PropTypes.any, + defaultSelectedItems: PropTypes.any, /** * If `true`, will allow focus on disabled items. * @default false @@ -127,10 +127,10 @@ TreeView.propTypes = { */ disableSelection: PropTypes.bool, /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is controlled. */ - expandedNodes: PropTypes.arrayOf(PropTypes.string), + expandedItems: PropTypes.arrayOf(PropTypes.string), /** * This prop is used to help implement the accessibility logic. * If you don't provide this prop. It falls back to a randomly generated id. @@ -144,9 +144,16 @@ TreeView.propTypes = { /** * Callback fired when tree items are expanded/collapsed. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeIds The ids of the expanded nodes. + * @param {array} itemIds The ids of the expanded items. */ - onExpandedNodesChange: PropTypes.func, + onExpandedItemsChange: PropTypes.func, + /** + * Callback fired when a tree item is expanded or collapsed. + * @param {React.SyntheticEvent} event The event source of the callback. + * @param {array} itemId The itemId of the modified item. + * @param {array} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed. + */ + onItemExpansionToggle: PropTypes.func, /** * Callback fired when tree items are focused. * @param {React.SyntheticEvent} event The event source of the callback **Warning**: This is a generic event not a focus event. @@ -154,32 +161,25 @@ TreeView.propTypes = { * @param {string} value of the focused item. */ onItemFocus: PropTypes.func, - /** - * Callback fired when a tree item is expanded or collapsed. - * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. - */ - onNodeExpansionToggle: PropTypes.func, /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isSelected `true` if the node has just been selected, `false` if it has just been deselected. + * @param {array} itemId The itemId of the modified item. + * @param {array} isSelected `true` if the item has just been selected, `false` if it has just been deselected. */ - onNodeSelectionToggle: PropTypes.func, + onItemSelectionToggle: PropTypes.func, /** * Callback fired when tree items are selected/deselected. * @param {React.SyntheticEvent} event The event source of the callback - * @param {string[] | string} nodeIds The ids of the selected nodes. + * @param {string[] | string} itemIds The ids of the selected items. * When `multiSelect` is `true`, this is an array of strings; when false (default) a string. */ - onSelectedNodesChange: PropTypes.func, + onSelectedItemsChange: PropTypes.func, /** - * Selected node ids. (Controlled) + * Selected item ids. (Controlled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. */ - selectedNodes: PropTypes.any, + selectedItems: PropTypes.any, /** * The props used for each component slot. */ diff --git a/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx index 2c73a697b1ac3..e6db24c035627 100644 --- a/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx +++ b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx @@ -39,7 +39,7 @@ export const useTreeItem2Utils = ({ } if (!status.focused) { - instance.focusNode(event, nodeId); + instance.focusItem(event, nodeId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); @@ -56,7 +56,7 @@ export const useTreeItem2Utils = ({ } if (!status.focused) { - instance.focusNode(event, nodeId); + instance.focusItem(event, nodeId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts index e26bcd3f6cd9c..1b89304453640 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts @@ -9,18 +9,18 @@ export const useTreeViewExpansion: TreeViewPlugin params, models, }) => { - const setExpandedNodes = (event: React.SyntheticEvent, value: string[]) => { - params.onExpandedNodesChange?.(event, value); - models.expandedNodes.setControlledValue(value); + const setExpandedItems = (event: React.SyntheticEvent, value: string[]) => { + params.onExpandedItemsChange?.(event, value); + models.expandedItems.setControlledValue(value); }; const isNodeExpanded = React.useCallback( (nodeId: string) => { - return Array.isArray(models.expandedNodes.value) - ? models.expandedNodes.value.indexOf(nodeId) !== -1 + return Array.isArray(models.expandedItems.value) + ? models.expandedItems.value.indexOf(nodeId) !== -1 : false; }, - [models.expandedNodes.value], + [models.expandedItems.value], ); const isNodeExpandable = React.useCallback( @@ -29,25 +29,25 @@ export const useTreeViewExpansion: TreeViewPlugin ); const toggleNodeExpansion = useEventCallback( - (event: React.SyntheticEvent, nodeId: string | null) => { - if (nodeId == null) { + (event: React.SyntheticEvent, itemId: string | null) => { + if (itemId == null) { return; } - const isExpandedBefore = models.expandedNodes.value.indexOf(nodeId!) !== -1; + const isExpandedBefore = models.expandedItems.value.indexOf(itemId!) !== -1; let newExpanded: string[]; if (isExpandedBefore) { - newExpanded = models.expandedNodes.value.filter((id) => id !== nodeId); + newExpanded = models.expandedItems.value.filter((id) => id !== itemId); } else { - newExpanded = [nodeId].concat(models.expandedNodes.value); + newExpanded = [itemId].concat(models.expandedItems.value); } - if (params.onNodeExpansionToggle) { - params.onNodeExpansionToggle(event, nodeId, !isExpandedBefore); + if (params.onItemExpansionToggle) { + params.onItemExpansionToggle(event, itemId, !isExpandedBefore); } - setExpandedNodes(event, newExpanded); + setExpandedItems(event, newExpanded); }, ); @@ -59,16 +59,16 @@ export const useTreeViewExpansion: TreeViewPlugin (child) => instance.isNodeExpandable(child) && !instance.isNodeExpanded(child), ); - const newExpanded = models.expandedNodes.value.concat(diff); + const newExpanded = models.expandedItems.value.concat(diff); if (diff.length > 0) { - if (params.onNodeExpansionToggle) { + if (params.onItemExpansionToggle) { diff.forEach((newlyExpandedNodeId) => { - params.onNodeExpansionToggle!(event, newlyExpandedNodeId, true); + params.onItemExpansionToggle!(event, newlyExpandedNodeId, true); }); } - setExpandedNodes(event, newExpanded); + setExpandedItems(event, newExpanded); } }; @@ -81,8 +81,8 @@ export const useTreeViewExpansion: TreeViewPlugin }; useTreeViewExpansion.models = { - expandedNodes: { - getDefaultValue: (params) => params.defaultExpandedNodes, + expandedItems: { + getDefaultValue: (params) => params.defaultExpandedItems, }, }; @@ -90,12 +90,12 @@ const DEFAULT_EXPANDED_NODES: string[] = []; useTreeViewExpansion.getDefaultizedParams = (params) => ({ ...params, - defaultExpandedNodes: params.defaultExpandedNodes ?? DEFAULT_EXPANDED_NODES, + defaultExpandedItems: params.defaultExpandedItems ?? DEFAULT_EXPANDED_NODES, }); useTreeViewExpansion.params = { - expandedNodes: true, - defaultExpandedNodes: true, - onExpandedNodesChange: true, - onNodeExpansionToggle: true, + expandedItems: true, + defaultExpandedItems: true, + onExpandedItemsChange: true, + onItemExpansionToggle: true, }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts index 46a2e2c80fa57..966b89a82bf14 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts @@ -11,44 +11,44 @@ export interface UseTreeViewExpansionInstance { export interface UseTreeViewExpansionParameters { /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is controlled. */ - expandedNodes?: string[]; + expandedItems?: string[]; /** - * Expanded node ids. + * Expanded item ids. * Used when the item's expansion is not controlled. * @default [] */ - defaultExpandedNodes?: string[]; + defaultExpandedItems?: string[]; /** * Callback fired when tree items are expanded/collapsed. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeIds The ids of the expanded nodes. + * @param {array} itemIds The ids of the expanded items. */ - onExpandedNodesChange?: (event: React.SyntheticEvent, nodeIds: string[]) => void; + onExpandedItemsChange?: (event: React.SyntheticEvent, itemIds: string[]) => void; /** * Callback fired when a tree item is expanded or collapsed. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isExpanded `true` if the node has just been expanded, `false` if it has just been collapsed. + * @param {array} itemId The itemId of the modified item. + * @param {array} isExpanded `true` if the item has just been expanded, `false` if it has just been collapsed. */ - onNodeExpansionToggle?: ( + onItemExpansionToggle?: ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isExpanded: boolean, ) => void; } export type UseTreeViewExpansionDefaultizedParameters = DefaultizedProps< UseTreeViewExpansionParameters, - 'defaultExpandedNodes' + 'defaultExpandedItems' >; export type UseTreeViewExpansionSignature = TreeViewPluginSignature<{ params: UseTreeViewExpansionParameters; defaultizedParams: UseTreeViewExpansionDefaultizedParameters; instance: UseTreeViewExpansionInstance; - modelNames: 'expandedNodes'; + modelNames: 'expandedItems'; dependantPlugins: [UseTreeViewNodesSignature]; }>; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts index c238b4d457275..0e90c9e787b3f 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts @@ -39,7 +39,7 @@ export const useTreeViewFocus: TreeViewPlugin = ({ return node && (node.parentId == null || instance.isNodeExpanded(node.parentId)); }; - const focusNode = useEventCallback((event: React.SyntheticEvent, nodeId: string | null) => { + const focusItem = useEventCallback((event: React.SyntheticEvent, nodeId: string | null) => { // if we receive a nodeId, and it is visible, the focus will be set to it if (nodeId && isNodeVisible(nodeId)) { if (!isTreeViewFocused()) { @@ -54,10 +54,10 @@ export const useTreeViewFocus: TreeViewPlugin = ({ const focusDefaultNode = useEventCallback((event: React.SyntheticEvent) => { let nodeToFocusId: string | null | undefined; - if (Array.isArray(models.selectedNodes.value)) { - nodeToFocusId = models.selectedNodes.value.find(isNodeVisible); - } else if (models.selectedNodes.value != null && isNodeVisible(models.selectedNodes.value)) { - nodeToFocusId = models.selectedNodes.value; + if (Array.isArray(models.selectedItems.value)) { + nodeToFocusId = models.selectedItems.value.find(isNodeVisible); + } else if (models.selectedItems.value != null && isNodeVisible(models.selectedItems.value)) { + nodeToFocusId = models.selectedItems.value; } if (nodeToFocusId == null) { @@ -76,13 +76,13 @@ export const useTreeViewFocus: TreeViewPlugin = ({ populateInstance(instance, { isNodeFocused, - focusNode, + focusItem, focusRoot, focusDefaultNode, }); populatePublicAPI(publicAPI, { - focusNode, + focusItem, }); useInstanceEventHandler(instance, 'removeNode', ({ id }) => { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts index b4ed569f1520f..c9294f14d09db 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts @@ -7,12 +7,12 @@ import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion'; export interface UseTreeViewFocusInstance { isNodeFocused: (nodeId: string) => boolean; - focusNode: (event: React.SyntheticEvent, nodeId: string | null) => void; + focusItem: (event: React.SyntheticEvent, itemId: string | null) => void; focusDefaultNode: (event: React.SyntheticEvent) => void; focusRoot: () => void; } -export interface UseTreeViewFocusPublicAPI extends Pick {} +export interface UseTreeViewFocusPublicAPI extends Pick {} export interface UseTreeViewFocusParameters { /** diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts index deeb8cd5bb6f2..48acfb2c2e831 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts @@ -174,7 +174,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< const nextNode = getNextNode(instance, state.focusedNodeId); if (nextNode) { event.preventDefault(); - instance.focusNode(event, nextNode); + instance.focusItem(event, nextNode); // Multi select behavior when pressing Shift + ArrowDown // Toggles the selection state of the next node @@ -198,7 +198,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< const previousNode = getPreviousNode(instance, state.focusedNodeId); if (previousNode) { event.preventDefault(); - instance.focusNode(event, previousNode); + instance.focusItem(event, previousNode); // Multi select behavior when pressing Shift + ArrowUp // Toggles the selection state of the previous node @@ -221,7 +221,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // If the focused node is collapsed and has children, we expand it case (key === 'ArrowRight' && !isRTL) || (key === 'ArrowLeft' && isRTL): { if (instance.isNodeExpanded(state.focusedNodeId)) { - instance.focusNode(event, getNextNode(instance, state.focusedNodeId)); + instance.focusItem(event, getNextNode(instance, state.focusedNodeId)); event.preventDefault(); } else if (canToggleNodeExpansion(state.focusedNodeId)) { instance.toggleNodeExpansion(event, state.focusedNodeId); @@ -243,7 +243,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< } else { const parent = instance.getNode(state.focusedNodeId).parentId; if (parent) { - instance.focusNode(event, parent); + instance.focusItem(event, parent); event.preventDefault(); } } @@ -253,7 +253,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Focuses the first node in the tree case key === 'Home': { - instance.focusNode(event, getFirstNode(instance)); + instance.focusItem(event, getFirstNode(instance)); // Multi select behavior when pressing Ctrl + Shift + Home // Selects the focused node and all nodes up to the first node. @@ -272,7 +272,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Focuses the last node in the tree case key === 'End': { - instance.focusNode(event, getLastNode(instance)); + instance.focusItem(event, getLastNode(instance)); // Multi select behavior when pressing Ctrl + Shirt + End // Selects the focused node and all the nodes down to the last node. @@ -312,7 +312,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key): { const matchingNode = getFirstMatchingNode(state.focusedNodeId, key); if (matchingNode != null) { - instance.focusNode(event, matchingNode); + instance.focusItem(event, matchingNode); event.preventDefault(); } break; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts index 3d30f49d8f5a4..1d818fdf74eac 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts @@ -18,47 +18,47 @@ export const useTreeViewSelection: TreeViewPlugin const lastSelectionWasRange = React.useRef(false); const currentRangeSelection = React.useRef([]); - const setSelectedNodes = ( + const setSelectedItems = ( event: React.SyntheticEvent, - newSelectedNodes: typeof params.defaultSelectedNodes, + newSelectedItems: typeof params.defaultSelectedItems, ) => { - if (params.onNodeSelectionToggle) { + if (params.onItemSelectionToggle) { if (params.multiSelect) { - const addedNodes = (newSelectedNodes as string[]).filter( - (nodeId) => !instance.isNodeSelected(nodeId), + const addedItems = (newSelectedItems as string[]).filter( + (itemId) => !instance.isNodeSelected(itemId), ); - const removedNodes = (models.selectedNodes.value as string[]).filter( - (nodeId) => !(newSelectedNodes as string[]).includes(nodeId), + const removedItems = (models.selectedItems.value as string[]).filter( + (itemId) => !(newSelectedItems as string[]).includes(itemId), ); - addedNodes.forEach((nodeId) => { - params.onNodeSelectionToggle!(event, nodeId, true); + addedItems.forEach((itemId) => { + params.onItemSelectionToggle!(event, itemId, true); }); - removedNodes.forEach((nodeId) => { - params.onNodeSelectionToggle!(event, nodeId, false); + removedItems.forEach((itemId) => { + params.onItemSelectionToggle!(event, itemId, false); }); - } else if (newSelectedNodes !== models.selectedNodes.value) { - if (models.selectedNodes.value != null) { - params.onNodeSelectionToggle(event, models.selectedNodes.value as string, false); + } else if (newSelectedItems !== models.selectedItems.value) { + if (models.selectedItems.value != null) { + params.onItemSelectionToggle(event, models.selectedItems.value as string, false); } - if (newSelectedNodes != null) { - params.onNodeSelectionToggle(event, newSelectedNodes as string, true); + if (newSelectedItems != null) { + params.onItemSelectionToggle(event, newSelectedItems as string, true); } } } - if (params.onSelectedNodesChange) { - params.onSelectedNodesChange(event, newSelectedNodes); + if (params.onSelectedItemsChange) { + params.onSelectedItemsChange(event, newSelectedItems); } - models.selectedNodes.setControlledValue(newSelectedNodes); + models.selectedItems.setControlledValue(newSelectedItems); }; const isNodeSelected = (nodeId: string) => - Array.isArray(models.selectedNodes.value) - ? models.selectedNodes.value.indexOf(nodeId) !== -1 - : models.selectedNodes.value === nodeId; + Array.isArray(models.selectedItems.value) + ? models.selectedItems.value.indexOf(nodeId) !== -1 + : models.selectedItems.value === nodeId; const selectNode = (event: React.SyntheticEvent, nodeId: string, multiple = false) => { if (params.disableSelection) { @@ -66,19 +66,19 @@ export const useTreeViewSelection: TreeViewPlugin } if (multiple) { - if (Array.isArray(models.selectedNodes.value)) { + if (Array.isArray(models.selectedItems.value)) { let newSelected: string[]; - if (models.selectedNodes.value.indexOf(nodeId) !== -1) { - newSelected = models.selectedNodes.value.filter((id) => id !== nodeId); + if (models.selectedItems.value.indexOf(nodeId) !== -1) { + newSelected = models.selectedItems.value.filter((id) => id !== nodeId); } else { - newSelected = [nodeId].concat(models.selectedNodes.value); + newSelected = [nodeId].concat(models.selectedItems.value); } - setSelectedNodes(event, newSelected); + setSelectedItems(event, newSelected); } } else { const newSelected = params.multiSelect ? [nodeId] : nodeId; - setSelectedNodes(event, newSelected); + setSelectedItems(event, newSelected); } lastSelectedNode.current = nodeId; lastSelectionWasRange.current = false; @@ -100,7 +100,7 @@ export const useTreeViewSelection: TreeViewPlugin }; const handleRangeArrowSelect = (event: React.SyntheticEvent, nodes: TreeViewItemRange) => { - let base = (models.selectedNodes.value as string[]).slice(); + let base = (models.selectedItems.value as string[]).slice(); const { start, next, current } = nodes; if (!next || !current) { @@ -125,14 +125,14 @@ export const useTreeViewSelection: TreeViewPlugin base.push(next); currentRangeSelection.current.push(current, next); } - setSelectedNodes(event, base); + setSelectedItems(event, base); }; const handleRangeSelect = ( event: React.SyntheticEvent, nodes: { start: string; end: string }, ) => { - let base = (models.selectedNodes.value as string[]).slice(); + let base = (models.selectedItems.value as string[]).slice(); const { start, end } = nodes; // If last selection was a range selection ignore nodes that were selected. if (lastSelectionWasRange.current) { @@ -144,7 +144,7 @@ export const useTreeViewSelection: TreeViewPlugin currentRangeSelection.current = range; let newSelected = base.concat(range); newSelected = newSelected.filter((id, i) => newSelected.indexOf(id) === i); - setSelectedNodes(event, newSelected); + setSelectedItems(event, newSelected); }; const selectRange = (event: React.SyntheticEvent, nodes: TreeViewItemRange, stacked = false) => { @@ -208,8 +208,8 @@ export const useTreeViewSelection: TreeViewPlugin }; useTreeViewSelection.models = { - selectedNodes: { - getDefaultValue: (params) => params.defaultSelectedNodes, + selectedItems: { + getDefaultValue: (params) => params.defaultSelectedItems, }, }; @@ -219,15 +219,15 @@ useTreeViewSelection.getDefaultizedParams = (params) => ({ ...params, disableSelection: params.disableSelection ?? false, multiSelect: params.multiSelect ?? false, - defaultSelectedNodes: - params.defaultSelectedNodes ?? (params.multiSelect ? DEFAULT_SELECTED_NODES : null), + defaultSelectedItems: + params.defaultSelectedItems ?? (params.multiSelect ? DEFAULT_SELECTED_NODES : null), }); useTreeViewSelection.params = { disableSelection: true, multiSelect: true, - defaultSelectedNodes: true, - selectedNodes: true, - onSelectedNodesChange: true, - onNodeSelectionToggle: true, + defaultSelectedItems: true, + selectedItems: true, + onSelectedItemsChange: true, + onItemSelectionToggle: true, }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts index 6afbfaaafd8e5..5ad17089bd114 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts @@ -22,16 +22,16 @@ export interface UseTreeViewSelectionParameters; + defaultSelectedItems?: TreeViewSelectionValue; /** - * Selected node ids. (Controlled) + * Selected item ids. (Controlled) * When `multiSelect` is true this takes an array of strings; when false (default) a string. */ - selectedNodes?: TreeViewSelectionValue; + selectedItems?: TreeViewSelectionValue; /** * If true `ctrl` and `shift` will trigger multiselect. * @default false @@ -40,29 +40,29 @@ export interface UseTreeViewSelectionParameters, + itemIds: TreeViewSelectionValue, ) => void; /** * Callback fired when a tree item is selected or deselected. * @param {React.SyntheticEvent} event The event source of the callback. - * @param {array} nodeId The nodeId of the modified node. - * @param {array} isSelected `true` if the node has just been selected, `false` if it has just been deselected. + * @param {array} itemId The itemId of the modified item. + * @param {array} isSelected `true` if the item has just been selected, `false` if it has just been deselected. */ - onNodeSelectionToggle?: ( + onItemSelectionToggle?: ( event: React.SyntheticEvent, - nodeId: string, + itemId: string, isSelected: boolean, ) => void; } export type UseTreeViewSelectionDefaultizedParameters = DefaultizedProps< UseTreeViewSelectionParameters, - 'disableSelection' | 'defaultSelectedNodes' | 'multiSelect' + 'disableSelection' | 'defaultSelectedItems' | 'multiSelect' >; interface UseTreeViewSelectionContextValue { @@ -74,7 +74,7 @@ export type UseTreeViewSelectionSignature = TreeViewPluginSignature<{ defaultizedParams: UseTreeViewSelectionDefaultizedParameters; instance: UseTreeViewSelectionInstance; contextValue: UseTreeViewSelectionContextValue; - modelNames: 'selectedNodes'; + modelNames: 'selectedItems'; dependantPlugins: [ UseTreeViewNodesSignature, UseTreeViewExpansionSignature, diff --git a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts index 8d8b47a5147b1..e7f29833807e6 100644 --- a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts +++ b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts @@ -8,7 +8,7 @@ createTheme({ components: { MuiSimpleTreeView: { defaultProps: { - defaultExpandedNodes: ['root'], + defaultExpandedItems: ['root'], // @ts-expect-error invalid MuiSimpleTreeView prop someRandomProp: true, }, @@ -27,7 +27,7 @@ createTheme({ }, MuiRichTreeView: { defaultProps: { - defaultExpandedNodes: ['root'], + defaultExpandedItems: ['root'], // @ts-expect-error invalid MuiRichTreeView prop someRandomProp: true, }, @@ -46,7 +46,7 @@ createTheme({ }, MuiTreeView: { defaultProps: { - defaultExpandedNodes: ['root'], + defaultExpandedItems: ['root'], // @ts-expect-error invalid MuiTreeView prop someRandomProp: true, }, diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts index 8acd94e62edd9..09e5d548d739e 100644 --- a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts @@ -48,7 +48,7 @@ export const useTreeItem2 = Date: Tue, 12 Mar 2024 20:52:49 +0100 Subject: [PATCH 33/49] [DataGridPremium] Fix auto-scroll not working when selecting cell range (#12267) Co-authored-by: Bilal Shafi --- docs/pages/x/api/data-grid/selectors.json | 6 +++++ .../cellSelection/useGridCellSelection.ts | 24 +++++++++---------- .../cellSelection.DataGridPremium.test.tsx | 14 +++++++---- .../features/columns/gridColumnsUtils.ts | 5 +++- .../features/dimensions/useGridDimensions.ts | 6 +---- .../gridHeaderFilteringSelectors.ts | 6 +++++ .../headerFiltering/useGridHeaderFiltering.ts | 23 ++++++++++++++---- .../src/models/gridHeaderFilteringModel.ts | 1 + scripts/x-data-grid-premium.exports.json | 1 + scripts/x-data-grid-pro.exports.json | 1 + scripts/x-data-grid.exports.json | 1 + 11 files changed, 61 insertions(+), 27 deletions(-) diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index c381b10af9ae1..fc2fa812d4817 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -264,6 +264,12 @@ "description": "", "supportsApiRef": true }, + { + "name": "gridHeaderFilteringEnabledSelector", + "returnType": "boolean", + "description": "", + "supportsApiRef": true + }, { "name": "gridHeaderFilteringMenuSelector", "returnType": "string | null", diff --git a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts index 0f69b04b68af9..ce473b32e794b 100644 --- a/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts +++ b/packages/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts @@ -3,6 +3,7 @@ import { ownerDocument, useEventCallback } from '@mui/material/utils'; import { GridPipeProcessor, GridStateInitializer, + getTotalHeaderHeight, isNavigationKey, serializeCellValue, useGridRegisterPipeProcessor, @@ -25,6 +26,7 @@ import { GRID_REORDER_COL_DEF, useGridSelector, gridSortedRowIdsSelector, + gridDimensionsSelector, } from '@mui/x-data-grid-pro'; import { gridCellSelectionStateSelector } from './gridCellSelectionSelector'; import { GridCellSelectionApi } from './gridCellSelectionInterfaces'; @@ -56,6 +58,7 @@ export const useGridCellSelection = ( | 'paginationMode' | 'ignoreValueFormatterDuringExport' | 'clipboardCopyCellDelimiter' + | 'columnHeaderHeight' >, ) => { const visibleRows = useGridVisibleRows(apiRef, props); @@ -64,6 +67,8 @@ export const useGridCellSelection = ( const mousePosition = React.useRef<{ x: number; y: number } | null>(null); const autoScrollRAF = React.useRef(); const sortedRowIds = useGridSelector(apiRef, gridSortedRowIdsSelector); + const dimensions = useGridSelector(apiRef, gridDimensionsSelector); + const totalHeaderHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight); const ignoreValueFormatterProp = props.ignoreValueFormatterDuringExport; const ignoreValueFormatter = @@ -265,26 +270,18 @@ export const useGridCellSelection = ( return; } - const virtualScrollerRect = apiRef.current.virtualScrollerRef?.current?.getBoundingClientRect(); - - if (!virtualScrollerRect) { - return; - } - function autoScroll() { if (!mousePosition.current || !apiRef.current.virtualScrollerRef?.current) { return; } const { x: mouseX, y: mouseY } = mousePosition.current; - const { height, width } = virtualScrollerRect; + const { height, width } = dimensions.viewportInnerSize; let deltaX = 0; let deltaY = 0; let factor = 0; - const dimensions = apiRef.current.getRootDimensions(); - if (mouseY <= AUTO_SCROLL_SENSITIVITY && dimensions.hasScrollY) { // When scrolling up, the multiplier increases going closer to the top edge factor = (AUTO_SCROLL_SENSITIVITY - mouseY) / -AUTO_SCROLL_SENSITIVITY; @@ -316,7 +313,7 @@ export const useGridCellSelection = ( } autoScroll(); - }, [apiRef]); + }, [apiRef, dimensions]); const handleCellMouseOver = React.useCallback>( (params, event) => { @@ -339,9 +336,10 @@ export const useGridCellSelection = ( return; } - const { height, width, x, y } = virtualScrollerRect; + const { x, y } = virtualScrollerRect; + const { height, width } = dimensions.viewportInnerSize; const mouseX = event.clientX - x; - const mouseY = event.clientY - y; + const mouseY = event.clientY - y - totalHeaderHeight; mousePosition.current = { x: mouseX, y: mouseY }; const hasEnteredVerticalSensitivityArea = @@ -361,7 +359,7 @@ export const useGridCellSelection = ( stopAutoScroll(); } }, - [apiRef, startAutoScroll, stopAutoScroll], + [apiRef, startAutoScroll, stopAutoScroll, totalHeaderHeight, dimensions], ); const handleCellClick = useEventCallback< diff --git a/packages/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx index ec0c79acb7677..87eb8977c8f55 100644 --- a/packages/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { stub, SinonStub } from 'sinon'; import { expect } from 'chai'; -import { spyApi, getCell } from 'test/utils/helperFn'; +import { spyApi, getCell, grid } from 'test/utils/helperFn'; import { createRenderer, fireEvent, act, userEvent, screen } from '@mui-internal/test-utils'; import { DataGridPremium, @@ -388,17 +388,23 @@ describe(' - Cell selection', () => { fireEvent.click(cell71); const virtualScroller = document.querySelector(`.${gridClasses.virtualScroller}`)!; - const rect = virtualScroller.getBoundingClientRect(); + const gridRect = grid('root')!.getBoundingClientRect(); virtualScroller.scrollTop = 30; virtualScroller.dispatchEvent(new Event('scroll')); expect(virtualScroller.scrollTop).to.equal(30); const cell11 = getCell(1, 1); - fireEvent.mouseOver(cell11, { clientX: rect.x, clientY: rect.y + 25 }); // 25=half speed + fireEvent.mouseOver(cell11, { + clientX: gridRect.x, + clientY: gridRect.y + border + columnHeaderHeight + 25, // 25=half speed + }); expect(virtualScroller.scrollTop).to.equal(20); - fireEvent.mouseOver(cell11, { clientX: rect.x, clientY: rect.y }); // 0=full speed + fireEvent.mouseOver(cell11, { + clientX: gridRect.x, + clientY: gridRect.y + border + columnHeaderHeight + 0, // 0=full speed + }); expect(virtualScroller.scrollTop).to.equal(0); (window.requestAnimationFrame as SinonStub).restore(); diff --git a/packages/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts b/packages/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts index 5c1962faa6378..5dc3c3b1c836e 100644 --- a/packages/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts +++ b/packages/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts @@ -19,6 +19,7 @@ import { clamp } from '../../../utils/utils'; import { GridApiCommon } from '../../../models/api/gridApiCommon'; import { GridRowEntry } from '../../../models/gridRows'; import { gridDensityFactorSelector } from '../density/densitySelector'; +import { gridHeaderFilteringEnabledSelector } from '../headerFiltering/gridHeaderFilteringSelectors'; import { gridColumnGroupsHeaderMaxDepthSelector } from '../columnGrouping/gridColumnGroupsSelector'; export const COLUMNS_DIMENSION_PROPERTIES = ['maxWidth', 'minWidth', 'width', 'flex'] as const; @@ -464,5 +465,7 @@ export function getTotalHeaderHeight( ) { const densityFactor = gridDensityFactorSelector(apiRef); const maxDepth = gridColumnGroupsHeaderMaxDepthSelector(apiRef); - return Math.floor(headerHeight * densityFactor) * ((maxDepth ?? 0) + 1); + const isHeaderFilteringEnabled = gridHeaderFilteringEnabledSelector(apiRef); + const multiplicationFactor = isHeaderFilteringEnabled ? 2 : 1; + return Math.floor(headerHeight * densityFactor) * ((maxDepth ?? 0) + multiplicationFactor); } diff --git a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts index f20acf1202265..c49c9d6818573 100644 --- a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts +++ b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts @@ -89,11 +89,7 @@ export function useGridDimensions( const rowHeight = Math.floor(props.rowHeight * densityFactor); const headerHeight = Math.floor(props.columnHeaderHeight * densityFactor); const columnsTotalWidth = roundToDecimalPlaces(gridColumnsTotalWidthSelector(apiRef), 6); - // XXX: The `props as any` below is not resilient to change. - const hasHeaderFilters = Boolean((props as any).headerFilters); - const headersTotalHeight = - getTotalHeaderHeight(apiRef, props.columnHeaderHeight) + - Number(hasHeaderFilters) * headerHeight; + const headersTotalHeight = getTotalHeaderHeight(apiRef, props.columnHeaderHeight); const leftPinnedWidth = pinnedColumns.left.reduce((w, col) => w + col.computedWidth, 0); const rightPinnedWidth = pinnedColumns.right.reduce((w, col) => w + col.computedWidth, 0); diff --git a/packages/x-data-grid/src/hooks/features/headerFiltering/gridHeaderFilteringSelectors.ts b/packages/x-data-grid/src/hooks/features/headerFiltering/gridHeaderFilteringSelectors.ts index 58572b2ec63c0..bacc3556dfba9 100644 --- a/packages/x-data-grid/src/hooks/features/headerFiltering/gridHeaderFilteringSelectors.ts +++ b/packages/x-data-grid/src/hooks/features/headerFiltering/gridHeaderFilteringSelectors.ts @@ -4,6 +4,12 @@ import { GridStateCommunity } from '../../../models/gridStateCommunity'; export const gridHeaderFilteringStateSelector = (state: GridStateCommunity) => state.headerFiltering; +export const gridHeaderFilteringEnabledSelector = createSelector( + gridHeaderFilteringStateSelector, + // No initialization in MIT, so we need to default to false to be used by `getTotalHeaderHeight` + (headerFilteringState) => headerFilteringState?.enabled ?? false, +); + export const gridHeaderFilteringEditFieldSelector = createSelector( gridHeaderFilteringStateSelector, (headerFilteringState) => headerFilteringState.editing, diff --git a/packages/x-data-grid/src/hooks/features/headerFiltering/useGridHeaderFiltering.ts b/packages/x-data-grid/src/hooks/features/headerFiltering/useGridHeaderFiltering.ts index 414d1a167a532..007525abf67fd 100644 --- a/packages/x-data-grid/src/hooks/features/headerFiltering/useGridHeaderFiltering.ts +++ b/packages/x-data-grid/src/hooks/features/headerFiltering/useGridHeaderFiltering.ts @@ -15,9 +15,10 @@ import { GridHeaderFilteringPrivateApi, } from '../../../models/api/gridHeaderFilteringApi'; -export const headerFilteringStateInitializer: GridStateInitializer = (state) => ({ +export const headerFilteringStateInitializer: GridStateInitializer = (state, props) => ({ ...state, - headerFiltering: { editing: null, menuOpen: null }, + // @ts-expect-error Access `Pro` prop in MIT + headerFiltering: { enabled: props.headerFilters ?? false, editing: null, menuOpen: null }, }); export const useGridHeaderFiltering = ( @@ -25,7 +26,8 @@ export const useGridHeaderFiltering = ( props: Pick, ) => { const logger = useGridLogger(apiRef, 'useGridHeaderFiltering'); - + // @ts-expect-error Access `Pro` prop in MIT + const isHeaderFilteringEnabled = props.headerFilters ?? false; const setHeaderFilterState = React.useCallback( (headerFilterState: Partial) => { apiRef.current.setState((state) => { @@ -37,6 +39,7 @@ export const useGridHeaderFiltering = ( return { ...state, headerFiltering: { + enabled: isHeaderFilteringEnabled ?? false, editing: headerFilterState.editing ?? null, menuOpen: headerFilterState.menuOpen ?? null, }, @@ -44,7 +47,7 @@ export const useGridHeaderFiltering = ( }); apiRef.current.forceUpdate(); }, - [apiRef, props.signature], + [apiRef, props.signature, isHeaderFilteringEnabled], ); const startHeaderFilterEditMode = React.useCallback< @@ -117,4 +120,16 @@ export const useGridHeaderFiltering = ( useGridApiMethod(apiRef, headerFilterApi, 'public'); useGridApiMethod(apiRef, headerFilterPrivateApi, 'private'); + + /* + * EFFECTS + */ + const isFirstRender = React.useRef(true); + React.useEffect(() => { + if (isFirstRender.current) { + isFirstRender.current = false; + } else { + apiRef.current.setHeaderFilterState({ enabled: isHeaderFilteringEnabled }); + } + }, [apiRef, isHeaderFilteringEnabled]); }; diff --git a/packages/x-data-grid/src/models/gridHeaderFilteringModel.ts b/packages/x-data-grid/src/models/gridHeaderFilteringModel.ts index c532cd1b45808..a5c3c8e8f4998 100644 --- a/packages/x-data-grid/src/models/gridHeaderFilteringModel.ts +++ b/packages/x-data-grid/src/models/gridHeaderFilteringModel.ts @@ -1,6 +1,7 @@ import { GridColDef } from './colDef'; export type GridHeaderFilteringState = { + enabled: boolean; editing: GridColDef['field'] | null; menuOpen: GridColDef['field'] | null; }; diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index e2297f6f75672..f78887dcd8e65 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -369,6 +369,7 @@ { "name": "GridHeaderFilterCellProps", "kind": "Interface" }, { "name": "GridHeaderFilterEventLookup", "kind": "Interface" }, { "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, + { "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringStateSelector", "kind": "Variable" }, { "name": "GridHeaderFilterMenu", "kind": "Function" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index ab58d533526b5..dc10d95ed9ef0 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -333,6 +333,7 @@ { "name": "GridHeaderFilterCellProps", "kind": "Interface" }, { "name": "GridHeaderFilterEventLookup", "kind": "Interface" }, { "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, + { "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringStateSelector", "kind": "Variable" }, { "name": "GridHeaderFilterMenu", "kind": "Function" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 08d4b5ebc605d..a3f114557c0a5 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -306,6 +306,7 @@ { "name": "GridHeaderCheckbox", "kind": "Variable" }, { "name": "GridHeaderFilterEventLookup", "kind": "Interface" }, { "name": "gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, + { "name": "gridHeaderFilteringEnabledSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringMenuSelector", "kind": "Variable" }, { "name": "gridHeaderFilteringStateSelector", "kind": "Variable" }, { "name": "GridHeaderSelectionCheckboxParams", "kind": "Interface" }, From ba66f909fda4f815d6b9e32ba05ce84b9fe9427a Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Wed, 13 Mar 2024 15:51:35 +0100 Subject: [PATCH 34/49] [charts] Fix axis highlight when axis is reversed (#12407) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Michel Engelen <32863416+michelengelen@users.noreply.github.com> --- docs/data/charts/axis/ReverseExampleNoSnap.js | 4 ++++ packages/x-charts/src/hooks/useAxisEvents.ts | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/data/charts/axis/ReverseExampleNoSnap.js b/docs/data/charts/axis/ReverseExampleNoSnap.js index 7d80dd08131e5..4a948a496aabe 100644 --- a/docs/data/charts/axis/ReverseExampleNoSnap.js +++ b/docs/data/charts/axis/ReverseExampleNoSnap.js @@ -9,6 +9,8 @@ import { LinePlot, MarkPlot } from '@mui/x-charts/LineChart'; import { BarPlot } from '@mui/x-charts/BarChart'; import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; +import { ChartsGrid } from '@mui/x-charts/ChartsGrid'; +import { ChartsTooltip } from '@mui/x-charts/ChartsTooltip'; const dataset = [ { min: -12, max: -4, precip: 79, month: 'Jan' }, @@ -82,6 +84,7 @@ export default function ReverseExampleNoSnap() { dataset={dataset} height={400} > + @@ -93,6 +96,7 @@ export default function ReverseExampleNoSnap() { position="right" label="precipitation (mm)" /> + diff --git a/packages/x-charts/src/hooks/useAxisEvents.ts b/packages/x-charts/src/hooks/useAxisEvents.ts index 4cf77af1e0e19..d31dc834ec7f6 100644 --- a/packages/x-charts/src/hooks/useAxisEvents.ts +++ b/packages/x-charts/src/hooks/useAxisEvents.ts @@ -34,7 +34,7 @@ export const useAxisEvents = (disableAxisListener: boolean) => { if (usedXAxis === null) { return null; } - const { scale, data: axisData } = axisConfig; + const { scale, data: axisData, reverse } = axisConfig; if (!isBandScale(scale)) { const value = scale.invert(mouseValue); @@ -81,6 +81,13 @@ export const useAxisEvents = (disableAxisListener: boolean) => { if (dataIndex < 0 || dataIndex >= axisData!.length) { return null; } + if (reverse) { + const reverseIndex = axisData!.length - 1 - dataIndex; + return { + index: reverseIndex, + value: axisData![reverseIndex], + }; + } return { index: dataIndex, value: axisData![dataIndex], From a85fb9a1a5f91d25c67c2105b858b22c4181ef63 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Wed, 13 Mar 2024 12:23:22 -0400 Subject: [PATCH 35/49] [DataGrid] Remove baseSwitch (#12439) --- .../x/api/data-grid/data-grid-premium.json | 6 --- docs/pages/x/api/data-grid/data-grid-pro.json | 6 --- docs/pages/x/api/data-grid/data-grid.json | 6 --- .../data-grid-premium/data-grid-premium.json | 1 - .../data-grid-pro/data-grid-pro.json | 1 - .../data-grid/data-grid/data-grid.json | 1 - packages/x-data-grid/src/joy/joySlots.tsx | 53 ------------------- packages/x-data-grid/src/material/index.ts | 2 - .../src/models/gridSlotsComponent.ts | 5 -- 9 files changed, 81 deletions(-) diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 5305710f3f4ea..a58b5596dbaf8 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -764,12 +764,6 @@ "default": "Select", "class": null }, - { - "name": "baseSwitch", - "description": "The custom Switch component used in the grid.", - "default": "Switch", - "class": null - }, { "name": "baseButton", "description": "The custom Button component used in the grid.", diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 30bfeb697b268..8cc02ac2d7c64 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -703,12 +703,6 @@ "default": "Select", "class": null }, - { - "name": "baseSwitch", - "description": "The custom Switch component used in the grid.", - "default": "Switch", - "class": null - }, { "name": "baseButton", "description": "The custom Button component used in the grid.", diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 338574d351b13..aaf820b0be44a 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -602,12 +602,6 @@ "default": "Select", "class": null }, - { - "name": "baseSwitch", - "description": "The custom Switch component used in the grid.", - "default": "Switch", - "class": null - }, { "name": "baseButton", "description": "The custom Button component used in the grid.", diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 521fe49f8b734..c7caf3bd8733b 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -1235,7 +1235,6 @@ "basePopper": "The custom Popper component used in the grid.", "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", - "baseSwitch": "The custom Switch component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index ae15af72b8042..520fbf1059d2f 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -1177,7 +1177,6 @@ "basePopper": "The custom Popper component used in the grid.", "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", - "baseSwitch": "The custom Switch component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index cdbd61fc7c128..06e1990d8b3af 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -1067,7 +1067,6 @@ "basePopper": "The custom Popper component used in the grid.", "baseSelect": "The custom Select component used in the grid.", "baseSelectOption": "The custom SelectOption component used in the grid.", - "baseSwitch": "The custom Switch component used in the grid.", "baseTextField": "The custom TextField component used in the grid.", "baseTooltip": "The custom Tooltip component used in the grid.", "booleanCellFalseIcon": "Icon displayed on the boolean cell to represent the false value.", diff --git a/packages/x-data-grid/src/joy/joySlots.tsx b/packages/x-data-grid/src/joy/joySlots.tsx index 7c5819657da0f..246799f568a69 100644 --- a/packages/x-data-grid/src/joy/joySlots.tsx +++ b/packages/x-data-grid/src/joy/joySlots.tsx @@ -7,7 +7,6 @@ import JoyFormControl from '@mui/joy/FormControl'; import JoyFormLabel from '@mui/joy/FormLabel'; import JoyButton from '@mui/joy/Button'; import JoyIconButton from '@mui/joy/IconButton'; -import JoySwitch, { SwitchProps as JoySwitchProps } from '@mui/joy/Switch'; import JoySelect, { SelectProps as JoySelectProps } from '@mui/joy/Select'; import JoyOption, { OptionProps as JoyOptionProps } from '@mui/joy/Option'; import JoyBox from '@mui/joy/Box'; @@ -150,57 +149,6 @@ const IconButton = React.forwardRef< ); }); -const Switch = React.forwardRef(function Switch( - { - name, - checkedIcon, - color: colorProp, - disableRipple, - disableFocusRipple, - disableTouchRipple, - edge, - icon, - inputProps, - inputRef, - size, - sx, - onChange, - onClick, - ...props - }, - ref, -) { - return ( - - } - /> - ); -}); - const Select = React.forwardRef( ( { @@ -430,7 +378,6 @@ const joySlots: Partial = { baseTextField: TextField, baseButton: Button, baseIconButton: IconButton, - baseSwitch: Switch, baseSelect: Select, baseSelectOption: Option, baseInputLabel: InputLabel, diff --git a/packages/x-data-grid/src/material/index.ts b/packages/x-data-grid/src/material/index.ts index 4690784e16777..6011f2c4606e6 100644 --- a/packages/x-data-grid/src/material/index.ts +++ b/packages/x-data-grid/src/material/index.ts @@ -2,7 +2,6 @@ import MUICheckbox from '@mui/material/Checkbox'; import MUITextField from '@mui/material/TextField'; import MUIFormControl from '@mui/material/FormControl'; import MUISelect from '@mui/material/Select'; -import MUISwitch from '@mui/material/Switch'; import MUIButton from '@mui/material/Button'; import MUIIconButton from '@mui/material/IconButton'; import MUIInputAdornment from '@mui/material/InputAdornment'; @@ -86,7 +85,6 @@ const materialSlots: GridBaseSlots & GridIconSlotsComponent = { baseTextField: MUITextField, baseFormControl: MUIFormControl, baseSelect: MUISelect, - baseSwitch: MUISwitch, baseButton: MUIButton, baseIconButton: MUIIconButton, baseInputAdornment: MUIInputAdornment, diff --git a/packages/x-data-grid/src/models/gridSlotsComponent.ts b/packages/x-data-grid/src/models/gridSlotsComponent.ts index aedf8ceb29a95..e9badaecaf5d0 100644 --- a/packages/x-data-grid/src/models/gridSlotsComponent.ts +++ b/packages/x-data-grid/src/models/gridSlotsComponent.ts @@ -35,11 +35,6 @@ export interface GridBaseSlots { * @default Select */ baseSelect: React.JSXElementConstructor; - /** - * The custom Switch component used in the grid. - * @default Switch - */ - baseSwitch: React.JSXElementConstructor; /** * The custom Button component used in the grid. * @default Button From 684fca744e8909e677e42a2aa5e16634149e3e9b Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Wed, 13 Mar 2024 17:30:03 +0100 Subject: [PATCH 36/49] [DataGridPro] Render pinned and non-pinned column headers in one row (#12376) --- .../ColDefChangesGridNoSnap.js | 9 +- .../migration-data-grid-v6.md | 4 + .../x/api/data-grid/data-grid-premium.json | 78 +--- docs/pages/x/api/data-grid/data-grid-pro.json | 78 +--- docs/pages/x/api/data-grid/data-grid.json | 78 +--- .../data-grid-premium/data-grid-premium.json | 47 +-- .../data-grid-pro/data-grid-pro.json | 47 +-- .../data-grid/data-grid/data-grid.json | 47 +-- .../src/components/GridColumnHeaders.tsx | 261 +------------ .../headerFiltering/GridHeaderFilterCell.tsx | 47 ++- .../columnHeaders/useGridColumnHeaders.tsx | 60 ++- .../tests/columnPinning.DataGridPro.test.tsx | 18 +- .../x-data-grid-pro/src/utils/domUtils.ts | 139 ------- .../src/components/GridColumnHeaders.tsx | 45 +-- .../src/components/GridHeaders.tsx | 5 +- .../x-data-grid/src/components/GridRow.tsx | 33 +- .../src/components/GridScrollArea.tsx | 45 ++- .../src/components/cell/GridCell.tsx | 25 +- .../columnHeaders/GridBaseColumnHeaders.tsx | 1 + .../columnHeaders/GridColumnGroupHeader.tsx | 42 +- .../columnHeaders/GridColumnHeaderItem.tsx | 44 ++- .../columnHeaders/GridColumnHeadersInner.tsx | 74 ---- .../GridGenericColumnHeaderItem.tsx | 5 +- .../components/containers/GridRootStyles.ts | 31 +- .../virtualization/GridVirtualScroller.tsx | 3 + .../GridVirtualScrollerFiller.tsx | 3 +- .../x-data-grid/src/constants/gridClasses.ts | 49 +-- .../columnHeaders/useGridColumnHeaders.tsx | 365 ++++++++++++------ .../columnResize/useGridColumnResize.tsx | 25 +- .../features/export/useGridPrintExport.tsx | 6 - .../hooks/features/scroll/useGridScroll.ts | 2 +- .../gridVirtualizationSelectors.ts | 8 +- .../virtualization/useGridVirtualScroller.tsx | 9 +- .../x-data-grid/src/hooks/utils/useLazyRef.ts | 15 +- .../x-data-grid/src/hooks/utils/useOnMount.ts | 10 +- .../x-data-grid/src/hooks/utils/useTimeout.ts | 42 +- packages/x-data-grid/src/internals/index.ts | 1 - .../internals/utils/getPinnedCellOffset.ts | 34 ++ .../x-data-grid/src/models/api/gridCoreApi.ts | 6 +- .../src/models/params/gridScrollParams.ts | 8 +- .../x-data-grid/src/utils/cellBorderUtils.ts | 23 ++ packages/x-data-grid/src/utils/domUtils.ts | 108 ++++-- .../src/internals/hooks/useLazyRef.ts | 15 +- .../src/internals/hooks/useOnMount.ts | 10 +- .../src/internals/hooks/useTimeout.ts | 42 +- scripts/x-data-grid-premium.exports.json | 1 + scripts/x-data-grid-pro.exports.json | 1 + scripts/x-data-grid.exports.json | 1 + 48 files changed, 799 insertions(+), 1251 deletions(-) delete mode 100644 packages/x-data-grid-pro/src/utils/domUtils.ts rename packages/{x-data-grid-pro => x-data-grid}/src/components/GridScrollArea.tsx (76%) delete mode 100644 packages/x-data-grid/src/components/columnHeaders/GridColumnHeadersInner.tsx create mode 100644 packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts create mode 100644 packages/x-data-grid/src/utils/cellBorderUtils.ts diff --git a/docs/data/migration/migration-data-grid-v6/ColDefChangesGridNoSnap.js b/docs/data/migration/migration-data-grid-v6/ColDefChangesGridNoSnap.js index 79205b597bab4..8754b4cddae93 100644 --- a/docs/data/migration/migration-data-grid-v6/ColDefChangesGridNoSnap.js +++ b/docs/data/migration/migration-data-grid-v6/ColDefChangesGridNoSnap.js @@ -21,6 +21,7 @@ const columns = [ field: 'propsToControlProgrammatically', headerName: 'Props to control programmatically', minWidth: 300, + display: 'flex', renderCell: List, }, { @@ -33,7 +34,7 @@ const columns = [ field: 'docs', headerName: 'Docs', renderCell: DocsLink, - width: 60, + width: 61, disableColumnMenu: true, sortable: false, resizable: false, @@ -125,7 +126,7 @@ export default function ColDefChangesGridNoSnap() { function DocsLink(params) { return ( - + ); @@ -143,7 +144,9 @@ function List(params) { return (
    {params.value.split(', ').map((v) => ( -
  • {v}
  • +
  • + {v} +
  • ))}
); diff --git a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md index 5d26059f9ef83..3478a5c49063c 100644 --- a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md +++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md @@ -478,6 +478,10 @@ See the [Direct state access](/x/react-data-grid/state/#direct-selector-access) - The `.MuiDataGrid--pinnedColumns-(left\|right)` class for pinned columns has been removed. - The `.MuiDataGrid-cell--withRenderer` class has been removed. - The cell element isn't `display: flex` by default. You can add `display: 'flex'` on the column definition to restore the behavior. This also means cells aren't vertically centered by default anymore, so if you have dynamic row height, you might want to set the `display: 'flex'` for all non-dynamic columns. +- The `columnHeader--showColumnBorder` class was replaced by `columnHeader--withLeftBorder` and `columnHeader--withRightBorder`. +- The `columnHeadersInner`, `columnHeadersInner--scrollable`, and `columnHeaderDropZone` classes were removed since the inner wrapper was removed in our effort to simplify the DOM structure and improve accessibility. +- The `pinnedColumnHeaders`, `pinnedColumnHeaders--left`, and `pinnedColumnHeaders--right` classes were removed along with the element they were applied to. + The pinned column headers now use `position: 'sticky'` and are rendered in the same row element as the regular column headers. ### Changes to the public API diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index a58b5596dbaf8..ba181402628c8 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -1210,12 +1210,6 @@ "description": "Styles applied to the selection checkbox element.", "isGlobal": false }, - { - "key": "columnGroupHeader", - "className": "MuiDataGridPremium-columnGroupHeader", - "description": "Styles applied to the column group header element.", - "isGlobal": false - }, { "key": "columnHeader", "className": "MuiDataGridPremium-columnHeader", @@ -1277,9 +1271,15 @@ "isGlobal": false }, { - "key": "columnHeader--showColumnBorder", - "className": "MuiDataGridPremium-columnHeader--showColumnBorder", - "description": "Styles applied to the column group header cell when show column border.", + "key": "columnHeader--pinnedLeft", + "className": "MuiDataGridPremium-columnHeader--pinnedLeft", + "description": "", + "isGlobal": false + }, + { + "key": "columnHeader--pinnedRight", + "className": "MuiDataGridPremium-columnHeader--pinnedRight", + "description": "", "isGlobal": false }, { @@ -1294,6 +1294,12 @@ "description": "Styles applied to the column header if the column is sorted.", "isGlobal": false }, + { + "key": "columnHeader--withLeftBorder", + "className": "MuiDataGridPremium-columnHeader--withLeftBorder", + "description": "", + "isGlobal": false + }, { "key": "columnHeader--withRightBorder", "className": "MuiDataGridPremium-columnHeader--withRightBorder", @@ -1312,24 +1318,6 @@ "description": "Styles applied to the column header's draggable container element.", "isGlobal": false }, - { - "key": "columnHeaderDropZone", - "className": "MuiDataGridPremium-columnHeaderDropZone", - "description": "Styles applied to the column headers wrapper if a column is being dragged.", - "isGlobal": false - }, - { - "key": "columnHeadersInner", - "className": "MuiDataGridPremium-columnHeadersInner", - "description": "Styles applied to the column headers's inner element.", - "isGlobal": false - }, - { - "key": "columnHeadersInner--scrollable", - "className": "MuiDataGridPremium-columnHeadersInner--scrollable", - "description": "Styles applied to the column headers's inner element if there is a horizontal scrollbar.", - "isGlobal": false - }, { "key": "columnHeaderTitle", "className": "MuiDataGridPremium-columnHeaderTitle", @@ -1438,24 +1426,6 @@ "description": "Styles applied to the root of the input component.", "isGlobal": false }, - { - "key": "filler", - "className": "MuiDataGridPremium-filler", - "description": "Styles applied to the filler row.", - "isGlobal": false - }, - { - "key": "filler--pinnedLeft", - "className": "MuiDataGridPremium-filler--pinnedLeft", - "description": "Styles applied to the filler row pinned left section.", - "isGlobal": false - }, - { - "key": "filler--pinnedRight", - "className": "MuiDataGridPremium-filler--pinnedRight", - "description": "Styles applied to the filler row pinned right section.", - "isGlobal": false - }, { "key": "filterForm", "className": "MuiDataGridPremium-filterForm", @@ -1630,24 +1600,6 @@ "description": "Styles applied to the paper element.", "isGlobal": false }, - { - "key": "pinnedColumnHeaders", - "className": "MuiDataGridPremium-pinnedColumnHeaders", - "description": "Styles applied to the pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--left", - "className": "MuiDataGridPremium-pinnedColumnHeaders--left", - "description": "Styles applied to the left pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--right", - "className": "MuiDataGridPremium-pinnedColumnHeaders--right", - "description": "Styles applied to the right pinned column headers.", - "isGlobal": false - }, { "key": "pinnedColumns", "className": "MuiDataGridPremium-pinnedColumns", diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 8cc02ac2d7c64..198c5f68c6da7 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -1131,12 +1131,6 @@ "description": "Styles applied to the selection checkbox element.", "isGlobal": false }, - { - "key": "columnGroupHeader", - "className": "MuiDataGridPro-columnGroupHeader", - "description": "Styles applied to the column group header element.", - "isGlobal": false - }, { "key": "columnHeader", "className": "MuiDataGridPro-columnHeader", @@ -1198,9 +1192,15 @@ "isGlobal": false }, { - "key": "columnHeader--showColumnBorder", - "className": "MuiDataGridPro-columnHeader--showColumnBorder", - "description": "Styles applied to the column group header cell when show column border.", + "key": "columnHeader--pinnedLeft", + "className": "MuiDataGridPro-columnHeader--pinnedLeft", + "description": "", + "isGlobal": false + }, + { + "key": "columnHeader--pinnedRight", + "className": "MuiDataGridPro-columnHeader--pinnedRight", + "description": "", "isGlobal": false }, { @@ -1215,6 +1215,12 @@ "description": "Styles applied to the column header if the column is sorted.", "isGlobal": false }, + { + "key": "columnHeader--withLeftBorder", + "className": "MuiDataGridPro-columnHeader--withLeftBorder", + "description": "", + "isGlobal": false + }, { "key": "columnHeader--withRightBorder", "className": "MuiDataGridPro-columnHeader--withRightBorder", @@ -1233,24 +1239,6 @@ "description": "Styles applied to the column header's draggable container element.", "isGlobal": false }, - { - "key": "columnHeaderDropZone", - "className": "MuiDataGridPro-columnHeaderDropZone", - "description": "Styles applied to the column headers wrapper if a column is being dragged.", - "isGlobal": false - }, - { - "key": "columnHeadersInner", - "className": "MuiDataGridPro-columnHeadersInner", - "description": "Styles applied to the column headers's inner element.", - "isGlobal": false - }, - { - "key": "columnHeadersInner--scrollable", - "className": "MuiDataGridPro-columnHeadersInner--scrollable", - "description": "Styles applied to the column headers's inner element if there is a horizontal scrollbar.", - "isGlobal": false - }, { "key": "columnHeaderTitle", "className": "MuiDataGridPro-columnHeaderTitle", @@ -1359,24 +1347,6 @@ "description": "Styles applied to the root of the input component.", "isGlobal": false }, - { - "key": "filler", - "className": "MuiDataGridPro-filler", - "description": "Styles applied to the filler row.", - "isGlobal": false - }, - { - "key": "filler--pinnedLeft", - "className": "MuiDataGridPro-filler--pinnedLeft", - "description": "Styles applied to the filler row pinned left section.", - "isGlobal": false - }, - { - "key": "filler--pinnedRight", - "className": "MuiDataGridPro-filler--pinnedRight", - "description": "Styles applied to the filler row pinned right section.", - "isGlobal": false - }, { "key": "filterForm", "className": "MuiDataGridPro-filterForm", @@ -1551,24 +1521,6 @@ "description": "Styles applied to the paper element.", "isGlobal": false }, - { - "key": "pinnedColumnHeaders", - "className": "MuiDataGridPro-pinnedColumnHeaders", - "description": "Styles applied to the pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--left", - "className": "MuiDataGridPro-pinnedColumnHeaders--left", - "description": "Styles applied to the left pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--right", - "className": "MuiDataGridPro-pinnedColumnHeaders--right", - "description": "Styles applied to the right pinned column headers.", - "isGlobal": false - }, { "key": "pinnedColumns", "className": "MuiDataGridPro-pinnedColumns", diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index aaf820b0be44a..468423143dd70 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -1018,12 +1018,6 @@ "description": "Styles applied to the selection checkbox element.", "isGlobal": false }, - { - "key": "columnGroupHeader", - "className": "MuiDataGrid-columnGroupHeader", - "description": "Styles applied to the column group header element.", - "isGlobal": false - }, { "key": "columnHeader", "className": "MuiDataGrid-columnHeader", @@ -1085,9 +1079,15 @@ "isGlobal": false }, { - "key": "columnHeader--showColumnBorder", - "className": "MuiDataGrid-columnHeader--showColumnBorder", - "description": "Styles applied to the column group header cell when show column border.", + "key": "columnHeader--pinnedLeft", + "className": "MuiDataGrid-columnHeader--pinnedLeft", + "description": "", + "isGlobal": false + }, + { + "key": "columnHeader--pinnedRight", + "className": "MuiDataGrid-columnHeader--pinnedRight", + "description": "", "isGlobal": false }, { @@ -1102,6 +1102,12 @@ "description": "Styles applied to the column header if the column is sorted.", "isGlobal": false }, + { + "key": "columnHeader--withLeftBorder", + "className": "MuiDataGrid-columnHeader--withLeftBorder", + "description": "", + "isGlobal": false + }, { "key": "columnHeader--withRightBorder", "className": "MuiDataGrid-columnHeader--withRightBorder", @@ -1120,24 +1126,6 @@ "description": "Styles applied to the column header's draggable container element.", "isGlobal": false }, - { - "key": "columnHeaderDropZone", - "className": "MuiDataGrid-columnHeaderDropZone", - "description": "Styles applied to the column headers wrapper if a column is being dragged.", - "isGlobal": false - }, - { - "key": "columnHeadersInner", - "className": "MuiDataGrid-columnHeadersInner", - "description": "Styles applied to the column headers's inner element.", - "isGlobal": false - }, - { - "key": "columnHeadersInner--scrollable", - "className": "MuiDataGrid-columnHeadersInner--scrollable", - "description": "Styles applied to the column headers's inner element if there is a horizontal scrollbar.", - "isGlobal": false - }, { "key": "columnHeaderTitle", "className": "MuiDataGrid-columnHeaderTitle", @@ -1246,24 +1234,6 @@ "description": "Styles applied to the root of the input component.", "isGlobal": false }, - { - "key": "filler", - "className": "MuiDataGrid-filler", - "description": "Styles applied to the filler row.", - "isGlobal": false - }, - { - "key": "filler--pinnedLeft", - "className": "MuiDataGrid-filler--pinnedLeft", - "description": "Styles applied to the filler row pinned left section.", - "isGlobal": false - }, - { - "key": "filler--pinnedRight", - "className": "MuiDataGrid-filler--pinnedRight", - "description": "Styles applied to the filler row pinned right section.", - "isGlobal": false - }, { "key": "filterForm", "className": "MuiDataGrid-filterForm", @@ -1438,24 +1408,6 @@ "description": "Styles applied to the paper element.", "isGlobal": false }, - { - "key": "pinnedColumnHeaders", - "className": "MuiDataGrid-pinnedColumnHeaders", - "description": "Styles applied to the pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--left", - "className": "MuiDataGrid-pinnedColumnHeaders--left", - "description": "Styles applied to the left pinned column headers.", - "isGlobal": false - }, - { - "key": "pinnedColumnHeaders--right", - "className": "MuiDataGrid-pinnedColumnHeaders--right", - "description": "Styles applied to the right pinned column headers.", - "isGlobal": false - }, { "key": "pinnedColumns", "className": "MuiDataGrid-pinnedColumns", diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index c7caf3bd8733b..7f8c28d5b7175 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -762,10 +762,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the selection checkbox element" }, - "columnGroupHeader": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column group header element" - }, "columnHeader": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header element" @@ -814,11 +810,8 @@ "nodeName": "the column header", "conditions": "the type of the column is number" }, - "columnHeader--showColumnBorder": { - "description": "Styles applied to {{nodeName}} when {{conditions}}.", - "nodeName": "the column group header cell", - "conditions": "show column border" - }, + "columnHeader--pinnedLeft": { "description": "" }, + "columnHeader--pinnedRight": { "description": "" }, "columnHeader--sortable": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the column header", @@ -829,6 +822,7 @@ "nodeName": "the column header", "conditions": "the column is sorted" }, + "columnHeader--withLeftBorder": { "description": "" }, "columnHeader--withRightBorder": { "description": "Styles applied the column header if showColumnVerticalBorder={true}." }, @@ -840,20 +834,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header's draggable container element" }, - "columnHeaderDropZone": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers wrapper", - "conditions": "a column is being dragged" - }, - "columnHeadersInner": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column headers's inner element" - }, - "columnHeadersInner--scrollable": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers's inner element", - "conditions": "there is a horizontal scrollbar" - }, "columnHeaderTitle": { "description": "Styles applied to the column header's title element;" }, @@ -930,15 +910,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the input component" }, - "filler": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the filler row" }, - "filler--pinnedLeft": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned left section" - }, - "filler--pinnedRight": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned right section" - }, "filterForm": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the filter form component" @@ -1049,18 +1020,6 @@ "nodeName": "the panel wrapper element" }, "paper": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the paper element" }, - "pinnedColumnHeaders": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the pinned column headers" - }, - "pinnedColumnHeaders--left": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the left pinned column headers" - }, - "pinnedColumnHeaders--right": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the right pinned column headers" - }, "pinnedColumns": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the pinned columns" diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index 520fbf1059d2f..c4f04eb81c606 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -704,10 +704,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the selection checkbox element" }, - "columnGroupHeader": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column group header element" - }, "columnHeader": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header element" @@ -756,11 +752,8 @@ "nodeName": "the column header", "conditions": "the type of the column is number" }, - "columnHeader--showColumnBorder": { - "description": "Styles applied to {{nodeName}} when {{conditions}}.", - "nodeName": "the column group header cell", - "conditions": "show column border" - }, + "columnHeader--pinnedLeft": { "description": "" }, + "columnHeader--pinnedRight": { "description": "" }, "columnHeader--sortable": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the column header", @@ -771,6 +764,7 @@ "nodeName": "the column header", "conditions": "the column is sorted" }, + "columnHeader--withLeftBorder": { "description": "" }, "columnHeader--withRightBorder": { "description": "Styles applied the column header if showColumnVerticalBorder={true}." }, @@ -782,20 +776,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header's draggable container element" }, - "columnHeaderDropZone": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers wrapper", - "conditions": "a column is being dragged" - }, - "columnHeadersInner": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column headers's inner element" - }, - "columnHeadersInner--scrollable": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers's inner element", - "conditions": "there is a horizontal scrollbar" - }, "columnHeaderTitle": { "description": "Styles applied to the column header's title element;" }, @@ -872,15 +852,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the input component" }, - "filler": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the filler row" }, - "filler--pinnedLeft": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned left section" - }, - "filler--pinnedRight": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned right section" - }, "filterForm": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the filter form component" @@ -991,18 +962,6 @@ "nodeName": "the panel wrapper element" }, "paper": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the paper element" }, - "pinnedColumnHeaders": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the pinned column headers" - }, - "pinnedColumnHeaders--left": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the left pinned column headers" - }, - "pinnedColumnHeaders--right": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the right pinned column headers" - }, "pinnedColumns": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the pinned columns" diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index 06e1990d8b3af..c6d25a3f35a80 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -594,10 +594,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the selection checkbox element" }, - "columnGroupHeader": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column group header element" - }, "columnHeader": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header element" @@ -646,11 +642,8 @@ "nodeName": "the column header", "conditions": "the type of the column is number" }, - "columnHeader--showColumnBorder": { - "description": "Styles applied to {{nodeName}} when {{conditions}}.", - "nodeName": "the column group header cell", - "conditions": "show column border" - }, + "columnHeader--pinnedLeft": { "description": "" }, + "columnHeader--pinnedRight": { "description": "" }, "columnHeader--sortable": { "description": "Styles applied to {{nodeName}} if {{conditions}}.", "nodeName": "the column header", @@ -661,6 +654,7 @@ "nodeName": "the column header", "conditions": "the column is sorted" }, + "columnHeader--withLeftBorder": { "description": "" }, "columnHeader--withRightBorder": { "description": "Styles applied the column header if showColumnVerticalBorder={true}." }, @@ -672,20 +666,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the column header's draggable container element" }, - "columnHeaderDropZone": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers wrapper", - "conditions": "a column is being dragged" - }, - "columnHeadersInner": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the column headers's inner element" - }, - "columnHeadersInner--scrollable": { - "description": "Styles applied to {{nodeName}} if {{conditions}}.", - "nodeName": "the column headers's inner element", - "conditions": "there is a horizontal scrollbar" - }, "columnHeaderTitle": { "description": "Styles applied to the column header's title element;" }, @@ -762,15 +742,6 @@ "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the input component" }, - "filler": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the filler row" }, - "filler--pinnedLeft": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned left section" - }, - "filler--pinnedRight": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the filler row pinned right section" - }, "filterForm": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the root of the filter form component" @@ -881,18 +852,6 @@ "nodeName": "the panel wrapper element" }, "paper": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the paper element" }, - "pinnedColumnHeaders": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the pinned column headers" - }, - "pinnedColumnHeaders--left": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the left pinned column headers" - }, - "pinnedColumnHeaders--right": { - "description": "Styles applied to {{nodeName}}.", - "nodeName": "the right pinned column headers" - }, "pinnedColumns": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the pinned columns" diff --git a/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx b/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx index 6a03a203063cd..cff0add8e8c5f 100644 --- a/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx +++ b/packages/x-data-grid-pro/src/components/GridColumnHeaders.tsx @@ -1,112 +1,23 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { refType, unstable_composeClasses as composeClasses } from '@mui/utils'; import { styled } from '@mui/material/styles'; -import { - getDataGridUtilityClass, - gridClasses, - GridColumnHeaderSeparatorSides, - GridPinnedColumnPosition, - useGridSelector, - gridVisiblePinnedColumnDefinitionsSelector, -} from '@mui/x-data-grid'; -import { - GridBaseColumnHeaders, - GridColumnHeadersInner, - GridPinnedColumnFields, - UseGridColumnHeadersProps, -} from '@mui/x-data-grid/internals'; -import { useGridRootProps } from '../hooks/utils/useGridRootProps'; -import { useGridApiContext } from '../hooks/utils/useGridApiContext'; -import { DataGridProProcessedProps } from '../models/dataGridProProps'; +import { GridBaseColumnHeaders, UseGridColumnHeadersProps } from '@mui/x-data-grid/internals'; import { useGridColumnHeaders } from '../hooks/features/columnHeaders/useGridColumnHeaders'; -import { GridScrollArea } from './GridScrollArea'; - -type OwnerState = DataGridProProcessedProps & { - leftPinnedColumns: GridPinnedColumnFields['left']; - rightPinnedColumns: GridPinnedColumnFields['right']; -}; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { leftPinnedColumns, rightPinnedColumns, classes } = ownerState; - - const slots = { - leftPinnedColumns: [ - 'pinnedColumnHeaders', - leftPinnedColumns && leftPinnedColumns.length > 0 && `pinnedColumnHeaders--left`, - ], - rightPinnedColumns: [ - 'pinnedColumnHeaders', - rightPinnedColumns && rightPinnedColumns.length > 0 && `pinnedColumnHeaders--right`, - 'withBorderColor', - ], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -interface GridColumnHeadersPinnedColumnHeadersProps { - side: GridPinnedColumnPosition; - showCellVerticalBorder: boolean; -} - -const GridColumnHeadersPinnedColumnHeaders = styled('div', { - name: 'MuiDataGrid', - slot: 'PinnedColumnHeaders', - overridesResolver: (_, styles) => [ - { [`&.${gridClasses['pinnedColumnHeaders--left']}`]: styles['pinnedColumnHeaders--left'] }, - { [`&.${gridClasses['pinnedColumnHeaders--right']}`]: styles['pinnedColumnHeaders--right'] }, - styles.pinnedColumnHeaders, - ], -})<{ ownerState: OwnerState & GridColumnHeadersPinnedColumnHeadersProps }>(({ ownerState }) => ({ - position: 'sticky', - zIndex: 5, - top: 0, - display: 'flex', - flexDirection: 'column', - boxSizing: 'border-box', - backgroundColor: 'var(--DataGrid-pinnedBackground)', - ...(ownerState.side === GridPinnedColumnPosition.LEFT && { left: 0 }), - ...(ownerState.side === GridPinnedColumnPosition.RIGHT && { right: 0 }), - [`&.${gridClasses['pinnedColumnHeaders--left']}`]: { - left: 0, - '& > [role="row"] > [role="columnheader"]:last-of-type': { - borderRight: '1px solid var(--DataGrid-rowBorderColor)', - }, - }, - [`&.${gridClasses['pinnedColumnHeaders--right']}`]: { - right: 0, - '& > [role="row"] > [role="columnheader"]:first-of-type': { - borderLeft: '1px solid var(--DataGrid-rowBorderColor)', - }, - }, -})); const Filler = styled('div')({ flex: 1, backgroundColor: 'var(--DataGrid-containerBackground)', }); -GridColumnHeadersPinnedColumnHeaders.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - ownerState: PropTypes.object.isRequired, -} as any; - interface DataGridProColumnHeadersProps extends React.HTMLAttributes, - Omit { - innerRef?: React.Ref; -} + UseGridColumnHeadersProps {} const GridColumnHeaders = React.forwardRef( function GridColumnHeaders(props, ref) { const { style, className, - innerRef, visibleColumns, sortColumnLookup, filterColumnLookup, @@ -121,158 +32,29 @@ const GridColumnHeaders = React.forwardRef c.field), - rightPinnedColumns: visiblePinnedColumns.right.map((c) => c.field), - classes: rootProps.classes, - }; - const classes = useUtilityClasses(ownerState); - - const leftRenderContext = - renderContext && visiblePinnedColumns.left.length - ? { - ...renderContext, - firstColumnIndex: 0, - lastColumnIndex: visiblePinnedColumns.left.length, - } - : null; - - const rightRenderContext = - renderContext && visiblePinnedColumns.right.length - ? { - ...renderContext, - firstColumnIndex: visibleColumns.length - visiblePinnedColumns.right.length, - lastColumnIndex: visibleColumns.length, - } - : null; - - const innerProps = getInnerProps(); - - const pinnedColumnHeadersProps = { - role: innerProps.role, - }; + const { getInnerProps, getColumnHeadersRow, getColumnFiltersRow, getColumnGroupHeadersRows } = + useGridColumnHeaders({ + visibleColumns, + sortColumnLookup, + filterColumnLookup, + columnHeaderTabIndexState, + hasOtherElementInTabSequence, + columnGroupHeaderTabIndexState, + columnHeaderFocus, + columnGroupHeaderFocus, + headerGroupingMaxDepth, + columnMenuState, + columnVisibility, + columnGroupsHeaderStructure, + }); return ( - - {leftRenderContext && ( - - {getColumnGroupHeaders({ - position: GridPinnedColumnPosition.LEFT, - renderContext: leftRenderContext, - minFirstColumn: leftRenderContext.firstColumnIndex, - maxLastColumn: leftRenderContext.lastColumnIndex, - })} - {getColumnHeaders( - { - position: GridPinnedColumnPosition.LEFT, - renderContext: leftRenderContext, - minFirstColumn: leftRenderContext.firstColumnIndex, - maxLastColumn: leftRenderContext.lastColumnIndex, - }, - { disableReorder: true }, - )} - {getColumnFilters({ - position: GridPinnedColumnPosition.LEFT, - renderContext: leftRenderContext, - minFirstColumn: leftRenderContext.firstColumnIndex, - maxLastColumn: leftRenderContext.lastColumnIndex, - })} - - )} - - - - {getColumnGroupHeaders({ - renderContext, - minFirstColumn: visiblePinnedColumns.left.length, - maxLastColumn: visibleColumns.length - visiblePinnedColumns.right.length, - })} - {getColumnHeaders({ - renderContext, - minFirstColumn: visiblePinnedColumns.left.length, - maxLastColumn: visibleColumns.length - visiblePinnedColumns.right.length, - })} - {getColumnFilters({ - renderContext, - minFirstColumn: visiblePinnedColumns.left.length, - maxLastColumn: visibleColumns.length - visiblePinnedColumns.right.length, - })} - - + + {getColumnGroupHeadersRows()} + {getColumnHeadersRow()} + {getColumnFiltersRow()} - {rightRenderContext && ( - - {getColumnGroupHeaders({ - position: GridPinnedColumnPosition.RIGHT, - renderContext: rightRenderContext, - minFirstColumn: rightRenderContext.firstColumnIndex, - maxLastColumn: rightRenderContext.lastColumnIndex, - })} - {getColumnHeaders( - { - position: GridPinnedColumnPosition.RIGHT, - renderContext: rightRenderContext, - minFirstColumn: rightRenderContext.firstColumnIndex, - maxLastColumn: rightRenderContext.lastColumnIndex, - }, - { disableReorder: true, separatorSide: GridColumnHeaderSeparatorSides.Left }, - )} - {getColumnFilters({ - position: GridPinnedColumnPosition.RIGHT, - renderContext: rightRenderContext, - minFirstColumn: rightRenderContext.firstColumnIndex, - maxLastColumn: rightRenderContext.lastColumnIndex, - })} - - )} ); }, @@ -313,7 +95,6 @@ GridColumnHeaders.propTypes = { filterColumnLookup: PropTypes.object.isRequired, hasOtherElementInTabSequence: PropTypes.bool.isRequired, headerGroupingMaxDepth: PropTypes.number.isRequired, - innerRef: refType, sortColumnLookup: PropTypes.object.isRequired, visibleColumns: PropTypes.arrayOf(PropTypes.object).isRequired, } as any; diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index 9f7d6e6c3d884..16d4e3618500a 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -16,6 +16,7 @@ import { useGridSelector, gridFilterModelSelector, gridFilterableColumnLookupSelector, + GridPinnedColumnPosition, } from '@mui/x-data-grid'; import { fastMemo, @@ -25,6 +26,10 @@ import { gridHeaderFilteringMenuSelector, isNavigationKey, } from '@mui/x-data-grid/internals'; +import { + shouldCellShowLeftBorder, + shouldCellShowRightBorder, +} from '@mui/x-data-grid/utils/cellBorderUtils'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { DataGridProProcessedProps } from '../../models/dataGridProProps'; import { GridHeaderFilterMenuContainer } from './GridHeaderFilterMenuContainer'; @@ -46,12 +51,21 @@ export interface GridHeaderFilterCellProps extends Pick { - const { colDef, classes, showColumnVerticalBorder } = ownerState; + const { colDef, classes, showRightBorder, showLeftBorder, pinnedPosition } = ownerState; const slots = { root: [ @@ -60,7 +74,10 @@ const useUtilityClasses = (ownerState: OwnerState) => { colDef.headerAlign === 'center' && 'columnHeader--alignCenter', colDef.headerAlign === 'right' && 'columnHeader--alignRight', 'withBorderColor', - showColumnVerticalBorder && 'columnHeader--withRightBorder', + showRightBorder && 'columnHeader--withRightBorder', + showLeftBorder && 'columnHeader--withLeftBorder', + pinnedPosition === 'left' && 'columnHeader--pinnedLeft', + pinnedPosition === 'right' && 'columnHeader--pinnedRight', ], }; @@ -84,6 +101,10 @@ const GridHeaderFilterCell = React.forwardRef { apiRef, gridTabIndexColumnHeaderFilterSelector, ); - const { getColumnsToRender, ...otherProps } = useGridColumnHeadersCommunity({ + const { + getColumnsToRender, + renderContext, + leftRenderContext, + rightRenderContext, + pinnedColumns, + visibleColumns, + getCellOffsetStyle, + ...otherProps + } = useGridColumnHeadersCommunity({ ...props, hasOtherElementInTabSequence: hasOtherElementInTabSequence || columnHeaderFilterTabIndexState !== null, @@ -82,11 +92,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { [filterModel], ); - const getColumnFilters = (params?: GetHeadersParams, other = {}) => { - if (disableHeaderFiltering) { - return null; - } - + const getColumnFilters = (params?: GetHeadersParams) => { const { renderedColumns, firstColumnToRender } = getColumnsToRender(params); const filters: React.JSX.Element[] = []; @@ -108,6 +114,13 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const item = getFilterItem(colDef); + const pinnedPosition = params?.position; + const style = getCellOffsetStyle({ + pinnedPosition, + columnIndex, + computedWidth: colDef.computedWidth, + }); + filters.push( { headerClassName={headerClassName} data-field={colDef.field} item={item} + pinnedPosition={pinnedPosition} + style={style} + indexInSection={i} + sectionLength={renderedColumns.length} {...rootProps.slotProps?.headerFilterCell} - {...other} />, ); } + return otherProps.getFillers(params, filters, 0, true); + }; + + const getColumnFiltersRow = () => { + if (disableHeaderFiltering) { + return null; + } + return ( { aria-rowindex={headerGroupingMaxDepth + 2} ownerState={rootProps} > - {otherProps.getFillers(params, filters, 0, true)} + {leftRenderContext && + getColumnFilters({ + position: GridPinnedColumnPosition.LEFT, + renderContext: leftRenderContext, + minFirstColumn: leftRenderContext.firstColumnIndex, + maxLastColumn: leftRenderContext.lastColumnIndex, + })} + {getColumnFilters({ + renderContext, + minFirstColumn: pinnedColumns.left.length, + maxLastColumn: visibleColumns.length - pinnedColumns.right.length, + })} + {rightRenderContext && + getColumnFilters({ + position: GridPinnedColumnPosition.RIGHT, + renderContext: rightRenderContext, + minFirstColumn: rightRenderContext.firstColumnIndex, + maxLastColumn: rightRenderContext.lastColumnIndex, + })} ); }; return { ...otherProps, - getColumnFilters, + getColumnFiltersRow, }; }; diff --git a/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx index 5836cf742655c..52a329866badd 100644 --- a/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx @@ -171,19 +171,23 @@ describe(' - Column pinning', () => { }); it('should not allow to drop a column on top of a pinned column', () => { - render(); - expect( - document.querySelector('.MuiDataGrid-pinnedColumnHeaders--right')?.textContent, - ).to.deep.equal('1M'); + const onPinnedColumnsChange = spy(); + render( + , + ); + const dragCol = getColumnHeaderCell(1).firstChild!; const targetCell = getCell(0, 2)!; fireEvent.dragStart(dragCol); fireEvent.dragEnter(targetCell); const dragOverEvent = createDragOverEvent(targetCell); fireEvent(targetCell, dragOverEvent); - expect( - document.querySelector('.MuiDataGrid-pinnedColumnHeaders--right')?.textContent, - ).to.deep.equal('1M'); + + expect(onPinnedColumnsChange.callCount).to.equal(0); }); it('should filter out invalid columns when blocking a column from being dropped', () => { diff --git a/packages/x-data-grid-pro/src/utils/domUtils.ts b/packages/x-data-grid-pro/src/utils/domUtils.ts deleted file mode 100644 index 367624301cbed..0000000000000 --- a/packages/x-data-grid-pro/src/utils/domUtils.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { gridClasses } from '@mui/x-data-grid'; -import { findParentElementFromClassName } from '@mui/x-data-grid/internals'; -import { GridPrivateApiPro } from '../models/gridApiPro'; - -export function getFieldFromHeaderElem(colCellEl: Element): string { - return colCellEl.getAttribute('data-field')!; -} - -export function findHeaderElementFromField(elem: Element, field: string): HTMLDivElement { - return elem.querySelector(`[data-field="${field}"]`)!; -} - -export function findGroupHeaderElementsFromField(elem: Element, field: string): Element[] { - return Array.from(elem.querySelectorAll(`[data-fields*="|-${field}-|"]`) ?? []); -} - -export function findGridCellElementsFromCol(col: HTMLElement, api: GridPrivateApiPro) { - const root = findParentElementFromClassName(col, gridClasses.root); - if (!root) { - throw new Error('MUI X: The root element is not found.'); - } - - const ariaColIndex = col.getAttribute('aria-colindex'); - if (!ariaColIndex) { - return []; - } - - const colIndex = Number(ariaColIndex) - 1; - const cells: Element[] = []; - - if (!api.virtualScrollerRef?.current) { - return []; - } - - queryRows(api).forEach((rowElement) => { - const rowId = rowElement.getAttribute('data-id'); - if (!rowId) { - return; - } - - let columnIndex = colIndex; - - const cellColSpanInfo = api.unstable_getCellColSpanInfo(rowId, colIndex); - if (cellColSpanInfo && cellColSpanInfo.spannedByColSpan) { - columnIndex = cellColSpanInfo.leftVisibleCellIndex; - } - const cell = rowElement.querySelector(`[data-colindex="${columnIndex}"]`); - if (cell) { - cells.push(cell); - } - }); - - return cells; -} - -export function findGridElement(api: GridPrivateApiPro, klass: keyof typeof gridClasses) { - return api.rootElementRef.current!.querySelector(`.${gridClasses[klass]}`)! as HTMLElement; -} - -export function findLeftPinnedCellsAfterCol(api: GridPrivateApiPro, col: HTMLElement) { - const colIndex = parseCellColIndex(col); - if (colIndex === null) { - return []; - } - - const cells: HTMLElement[] = []; - - queryRows(api).forEach((rowElement) => { - const rowId = rowElement.getAttribute('data-id'); - if (!rowId) { - return; - } - - const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedLeft']}`); - rightPinnedCells.forEach((cell) => { - const currentColIndex = parseCellColIndex(cell); - if (currentColIndex !== null && currentColIndex > colIndex) { - cells.push(cell as HTMLElement); - } - }); - }); - - return cells; -} - -export function findRightPinnedCellsBeforeCol(api: GridPrivateApiPro, col: HTMLElement) { - const colIndex = parseCellColIndex(col); - if (colIndex === null) { - return []; - } - - const cells: HTMLElement[] = []; - - queryRows(api).forEach((rowElement) => { - const rowId = rowElement.getAttribute('data-id'); - if (!rowId) { - return; - } - - const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedRight']}`); - rightPinnedCells.forEach((cell) => { - const currentColIndex = parseCellColIndex(cell); - if (currentColIndex !== null && currentColIndex < colIndex) { - cells.push(cell as HTMLElement); - } - }); - }); - - return cells; -} - -export function findGridHeader(api: GridPrivateApiPro, field: string) { - const headers = api.columnHeadersContainerElementRef!.current!; - return headers.querySelector(`:scope > div > div > [data-field="${field}"][role="columnheader"]`); -} - -export function findGridCells(api: GridPrivateApiPro, field: string) { - const container = api.virtualScrollerRef!.current!; - return Array.from( - container.querySelectorAll( - `:scope > div > div > div > [data-field="${field}"][role="gridcell"]`, - ), - ); -} - -function queryRows(api: GridPrivateApiPro) { - return api.virtualScrollerRef.current!.querySelectorAll( - // Use > to ignore rows from nested data grids (e.g. in detail panel) - `:scope > div > div > .${gridClasses.row}`, - ); -} - -function parseCellColIndex(col: Element) { - const ariaColIndex = col.getAttribute('aria-colindex'); - if (!ariaColIndex) { - return null; - } - return Number(ariaColIndex) - 1; -} diff --git a/packages/x-data-grid/src/components/GridColumnHeaders.tsx b/packages/x-data-grid/src/components/GridColumnHeaders.tsx index ae1bd9bbd0609..8f737ae942df3 100644 --- a/packages/x-data-grid/src/components/GridColumnHeaders.tsx +++ b/packages/x-data-grid/src/components/GridColumnHeaders.tsx @@ -1,25 +1,21 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { refType } from '@mui/utils'; import { fastMemo } from '../utils/fastMemo'; import { useGridColumnHeaders, UseGridColumnHeadersProps, } from '../hooks/features/columnHeaders/useGridColumnHeaders'; import { GridBaseColumnHeaders } from './columnHeaders/GridBaseColumnHeaders'; -import { GridColumnHeadersInner } from './columnHeaders/GridColumnHeadersInner'; export interface GridColumnHeadersProps extends React.HTMLAttributes, - Omit { + UseGridColumnHeadersProps { ref?: React.Ref; - innerRef?: React.Ref; } const GridColumnHeaders = React.forwardRef( function GridColumnHeaders(props, ref) { const { - innerRef, className, visibleColumns, sortColumnLookup, @@ -36,29 +32,25 @@ const GridColumnHeaders = React.forwardRef - - {getColumnGroupHeaders()} - {getColumnHeaders()} - + + {getColumnGroupHeadersRows()} + {getColumnHeadersRow()} ); }, @@ -99,7 +91,6 @@ GridColumnHeaders.propTypes = { filterColumnLookup: PropTypes.object.isRequired, hasOtherElementInTabSequence: PropTypes.bool.isRequired, headerGroupingMaxDepth: PropTypes.number.isRequired, - innerRef: refType, sortColumnLookup: PropTypes.object.isRequired, visibleColumns: PropTypes.arrayOf(PropTypes.object).isRequired, } as any; diff --git a/packages/x-data-grid/src/components/GridHeaders.tsx b/packages/x-data-grid/src/components/GridHeaders.tsx index dcc2ab52c2e26..8a3254bb4a6a3 100644 --- a/packages/x-data-grid/src/components/GridHeaders.tsx +++ b/packages/x-data-grid/src/components/GridHeaders.tsx @@ -54,18 +54,15 @@ function GridHeaders() { cellTabIndexState === null ); - const columnHeadersRef = React.useRef(null); const columnsContainerRef = React.useRef(null); apiRef.current.register('private', { - columnHeadersContainerElementRef: columnsContainerRef, - columnHeadersElementRef: columnHeadersRef, + columnHeadersContainerRef: columnsContainerRef, }); return ( { row: GridRowModel; @@ -369,25 +370,13 @@ const GridRow = React.forwardRef(function GridRow( const width = cellColSpanInfo?.cellProps.width ?? column.computedWidth; const colSpan = cellColSpanInfo?.cellProps.colSpan ?? 1; - let pinnedOffset: number; - // FIXME: Why is the switch check exhaustiveness not validated with typescript-eslint? - // eslint-disable-next-line default-case - switch (pinnedPosition) { - case PinnedPosition.LEFT: - pinnedOffset = columnPositions[indexRelativeToAllColumns]; - break; - case PinnedPosition.RIGHT: - pinnedOffset = - dimensions.columnsTotalWidth - - columnPositions[indexRelativeToAllColumns] - - column.computedWidth + - scrollbarWidth; - break; - case PinnedPosition.NONE: - case PinnedPosition.VIRTUAL: - pinnedOffset = 0; - break; - } + const pinnedOffset = getPinnedCellOffset( + gridPinnedColumnPositionLookup[pinnedPosition], + column.computedWidth, + indexRelativeToAllColumns, + columnPositions, + dimensions, + ); if (rowNode?.type === 'skeletonRow') { return ( @@ -533,7 +522,7 @@ const GridRow = React.forwardRef(function GridRow( /> {cells} {emptyCellWidth > 0 && } - {rightCells.length > 0 &&
} + {rightCells.length > 0 &&
} {rightCells} {scrollbarWidth !== 0 && 0} />}
diff --git a/packages/x-data-grid-pro/src/components/GridScrollArea.tsx b/packages/x-data-grid/src/components/GridScrollArea.tsx similarity index 76% rename from packages/x-data-grid-pro/src/components/GridScrollArea.tsx rename to packages/x-data-grid/src/components/GridScrollArea.tsx index a8aec7987f265..8abd78dfb49d1 100644 --- a/packages/x-data-grid-pro/src/components/GridScrollArea.tsx +++ b/packages/x-data-grid/src/components/GridScrollArea.tsx @@ -6,20 +6,20 @@ import { unstable_useEventCallback as useEventCallback, } from '@mui/utils'; import { styled } from '@mui/system'; -import { getTotalHeaderHeight, fastMemo, useTimeout } from '@mui/x-data-grid/internals'; -import { - GridEventListener, - GridScrollParams, - getDataGridUtilityClass, - gridClasses, - gridDensityFactorSelector, - useGridApiContext, - useGridApiEventHandler, - useGridSelector, - gridColumnsTotalWidthSelector, -} from '@mui/x-data-grid'; -import { DataGridProProcessedProps } from '../models/dataGridProProps'; +import { DataGridProcessedProps } from '../models/props/DataGridProps'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; +import { getDataGridUtilityClass, gridClasses } from '../constants'; +import { useGridApiContext } from '../hooks/utils/useGridApiContext'; +import { useGridApiEventHandler } from '../hooks/utils/useGridApiEventHandler'; +import { useGridSelector } from '../hooks/utils/useGridSelector'; +import { gridDimensionsSelector } from '../hooks/features/dimensions/gridDimensionsSelectors'; +import { gridDensityFactorSelector } from '../hooks/features/density/densitySelector'; +import { gridColumnsTotalWidthSelector } from '../hooks/features/columns/gridColumnsSelector'; +import { GridScrollParams } from '../models/params/gridScrollParams'; +import { GridEventListener } from '../models/events'; +import { useTimeout } from '../hooks/utils/useTimeout'; +import { getTotalHeaderHeight } from '../hooks/features/columns/gridColumnsUtils'; +import { fastMemo } from '../utils/fastMemo'; const CLIFF = 1; const SLOP = 1.5; @@ -28,7 +28,7 @@ interface ScrollAreaProps { scrollDirection: 'left' | 'right'; } -type OwnerState = DataGridProProcessedProps & Pick; +type OwnerState = DataGridProcessedProps & Pick; const useUtilityClasses = (ownerState: OwnerState) => { const { scrollDirection, classes } = ownerState; @@ -69,6 +69,7 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { const timeout = useTimeout(); const densityFactor = useGridSelector(apiRef, gridDensityFactorSelector); const columnsTotalWidth = useGridSelector(apiRef, gridColumnsTotalWidthSelector); + const dimensions = useGridSelector(apiRef, gridDimensionsSelector); const scrollPosition = React.useRef({ left: 0, @@ -82,8 +83,6 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { } if (scrollDirection === 'right') { - const dimensions = apiRef.current.getRootDimensions(); - // Only render if the user has not reached yet the end of the list const maxScrollLeft = columnsTotalWidth - dimensions.viewportInnerSize.width; return scrollPosition.current.left < maxScrollLeft; @@ -101,6 +100,18 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { const totalHeaderHeight = getTotalHeaderHeight(apiRef, rootProps.columnHeaderHeight); const headerHeight = Math.floor(rootProps.columnHeaderHeight * densityFactor); + const style: React.CSSProperties = { + height: headerHeight, + top: totalHeaderHeight - headerHeight, + }; + + if (scrollDirection === 'left') { + style.left = dimensions.leftPinnedWidth; + } else if (scrollDirection === 'right') { + style.right = + dimensions.rightPinnedWidth + (dimensions.hasScrollX ? dimensions.scrollbarSize : 0); + } + const handleScrolling: GridEventListener<'scrollPositionChange'> = (newScrollPosition) => { scrollPosition.current = newScrollPosition; @@ -154,7 +165,7 @@ function GridScrollAreaRaw(props: ScrollAreaProps) { className={clsx(classes.root)} ownerState={ownerState} onDragOver={handleDragOver} - style={{ height: headerHeight, top: totalHeaderHeight - headerHeight }} + style={style} /> ); } diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 88165af4e02a3..26ebc53d1ec88 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -32,6 +32,8 @@ import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridFocusCellSelector } from '../../hooks/features/focus/gridFocusStateSelector'; import { MissingRowIdError } from '../../hooks/features/rows/useGridParamsApi'; import type { DataGridProcessedProps } from '../../models/props/DataGridProps'; +import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../../utils/cellBorderUtils'; +import { GridPinnedColumnPosition } from '../../hooks/features/columns/gridColumnsInterfaces'; export enum PinnedPosition { NONE, @@ -40,6 +42,13 @@ export enum PinnedPosition { VIRTUAL, } +export const gridPinnedColumnPositionLookup = { + [PinnedPosition.LEFT]: GridPinnedColumnPosition.LEFT, + [PinnedPosition.RIGHT]: GridPinnedColumnPosition.RIGHT, + [PinnedPosition.NONE]: undefined, + [PinnedPosition.VIRTUAL]: undefined, +}; + export type GridCellProps = { align: GridAlignment; className?: string; @@ -251,14 +260,14 @@ const GridCell = React.forwardRef((props, ref) => // @ts-expect-error To access `cellSelection` flag as it's a `premium` feature const isSelectionMode = rootProps.cellSelection ?? false; - const isSectionLastCell = sectionIndex === sectionLength - 1; - - const showLeftBorder = pinnedPosition === PinnedPosition.RIGHT && sectionIndex === 0; - - const showRightBorder = - (rootProps.showCellVerticalBorder && - (pinnedPosition !== PinnedPosition.LEFT ? !isSectionLastCell : true)) || - (pinnedPosition === PinnedPosition.LEFT && isSectionLastCell); + const position = gridPinnedColumnPositionLookup[pinnedPosition]; + const showLeftBorder = shouldCellShowLeftBorder(position, sectionIndex); + const showRightBorder = shouldCellShowRightBorder( + position, + sectionIndex, + sectionLength, + rootProps.showCellVerticalBorder, + ); const ownerState = { align, diff --git a/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx b/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx index 8e2a9f7cb6c10..aa640c82d98bb 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridBaseColumnHeaders.tsx @@ -24,6 +24,7 @@ const GridColumnHeadersRoot = styled('div', { overridesResolver: (props, styles) => styles.columnHeaders, })<{ ownerState: OwnerState }>({ display: 'flex', + flexDirection: 'column', borderTopLeftRadius: 'var(--unstable_DataGrid-radius)', borderTopRightRadius: 'var(--unstable_DataGrid-radius)', }); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx index b9f7cc2beb460..cc4ba677c225c 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnGroupHeader.tsx @@ -12,6 +12,8 @@ import { GridColumnGroup } from '../../models/gridColumnGrouping'; import { GridColumnGroupHeaderEventLookup } from '../../models/events'; import { GridColumnGroupHeaderParams } from '../../models/params'; import { isEventTargetInPortal } from '../../utils/domUtils'; +import { GridPinnedColumnPosition } from '../../hooks/features/columns/gridColumnsInterfaces'; +import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../../utils/cellBorderUtils'; interface GridColumnGroupHeaderProps { groupId: string | null; @@ -24,18 +26,32 @@ interface GridColumnGroupHeaderProps { height: number; hasFocus?: boolean; tabIndex: 0 | -1; + pinnedPosition?: GridPinnedColumnPosition; + style?: React.CSSProperties; + indexInSection: number; + sectionLength: number; } type OwnerState = { groupId: GridColumnGroupHeaderProps['groupId']; - showColumnBorder: boolean; + showLeftBorder: boolean; + showRightBorder: boolean; isDragging: boolean; headerAlign?: GridAlignment; classes?: DataGridProcessedProps['classes']; + pinnedPosition?: GridPinnedColumnPosition; }; const useUtilityClasses = (ownerState: OwnerState) => { - const { classes, headerAlign, isDragging, showColumnBorder, groupId } = ownerState; + const { + classes, + headerAlign, + isDragging, + showLeftBorder, + showRightBorder, + groupId, + pinnedPosition, + } = ownerState; const slots = { root: [ @@ -44,10 +60,12 @@ const useUtilityClasses = (ownerState: OwnerState) => { headerAlign === 'center' && 'columnHeader--alignCenter', headerAlign === 'right' && 'columnHeader--alignRight', isDragging && 'columnHeader--moving', - showColumnBorder && 'columnHeader--showColumnBorder', - showColumnBorder && 'columnHeader--withRightBorder', + showRightBorder && 'columnHeader--withRightBorder', + showLeftBorder && 'columnHeader--withLeftBorder', 'withBorderColor', groupId === null ? 'columnHeader--emptyGroup' : 'columnHeader--filledGroup', + pinnedPosition === 'left' && 'columnHeader--pinnedLeft', + pinnedPosition === 'right' && 'columnHeader--pinnedRight', ], draggableContainer: ['columnHeaderDraggableContainer'], titleContainer: ['columnHeaderTitleContainer', 'withBorderColor'], @@ -69,6 +87,10 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { hasFocus, tabIndex, isLastColumn, + pinnedPosition, + style, + indexInSection, + sectionLength, } = props; const rootProps = useGridRootProps(); @@ -101,12 +123,19 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { headerComponent = render(renderParams); } - const showColumnBorder = rootProps.showColumnVerticalBorder; + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + ); const ownerState = { ...props, classes: rootProps.classes, - showColumnBorder, + showLeftBorder, + showRightBorder, headerAlign, depth, isDragging: false, @@ -178,6 +207,7 @@ function GridColumnGroupHeader(props: GridColumnGroupHeaderProps) { aria-colspan={fields.length} // The fields are wrapped between |-...-| to avoid confusion between fields "id" and "id2" when using selector data-fields~= data-fields={`|-${fields.join('-|-')}-|`} + style={style} {...mouseEventsHandlers} /> ); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx index b62111481e544..da14c18250aec 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx @@ -15,6 +15,8 @@ import { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { GridGenericColumnHeaderItem } from './GridGenericColumnHeaderItem'; import { GridColumnHeaderEventLookup } from '../../models/events'; import { isEventTargetInPortal } from '../../utils/domUtils'; +import { shouldCellShowLeftBorder, shouldCellShowRightBorder } from '../../utils/cellBorderUtils'; +import { GridPinnedColumnPosition } from '../../hooks/features/columns/gridColumnsInterfaces'; interface GridColumnHeaderItemProps { colIndex: number; @@ -30,16 +32,29 @@ interface GridColumnHeaderItemProps { tabIndex: 0 | -1; disableReorder?: boolean; separatorSide?: GridColumnHeaderSeparatorProps['side']; + pinnedPosition?: GridPinnedColumnPosition; + style?: React.CSSProperties; + indexInSection: number; + sectionLength: number; } type OwnerState = GridColumnHeaderItemProps & { showRightBorder: boolean; + showLeftBorder: boolean; classes?: DataGridProcessedProps['classes']; }; const useUtilityClasses = (ownerState: OwnerState) => { - const { colDef, classes, isDragging, sortDirection, showRightBorder, filterItemsCounter } = - ownerState; + const { + colDef, + classes, + isDragging, + sortDirection, + showRightBorder, + showLeftBorder, + filterItemsCounter, + pinnedPosition, + } = ownerState; const isColumnSorted = sortDirection != null; const isColumnFiltered = filterItemsCounter != null && filterItemsCounter > 0; @@ -59,6 +74,9 @@ const useUtilityClasses = (ownerState: OwnerState) => { isColumnNumeric && 'columnHeader--numeric', 'withBorderColor', showRightBorder && 'columnHeader--withRightBorder', + showLeftBorder && 'columnHeader--withLeftBorder', + pinnedPosition === 'left' && 'columnHeader--pinnedLeft', + pinnedPosition === 'right' && 'columnHeader--pinnedRight', ], draggableContainer: ['columnHeaderDraggableContainer'], titleContainer: ['columnHeaderTitleContainer'], @@ -82,6 +100,10 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { tabIndex, disableReorder, separatorSide, + style, + pinnedPosition, + indexInSection, + sectionLength, } = props; const apiRef = useGridPrivateApiContext(); const rootProps = useGridRootProps(); @@ -101,10 +123,19 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { headerComponent = colDef.renderHeader(apiRef.current.getColumnHeaderParams(colDef.field)); } + const showLeftBorder = shouldCellShowLeftBorder(pinnedPosition, indexInSection); + const showRightBorder = shouldCellShowRightBorder( + pinnedPosition, + indexInSection, + sectionLength, + rootProps.showCellVerticalBorder, + ); + const ownerState = { ...props, classes: rootProps.classes, - showRightBorder: rootProps.showColumnVerticalBorder, + showRightBorder, + showLeftBorder, }; const classes = useUtilityClasses(ownerState); @@ -227,7 +258,7 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { const focusableElement = headerCellRef.current!.querySelector('[tabindex="0"]'); const elementToFocus = focusableElement || headerCellRef.current; elementToFocus?.focus(); - apiRef.current.columnHeadersContainerElementRef!.current!.scrollLeft = 0; + apiRef.current.columnHeadersContainerRef!.current!.scrollLeft = 0; } }, [apiRef, hasFocus]); @@ -264,6 +295,7 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { columnMenu={columnMenu} draggableContainerProps={draggableEventHandlers} columnHeaderSeparatorProps={columnHeaderSeparatorProps} + style={style} {...mouseEventsHandlers} /> ); @@ -281,11 +313,15 @@ GridColumnHeaderItem.propTypes = { filterItemsCounter: PropTypes.number, hasFocus: PropTypes.bool, headerHeight: PropTypes.number.isRequired, + indexInSection: PropTypes.number.isRequired, isDragging: PropTypes.bool.isRequired, isResizing: PropTypes.bool.isRequired, + pinnedPosition: PropTypes.oneOf(['left', 'right']), + sectionLength: PropTypes.number.isRequired, separatorSide: PropTypes.oneOf(['left', 'right']), sortDirection: PropTypes.oneOf(['asc', 'desc']), sortIndex: PropTypes.number, + style: PropTypes.object, tabIndex: PropTypes.oneOf([-1, 0]).isRequired, } as any; diff --git a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeadersInner.tsx b/packages/x-data-grid/src/components/columnHeaders/GridColumnHeadersInner.tsx deleted file mode 100644 index 05c0b25c4df93..0000000000000 --- a/packages/x-data-grid/src/components/columnHeaders/GridColumnHeadersInner.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from 'react'; -import clsx from 'clsx'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; -import { styled, SxProps, Theme } from '@mui/system'; -import { gridClasses, getDataGridUtilityClass } from '../../constants/gridClasses'; -import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; -import { DataGridProcessedProps } from '../../models/props/DataGridProps'; -import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; -import { GridDimensions } from '../../hooks/features/dimensions/gridDimensionsApi'; - -type OwnerState = DataGridProcessedProps & - Pick & { hasScrollX: GridDimensions['hasScrollX'] }; - -const useUtilityClasses = (ownerState: OwnerState) => { - const { isDragging, hasScrollX, classes } = ownerState; - - const slots = { - root: [ - 'columnHeadersInner', - isDragging && 'columnHeaderDropZone', - hasScrollX && 'columnHeadersInner--scrollable', - ], - }; - - return composeClasses(slots, getDataGridUtilityClass, classes); -}; - -const GridColumnHeadersInnerRoot = styled('div', { - name: 'MuiDataGrid', - slot: 'columnHeadersInner', - overridesResolver: (props, styles) => [ - { [`&.${gridClasses.columnHeaderDropZone}`]: styles.columnHeaderDropZone }, - styles.columnHeadersInner, - ], -})<{ ownerState: OwnerState }>(() => ({ - display: 'flex', - alignItems: 'flex-start', - flexDirection: 'column', - [`&.${gridClasses.columnHeaderDropZone} .${gridClasses.columnHeaderDraggableContainer}`]: { - cursor: 'move', - }, - [`&.${gridClasses['columnHeadersInner--scrollable']} .${gridClasses.columnHeader}:last-child`]: { - borderRight: 'none', - }, -})); - -interface GridColumnHeadersInnerProps extends React.HTMLAttributes { - isDragging: boolean; - sx?: SxProps; -} - -export const GridColumnHeadersInner = React.forwardRef( - function GridColumnHeadersInner(props, ref) { - const { isDragging, className, ...other } = props; - const apiRef = useGridApiContext(); - const rootProps = useGridRootProps(); - - const ownerState = { - ...rootProps, - isDragging, - hasScrollX: apiRef.current.getRootDimensions().hasScrollX, - }; - const classes = useUtilityClasses(ownerState); - - return ( - - ); - }, -); diff --git a/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx b/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx index 48de7245fd557..8f45f8176d4a9 100644 --- a/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx +++ b/packages/x-data-grid/src/components/columnHeaders/GridGenericColumnHeaderItem.tsx @@ -39,6 +39,7 @@ interface GridGenericColumnHeaderItemProps label: string; draggableContainerProps?: Partial>; columnHeaderSeparatorProps?: Partial; + style?: React.CSSProperties; } const GridGenericColumnHeaderItem = React.forwardRef(function GridGenericColumnHeaderItem( @@ -68,6 +69,7 @@ const GridGenericColumnHeaderItem = React.forwardRef(function GridGenericColumnH resizable, draggableContainerProps, columnHeaderSeparatorProps, + style, ...other } = props; @@ -95,7 +97,7 @@ const GridGenericColumnHeaderItem = React.forwardRef(function GridGenericColumnH const focusableElement = headerCellRef.current!.querySelector('[tabindex="0"]'); const elementToFocus = focusableElement || headerCellRef.current; elementToFocus?.focus(); - apiRef.current.columnHeadersContainerElementRef!.current!.scrollLeft = 0; + apiRef.current.columnHeadersContainerRef!.current!.scrollLeft = 0; } }, [apiRef, hasFocus]); @@ -104,6 +106,7 @@ const GridGenericColumnHeaderItem = React.forwardRef(function GridGenericColumnH ref={handleRef} className={clsx(classes.root, headerClassName)} style={{ + ...style, height, width, minWidth: width, diff --git a/packages/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/x-data-grid/src/components/containers/GridRootStyles.ts index 691cc8643c79b..3c7708692775e 100644 --- a/packages/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/x-data-grid/src/components/containers/GridRootStyles.ts @@ -311,15 +311,6 @@ export const GridRootStyles = styled('div', { borderBottomStyle: 'solid', boxSizing: 'border-box', }, - [`& .${c['columnHeader--filledGroup']}.${c['columnHeader--showColumnBorder']} .${c.columnHeaderTitleContainer}`]: - { - borderBottom: `none`, - }, - [`& .${c['columnHeader--filledGroup']}.${c['columnHeader--showColumnBorder']}`]: { - borderBottomWidth: '1px', - borderBottomStyle: 'solid', - boxSizing: 'border-box', - }, [`& .${c.sortIcon}, & .${c.filterIcon}`]: { fontSize: 'inherit', }, @@ -346,6 +337,11 @@ export const GridRootStyles = styled('div', { [`& .${c['columnHeader--moving']}`]: { backgroundColor: (t.vars || t).palette.action.hover, }, + [`& .${c['columnHeader--pinnedLeft']}, & .${c['columnHeader--pinnedRight']}`]: { + position: 'sticky', + zIndex: 4, // Should be above the column separator + background: 'var(--DataGrid-pinnedBackground)', + }, [`& .${c.columnSeparator}`]: { visibility: 'hidden', position: 'absolute', @@ -440,9 +436,6 @@ export const GridRootStyles = styled('div', { '[role=row]': { background: 'var(--DataGrid-containerBackground)', }, - [`.${c.pinnedColumnHeaders} [role=row]`]: { - background: 'var(--DataGrid-pinnedBackground)', - }, }, /* Cell styles */ @@ -532,20 +525,16 @@ export const GridRootStyles = styled('div', { [`.${c.withBorderColor}`]: { borderColor, }, - [`& .${c['cell--withLeftBorder']}`]: { + [`& .${c['cell--withLeftBorder']}, & .${c['columnHeader--withLeftBorder']}`]: { borderLeftColor: 'var(--DataGrid-rowBorderColor)', borderLeftWidth: '1px', borderLeftStyle: 'solid', }, - [`& .${c['cell--withRightBorder']}`]: { + [`& .${c['cell--withRightBorder']}, & .${c['columnHeader--withRightBorder']}`]: { borderRightColor: 'var(--DataGrid-rowBorderColor)', borderRightWidth: '1px', borderRightStyle: 'solid', }, - [`& .${c['columnHeader--withRightBorder']}`]: { - borderRightWidth: '1px', - borderRightStyle: 'solid', - }, [`& .${c['cell--flex']}`]: { display: 'flex', alignItems: 'center', @@ -649,12 +638,14 @@ export const GridRootStyles = styled('div', { }, [`&.${c['scrollbarFiller--pinnedRight']}`]: { backgroundColor: 'var(--DataGrid-pinnedBackground)', - }, - [`&.${c['scrollbarFiller--pinnedRight']}:not(.${c['scrollbarFiller--header']})`]: { position: 'sticky', right: 0, }, }, + + [`& .${c.filler}`]: { + flex: 1, + }, }; return gridStyle; diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx index 8ab229b2031e6..aa17ed93be6d9 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScroller.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { styled } from '@mui/system'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; +import { GridScrollArea } from '../GridScrollArea'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; @@ -77,6 +78,8 @@ function GridVirtualScroller(props: GridVirtualScrollerProps) { return ( + + diff --git a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerFiller.tsx b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerFiller.tsx index b97d6d4dc0a4c..eeaafcdd67ada 100644 --- a/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerFiller.tsx +++ b/packages/x-data-grid/src/components/virtualization/GridVirtualScrollerFiller.tsx @@ -40,6 +40,7 @@ function GridVirtualScrollerFiller() { viewportOuterSize, minimumSize, hasScrollX, + hasScrollY, scrollbarSize, leftPinnedWidth, rightPinnedWidth, @@ -64,7 +65,7 @@ function GridVirtualScrollerFiller() { {rightPinnedWidth > 0 && ( )} diff --git a/packages/x-data-grid/src/constants/gridClasses.ts b/packages/x-data-grid/src/constants/gridClasses.ts index 60c304646eda9..1d1e43e31457a 100644 --- a/packages/x-data-grid/src/constants/gridClasses.ts +++ b/packages/x-data-grid/src/constants/gridClasses.ts @@ -153,14 +153,12 @@ export interface GridClasses { * Styles applied to the column header if the column has a filter applied to it. */ 'columnHeader--filtered': string; + 'columnHeader--pinnedLeft': string; + 'columnHeader--pinnedRight': string; /** * Styles applied to the column header element. */ columnHeader: string; - /** - * Styles applied to the column group header element. - */ - columnGroupHeader: string; /** * Styles applied to the header checkbox cell element. */ @@ -173,10 +171,6 @@ export interface GridClasses { * Styles applied to the row's draggable placeholder element inside the special row reorder cell. */ rowReorderCellPlaceholder: string; - /** - * Styles applied to the column headers wrapper if a column is being dragged. - */ - columnHeaderDropZone: string; /** * Styles applied to the column header's title element; */ @@ -197,22 +191,10 @@ export interface GridClasses { * Styles applied to the empty column group header cell. */ 'columnHeader--emptyGroup': string; - /** - * Styles applied to the column group header cell when show column border. - */ - 'columnHeader--showColumnBorder': string; /** * Styles applied to the column headers. */ columnHeaders: string; - /** - * Styles applied to the column headers's inner element. - */ - columnHeadersInner: string; - /** - * Styles applied to the column headers's inner element if there is a horizontal scrollbar. - */ - 'columnHeadersInner--scrollable': string; /** * Styles applied to the column header separator if the column is resizable. */ @@ -307,14 +289,17 @@ export interface GridClasses { editBooleanCell: string; /** * Styles applied to the filler row. + * @ignore - do not document. */ filler: string; /** * Styles applied to the filler row pinned left section. + * @ignore - do not document. */ 'filler--pinnedLeft': string; /** * Styles applied to the filler row pinned right section. + * @ignore - do not document. */ 'filler--pinnedRight': string; /** @@ -425,18 +410,6 @@ export interface GridClasses { * Styles applied to the pinned columns. */ pinnedColumns: string; - /** - * Styles applied to the pinned column headers. - */ - pinnedColumnHeaders: string; - /** - * Styles applied to the left pinned column headers. - */ - 'pinnedColumnHeaders--left': string; - /** - * Styles applied to the right pinned column headers. - */ - 'pinnedColumnHeaders--right': string; /** * Styles applied to the root element. */ @@ -587,6 +560,7 @@ export interface GridClasses { * Styles applied the column header if `showColumnVerticalBorder={true}`. */ 'columnHeader--withRightBorder': string; + 'columnHeader--withLeftBorder': string; /** * Styles applied to the root of the grouping column of the tree data. */ @@ -665,20 +639,17 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'columnHeader--sortable', 'columnHeader--sorted', 'columnHeader--filtered', + 'columnHeader--pinnedLeft', + 'columnHeader--pinnedRight', 'columnHeader', 'columnHeaderCheckbox', 'columnHeaderDraggableContainer', - 'columnHeaderDropZone', 'columnHeaderTitle', 'columnHeaderTitleContainer', 'columnHeaderTitleContainerContent', - 'columnGroupHeader', 'columnHeader--filledGroup', 'columnHeader--emptyGroup', - 'columnHeader--showColumnBorder', 'columnHeaders', - 'columnHeadersInner', - 'columnHeadersInner--scrollable', 'columnSeparator--resizable', 'columnSeparator--resizing', 'columnSeparator--sideLeft', @@ -764,14 +735,12 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'virtualScrollerContent--overflowed', 'virtualScrollerRenderZone', 'pinnedColumns', - 'pinnedColumnHeaders', - 'pinnedColumnHeaders--left', - 'pinnedColumnHeaders--right', 'withVerticalBorder', 'withBorderColor', 'cell--withRightBorder', 'cell--withLeftBorder', 'columnHeader--withRightBorder', + 'columnHeader--withLeftBorder', 'treeDataGroupingCell', 'treeDataGroupingCellToggle', 'groupingCriteriaCell', diff --git a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx index 9b0275319ffe8..419e8e7e9786d 100644 --- a/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx +++ b/packages/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx @@ -1,11 +1,10 @@ import * as React from 'react'; -import { unstable_useForkRef as useForkRef } from '@mui/utils'; import { styled, useTheme } from '@mui/material/styles'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { useGridSelector } from '../../utils'; import { useGridRootProps } from '../../utils/useGridRootProps'; import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext'; -import { GridRenderContext } from '../../../models/params/gridScrollParams'; +import type { GridColumnsRenderContext } from '../../../models/params/gridScrollParams'; import { useGridApiEventHandler } from '../../utils/useGridApiEventHandler'; import { GridEventListener } from '../../../models/events'; import { GridColumnHeaderItem } from '../../../components/columnHeaders/GridColumnHeaderItem'; @@ -30,6 +29,8 @@ import { } from '../columns'; import { GridGroupingStructure } from '../columnGrouping/gridColumnGroupsInterfaces'; import { GridScrollbarFillerCell as ScrollbarFiller } from '../../../components/GridScrollbarFillerCell'; +import { getPinnedCellOffset } from '../../../internals/utils/getPinnedCellOffset'; +import { GridColumnHeaderSeparatorSides } from '../../../components/columnHeaders/GridColumnHeaderSeparator'; import { gridClasses } from '../../../constants/gridClasses'; interface HeaderInfo { @@ -43,7 +44,6 @@ interface HeaderInfo { } export interface UseGridColumnHeadersProps { - innerRef?: React.Ref; visibleColumns: GridStateColDef[]; sortColumnLookup: GridSortColumnLookup; filterColumnLookup: GridFilterActiveItemsLookup; @@ -60,19 +60,11 @@ export interface UseGridColumnHeadersProps { export interface GetHeadersParams { position?: GridPinnedColumnPosition; - renderContext?: GridRenderContext; + renderContext?: GridColumnsRenderContext; minFirstColumn?: number; maxLastColumn?: number; } -const SpaceFiller = styled('div')({ - /* GridRootStyles conflict */ - '&&&': { - padding: 0, - width: 'calc(var(--DataGrid-width) - var(--DataGrid-columnsTotalWidth))', - }, -}); - type OwnerState = DataGridProcessedProps; export const GridColumnHeaderRow = styled('div', { @@ -86,7 +78,6 @@ export const GridColumnHeaderRow = styled('div', { export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const { - innerRef: innerRefProp, visibleColumns, sortColumnLookup, filterColumnLookup, @@ -109,8 +100,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const rootProps = useGridRootProps(); const hasVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector); - const innerRef = React.useRef(null); - const handleInnerRef = useForkRef(innerRefProp, innerRef); const dimensions = useGridSelector(apiRef, gridDimensionsSelector); const columnPositions = useGridSelector(apiRef, gridColumnPositionsSelector); const renderContext = useGridSelector(apiRef, gridRenderContextColumnsSelector); @@ -123,7 +112,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { ); React.useEffect(() => { - apiRef.current.columnHeadersContainerElementRef!.current!.scrollLeft = 0; + apiRef.current.columnHeadersContainerRef!.current!.scrollLeft = 0; }, [apiRef]); const handleColumnResizeStart = React.useCallback>( @@ -144,6 +133,24 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { [], ); + const leftRenderContext = React.useMemo(() => { + return pinnedColumns.left.length + ? { + firstColumnIndex: 0, + lastColumnIndex: pinnedColumns.left.length, + } + : null; + }, [pinnedColumns.left.length]); + + const rightRenderContext = React.useMemo(() => { + return pinnedColumns.right.length + ? { + firstColumnIndex: visibleColumns.length - pinnedColumns.right.length, + lastColumnIndex: visibleColumns.length, + } + : null; + }, [pinnedColumns.right.length, visibleColumns.length]); + useGridApiEventHandler(apiRef, 'columnResizeStart', handleColumnResizeStart); useGridApiEventHandler(apiRef, 'columnResizeStop', handleColumnResizeStop); useGridApiEventHandler(apiRef, 'columnHeaderDragStart', handleColumnReorderStart); @@ -187,7 +194,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { {isNotPinned &&
} {children} - {isNotPinned && } + {isNotPinned &&
} {hasScrollbarFiller && ( )} @@ -195,6 +202,36 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { ); }; + const getCellOffsetStyle = ({ + pinnedPosition, + columnIndex, + computedWidth, + }: { + pinnedPosition?: GridPinnedColumnPosition; + columnIndex: number; + computedWidth: number; + }) => { + let style: React.CSSProperties | undefined; + if (pinnedPosition === 'left' || pinnedPosition === 'right') { + const pinnedOffset = getPinnedCellOffset( + pinnedPosition, + computedWidth, + columnIndex, + columnPositions, + dimensions, + ); + if (pinnedPosition === 'left') { + style = { left: pinnedOffset }; + } + + if (pinnedPosition === 'right') { + style = { right: pinnedOffset }; + } + } + + return style; + }; + const getColumnHeaders = (params?: GetHeadersParams, other = {}) => { const { renderedColumns, firstColumnToRender } = getColumnsToRender(params); @@ -211,6 +248,13 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { : -1; const hasFocus = columnHeaderFocus !== null && columnHeaderFocus.field === colDef.field; const open = columnMenuState.open && columnMenuState.field === colDef.field; + const pinnedPosition = params?.position; + + const style = getCellOffsetStyle({ + pinnedPosition, + columnIndex, + computedWidth: colDef.computedWidth, + }); columns.push( { isResizing={resizeCol === colDef.field} hasFocus={hasFocus} tabIndex={tabIndex} + pinnedPosition={pinnedPosition} + style={style} + indexInSection={i} + sectionLength={renderedColumns.length} {...other} />, ); } + return getFillers(params, columns, 0); + }; + + const getColumnHeadersRow = () => { return ( - {getFillers(params, columns, 0)} + {leftRenderContext && + getColumnHeaders( + { + position: GridPinnedColumnPosition.LEFT, + renderContext: leftRenderContext, + minFirstColumn: leftRenderContext.firstColumnIndex, + maxLastColumn: leftRenderContext.lastColumnIndex, + }, + { disableReorder: true }, + )} + {getColumnHeaders({ + renderContext, + minFirstColumn: pinnedColumns.left.length, + maxLastColumn: visibleColumns.length - pinnedColumns.right.length, + })} + {rightRenderContext && + getColumnHeaders( + { + position: GridPinnedColumnPosition.RIGHT, + renderContext: rightRenderContext, + minFirstColumn: rightRenderContext.firstColumnIndex, + maxLastColumn: rightRenderContext.lastColumnIndex, + }, + { + disableReorder: true, + separatorSide: GridColumnHeaderSeparatorSides.Left, + }, + )} ); }; - const getColumnGroupHeaders = (params?: GetHeadersParams) => { - if (headerGroupingMaxDepth === 0) { - return null; - } - + const getColumnGroupHeaders = ({ + depth, + params, + }: { + depth: number; + params: GetHeadersParams; + }) => { const columnsToRender = getColumnsToRender(params); if (columnsToRender.renderedColumns.length === 0) { return null; } - const { firstColumnToRender, lastColumnToRender } = columnsToRender; + const { renderedColumns, firstColumnToRender, lastColumnToRender } = columnsToRender; - const columns: React.JSX.Element[] = []; + const rowStructure = columnGroupsHeaderStructure[depth]; - const headerToRender: { - leftOverflow: number; - elements: HeaderInfo[]; - }[] = []; + const firstColumnFieldToRender = visibleColumns[firstColumnToRender].field; + const firstGroupToRender = + apiRef.current.getColumnGroupPath(firstColumnFieldToRender)[depth] ?? null; - for (let depth = 0; depth < headerGroupingMaxDepth; depth += 1) { - const rowStructure = columnGroupsHeaderStructure[depth]; - - const firstColumnFieldToRender = visibleColumns[firstColumnToRender].field; - const firstGroupToRender = - apiRef.current.getColumnGroupPath(firstColumnFieldToRender)[depth] ?? null; - - const firstGroupIndex = rowStructure.findIndex( - ({ groupId, columnFields }) => - groupId === firstGroupToRender && columnFields.includes(firstColumnFieldToRender), - ); + const firstGroupIndex = rowStructure.findIndex( + ({ groupId, columnFields }) => + groupId === firstGroupToRender && columnFields.includes(firstColumnFieldToRender), + ); - const lastColumnFieldToRender = visibleColumns[lastColumnToRender - 1].field; - const lastGroupToRender = - apiRef.current.getColumnGroupPath(lastColumnFieldToRender)[depth] ?? null; - const lastGroupIndex = rowStructure.findIndex( - ({ groupId, columnFields }) => - groupId === lastGroupToRender && columnFields.includes(lastColumnFieldToRender), - ); + const lastColumnFieldToRender = visibleColumns[lastColumnToRender - 1].field; + const lastGroupToRender = + apiRef.current.getColumnGroupPath(lastColumnFieldToRender)[depth] ?? null; + const lastGroupIndex = rowStructure.findIndex( + ({ groupId, columnFields }) => + groupId === lastGroupToRender && columnFields.includes(lastColumnFieldToRender), + ); - const visibleColumnGroupHeader = rowStructure - .slice(firstGroupIndex, lastGroupIndex + 1) - .map((groupStructure) => { - return { - ...groupStructure, - columnFields: groupStructure.columnFields.filter( - (field) => columnVisibility[field] !== false, - ), - }; - }) - .filter((groupStructure) => groupStructure.columnFields.length > 0); - - const firstVisibleColumnIndex = - visibleColumnGroupHeader[0].columnFields.indexOf(firstColumnFieldToRender); - const hiddenGroupColumns = visibleColumnGroupHeader[0].columnFields.slice( - 0, - firstVisibleColumnIndex, - ); - const leftOverflow = hiddenGroupColumns.reduce((acc, field) => { - const column = apiRef.current.getColumn(field); - return acc + (column.computedWidth ?? 0); - }, 0); - - let columnIndex = firstColumnToRender; - const elements = visibleColumnGroupHeader.map(({ groupId, columnFields }) => { - const hasFocus = - columnGroupHeaderFocus !== null && - columnGroupHeaderFocus.depth === depth && - columnFields.includes(columnGroupHeaderFocus.field); - const tabIndex: 0 | -1 = - columnGroupHeaderTabIndexState !== null && - columnGroupHeaderTabIndexState.depth === depth && - columnFields.includes(columnGroupHeaderTabIndexState.field) - ? 0 - : -1; - - const headerInfo: HeaderInfo = { - groupId, - width: columnFields.reduce( - (acc, field) => acc + apiRef.current.getColumn(field).computedWidth, - 0, + const visibleColumnGroupHeader = rowStructure + .slice(firstGroupIndex, lastGroupIndex + 1) + .map((groupStructure) => { + return { + ...groupStructure, + columnFields: groupStructure.columnFields.filter( + (field) => columnVisibility[field] !== false, ), - fields: columnFields, - colIndex: columnIndex, - hasFocus, - tabIndex, }; + }) + .filter((groupStructure) => groupStructure.columnFields.length > 0); + + const firstVisibleColumnIndex = + visibleColumnGroupHeader[0].columnFields.indexOf(firstColumnFieldToRender); + const hiddenGroupColumns = visibleColumnGroupHeader[0].columnFields.slice( + 0, + firstVisibleColumnIndex, + ); + const leftOverflow = hiddenGroupColumns.reduce((acc, field) => { + const column = apiRef.current.getColumn(field); + return acc + (column.computedWidth ?? 0); + }, 0); + + let columnIndex = firstColumnToRender; + const children = visibleColumnGroupHeader.map(({ groupId, columnFields }, index) => { + const hasFocus = + columnGroupHeaderFocus !== null && + columnGroupHeaderFocus.depth === depth && + columnFields.includes(columnGroupHeaderFocus.field); + const tabIndex: 0 | -1 = + columnGroupHeaderTabIndexState !== null && + columnGroupHeaderTabIndexState.depth === depth && + columnFields.includes(columnGroupHeaderTabIndexState.field) + ? 0 + : -1; - columnIndex += columnFields.length; - return headerInfo; + const headerInfo: HeaderInfo = { + groupId, + width: columnFields.reduce( + (acc, field) => acc + apiRef.current.getColumn(field).computedWidth, + 0, + ), + fields: columnFields, + colIndex: columnIndex, + hasFocus, + tabIndex, + }; + + const pinnedPosition = params.position; + const style = getCellOffsetStyle({ + pinnedPosition, + columnIndex, + computedWidth: headerInfo.width, }); - headerToRender.push({ leftOverflow, elements }); + columnIndex += columnFields.length; + + let indexInSection = index; + if (pinnedPosition === 'left') { + // Group headers can expand to multiple columns, we need to adjust the index + indexInSection = columnIndex - 1; + } + + return ( + + ); + }); + + return getFillers(params, children, leftOverflow); + }; + + const getColumnGroupHeadersRows = () => { + if (headerGroupingMaxDepth === 0) { + return null; } - headerToRender.forEach((depthInfo, depthIndex) => { - const children = depthInfo.elements.map( - ({ groupId, width, fields, colIndex, hasFocus, tabIndex }, groupIndex) => { - return ( - - ); - }, - ); + const headerRows: React.JSX.Element[] = []; - columns.push( + for (let depth = 0; depth < headerGroupingMaxDepth; depth += 1) { + headerRows.push( - {getFillers(params, children, depthInfo.leftOverflow)} + {leftRenderContext && + getColumnGroupHeaders({ + depth, + params: { + position: GridPinnedColumnPosition.LEFT, + renderContext: leftRenderContext, + minFirstColumn: leftRenderContext.firstColumnIndex, + maxLastColumn: leftRenderContext.lastColumnIndex, + }, + })} + {getColumnGroupHeaders({ depth, params: { renderContext } })} + {rightRenderContext && + getColumnGroupHeaders({ + depth, + params: { + position: GridPinnedColumnPosition.RIGHT, + renderContext: rightRenderContext, + minFirstColumn: rightRenderContext.firstColumnIndex, + maxLastColumn: rightRenderContext.lastColumnIndex, + }, + })} , ); - }); - return columns; + } + + return headerRows; }; return { renderContext, + leftRenderContext, + rightRenderContext, + pinnedColumns, + visibleColumns, + getCellOffsetStyle, getFillers, - getColumnHeaders, + getColumnHeadersRow, getColumnsToRender, - getColumnGroupHeaders, + getColumnGroupHeadersRows, isDragging: !!dragCol, getInnerProps: () => ({ - ref: handleInnerRef, role: 'rowgroup', }), }; diff --git a/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx index c54292496cdba..9f6ed9c817b6c 100644 --- a/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx @@ -15,6 +15,8 @@ import { findGridHeader, findGridCells, findParentElementFromClassName, + findLeftPinnedHeadersAfterCol, + findRightPinnedHeadersBeforeCol, } from '../../../utils/domUtils'; import { GridAutosizeOptions, @@ -290,6 +292,8 @@ export const useGridColumnResize = ( const rightPinnedCellsBeforeRef = React.useRef([]); const fillerLeftRef = React.useRef(); const fillerRightRef = React.useRef(); + const leftPinnedHeadersAfterRef = React.useRef([]); + const rightPinnedHeadersBeforeRef = React.useRef([]); // To improve accessibility, the separator has padding on both sides. // Clicking inside the padding area should be treated as a click in the separator. @@ -365,6 +369,9 @@ export const useGridColumnResize = ( leftPinnedCellsAfterRef.current.forEach((cell) => { updateProperty(cell, 'left', widthDiff); }); + leftPinnedHeadersAfterRef.current.forEach((header) => { + updateProperty(header, 'left', widthDiff); + }); } if (pinnedPosition === GridPinnedColumnPosition.RIGHT) { @@ -373,6 +380,9 @@ export const useGridColumnResize = ( rightPinnedCellsBeforeRef.current.forEach((cell) => { updateProperty(cell, 'right', widthDiff); }); + rightPinnedHeadersBeforeRef.current.forEach((header) => { + updateProperty(header, 'right', widthDiff); + }); } }; @@ -416,7 +426,7 @@ export const useGridColumnResize = ( colDefRef.current = colDef as GridStateColDef; columnHeaderElementRef.current = findHeaderElementFromField( - apiRef.current.columnHeadersContainerElementRef!.current!, + apiRef.current.columnHeadersContainerRef!.current!, colDef.field, ); @@ -428,7 +438,7 @@ export const useGridColumnResize = ( } groupHeaderElementsRef.current = findGroupHeaderElementsFromField( - apiRef.current.columnHeadersContainerElementRef?.current!, + apiRef.current.columnHeadersContainerRef?.current!, colDef.field, ); @@ -455,6 +465,15 @@ export const useGridColumnResize = ( ? [] : findRightPinnedCellsBeforeCol(apiRef.current, columnHeaderElementRef.current); + leftPinnedHeadersAfterRef.current = + pinnedPosition !== GridPinnedColumnPosition.LEFT + ? [] + : findLeftPinnedHeadersAfterCol(apiRef.current, columnHeaderElementRef.current); + rightPinnedHeadersBeforeRef.current = + pinnedPosition !== GridPinnedColumnPosition.RIGHT + ? [] + : findRightPinnedHeadersBeforeCol(apiRef.current, columnHeaderElementRef.current); + resizeDirection.current = getResizeDirection(separator, theme.direction); initialOffsetToSeparator.current = computeOffsetToSeparator( @@ -763,7 +782,7 @@ export const useGridColumnResize = ( useGridNativeEventListener( apiRef, - () => apiRef.current.columnHeadersElementRef?.current, + () => apiRef.current.columnHeadersContainerRef?.current, 'touchstart', handleTouchStart, { passive: doesSupportTouchActionNone() }, diff --git a/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx b/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx index c24e98a1efd35..68957d6884cb9 100644 --- a/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx +++ b/packages/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx @@ -140,12 +140,6 @@ export const useGridPrintExport = ( // See https://support.google.com/chrome/thread/191619088?hl=en&msgid=193009642 gridClone!.style.contain = 'size'; - const columnHeaders = gridClone.querySelector(`.${gridClasses.columnHeaders}`); - const columnHeadersInner = columnHeaders!.querySelector( - `.${gridClasses.columnHeadersInner}`, - )!; - columnHeadersInner.style.width = '100%'; - let gridToolbarElementHeight = gridRootElement!.querySelector(`.${gridClasses.toolbarContainer}`) ?.offsetHeight || 0; diff --git a/packages/x-data-grid/src/hooks/features/scroll/useGridScroll.ts b/packages/x-data-grid/src/hooks/features/scroll/useGridScroll.ts index a981f12b49c01..8e5f2ba8dab18 100644 --- a/packages/x-data-grid/src/hooks/features/scroll/useGridScroll.ts +++ b/packages/x-data-grid/src/hooks/features/scroll/useGridScroll.ts @@ -57,7 +57,7 @@ export const useGridScroll = ( ): void => { const theme = useTheme(); const logger = useGridLogger(apiRef, 'useGridScroll'); - const colRef = apiRef.current.columnHeadersElementRef!; + const colRef = apiRef.current.columnHeadersContainerRef!; const virtualScrollerRef = apiRef.current.virtualScrollerRef!; const visibleSortedRows = useGridSelector(apiRef, gridExpandedSortedRowEntriesSelector); diff --git a/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts b/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts index 2b70baf64ac9b..17dc3004bcf33 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts +++ b/packages/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts @@ -1,6 +1,6 @@ import { createSelector, createSelectorMemoized } from '../../../utils/createSelector'; -import { GridRenderContext } from '../../../models/params/gridScrollParams'; -import { GridStateCommunity } from '../../../models/gridStateCommunity'; +import type { GridColumnsRenderContext } from '../../../models/params/gridScrollParams'; +import type { GridStateCommunity } from '../../../models/gridStateCommunity'; /** * Get the columns state @@ -45,9 +45,7 @@ export const gridRenderContextSelector = createSelector( export const gridRenderContextColumnsSelector = createSelectorMemoized( (state: GridStateCommunity) => state.virtualization.renderContext.firstColumnIndex, (state: GridStateCommunity) => state.virtualization.renderContext.lastColumnIndex, - (firstColumnIndex, lastColumnIndex): GridRenderContext => ({ - firstRowIndex: -1, - lastRowIndex: -1, + (firstColumnIndex, lastColumnIndex): GridColumnsRenderContext => ({ firstColumnIndex, lastColumnIndex, }), diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index 01803fc493758..50c85b23376d7 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -24,7 +24,12 @@ import { gridFocusCellSelector, gridTabIndexCellSelector } from '../focus/gridFo import { useGridVisibleRows, getVisibleRows } from '../../utils/useGridVisibleRows'; import { useGridApiEventHandler } from '../../utils'; import { clamp, range } from '../../../utils/utils'; -import { GridRenderContext, GridRowEntry, GridRowId } from '../../../models'; +import type { + GridRenderContext, + GridColumnsRenderContext, + GridRowEntry, + GridRowId, +} from '../../../models'; import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; @@ -790,7 +795,7 @@ export function areRenderContextsEqual(context1: GridRenderContext, context2: Gr export function computeOffsetLeft( columnPositions: number[], - renderContext: GridRenderContext, + renderContext: GridColumnsRenderContext, direction: Theme['direction'], pinnedLeftLength: number, ) { diff --git a/packages/x-data-grid/src/hooks/utils/useLazyRef.ts b/packages/x-data-grid/src/hooks/utils/useLazyRef.ts index 2818c1745cbe2..69d4afb02ba5a 100644 --- a/packages/x-data-grid/src/hooks/utils/useLazyRef.ts +++ b/packages/x-data-grid/src/hooks/utils/useLazyRef.ts @@ -1,14 +1 @@ -import * as React from 'react'; - -const UNINITIALIZED = {}; - -// See https://github.com/facebook/react/issues/14490 for when to use this. -export function useLazyRef(init: (arg?: U) => T, initArg?: U) { - const ref = React.useRef(UNINITIALIZED as unknown as T); - - if (ref.current === UNINITIALIZED) { - ref.current = init(initArg); - } - - return ref; -} +export { default as useLazyRef } from '@mui/utils/useLazyRef'; diff --git a/packages/x-data-grid/src/hooks/utils/useOnMount.ts b/packages/x-data-grid/src/hooks/utils/useOnMount.ts index fe624200f7b2d..41b20cdd20206 100644 --- a/packages/x-data-grid/src/hooks/utils/useOnMount.ts +++ b/packages/x-data-grid/src/hooks/utils/useOnMount.ts @@ -1,9 +1 @@ -import * as React from 'react'; - -const EMPTY = [] as unknown[]; - -export function useOnMount(fn: React.EffectCallback) { - /* eslint-disable react-hooks/exhaustive-deps */ - React.useEffect(fn, EMPTY); - /* eslint-enable react-hooks/exhaustive-deps */ -} +export { default as useOnMount } from '@mui/utils/useOnMount'; diff --git a/packages/x-data-grid/src/hooks/utils/useTimeout.ts b/packages/x-data-grid/src/hooks/utils/useTimeout.ts index 7a837b7523dac..ebd97b60880a4 100644 --- a/packages/x-data-grid/src/hooks/utils/useTimeout.ts +++ b/packages/x-data-grid/src/hooks/utils/useTimeout.ts @@ -1,41 +1 @@ -'use client'; -import { useLazyRef } from './useLazyRef'; -import { useOnMount } from './useOnMount'; - -export class Timeout { - static create() { - return new Timeout(); - } - - currentId: ReturnType | null = null; - - /** - * Executes `fn` after `delay`, clearing any previously scheduled call. - */ - start(delay: number, fn: Function) { - this.clear(); - this.currentId = setTimeout(() => { - this.currentId = null; - fn(); - }, delay); - } - - clear = () => { - if (this.currentId !== null) { - clearTimeout(this.currentId); - this.currentId = null; - } - }; - - disposeEffect = () => { - return this.clear; - }; -} - -export function useTimeout() { - const timeout = useLazyRef(Timeout.create).current; - - useOnMount(timeout.disposeEffect); - - return timeout; -} +export { default as useTimeout } from '@mui/utils/useTimeout'; diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 3ef2cfab4a039..1a61da047e6f3 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -10,7 +10,6 @@ export type { GridDetailPanelsProps } from '../components/GridDetailPanels'; export type { GridPinnedRowsProps } from '../components/GridPinnedRows'; export { GridHeaders } from '../components/GridHeaders'; export { GridBaseColumnHeaders } from '../components/columnHeaders/GridBaseColumnHeaders'; -export { GridColumnHeadersInner } from '../components/columnHeaders/GridColumnHeadersInner'; export { DATA_GRID_DEFAULT_SLOTS_COMPONENTS } from '../constants/defaultGridSlotsComponents'; export { getGridFilter } from '../components/panel/filterPanel/GridFilterPanel'; diff --git a/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts b/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts new file mode 100644 index 0000000000000..f58566ff035eb --- /dev/null +++ b/packages/x-data-grid/src/internals/utils/getPinnedCellOffset.ts @@ -0,0 +1,34 @@ +import { + GridPinnedColumnPosition, + gridColumnPositionsSelector, +} from '../../hooks/features/columns'; +import type { GridDimensions } from '../../hooks/features/dimensions'; + +export const getPinnedCellOffset = ( + pinnedPosition: GridPinnedColumnPosition | undefined, + computedWidth: number, + columnIndex: number, + columnPositions: ReturnType, + dimensions: GridDimensions, +) => { + const scrollbarWidth = dimensions.hasScrollY ? dimensions.scrollbarSize : 0; + + let pinnedOffset: number; + switch (pinnedPosition) { + case GridPinnedColumnPosition.LEFT: + pinnedOffset = columnPositions[columnIndex]; + break; + case GridPinnedColumnPosition.RIGHT: + pinnedOffset = + dimensions.columnsTotalWidth - + columnPositions[columnIndex] - + computedWidth + + scrollbarWidth; + break; + default: + pinnedOffset = 0; + break; + } + + return pinnedOffset; +}; diff --git a/packages/x-data-grid/src/models/api/gridCoreApi.ts b/packages/x-data-grid/src/models/api/gridCoreApi.ts index 1c0ca17cea457..66ea2eb2001b5 100644 --- a/packages/x-data-grid/src/models/api/gridCoreApi.ts +++ b/packages/x-data-grid/src/models/api/gridCoreApi.ts @@ -76,11 +76,7 @@ export interface GridCorePrivateApi< /** * The React ref of the grid column container virtualized div element. */ - columnHeadersContainerElementRef?: React.RefObject; - /** - * The React ref of the grid column headers container element. - */ - columnHeadersElementRef?: React.RefObject; + columnHeadersContainerRef?: React.RefObject; /** * The React ref of the grid header filter row element. */ diff --git a/packages/x-data-grid/src/models/params/gridScrollParams.ts b/packages/x-data-grid/src/models/params/gridScrollParams.ts index 016cbff3d5700..4fc3bd3146f99 100644 --- a/packages/x-data-grid/src/models/params/gridScrollParams.ts +++ b/packages/x-data-grid/src/models/params/gridScrollParams.ts @@ -1,9 +1,11 @@ -export interface GridRenderContext { - firstRowIndex: number; - lastRowIndex: number; +export interface GridColumnsRenderContext { firstColumnIndex: number; lastColumnIndex: number; } +export interface GridRenderContext extends GridColumnsRenderContext { + firstRowIndex: number; + lastRowIndex: number; +} export interface GridScrollParams { left: number; diff --git a/packages/x-data-grid/src/utils/cellBorderUtils.ts b/packages/x-data-grid/src/utils/cellBorderUtils.ts new file mode 100644 index 0000000000000..3cf82d37cac30 --- /dev/null +++ b/packages/x-data-grid/src/utils/cellBorderUtils.ts @@ -0,0 +1,23 @@ +import { GridPinnedColumnPosition } from '../hooks/features/columns/gridColumnsInterfaces'; + +export const shouldCellShowRightBorder = ( + pinnedPosition: GridPinnedColumnPosition | undefined, + indexInSection: number, + sectionLength: number, + showCellVerticalBorderRootProp: boolean, +) => { + const isSectionLastCell = indexInSection === sectionLength - 1; + + return ( + (showCellVerticalBorderRootProp && + (pinnedPosition !== GridPinnedColumnPosition.LEFT ? !isSectionLastCell : true)) || + (pinnedPosition === GridPinnedColumnPosition.LEFT && isSectionLastCell) + ); +}; + +export const shouldCellShowLeftBorder = ( + pinnedPosition: GridPinnedColumnPosition | undefined, + indexInSection: number, +) => { + return pinnedPosition === GridPinnedColumnPosition.RIGHT && indexInSection === 0; +}; diff --git a/packages/x-data-grid/src/utils/domUtils.ts b/packages/x-data-grid/src/utils/domUtils.ts index 8f6b61bb93923..29dc7bf8df3c6 100644 --- a/packages/x-data-grid/src/utils/domUtils.ts +++ b/packages/x-data-grid/src/utils/domUtils.ts @@ -118,8 +118,17 @@ export function findGridElement(api: GridPrivateApiCommunity, klass: keyof typeo return api.rootElementRef.current!.querySelector(`.${gridClasses[klass]}`)! as HTMLElement; } -export function findLeftPinnedCellsAfterCol(api: GridPrivateApiCommunity, col: HTMLElement) { - const colIndex = parseCellColIndex(col); +const findPinnedCells = ({ + api, + colIndex, + position, + filterFn, +}: { + api: GridPrivateApiCommunity; + colIndex: number | null; + position: 'left' | 'right'; + filterFn: (colIndex: number) => boolean; +}) => { if (colIndex === null) { return []; } @@ -132,47 +141,96 @@ export function findLeftPinnedCellsAfterCol(api: GridPrivateApiCommunity, col: H return; } - const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedLeft']}`); - rightPinnedCells.forEach((cell) => { - const currentColIndex = parseCellColIndex(cell); - if (currentColIndex !== null && currentColIndex > colIndex) { - cells.push(cell as HTMLElement); - } - }); + rowElement + .querySelectorAll( + `.${gridClasses[position === 'left' ? 'cell--pinnedLeft' : 'cell--pinnedRight']}`, + ) + .forEach((cell) => { + const currentColIndex = parseCellColIndex(cell); + if (currentColIndex !== null && filterFn(currentColIndex)) { + cells.push(cell as HTMLElement); + } + }); }); return cells; +}; + +export function findLeftPinnedCellsAfterCol(api: GridPrivateApiCommunity, col: HTMLElement) { + const colIndex = parseCellColIndex(col); + return findPinnedCells({ + api, + colIndex, + position: 'left', + filterFn: (index) => index > colIndex!, + }); } export function findRightPinnedCellsBeforeCol(api: GridPrivateApiCommunity, col: HTMLElement) { const colIndex = parseCellColIndex(col); + return findPinnedCells({ + api, + colIndex, + position: 'right', + filterFn: (index) => index < colIndex!, + }); +} + +const findPinnedHeaders = ({ + api, + colIndex, + position, + filterFn, +}: { + api: GridPrivateApiCommunity; + colIndex: number | null; + position: 'left' | 'right'; + filterFn: (colIndex: number) => boolean; +}) => { + if (!api.columnHeadersContainerRef?.current) { + return []; + } if (colIndex === null) { return []; } - const cells: HTMLElement[] = []; - - queryRows(api).forEach((rowElement) => { - const rowId = rowElement.getAttribute('data-id'); - if (!rowId) { - return; - } - - const rightPinnedCells = rowElement.querySelectorAll(`.${gridClasses['cell--pinnedRight']}`); - rightPinnedCells.forEach((cell) => { - const currentColIndex = parseCellColIndex(cell); - if (currentColIndex !== null && currentColIndex < colIndex) { - cells.push(cell as HTMLElement); + const elements: HTMLElement[] = []; + api.columnHeadersContainerRef.current + .querySelectorAll( + `.${gridClasses[position === 'left' ? 'columnHeader--pinnedLeft' : 'columnHeader--pinnedRight']}`, + ) + .forEach((element) => { + const currentColIndex = parseCellColIndex(element); + if (currentColIndex !== null && filterFn(currentColIndex)) { + elements.push(element as HTMLElement); } }); + return elements; +}; + +export function findLeftPinnedHeadersAfterCol(api: GridPrivateApiCommunity, col: HTMLElement) { + const colIndex = parseCellColIndex(col); + return findPinnedHeaders({ + api, + position: 'left', + colIndex, + filterFn: (index) => index > colIndex!, }); +} - return cells; +export function findRightPinnedHeadersBeforeCol(api: GridPrivateApiCommunity, col: HTMLElement) { + const colIndex = parseCellColIndex(col); + return findPinnedHeaders({ + api, + position: 'right', + colIndex, + filterFn: (index) => index < colIndex!, + }); } export function findGridHeader(api: GridPrivateApiCommunity, field: string) { - const headers = api.columnHeadersContainerElementRef!.current!; - return headers.querySelector(`:scope > div > div > [data-field="${field}"][role="columnheader"]`); + const headers = api.columnHeadersContainerRef!.current!; + return headers.querySelector(`:scope > div > [data-field="${field}"][role="columnheader"]`); } export function findGridCells(api: GridPrivateApiCommunity, field: string) { diff --git a/packages/x-tree-view/src/internals/hooks/useLazyRef.ts b/packages/x-tree-view/src/internals/hooks/useLazyRef.ts index 2818c1745cbe2..69d4afb02ba5a 100644 --- a/packages/x-tree-view/src/internals/hooks/useLazyRef.ts +++ b/packages/x-tree-view/src/internals/hooks/useLazyRef.ts @@ -1,14 +1 @@ -import * as React from 'react'; - -const UNINITIALIZED = {}; - -// See https://github.com/facebook/react/issues/14490 for when to use this. -export function useLazyRef(init: (arg?: U) => T, initArg?: U) { - const ref = React.useRef(UNINITIALIZED as unknown as T); - - if (ref.current === UNINITIALIZED) { - ref.current = init(initArg); - } - - return ref; -} +export { default as useLazyRef } from '@mui/utils/useLazyRef'; diff --git a/packages/x-tree-view/src/internals/hooks/useOnMount.ts b/packages/x-tree-view/src/internals/hooks/useOnMount.ts index fe624200f7b2d..41b20cdd20206 100644 --- a/packages/x-tree-view/src/internals/hooks/useOnMount.ts +++ b/packages/x-tree-view/src/internals/hooks/useOnMount.ts @@ -1,9 +1 @@ -import * as React from 'react'; - -const EMPTY = [] as unknown[]; - -export function useOnMount(fn: React.EffectCallback) { - /* eslint-disable react-hooks/exhaustive-deps */ - React.useEffect(fn, EMPTY); - /* eslint-enable react-hooks/exhaustive-deps */ -} +export { default as useOnMount } from '@mui/utils/useOnMount'; diff --git a/packages/x-tree-view/src/internals/hooks/useTimeout.ts b/packages/x-tree-view/src/internals/hooks/useTimeout.ts index 7a837b7523dac..ebd97b60880a4 100644 --- a/packages/x-tree-view/src/internals/hooks/useTimeout.ts +++ b/packages/x-tree-view/src/internals/hooks/useTimeout.ts @@ -1,41 +1 @@ -'use client'; -import { useLazyRef } from './useLazyRef'; -import { useOnMount } from './useOnMount'; - -export class Timeout { - static create() { - return new Timeout(); - } - - currentId: ReturnType | null = null; - - /** - * Executes `fn` after `delay`, clearing any previously scheduled call. - */ - start(delay: number, fn: Function) { - this.clear(); - this.currentId = setTimeout(() => { - this.currentId = null; - fn(); - }, delay); - } - - clear = () => { - if (this.currentId !== null) { - clearTimeout(this.currentId); - this.currentId = null; - } - }; - - disposeEffect = () => { - return this.clear; - }; -} - -export function useTimeout() { - const timeout = useLazyRef(Timeout.create).current; - - useOnMount(timeout.disposeEffect); - - return timeout; -} +export { default as useTimeout } from '@mui/utils/useTimeout'; diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index f78887dcd8e65..227740915c9d6 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -220,6 +220,7 @@ { "name": "GridColumnsPanel", "kind": "Function" }, { "name": "GridColumnsPanelProps", "kind": "Interface" }, { "name": "GridColumnsRawState", "kind": "TypeAlias" }, + { "name": "GridColumnsRenderContext", "kind": "Interface" }, { "name": "GridColumnsState", "kind": "Interface" }, { "name": "gridColumnsStateSelector", "kind": "Variable" }, { "name": "gridColumnsTotalWidthSelector", "kind": "Variable" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index dc10d95ed9ef0..f9e4d9888c0e0 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -194,6 +194,7 @@ { "name": "GridColumnsPanel", "kind": "Function" }, { "name": "GridColumnsPanelProps", "kind": "Interface" }, { "name": "GridColumnsRawState", "kind": "TypeAlias" }, + { "name": "GridColumnsRenderContext", "kind": "Interface" }, { "name": "GridColumnsState", "kind": "Interface" }, { "name": "gridColumnsStateSelector", "kind": "Variable" }, { "name": "gridColumnsTotalWidthSelector", "kind": "Variable" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index a3f114557c0a5..5464d87a7253d 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -180,6 +180,7 @@ { "name": "GridColumnsPanel", "kind": "Function" }, { "name": "GridColumnsPanelProps", "kind": "Interface" }, { "name": "GridColumnsRawState", "kind": "TypeAlias" }, + { "name": "GridColumnsRenderContext", "kind": "Interface" }, { "name": "GridColumnsState", "kind": "Interface" }, { "name": "gridColumnsStateSelector", "kind": "Variable" }, { "name": "gridColumnsTotalWidthSelector", "kind": "Variable" }, From ae403ed7f2c17a241b3578e01ea7585054a26828 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Wed, 13 Mar 2024 22:27:45 +0100 Subject: [PATCH 37/49] [DataGridPro] Fix filler rendered for no reason when there are pinned columns (#12440) --- .../tests/columnPinning.DataGridPro.test.tsx | 35 +++++++++++++++++++ .../features/dimensions/useGridDimensions.ts | 6 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx index 52a329866badd..dc8aa7bb50fbd 100644 --- a/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx @@ -27,6 +27,7 @@ import { getCell, getColumnHeaderCell, getColumnHeadersTextContent, + grid, } from 'test/utils/helperFn'; // TODO Move to utils @@ -231,6 +232,40 @@ describe(' - Column pinning', () => { expect(borderLeftColor).to.not.equal('rgba(0, 0, 0, 0)'); }); + // https://github.com/mui/mui-x/issues/12431 + it('should not render unnecessary filler after the last row', function test() { + if (isJSDOM) { + // Needs layouting + this.skip(); + } + + const rowHeight = 50; + const columns: GridColDef[] = [ + { field: 'id', headerName: 'ID', width: 120 }, + { field: 'name', headerName: 'Name', width: 120 }, + ]; + const rows = [ + { id: 1, name: 'Robert Cooper' }, + { id: 2, name: 'Dora Wallace' }, + { id: 3, name: 'Howard Dixon' }, + { id: 4, name: 'Essie Reynolds' }, + ]; + + render( +
+ +
, + ); + + expect(grid('virtualScroller')?.scrollHeight).to.equal((rows.length + 1) * rowHeight); + }); + describe('props: onPinnedColumnsChange', () => { it('should call when a column is pinned', () => { const handlePinnedColumnsChange = spy(); diff --git a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts index c49c9d6818573..fbe44ed1036bd 100644 --- a/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts +++ b/packages/x-data-grid/src/hooks/features/dimensions/useGridDimensions.ts @@ -161,8 +161,10 @@ export function useGridDimensions( const topContainerHeight = headersTotalHeight + pinnedRowsHeight.top; const bottomContainerHeight = pinnedRowsHeight.bottom; + const nonPinnedColumnsTotalWidth = columnsTotalWidth - leftPinnedWidth - rightPinnedWidth; + const contentSize = { - width: columnsTotalWidth, + width: nonPinnedColumnsTotalWidth, height: rowsMeta.currentPageTotalHeight, }; @@ -223,7 +225,7 @@ export function useGridDimensions( ); const minimumSize = { - width: contentSize.width, + width: columnsTotalWidth, height: topContainerHeight + contentSize.height + bottomContainerHeight, }; From 9b4cb6c7ed833a98a2f776000bf55fa85e31ed71 Mon Sep 17 00:00:00 2001 From: Hyunsu Joo Date: Thu, 14 Mar 2024 08:27:17 +0900 Subject: [PATCH 38/49] [data grid] Fixed issue where pressing Delete key resets various cell values to empty string. (#12216) --- .../features/editing/useGridCellEditing.ts | 23 ++- .../src/tests/keyboard.DataGrid.test.tsx | 136 +++++++++++++++++- 2 files changed, 156 insertions(+), 3 deletions(-) diff --git a/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts b/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts index 4fb5e5502889c..79f51c6d850b5 100644 --- a/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts +++ b/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts @@ -328,8 +328,27 @@ export const useGridCellEditing = ( const { id, field, deleteValue, initialValue } = params; let newValue = apiRef.current.getCellValue(id, field); - if (deleteValue || initialValue) { - newValue = deleteValue ? '' : initialValue; + if (deleteValue) { + const fieldType = apiRef.current.getColumn(field).type; + switch (fieldType) { + case 'boolean': + newValue = false; + break; + case 'date': + case 'dateTime': + case 'number': + newValue = undefined; + break; + case 'singleSelect': + newValue = null; + break; + case 'string': + default: + newValue = ''; + break; + } + } else if (initialValue) { + newValue = initialValue; } const newProps = { diff --git a/packages/x-data-grid/src/tests/keyboard.DataGrid.test.tsx b/packages/x-data-grid/src/tests/keyboard.DataGrid.test.tsx index 2f27606d0c3ea..3508850d91a8a 100644 --- a/packages/x-data-grid/src/tests/keyboard.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/keyboard.DataGrid.test.tsx @@ -10,7 +10,14 @@ import { getColumnValues, getRow, } from 'test/utils/helperFn'; -import { DataGrid, DataGridProps, GridActionsCellItem, GridColDef } from '@mui/x-data-grid'; +import { + DataGrid, + DataGridProps, + GridActionsCellItem, + GridColDef, + GridColType, + GridValueSetter, +} from '@mui/x-data-grid'; import { useBasicDemoData, getBasicGridData } from '@mui/x-data-grid-generator'; import RestoreIcon from '@mui/icons-material/Restore'; @@ -836,4 +843,131 @@ describe(' - Keyboard', () => { act(() => cell.focus()); fireEvent.keyDown(cell, { key: 'ArrowDown' }); }); + + describe('After pressing the backspace/delete key, the reset value type should match the column type', () => { + function setupTest( + rows: Record[], + columns: GridColDef[], + ) { + const valueSetterMock = spy>( + (value, row, column) => { + return { + ...row, + [column.field]: value, + }; + }, + ); + columns.forEach((column) => { + column.valueSetter = valueSetterMock; + }); + + render(); + + return { valueSetterMock }; + } + + function testResetValue( + keyType: 'Delete' | 'Backspace', + field: string, + type: GridColType, + value: string | number | Date | boolean, + ) { + const columns: GridColDef[] = [ + { field: 'id', editable: true }, + { field, editable: true, type }, + ]; + const rows = [{ id: 1, [field]: value }]; + const { valueSetterMock } = setupTest(rows, columns); + const cell = getCell(0, 1); + + cell.focus(); + fireEvent.keyDown(cell, { key: keyType }); + + return { + cell: cell.textContent, + deletedValue: valueSetterMock.lastCall.args[0], + }; + } + + it(`should reset value on Backspace key press for number type`, () => { + const { cell, deletedValue } = testResetValue('Backspace', 'age', 'number', 24); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Backspace key press for date type`, () => { + const { cell, deletedValue } = testResetValue('Backspace', 'birthdate', 'date', new Date()); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Backspace key press for dateTime type`, () => { + const { cell, deletedValue } = testResetValue( + 'Backspace', + 'appointment', + 'dateTime', + new Date(), + ); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Backspace key press for boolean type`, () => { + const { cell, deletedValue } = testResetValue('Backspace', 'isVerified', 'boolean', true); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(false); + }); + + it(`should reset value on Backspace key press for singleSelect type`, () => { + const { cell, deletedValue } = testResetValue( + 'Backspace', + 'status', + 'singleSelect', + 'active', + ); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(null); + }); + + it(`should reset value on Delete key press for string type`, () => { + const { cell, deletedValue } = testResetValue('Delete', 'name', 'string', 'John Doe'); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(''); + }); + + it(`should reset value on Delete key press for number type`, () => { + const { cell, deletedValue } = testResetValue('Delete', 'age', 'number', 24); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Delete key press for date type`, () => { + const { cell, deletedValue } = testResetValue('Delete', 'birthdate', 'date', new Date()); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Delete key press for dateTime type`, () => { + const { cell, deletedValue } = testResetValue( + 'Delete', + 'appointment', + 'dateTime', + new Date(), + ); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(undefined); + }); + + it(`should reset value on Delete key press for boolean type`, () => { + const { cell, deletedValue } = testResetValue('Delete', 'isVerified', 'boolean', true); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(false); + }); + + it(`should reset value on Delete key press for singleSelect type`, () => { + const { cell, deletedValue } = testResetValue('Delete', 'status', 'singleSelect', 'active'); + expect(cell).to.equal(''); + expect(deletedValue).to.equal(null); + }); + }); }); From acea8ee7192e30df83a061032b8526475b1b242b Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 14 Mar 2024 11:31:45 +0100 Subject: [PATCH 39/49] [docs] Improve slots definitions for charts (#12408) --- docs/data/charts/tooltip/tooltip.md | 26 ++++++++++ docs/pages/x/api/charts/bar-chart.json | 49 ++++++++++++++++--- docs/pages/x/api/charts/charts-legend.json | 6 +++ docs/pages/x/api/charts/charts-tooltip.json | 21 ++++++-- .../x/api/charts/default-charts-legend.json | 7 +++ docs/pages/x/api/charts/line-chart.json | 49 ++++++++++++++++--- docs/pages/x/api/charts/pie-chart.json | 49 ++++++++++++++++--- docs/pages/x/api/charts/scatter-chart.json | 49 ++++++++++++++++--- docs/pages/x/api/charts/spark-line-chart.json | 21 ++++++-- .../api-docs/charts/bar-chart/bar-chart.json | 14 +++--- .../charts/charts-legend/charts-legend.json | 1 + .../charts/charts-tooltip/charts-tooltip.json | 6 ++- .../default-charts-legend.json | 3 +- .../charts/line-chart/line-chart.json | 14 +++--- .../api-docs/charts/pie-chart/pie-chart.json | 14 +++--- .../charts/scatter-chart/scatter-chart.json | 14 +++--- .../spark-line-chart/spark-line-chart.json | 6 +-- .../src/ChartsLegend/ChartsLegend.tsx | 6 +++ .../src/ChartsLegend/DefaultChartsLegend.tsx | 3 ++ .../src/ChartsTooltip/ChartsTooltip.tsx | 12 +++++ packages/x-charts/src/models/axis.ts | 16 ++++++ 21 files changed, 319 insertions(+), 67 deletions(-) diff --git a/docs/data/charts/tooltip/tooltip.md b/docs/data/charts/tooltip/tooltip.md index 43ba5b298d15f..0c94979395de6 100644 --- a/docs/data/charts/tooltip/tooltip.md +++ b/docs/data/charts/tooltip/tooltip.md @@ -81,6 +81,32 @@ It will remove the header showing the x-axis value from the tooltip. /> ``` +### Overriding content + +To modify the tooltip content, use `slots.itemContent` or `slots.axisContent`. +The first one is rendered when tooltip trigger is set to `"item"`. +The second one when trigger is set to `"axis"`. + +```jsx +// With single component + + +// With composition + + // ... + + +``` + ## Composition If you're using composition, by default, the axis will be listening for mouse events to get its current x/y values. diff --git a/docs/pages/x/api/charts/bar-chart.json b/docs/pages/x/api/charts/bar-chart.json index 5329fb31e47f3..f5440ca898e9e 100644 --- a/docs/pages/x/api/charts/bar-chart.json +++ b/docs/pages/x/api/charts/bar-chart.json @@ -111,10 +111,30 @@ "import { BarChart } from '@mui/x-charts';" ], "slots": [ - { "name": "axisLine", "description": "", "class": null }, - { "name": "axisTick", "description": "", "class": null }, - { "name": "axisTickLabel", "description": "", "class": null }, - { "name": "axisLabel", "description": "", "class": null }, + { + "name": "axisLine", + "description": "Custom component for the axis main line.", + "default": "'line'", + "class": null + }, + { + "name": "axisTick", + "description": "Custom component for the axis tick.", + "default": "'line'", + "class": null + }, + { + "name": "axisTickLabel", + "description": "Custom component for tick label.", + "default": "ChartsText", + "class": null + }, + { + "name": "axisLabel", + "description": "Custom component for axis label.", + "default": "ChartsText", + "class": null + }, { "name": "bar", "description": "The component that renders the bar.", @@ -127,9 +147,24 @@ "default": "DefaultChartsLegend", "class": null }, - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [], "muiName": "MuiBarChart", diff --git a/docs/pages/x/api/charts/charts-legend.json b/docs/pages/x/api/charts/charts-legend.json index 8125365e03f8a..2942b92921591 100644 --- a/docs/pages/x/api/charts/charts-legend.json +++ b/docs/pages/x/api/charts/charts-legend.json @@ -3,6 +3,12 @@ "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, "direction": { "type": { "name": "enum", "description": "'column'
| 'row'" } }, "hidden": { "type": { "name": "bool" }, "default": "false" }, + "position": { + "type": { + "name": "shape", + "description": "{ horizontal: 'left'
| 'middle'
| 'right', vertical: 'bottom'
| 'middle'
| 'top' }" + } + }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, diff --git a/docs/pages/x/api/charts/charts-tooltip.json b/docs/pages/x/api/charts/charts-tooltip.json index ec4df95eee229..52b6a9bd0056f 100644 --- a/docs/pages/x/api/charts/charts-tooltip.json +++ b/docs/pages/x/api/charts/charts-tooltip.json @@ -31,9 +31,24 @@ "import { ChartsTooltip } from '@mui/x-charts';" ], "slots": [ - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [ { diff --git a/docs/pages/x/api/charts/default-charts-legend.json b/docs/pages/x/api/charts/default-charts-legend.json index dd17c5d8d3bf3..8d4426a4f9d31 100644 --- a/docs/pages/x/api/charts/default-charts-legend.json +++ b/docs/pages/x/api/charts/default-charts-legend.json @@ -9,6 +9,13 @@ "type": { "name": "enum", "description": "'column'
| 'row'" }, "required": true }, + "position": { + "type": { + "name": "shape", + "description": "{ horizontal: 'left'
| 'middle'
| 'right', vertical: 'bottom'
| 'middle'
| 'top' }" + }, + "required": true + }, "hidden": { "type": { "name": "bool" }, "default": "false" }, "itemGap": { "type": { "name": "number" }, "default": "10" }, "itemMarkHeight": { "type": { "name": "number" }, "default": "20" }, diff --git a/docs/pages/x/api/charts/line-chart.json b/docs/pages/x/api/charts/line-chart.json index 1dce9cfb8b3fd..dd15be4e5d237 100644 --- a/docs/pages/x/api/charts/line-chart.json +++ b/docs/pages/x/api/charts/line-chart.json @@ -106,10 +106,30 @@ "import { LineChart } from '@mui/x-charts';" ], "slots": [ - { "name": "axisLine", "description": "", "class": null }, - { "name": "axisTick", "description": "", "class": null }, - { "name": "axisTickLabel", "description": "", "class": null }, - { "name": "axisLabel", "description": "", "class": null }, + { + "name": "axisLine", + "description": "Custom component for the axis main line.", + "default": "'line'", + "class": null + }, + { + "name": "axisTick", + "description": "Custom component for the axis tick.", + "default": "'line'", + "class": null + }, + { + "name": "axisTickLabel", + "description": "Custom component for tick label.", + "default": "ChartsText", + "class": null + }, + { + "name": "axisLabel", + "description": "Custom component for axis label.", + "default": "ChartsText", + "class": null + }, { "name": "area", "description": "The component that renders the area.", @@ -130,9 +150,24 @@ "default": "DefaultChartsLegend", "class": null }, - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [], "muiName": "MuiLineChart", diff --git a/docs/pages/x/api/charts/pie-chart.json b/docs/pages/x/api/charts/pie-chart.json index e1555deb9bb82..1e974abe72a0b 100644 --- a/docs/pages/x/api/charts/pie-chart.json +++ b/docs/pages/x/api/charts/pie-chart.json @@ -102,10 +102,30 @@ "import { PieChart } from '@mui/x-charts';" ], "slots": [ - { "name": "axisLine", "description": "", "class": null }, - { "name": "axisTick", "description": "", "class": null }, - { "name": "axisTickLabel", "description": "", "class": null }, - { "name": "axisLabel", "description": "", "class": null }, + { + "name": "axisLine", + "description": "Custom component for the axis main line.", + "default": "'line'", + "class": null + }, + { + "name": "axisTick", + "description": "Custom component for the axis tick.", + "default": "'line'", + "class": null + }, + { + "name": "axisTickLabel", + "description": "Custom component for tick label.", + "default": "ChartsText", + "class": null + }, + { + "name": "axisLabel", + "description": "Custom component for axis label.", + "default": "ChartsText", + "class": null + }, { "name": "pieArc", "description": "", "class": null }, { "name": "pieArcLabel", "description": "", "class": null }, { @@ -114,9 +134,24 @@ "default": "DefaultChartsLegend", "class": null }, - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [], "muiName": "MuiPieChart", diff --git a/docs/pages/x/api/charts/scatter-chart.json b/docs/pages/x/api/charts/scatter-chart.json index d6f1342a7e107..81ca7241fd46a 100644 --- a/docs/pages/x/api/charts/scatter-chart.json +++ b/docs/pages/x/api/charts/scatter-chart.json @@ -103,10 +103,30 @@ "import { ScatterChart } from '@mui/x-charts';" ], "slots": [ - { "name": "axisLine", "description": "", "class": null }, - { "name": "axisTick", "description": "", "class": null }, - { "name": "axisTickLabel", "description": "", "class": null }, - { "name": "axisLabel", "description": "", "class": null }, + { + "name": "axisLine", + "description": "Custom component for the axis main line.", + "default": "'line'", + "class": null + }, + { + "name": "axisTick", + "description": "Custom component for the axis tick.", + "default": "'line'", + "class": null + }, + { + "name": "axisTickLabel", + "description": "Custom component for tick label.", + "default": "ChartsText", + "class": null + }, + { + "name": "axisLabel", + "description": "Custom component for axis label.", + "default": "ChartsText", + "class": null + }, { "name": "scatter", "description": "", "class": null }, { "name": "legend", @@ -114,9 +134,24 @@ "default": "DefaultChartsLegend", "class": null }, - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [], "muiName": "MuiScatterChart", diff --git a/docs/pages/x/api/charts/spark-line-chart.json b/docs/pages/x/api/charts/spark-line-chart.json index 22a0dab3b73df..73fa87be1352b 100644 --- a/docs/pages/x/api/charts/spark-line-chart.json +++ b/docs/pages/x/api/charts/spark-line-chart.json @@ -74,9 +74,24 @@ "default": "BarElementPath", "class": null }, - { "name": "popper", "description": "", "class": null }, - { "name": "axisContent", "description": "", "class": null }, - { "name": "itemContent", "description": "", "class": null } + { + "name": "popper", + "description": "Custom component for the tooltip popper.", + "default": "ChartsTooltipRoot", + "class": null + }, + { + "name": "axisContent", + "description": "Custom component for displaying tooltip content when triggered by axis event.", + "default": "DefaultChartsAxisTooltipContent", + "class": null + }, + { + "name": "itemContent", + "description": "Custom component for displaying tooltip content when triggered by item event.", + "default": "DefaultChartsItemTooltipContent", + "class": null + } ], "classes": [], "muiName": "MuiSparkLineChart", diff --git a/docs/translations/api-docs/charts/bar-chart/bar-chart.json b/docs/translations/api-docs/charts/bar-chart/bar-chart.json index 49f5947d72705..37cbf6fb2c982 100644 --- a/docs/translations/api-docs/charts/bar-chart/bar-chart.json +++ b/docs/translations/api-docs/charts/bar-chart/bar-chart.json @@ -66,14 +66,14 @@ }, "classDescriptions": {}, "slotDescriptions": { - "axisContent": "", - "axisLabel": "", - "axisLine": "", - "axisTick": "", - "axisTickLabel": "", + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", + "axisLabel": "Custom component for axis label.", + "axisLine": "Custom component for the axis main line.", + "axisTick": "Custom component for the axis tick.", + "axisTickLabel": "Custom component for tick label.", "bar": "The component that renders the bar.", - "itemContent": "", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", "legend": "Custom rendering of the legend.", - "popper": "" + "popper": "Custom component for the tooltip popper." } } diff --git a/docs/translations/api-docs/charts/charts-legend/charts-legend.json b/docs/translations/api-docs/charts/charts-legend/charts-legend.json index eea7aaf8c2b2c..f48685ad6338b 100644 --- a/docs/translations/api-docs/charts/charts-legend/charts-legend.json +++ b/docs/translations/api-docs/charts/charts-legend/charts-legend.json @@ -6,6 +6,7 @@ "description": "The direction of the legend layout. The default depends on the chart." }, "hidden": { "description": "Set to true to hide the legend." }, + "position": { "description": "The position of the legend." }, "slotProps": { "description": "The props used for each component slot." }, "slots": { "description": "Overridable component slots." } }, diff --git a/docs/translations/api-docs/charts/charts-tooltip/charts-tooltip.json b/docs/translations/api-docs/charts/charts-tooltip/charts-tooltip.json index 170732a759d70..1eb9f471154ab 100644 --- a/docs/translations/api-docs/charts/charts-tooltip/charts-tooltip.json +++ b/docs/translations/api-docs/charts/charts-tooltip/charts-tooltip.json @@ -33,5 +33,9 @@ "nodeName": "the valueCell element" } }, - "slotDescriptions": { "axisContent": "", "itemContent": "", "popper": "" } + "slotDescriptions": { + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", + "popper": "Custom component for the tooltip popper." + } } diff --git a/docs/translations/api-docs/charts/default-charts-legend/default-charts-legend.json b/docs/translations/api-docs/charts/default-charts-legend/default-charts-legend.json index d39cdf7e37368..4f48245b770e5 100644 --- a/docs/translations/api-docs/charts/default-charts-legend/default-charts-legend.json +++ b/docs/translations/api-docs/charts/default-charts-legend/default-charts-legend.json @@ -13,7 +13,8 @@ "markGap": { "description": "Space between the mark and the label (in px)." }, "padding": { "description": "Legend padding (in px). Can either be a single number, or an object with top, left, bottom, right properties." - } + }, + "position": { "description": "The position of the legend." } }, "classDescriptions": { "mark": { "description": "" }, diff --git a/docs/translations/api-docs/charts/line-chart/line-chart.json b/docs/translations/api-docs/charts/line-chart/line-chart.json index c87c7b2a2e62b..8da425ba637df 100644 --- a/docs/translations/api-docs/charts/line-chart/line-chart.json +++ b/docs/translations/api-docs/charts/line-chart/line-chart.json @@ -65,16 +65,16 @@ "classDescriptions": {}, "slotDescriptions": { "area": "The component that renders the area.", - "axisContent": "", - "axisLabel": "", - "axisLine": "", - "axisTick": "", - "axisTickLabel": "", - "itemContent": "", + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", + "axisLabel": "Custom component for axis label.", + "axisLine": "Custom component for the axis main line.", + "axisTick": "Custom component for the axis tick.", + "axisTickLabel": "Custom component for tick label.", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", "legend": "Custom rendering of the legend.", "line": "The component that renders the line.", "lineHighlight": "", "mark": "", - "popper": "" + "popper": "Custom component for the tooltip popper." } } diff --git a/docs/translations/api-docs/charts/pie-chart/pie-chart.json b/docs/translations/api-docs/charts/pie-chart/pie-chart.json index 11e7de3ade71c..8293d8d7baaaf 100644 --- a/docs/translations/api-docs/charts/pie-chart/pie-chart.json +++ b/docs/translations/api-docs/charts/pie-chart/pie-chart.json @@ -52,15 +52,15 @@ }, "classDescriptions": {}, "slotDescriptions": { - "axisContent": "", - "axisLabel": "", - "axisLine": "", - "axisTick": "", - "axisTickLabel": "", - "itemContent": "", + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", + "axisLabel": "Custom component for axis label.", + "axisLine": "Custom component for the axis main line.", + "axisTick": "Custom component for the axis tick.", + "axisTickLabel": "Custom component for tick label.", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", "legend": "Custom rendering of the legend.", "pieArc": "", "pieArcLabel": "", - "popper": "" + "popper": "Custom component for the tooltip popper." } } diff --git a/docs/translations/api-docs/charts/scatter-chart/scatter-chart.json b/docs/translations/api-docs/charts/scatter-chart/scatter-chart.json index 771ec34c1fdb3..4aa4a64d3ea6a 100644 --- a/docs/translations/api-docs/charts/scatter-chart/scatter-chart.json +++ b/docs/translations/api-docs/charts/scatter-chart/scatter-chart.json @@ -63,14 +63,14 @@ }, "classDescriptions": {}, "slotDescriptions": { - "axisContent": "", - "axisLabel": "", - "axisLine": "", - "axisTick": "", - "axisTickLabel": "", - "itemContent": "", + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", + "axisLabel": "Custom component for axis label.", + "axisLine": "Custom component for the axis main line.", + "axisTick": "Custom component for the axis tick.", + "axisTickLabel": "Custom component for tick label.", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", "legend": "Custom rendering of the legend.", - "popper": "", + "popper": "Custom component for the tooltip popper.", "scatter": "" } } diff --git a/docs/translations/api-docs/charts/spark-line-chart/spark-line-chart.json b/docs/translations/api-docs/charts/spark-line-chart/spark-line-chart.json index 30ad9ccf5fb9f..e17bf65299692 100644 --- a/docs/translations/api-docs/charts/spark-line-chart/spark-line-chart.json +++ b/docs/translations/api-docs/charts/spark-line-chart/spark-line-chart.json @@ -41,12 +41,12 @@ "classDescriptions": {}, "slotDescriptions": { "area": "The component that renders the area.", - "axisContent": "", + "axisContent": "Custom component for displaying tooltip content when triggered by axis event.", "bar": "The component that renders the bar.", - "itemContent": "", + "itemContent": "Custom component for displaying tooltip content when triggered by item event.", "line": "The component that renders the line.", "lineHighlight": "", "mark": "", - "popper": "" + "popper": "Custom component for the tooltip popper." } } diff --git a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx index e29e1f1cfcb3b..291c13be609ed 100644 --- a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx +++ b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx @@ -23,6 +23,9 @@ export interface ChartsLegendSlotProps { } export type ChartsLegendProps = { + /** + * The position of the legend. + */ position?: AnchorPosition; /** * Override or extend the styles applied to the component. @@ -122,6 +125,9 @@ ChartsLegend.propTypes = { * @default false */ hidden: PropTypes.bool, + /** + * The position of the legend. + */ position: PropTypes.shape({ horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, diff --git a/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx b/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx index 14d05225c2e4e..1fcd0e4182837 100644 --- a/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx +++ b/packages/x-charts/src/ChartsLegend/DefaultChartsLegend.tsx @@ -359,6 +359,9 @@ DefaultChartsLegend.propTypes = { top: PropTypes.number, }), ]), + /** + * The position of the legend. + */ position: PropTypes.shape({ horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, diff --git a/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx b/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx index e0eca7dcd5d27..9af286fe77b62 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx @@ -22,8 +22,20 @@ import { ChartsAxisContentProps, ChartsAxisTooltipContent } from './ChartsAxisTo import { ChartsTooltipClasses, getChartsTooltipUtilityClass } from './chartsTooltipClasses'; export interface ChartsTooltipSlots { + /** + * Custom component for the tooltip popper. + * @default ChartsTooltipRoot + */ popper?: React.ElementType; + /** + * Custom component for displaying tooltip content when triggered by axis event. + * @default DefaultChartsAxisTooltipContent + */ axisContent?: React.ElementType; + /** + * Custom component for displaying tooltip content when triggered by item event. + * @default DefaultChartsItemTooltipContent + */ itemContent?: React.ElementType; } diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index b469b8075d2d3..060ff07b87f12 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -31,9 +31,25 @@ export type D3ContinuouseScale = | ScaleLinear; export interface ChartsAxisSlots { + /** + * Custom component for the axis main line. + * @default 'line' + */ axisLine?: React.JSXElementConstructor>; + /** + * Custom component for the axis tick. + * @default 'line' + */ axisTick?: React.JSXElementConstructor>; + /** + * Custom component for tick label. + * @default ChartsText + */ axisTickLabel?: React.JSXElementConstructor; + /** + * Custom component for axis label. + * @default ChartsText + */ axisLabel?: React.JSXElementConstructor; } From e5e95f3741152a0ea528c30808a4757e4de74b48 Mon Sep 17 00:00:00 2001 From: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:48:09 +0100 Subject: [PATCH 40/49] v7.0.0-beta.7 (#12445) Signed-off-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> Co-authored-by: Lukas --- CHANGELOG.md | 116 ++++++++++++++++++++ package.json | 2 +- packages/x-charts/package.json | 2 +- packages/x-codemod/package.json | 2 +- packages/x-data-grid-generator/package.json | 4 +- packages/x-data-grid-premium/package.json | 6 +- packages/x-data-grid-pro/package.json | 4 +- packages/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 4 +- packages/x-date-pickers/package.json | 2 +- packages/x-tree-view/package.json | 2 +- 11 files changed, 131 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd14a3acb4f6..f39c4060b14f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,102 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 7.0.0-beta.7 + +_Mar 14, 2024_ + +We'd like to offer a big thanks to the 11 contributors who made this release possible. Here are some highlights ✨: + +- 🦥 The Lazy loading feature is now stable and the `lazyLoading` feature flag was removed from the `experimentalFeatures` prop. +- 🌍 Improve Japanese (ja-JP) locale for the Data Grid +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### Breaking changes + +- The `columnHeader--showColumnBorder` class was replaced by `columnHeader--withLeftBorder` and `columnHeader--withRightBorder`. +- The `columnHeadersInner`, `columnHeadersInner--scrollable`, and `columnHeaderDropZone` classes were removed since the inner wrapper was removed in our effort to simplify the DOM structure and improve accessibility. +- The `pinnedColumnHeaders`, `pinnedColumnHeaders--left`, and `pinnedColumnHeaders--right` classes were removed along with the element they were applied to. + The pinned column headers now use `position: 'sticky'` and are rendered in the same row element as the regular column headers. + +#### `@mui/x-data-grid@7.0.0-beta.7` + +- [DataGrid] Fix focus visible style on scrollbar (#12402) @oliviertassinari +- [DataGrid] Fix the issue where pressing the Delete key resets various cell values to an empty string. (#12216) @sooster910 +- [DataGrid] Make `rowCount` part of the state (#12381) @MBilalShafi +- [DataGrid] Make column resizing and autosizing available in Community plan (#12420) @cherniavskii +- [DataGrid] Remove `baseSwitch` slot (#12439) @romgrk +- [l10n] Improve Japanese (ja-JP) locale (#12398) @makoto14 + +#### `@mui/x-data-grid-pro@7.0.0-beta.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-data-grid@7.0.0-beta.7`, plus: + +- [DataGridPro] Add `inputRef` to the props passed to `colDef.renderHeaderFilter` (#12328) @vovarudomanenko +- [DataGridPro] Fix filler rendered for no reason when there are pinned columns (#12440) @cherniavskii +- [DataGridPro] Make lazy loading feature stable (#12421) @cherniavskii +- [DataGridPro] Render pinned and non-pinned column headers in one row (#12376) @cherniavskii + +#### `@mui/x-data-grid-premium@7.0.0-beta.7` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan') + +Same changes as in `@mui/x-data-grid-pro@7.0.0-beta.7`, plus: + +- [DataGridPremium] Fix auto-scroll not working when selecting cell range (#12267) @cherniavskii + +### Date and Time Pickers + +#### `@mui/x-date-pickers@7.0.0-beta.7` + +- [fields] Fix `tabIndex` on accessible field DOM structure (#12311) @flaviendelangle +- [fields] Fix items alignment on multi input range fields (#12312) @flaviendelangle +- [pickers] Improve the customization of the range picker calendar header (#11988) @flaviendelangle +- [pickers] Keep the existing time when looking for closest enabled date (#12377) @LukasTy + +#### `@mui/x-date-pickers-pro@7.0.0-beta.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-date-pickers@7.0.0-beta.7`. + +### Charts + +#### `@mui/x-charts@7.0.0-beta.7` + +- [charts] Fix axis highlight when axis is reversed (#12407) @alexfauquette + +### Tree View + +#### Breaking changes + +The `onNodeFocus` callback has been renamed to `onItemFocus` for consistency: + +```diff + +``` + +#### `@mui/x-tree-view@7.0.0-beta.7` + +- [TreeView] Clean the usage of the term "item" and "node" in API introduced during v7 (#12368) @noraleonte +- [TreeView] Introduce a new `TreeItem2` component and a new `useTreeItem2` hook (#11721) @flaviendelangle +- [TreeView] Rename `onNodeFocus` to `onItemFocus` (#12419) @noraleonte + +### Docs + +- [docs] Add `legacy` bundle drop mention in migration pages (#12424) @LukasTy +- [docs] Add missing luxon `Info` import (#12427) @LukasTy +- [docs] Improve slots definitions for charts (#12408) @alexfauquette +- [docs] Polish What's new in MUI X blog titles (#12309) @oliviertassinari +- [docs] Replace `rel="noreferrer"` by `rel="noopener"` @oliviertassinari +- [docs] Update `date-fns` `weekStarsOn` overriding example (#12416) @LukasTy + +### Core + +- [core] Fix CI (#12414) @flaviendelangle +- [core] Fix PR deploy link for Tree View doc pages (#12411) @flaviendelangle + ## 7.0.0-beta.6 _Mar 8, 2024_ @@ -2521,6 +2617,26 @@ Here is an example of the renaming for the `` component. - [core] Update release instructions as per v7 configuration (#10962) @MBilalShafi - [license] Correctly throw errors (#10924) @oliviertassinari +## 6.19.7 + +_Mar 14, 2024_ + +We'd like to offer a big thanks to @LukasTy who made this release possible. + +### Date Pickers + +#### `@mui/x-date-pickers@6.19.7` + +- [pickers] Keep the existing time when looking for closest enabled date (#12410) @LukasTy + +#### `@mui/x-date-pickers-pro@6.19.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-date-pickers@6.19.7`. + +### Docs + +- [docs] Add Pickers custom start of week section (#12425) @LukasTy + ## 6.19.6 _Mar 1, 2024_ diff --git a/package.json b/package.json index 3de081a76fa1c..9c2387ee2ca1c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "private": true, "scripts": { "start": "yarn && yarn docs:dev", diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index dcfa223d0e6b7..85f3bb57bb507 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The community edition of the charts components (MUI X).", "author": "MUI Team", "main": "./src/index.js", diff --git a/packages/x-codemod/package.json b/packages/x-codemod/package.json index 584e7656f02ee..3eaba12224cf4 100644 --- a/packages/x-codemod/package.json +++ b/packages/x-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-codemod", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/x-data-grid-generator/package.json b/packages/x-data-grid-generator/package.json index 2e443e622ccac..0ba9efa091f9e 100644 --- a/packages/x-data-grid-generator/package.json +++ b/packages/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", @@ -34,7 +34,7 @@ "dependencies": { "@babel/runtime": "^7.24.0", "@mui/base": "^5.0.0-beta.36", - "@mui/x-data-grid-premium": "7.0.0-beta.6", + "@mui/x-data-grid-premium": "7.0.0-beta.7", "chance": "^1.1.11", "clsx": "^2.1.0", "lru-cache": "^7.18.3" diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index bd44e729ef13d..7e362f5b6ac16 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The Premium plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,8 +45,8 @@ "@babel/runtime": "^7.24.0", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-data-grid": "7.0.0-beta.6", - "@mui/x-data-grid-pro": "7.0.0-beta.6", + "@mui/x-data-grid": "7.0.0-beta.7", + "@mui/x-data-grid-pro": "7.0.0-beta.7", "@mui/x-license": "7.0.0-beta.6", "@types/format-util": "^1.0.4", "clsx": "^2.1.0", diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index c837376f80920..86eacf60fabbe 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The Pro plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,7 +45,7 @@ "@babel/runtime": "^7.24.0", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-data-grid": "7.0.0-beta.6", + "@mui/x-data-grid": "7.0.0-beta.7", "@mui/x-license": "7.0.0-beta.6", "@types/format-util": "^1.0.4", "clsx": "^2.1.0", diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index cc54a34e3ce8d..3d0fb9944d210 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The community edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 0ed650b4ee7a4..63f7862cfe44a 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The commercial edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -45,7 +45,7 @@ "@mui/base": "^5.0.0-beta.36", "@mui/system": "^5.15.9", "@mui/utils": "^5.15.9", - "@mui/x-date-pickers": "7.0.0-beta.6", + "@mui/x-date-pickers": "7.0.0-beta.7", "@mui/x-license": "7.0.0-beta.6", "clsx": "^2.1.0", "prop-types": "^15.8.1", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index 7a6a2e959de78..4bf22951c0ce0 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The community edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index e62f932681f9d..47319dacd2d45 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view", - "version": "7.0.0-beta.6", + "version": "7.0.0-beta.7", "description": "The community edition of the tree view components (MUI X).", "author": "MUI Team", "main": "src/index.ts", From a518d0a2ecd8cd3ed66c60526681f0130e6dba50 Mon Sep 17 00:00:00 2001 From: Nora <72460825+noraleonte@users.noreply.github.com> Date: Thu, 14 Mar 2024 19:29:00 +0200 Subject: [PATCH 41/49] [TreeView] Rename `nodeId` to `itemId` (#12418) Signed-off-by: Nora <72460825+noraleonte@users.noreply.github.com> Co-authored-by: Lukas --- .../migration-tree-view-v6.md | 48 +- .../getting-started/FirstComponent.js | 12 +- .../getting-started/FirstComponent.tsx | 12 +- .../FirstComponent.tsx.preview | 12 +- .../tree-view/overview/BasicSimpleTreeView.js | 14 +- .../overview/BasicSimpleTreeView.tsx | 14 +- .../overview/BasicSimpleTreeView.tsx.preview | 14 +- docs/data/tree-view/overview/overview.md | 8 +- .../customization/CustomContentTreeView.js | 6 +- .../customization/CustomContentTreeView.tsx | 6 +- .../customization/IconExpansionTreeView.js | 4 +- .../customization/IconExpansionTreeView.tsx | 4 +- .../customization/LabelSlotProps.js | 2 +- .../customization/LabelSlotProps.tsx | 2 +- .../customization/LabelSlots.js | 8 +- .../customization/LabelSlots.tsx | 10 +- .../headless/LogExpandedItems.js | 2 +- .../headless/LogExpandedItems.tsx | 2 +- .../rich-tree-view/headless/headless.md | 4 +- .../customization/BorderedTreeView.js | 22 +- .../customization/BorderedTreeView.tsx | 22 +- .../customization/CustomAnimation.js | 22 +- .../customization/CustomAnimation.tsx | 22 +- .../customization/CustomContentTreeView.js | 18 +- .../customization/CustomContentTreeView.tsx | 18 +- .../CustomContentTreeView.tsx.preview | 12 +- .../customization/CustomIcons.js | 22 +- .../customization/CustomIcons.tsx | 22 +- .../customization/CustomStyling.js | 22 +- .../customization/CustomStyling.tsx | 22 +- .../customization/CustomizedTreeView.js | 30 +- .../customization/CustomizedTreeView.tsx | 30 +- .../customization/GmailTreeView.js | 22 +- .../customization/GmailTreeView.tsx | 22 +- .../customization/IconExpansionTreeView.js | 14 +- .../customization/IconExpansionTreeView.tsx | 14 +- .../IconExpansionTreeView.tsx.preview | 12 +- .../customization/LabelSlotProps.js | 16 +- .../customization/LabelSlotProps.tsx | 16 +- .../customization/LabelSlotProps.tsx.preview | 14 +- .../customization/LabelSlots.js | 14 +- .../customization/LabelSlots.tsx | 14 +- .../expansion/ControlledExpansion.js | 22 +- .../expansion/ControlledExpansion.tsx | 22 +- .../expansion/TrackItemExpansionToggle.js | 22 +- .../expansion/TrackItemExpansionToggle.tsx | 22 +- .../focus/FocusedSimpleTreeView.js | 22 +- .../focus/FocusedSimpleTreeView.tsx | 22 +- .../items/BasicSimpleTreeView.js | 14 +- .../items/BasicSimpleTreeView.tsx | 14 +- .../items/BasicSimpleTreeView.tsx.preview | 14 +- .../items/DisabledItemsFocusable.js | 28 +- .../items/DisabledItemsFocusable.tsx | 28 +- .../simple-tree-view/items/DisabledJSXItem.js | 28 +- .../items/DisabledJSXItem.tsx | 28 +- .../tree-view/simple-tree-view/items/items.md | 4 +- .../selection/ControlledSelection.js | 22 +- .../selection/ControlledSelection.tsx | 22 +- .../selection/DisableSelection.js | 22 +- .../selection/DisableSelection.tsx | 22 +- .../selection/MultiSelectTreeView.js | 22 +- .../selection/MultiSelectTreeView.tsx | 22 +- .../selection/TrackItemSelectionToggle.js | 22 +- .../selection/TrackItemSelectionToggle.tsx | 22 +- docs/pages/x/api/tree-view/tree-item-2.json | 2 +- docs/pages/x/api/tree-view/tree-item.json | 2 +- .../tree-view/tree-item-2/tree-item-2.json | 8 +- .../tree-view/tree-item/tree-item.json | 2 +- packages/x-codemod/README.md | 17 +- .../tree-view/preset-safe/expected.spec.tsx | 2 +- .../src/v7.0.0/tree-view/preset-safe/index.ts | 2 + .../tree-view/rename-nodeid/actual.spec.js | 3 + .../tree-view/rename-nodeid/expected.spec.js | 3 + .../v7.0.0/tree-view/rename-nodeid/index.ts | 18 + .../rename-nodeid/rename-nodeid.test.ts | 27 + .../src/RichTreeView/RichTreeView.tsx | 14 +- .../src/RichTreeView/RichTreeView.types.ts | 2 +- .../SimpleTreeView/SimpleTreeView.test.tsx | 110 +-- .../src/TreeItem/TreeItem.test.tsx | 624 +++++++++--------- .../x-tree-view/src/TreeItem/TreeItem.tsx | 28 +- .../src/TreeItem/TreeItem.types.ts | 4 +- .../src/TreeItem/TreeItemContent.tsx | 32 +- .../src/TreeItem/useTreeItemState.ts | 26 +- .../x-tree-view/src/TreeItem2/TreeItem2.tsx | 20 +- .../TreeItem2Provider/TreeItem2Provider.tsx | 6 +- .../TreeItem2Provider.types.ts | 2 +- .../useTreeItem2Utils/useTreeItem2Utils.tsx | 26 +- .../src/internals/models/plugin.ts | 2 +- .../useTreeViewExpansion.ts | 14 +- .../useTreeViewExpansion.types.ts | 6 +- .../useTreeViewFocus/useTreeViewFocus.ts | 34 +- .../useTreeViewFocus.types.ts | 2 +- .../plugins/useTreeViewId/useTreeViewId.ts | 2 +- .../useTreeViewId/useTreeViewId.types.ts | 2 +- .../useTreeViewJSXNodes.tsx | 34 +- .../useTreeViewJSXNodes.types.ts | 4 +- .../useTreeViewKeyboardNavigation.ts | 86 +-- .../useTreeViewKeyboardNavigation.types.ts | 2 +- .../useTreeViewNodes.test.tsx | 4 +- .../useTreeViewNodes/useTreeViewNodes.ts | 46 +- .../useTreeViewNodes.types.ts | 22 +- .../useTreeViewSelection.ts | 30 +- .../useTreeViewSelection.types.ts | 8 +- .../src/internals/useTreeView/useTreeView.ts | 4 +- .../useTreeView/useTreeView.utils.ts | 46 +- .../themeAugmentation.spec.ts | 4 +- .../src/useTreeItem2/useTreeItem2.ts | 10 +- .../src/useTreeItem2/useTreeItem2.types.ts | 10 +- 108 files changed, 1269 insertions(+), 1175 deletions(-) create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/actual.spec.js create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/expected.spec.js create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/index.ts create mode 100644 packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/rename-nodeid.test.ts diff --git a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md index 851bd995d35ab..389ee9b382188 100644 --- a/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md +++ b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md @@ -12,7 +12,7 @@ productId: x-tree-view TBD -## Start using the alpha release +## Start using the beta release In `package.json`, change the version of the tree view package to `next`. @@ -41,6 +41,36 @@ The `legacy` bundle that used to support old browsers like IE11 is no longer inc If you need support for IE11, you will need to keep using the latest version of the `v6` release. ::: +### ✅ Rename `nodeId` to `itemId` + +The required `nodeId` prop used by the `TreeItem` has been renamed to `itemId` for consistency: + +```diff + +- ++ + +``` + +The same change has been applied to the and `ContentComponent` prop: + +```diff + const CustomContent = React.forwardRef((props, ref) => { +- const id = props.nodeId; ++ const id = props.itemId; + + // Render some UI + }); + + function App() { + return ( + + + + ) + } +``` + ### ✅ Use `SimpleTreeView` instead of `TreeView` The `TreeView` component has been deprecated and will be removed in the next major. @@ -56,7 +86,7 @@ You can start replacing it with the new `SimpleTreeView` component which has exa return ( - + - + - + ); @@ -131,7 +161,7 @@ you need to use the new `expandIcon` slot on this component: ```diff } + slots={{ expandIcon: MyCustomExpandIcon }} @@ -179,7 +209,7 @@ you need to use the new `collapseIcon` slot on this component: ```diff } + slots={{ collapseIcon: MyCustomCollapseIcon }} @@ -231,7 +261,7 @@ you need to use the new `endIcon` slot on this component: ```diff } + slots={{ endIcon: MyCustomEndIcon }} @@ -250,7 +280,7 @@ you need to use the new `icon` slot on this component: ```diff } + slots={{ icon: MyCustomIcon }} @@ -273,7 +303,7 @@ you need to use the new `groupTransition` slot on this component: ```diff { -- const { disabled } = useTreeItem(props.nodeId); -+ const { disabled } = useTreeItemState(props.nodeId); +- const { disabled } = useTreeItem(props.itemId); ++ const { disabled } = useTreeItemState(props.itemId); // Render some UI }); diff --git a/docs/data/tree-view/getting-started/FirstComponent.js b/docs/data/tree-view/getting-started/FirstComponent.js index ade354baeab47..f5b1eb6a76915 100644 --- a/docs/data/tree-view/getting-started/FirstComponent.js +++ b/docs/data/tree-view/getting-started/FirstComponent.js @@ -8,13 +8,13 @@ export default function FirstComponent() { aria-label="file system navigator" sx={{ height: 200, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > - - + + - - - - + + + + diff --git a/docs/data/tree-view/getting-started/FirstComponent.tsx b/docs/data/tree-view/getting-started/FirstComponent.tsx index ade354baeab47..f5b1eb6a76915 100644 --- a/docs/data/tree-view/getting-started/FirstComponent.tsx +++ b/docs/data/tree-view/getting-started/FirstComponent.tsx @@ -8,13 +8,13 @@ export default function FirstComponent() { aria-label="file system navigator" sx={{ height: 200, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > - - + + - - - - + + + + diff --git a/docs/data/tree-view/getting-started/FirstComponent.tsx.preview b/docs/data/tree-view/getting-started/FirstComponent.tsx.preview index b31f963565f84..8228e866c065c 100644 --- a/docs/data/tree-view/getting-started/FirstComponent.tsx.preview +++ b/docs/data/tree-view/getting-started/FirstComponent.tsx.preview @@ -2,13 +2,13 @@ aria-label="file system navigator" sx={{ height: 200, flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > - - + + - - - - + + + + \ No newline at end of file diff --git a/docs/data/tree-view/overview/BasicSimpleTreeView.js b/docs/data/tree-view/overview/BasicSimpleTreeView.js index e45b830807df9..5757cfb2964e7 100644 --- a/docs/data/tree-view/overview/BasicSimpleTreeView.js +++ b/docs/data/tree-view/overview/BasicSimpleTreeView.js @@ -7,14 +7,14 @@ export default function BasicSimpleTreeView() { return ( - - - - + + + + - - - + + + diff --git a/docs/data/tree-view/overview/BasicSimpleTreeView.tsx b/docs/data/tree-view/overview/BasicSimpleTreeView.tsx index e45b830807df9..5757cfb2964e7 100644 --- a/docs/data/tree-view/overview/BasicSimpleTreeView.tsx +++ b/docs/data/tree-view/overview/BasicSimpleTreeView.tsx @@ -7,14 +7,14 @@ export default function BasicSimpleTreeView() { return ( - - - - + + + + - - - + + + diff --git a/docs/data/tree-view/overview/BasicSimpleTreeView.tsx.preview b/docs/data/tree-view/overview/BasicSimpleTreeView.tsx.preview index 4f449dc9b561d..3641f9443f57a 100644 --- a/docs/data/tree-view/overview/BasicSimpleTreeView.tsx.preview +++ b/docs/data/tree-view/overview/BasicSimpleTreeView.tsx.preview @@ -1,11 +1,11 @@ - - - - + + + + - - - + + + \ No newline at end of file diff --git a/docs/data/tree-view/overview/overview.md b/docs/data/tree-view/overview/overview.md index 7c0145f2a6dd6..9429bf8a1d303 100644 --- a/docs/data/tree-view/overview/overview.md +++ b/docs/data/tree-view/overview/overview.md @@ -63,8 +63,8 @@ import { TreeItem } from '@mui/x-tree-view/TreeItem'; export default function App() { return ( - - + + ); } @@ -95,8 +95,8 @@ import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; export default function App() { return ( - - + + ); } diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js index 8215437c85450..d024ef094cc15 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.js @@ -40,7 +40,7 @@ const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ })); const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { - const { id, nodeId, label, disabled, children, ...other } = props; + const { id, itemId, label, disabled, children, ...other } = props; const { getRootProps, @@ -49,10 +49,10 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { getLabelProps, getGroupTransitionProps, status, - } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); return ( - + diff --git a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx index c426440118e53..7651bb7ed31ef 100644 --- a/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/CustomContentTreeView.tsx @@ -50,7 +50,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( props: CustomTreeItemProps, ref: React.Ref, ) { - const { id, nodeId, label, disabled, children, ...other } = props; + const { id, itemId, label, disabled, children, ...other } = props; const { getRootProps, @@ -59,10 +59,10 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( getLabelProps, getGroupTransitionProps, status, - } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); return ( - + diff --git a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js index c6eb49a5783bb..4c28c6cd280b0 100644 --- a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js +++ b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.js @@ -30,7 +30,7 @@ const CustomContent = React.forwardRef(function CustomContent(props, ref) { classes, className, label, - nodeId, + itemId, icon: iconProp, expansionIcon, displayIcon, @@ -44,7 +44,7 @@ const CustomContent = React.forwardRef(function CustomContent(props, ref) { handleExpansion, handleSelection, preventSelection, - } = useTreeItemState(nodeId); + } = useTreeItemState(itemId); const icon = iconProp || expansionIcon || displayIcon; diff --git a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx index fa1b76d6032fd..2847daf70967a 100644 --- a/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/IconExpansionTreeView.tsx @@ -34,7 +34,7 @@ const CustomContent = React.forwardRef(function CustomContent( classes, className, label, - nodeId, + itemId, icon: iconProp, expansionIcon, displayIcon, @@ -48,7 +48,7 @@ const CustomContent = React.forwardRef(function CustomContent( handleExpansion, handleSelection, preventSelection, - } = useTreeItemState(nodeId); + } = useTreeItemState(itemId); const icon = iconProp || expansionIcon || displayIcon; diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js index 1786ba3d4aa73..0261e5154c827 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.js @@ -28,7 +28,7 @@ const CustomTreeItem = React.forwardRef((props, ref) => ( {...props} slotProps={{ label: { - id: `${props.nodeId}-label`, + id: `${props.itemId}-label`, }, }} /> diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx index 7bac158b75a7c..99f29c8d5d29d 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlotProps.tsx @@ -30,7 +30,7 @@ const CustomTreeItem = React.forwardRef( {...props} slotProps={{ label: { - id: `${props.nodeId}-label`, + id: `${props.itemId}-label`, }, }} /> diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js index 655627dc07947..2b7de2e27e142 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.js @@ -59,14 +59,14 @@ const TreeItemContext = React.createContext({ onLabelValueChange: () => {} }); const CustomTreeItem = React.forwardRef((props, ref) => { const { interactions } = useTreeItem2Utils({ - nodeId: props.nodeId, + itemId: props.itemId, children: props.children, }); const { onLabelValueChange } = React.useContext(TreeItemContext); const handleLabelValueChange = (newLabel) => { - onLabelValueChange(props.nodeId, newLabel); + onLabelValueChange(props.itemId, newLabel); }; const handleContentClick = (event) => { @@ -123,10 +123,10 @@ export default function LabelSlots() { const context = React.useMemo( () => ({ - onLabelValueChange: (nodeId, label) => + onLabelValueChange: (itemId, label) => setProducts((prev) => { const walkTree = (item) => { - if (item.id === nodeId) { + if (item.id === itemId) { return { ...item, label }; } if (item.children) { diff --git a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx index fab56741721b0..0dbfa7e69e570 100644 --- a/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx +++ b/docs/data/tree-view/rich-tree-view/customization/LabelSlots.tsx @@ -69,20 +69,20 @@ function CustomLabel(props: CustomLabelProps) { } const TreeItemContext = React.createContext<{ - onLabelValueChange: (nodeId: string, label: string) => void; + onLabelValueChange: (itemId: string, label: string) => void; }>({ onLabelValueChange: () => {} }); const CustomTreeItem = React.forwardRef( (props: TreeItem2Props, ref: React.Ref) => { const { interactions } = useTreeItem2Utils({ - nodeId: props.nodeId, + itemId: props.itemId, children: props.children, }); const { onLabelValueChange } = React.useContext(TreeItemContext); const handleLabelValueChange = (newLabel: string) => { - onLabelValueChange(props.nodeId, newLabel); + onLabelValueChange(props.itemId, newLabel); }; const handleContentClick: UseTreeItem2ContentSlotOwnProps['onClick'] = ( @@ -142,10 +142,10 @@ export default function LabelSlots() { const context = React.useMemo( () => ({ - onLabelValueChange: (nodeId: string, label: string) => + onLabelValueChange: (itemId: string, label: string) => setProducts((prev) => { const walkTree = (item: TreeViewBaseItem): TreeViewBaseItem => { - if (item.id === nodeId) { + if (item.id === itemId) { return { ...item, label }; } if (item.children) { diff --git a/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js index 7105fc20cfb05..fbccfb57e3c21 100644 --- a/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js +++ b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.js @@ -59,7 +59,7 @@ function TreeView(inProps) { const renderNode = ({ children: itemChildren, ...itemProps }) => { return ( - + {itemChildren?.map(renderNode)} ); diff --git a/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx index 64197707f8f60..c8decec9b3ca6 100644 --- a/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx +++ b/docs/data/tree-view/rich-tree-view/headless/LogExpandedItems.tsx @@ -111,7 +111,7 @@ function TreeView( ...itemProps }: ReturnType[number]) => { return ( - + {itemChildren?.map(renderNode)} ); diff --git a/docs/data/tree-view/rich-tree-view/headless/headless.md b/docs/data/tree-view/rich-tree-view/headless/headless.md index 98eff9f4b29ce..6d920b03d2c52 100644 --- a/docs/data/tree-view/rich-tree-view/headless/headless.md +++ b/docs/data/tree-view/rich-tree-view/headless/headless.md @@ -219,7 +219,7 @@ const useCustomPlugin = ({ params }) => { }; }; -function useTreeItemState(nodeId: string) { +function useTreeItemState(itemId: string) { const { customPlugin, // ...other elements returned by the context @@ -237,7 +237,7 @@ function TreeItemContent() { const { customPlugin, // ...other elements returned by `useTreeItemState` - } = useTreeItemState(props.nodeId); + } = useTreeItemState(props.itemId); // Do something with customPlugin.enabled } diff --git a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js index 64bdeca0c9c65..10913836a21d5 100644 --- a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.js @@ -47,19 +47,19 @@ export default function BorderedTreeView() { }} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.tsx index f330fd1c9dc25..fd6a9e2a29294 100644 --- a/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/BorderedTreeView.tsx @@ -49,19 +49,19 @@ export default function BorderedTreeView() { }} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.js b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.js index c18fffa3cf981..8db5e31fc09df 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.js @@ -35,19 +35,19 @@ export default function CustomAnimation() { defaultExpandedItems={['1']} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx index 4d9ec87c58ff3..786650ea38e08 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomAnimation.tsx @@ -37,19 +37,19 @@ export default function CustomAnimation() { defaultExpandedItems={['1']} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js index 93805a66ff4d2..2076a1e87f939 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.js @@ -19,7 +19,7 @@ const CustomTreeItemContent = styled(TreeItem2Content)(({ theme }) => ({ })); const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { - const { id, nodeId, label, disabled, children, ...other } = props; + const { id, itemId, label, disabled, children, ...other } = props; const { getRootProps, @@ -28,10 +28,10 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { getLabelProps, getGroupTransitionProps, status, - } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); return ( - + @@ -65,13 +65,13 @@ export default function CustomContentTreeView() { sx={{ position: 'relative' }} defaultExpandedItems={['3']} > - - + + - - - - + + + + diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx index 4eed51b5755a5..a7e41eb533c37 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx @@ -29,7 +29,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( props: CustomTreeItemProps, ref: React.Ref, ) { - const { id, nodeId, label, disabled, children, ...other } = props; + const { id, itemId, label, disabled, children, ...other } = props; const { getRootProps, @@ -38,10 +38,10 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( getLabelProps, getGroupTransitionProps, status, - } = useTreeItem2({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem2({ id, itemId, children, label, disabled, rootRef: ref }); return ( - + @@ -75,13 +75,13 @@ export default function CustomContentTreeView() { sx={{ position: 'relative' }} defaultExpandedItems={['3']} > - - + + - - - - + + + + diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview index bcceb2d033480..8d935ef57ba73 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/customization/CustomContentTreeView.tsx.preview @@ -3,13 +3,13 @@ sx={{ position: 'relative' }} defaultExpandedItems={['3']} > - - + + - - - - + + + + \ No newline at end of file diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js index 93ca0557c4cef..3bfdaf46af80e 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.js @@ -40,19 +40,19 @@ export default function CustomIcons() { }} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.tsx index 81eb5382c0dec..f8746f6dd376f 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomIcons.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomIcons.tsx @@ -40,19 +40,19 @@ export default function CustomIcons() { }} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.js b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.js index 47825b6fd8f67..f2426eb5cbf07 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.js @@ -35,19 +35,19 @@ export default function CustomStyling() { defaultExpandedItems={['1']} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx index 68512c179f178..1f9aa0a895b67 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomStyling.tsx @@ -36,19 +36,19 @@ export default function CustomStyling() { defaultExpandedItems={['1']} sx={{ overflowX: 'hidden', minHeight: 270, flexGrow: 1, maxWidth: 300 }} > - - - - - - - - + + + + + + + + - + - - + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js index 3eb0ebf98490a..572f804db3727 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.js @@ -148,32 +148,32 @@ export default function CustomizedTreeView() { defaultSelectedItems="3" sx={{ height: 'fit-content', flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > - - - - - - + + + + + + - - + + - + - - + + - - + + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx index 62cf7aeadacf6..f28c321d880fc 100644 --- a/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/CustomizedTreeView.tsx @@ -163,32 +163,32 @@ export default function CustomizedTreeView() { defaultSelectedItems="3" sx={{ height: 'fit-content', flexGrow: 1, maxWidth: 400, overflowY: 'auto' }} > - - - - - - + + + + + + - - + + - + - - + + - - + + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js index 540f159f5df1c..078d26889f44e 100644 --- a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.js @@ -63,7 +63,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { const theme = useTheme(); const { id, - nodeId, + itemId, label, disabled, children, @@ -83,7 +83,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { getLabelProps, getGroupTransitionProps, status, - } = useTreeItem({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref }); const style = { '--tree-view-color': theme.palette.mode !== 'dark' ? color : colorForDarkMode, @@ -92,7 +92,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { }; return ( - + - - - + + + - + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx index 8116117180ba0..59274dac58397 100644 --- a/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/GmailTreeView.tsx @@ -87,7 +87,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( const theme = useTheme(); const { id, - nodeId, + itemId, label, disabled, children, @@ -107,7 +107,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( getLabelProps, getGroupTransitionProps, status, - } = useTreeItem({ id, nodeId, children, label, disabled, rootRef: ref }); + } = useTreeItem({ id, itemId, children, label, disabled, rootRef: ref }); const style = { '--tree-view-color': theme.palette.mode !== 'dark' ? color : colorForDarkMode, @@ -116,7 +116,7 @@ const CustomTreeItem = React.forwardRef(function CustomTreeItem( }; return ( - + - - - + + + - + ); } diff --git a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js index 0037ee1c64f1e..f924050a7f06d 100644 --- a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js +++ b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.js @@ -7,7 +7,7 @@ import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; const CustomTreeItem = React.forwardRef(function MyTreeItem(props, ref) { const { interactions } = useTreeItem2Utils({ - nodeId: props.nodeId, + itemId: props.itemId, children: props.children, }); @@ -36,13 +36,13 @@ export default function IconExpansionTreeView() { return ( - - + + - - - - + + + + diff --git a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx index 8e55c8a1626c7..7fda369d214d9 100644 --- a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx @@ -10,7 +10,7 @@ const CustomTreeItem = React.forwardRef(function MyTreeItem( ref: React.Ref, ) { const { interactions } = useTreeItem2Utils({ - nodeId: props.nodeId, + itemId: props.itemId, children: props.children, }); @@ -39,13 +39,13 @@ export default function IconExpansionTreeView() { return ( - - + + - - - - + + + + diff --git a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx.preview index 9c02dd229e67f..c72caa3b106d6 100644 --- a/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/customization/IconExpansionTreeView.tsx.preview @@ -1,11 +1,11 @@ - - + + - - - - + + + + \ No newline at end of file diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js index 7f59af2637850..59c40e034d249 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.js @@ -8,7 +8,7 @@ const CustomTreeItem = React.forwardRef((props, ref) => ( {...props} slotProps={{ label: { - id: `${props.nodeId}-label`, + id: `${props.itemId}-label`, }, }} /> @@ -21,14 +21,14 @@ export default function LabelSlotProps() { defaultExpandedItems={['pickers']} sx={{ overflowX: 'hidden', minHeight: 224, flexGrow: 1, maxWidth: 300 }} > - - - - + + + + - - - + + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx index 9f8a617aad0fb..3476b3b8e13ca 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx @@ -9,7 +9,7 @@ const CustomTreeItem = React.forwardRef( {...props} slotProps={{ label: { - id: `${props.nodeId}-label`, + id: `${props.itemId}-label`, }, }} /> @@ -23,14 +23,14 @@ export default function LabelSlotProps() { defaultExpandedItems={['pickers']} sx={{ overflowX: 'hidden', minHeight: 224, flexGrow: 1, maxWidth: 300 }} > - - - - + + + + - - - + + + ); diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview index 8c983099963e7..1aaad97525e67 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlotProps.tsx.preview @@ -3,13 +3,13 @@ defaultExpandedItems={['pickers']} sx={{ overflowX: 'hidden', minHeight: 224, flexGrow: 1, maxWidth: 300 }} > - - - - + + + + - - - + + + \ No newline at end of file diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js index 28d3659e4a0bd..cec9de6b0aebc 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.js @@ -31,31 +31,31 @@ export default function LabelSlots() { return ( - + - + diff --git a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx index cd688e5a96017..59882c3023c13 100644 --- a/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx +++ b/docs/data/tree-view/simple-tree-view/customization/LabelSlots.tsx @@ -46,31 +46,31 @@ export default function LabelSlots() { return ( - + - + diff --git a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.js b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.js index 44cd645380e57..a11d874a46598 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.js +++ b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.js @@ -43,20 +43,20 @@ export default function ControlledExpansion() { expandedItems={expandedItems} onExpandedItemsChange={handleExpandedItemsChange} > - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx index 6fcc35adf19b2..ec0f524d81cf1 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx +++ b/docs/data/tree-view/simple-tree-view/expansion/ControlledExpansion.tsx @@ -46,20 +46,20 @@ export default function ControlledExpansion() { expandedItems={expandedItems} onExpandedItemsChange={handleExpandedItemsChange} > - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js index b50e0cf04c880..f656970f2ebfe 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js +++ b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.js @@ -24,20 +24,20 @@ export default function TrackItemExpansionToggle() { - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx index db4da5fe2876e..6f257acad9b91 100644 --- a/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx +++ b/docs/data/tree-view/simple-tree-view/expansion/TrackItemExpansionToggle.tsx @@ -30,20 +30,20 @@ export default function TrackItemExpansionToggle() { )} - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js index e8b445182d595..9e12cc3ce0609 100644 --- a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js +++ b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.js @@ -18,20 +18,20 @@ export default function FocusedSimpleTreeView() { - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx index 810cae2363a28..bc98ac6e25a9b 100644 --- a/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/focus/FocusedSimpleTreeView.tsx @@ -18,20 +18,20 @@ export default function FocusedSimpleTreeView() { - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.js b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.js index e45b830807df9..5757cfb2964e7 100644 --- a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.js +++ b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.js @@ -7,14 +7,14 @@ export default function BasicSimpleTreeView() { return ( - - - - + + + + - - - + + + diff --git a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx index e45b830807df9..5757cfb2964e7 100644 --- a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx @@ -7,14 +7,14 @@ export default function BasicSimpleTreeView() { return ( - - - - + + + + - - - + + + diff --git a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx.preview b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx.preview index 4f449dc9b561d..3641f9443f57a 100644 --- a/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx.preview +++ b/docs/data/tree-view/simple-tree-view/items/BasicSimpleTreeView.tsx.preview @@ -1,11 +1,11 @@ - - - - + + + + - - - + + + \ No newline at end of file diff --git a/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.js b/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.js index 9277addefe8d6..773e68d490d01 100644 --- a/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.js +++ b/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.js @@ -27,24 +27,24 @@ export default function DisabledItemsFocusable() { - - - - + + + + - - - + + + - - + + - - - + + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.tsx b/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.tsx index f70a8450a3c1f..2cab7252b0a98 100644 --- a/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.tsx +++ b/docs/data/tree-view/simple-tree-view/items/DisabledItemsFocusable.tsx @@ -27,24 +27,24 @@ export default function DisabledItemsFocusable() { - - - - + + + + - - - + + + - - + + - - - + + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.js b/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.js index 19dda696cb4ff..14a73027e0ad4 100644 --- a/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.js +++ b/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.js @@ -7,24 +7,24 @@ export default function DisabledJSXItem() { return ( - - - - + + + + - - - + + + - - + + - - - + + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.tsx b/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.tsx index 19dda696cb4ff..14a73027e0ad4 100644 --- a/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.tsx +++ b/docs/data/tree-view/simple-tree-view/items/DisabledJSXItem.tsx @@ -7,24 +7,24 @@ export default function DisabledJSXItem() { return ( - - - - + + + + - - - + + + - - + + - - - + + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/items/items.md b/docs/data/tree-view/simple-tree-view/items/items.md index a8b34551b20fe..9105dc5588cc4 100644 --- a/docs/data/tree-view/simple-tree-view/items/items.md +++ b/docs/data/tree-view/simple-tree-view/items/items.md @@ -24,12 +24,12 @@ The Simple Tree View component receives its items directly as JSX children. ### Item identifier -Each Tree Item must have a unique `nodeId`. +Each Tree Item must have a unique `itemId`. This is used internally to identify the item in the various models, and to track it across updates. ```tsx - + ``` diff --git a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js index 1d47a26774c7f..12f8708449c37 100644 --- a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js +++ b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.js @@ -44,20 +44,20 @@ export default function ControlledSelection() { onSelectedItemsChange={handleSelectedItemsChange} multiSelect > - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx index 1b5d5f01692b3..b638e19eeceea 100644 --- a/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/ControlledSelection.tsx @@ -44,20 +44,20 @@ export default function ControlledSelection() { onSelectedItemsChange={handleSelectedItemsChange} multiSelect > - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/DisableSelection.js b/docs/data/tree-view/simple-tree-view/selection/DisableSelection.js index c5888258fb7b7..9db0d16ef7e6b 100644 --- a/docs/data/tree-view/simple-tree-view/selection/DisableSelection.js +++ b/docs/data/tree-view/simple-tree-view/selection/DisableSelection.js @@ -7,20 +7,20 @@ export default function DisableSelection() { return ( - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/DisableSelection.tsx b/docs/data/tree-view/simple-tree-view/selection/DisableSelection.tsx index c5888258fb7b7..9db0d16ef7e6b 100644 --- a/docs/data/tree-view/simple-tree-view/selection/DisableSelection.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/DisableSelection.tsx @@ -7,20 +7,20 @@ export default function DisableSelection() { return ( - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.js b/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.js index d960923d35020..eba9bb8cd17fe 100644 --- a/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.js +++ b/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.js @@ -7,20 +7,20 @@ export default function MultiSelectTreeView() { return ( - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.tsx b/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.tsx index d960923d35020..eba9bb8cd17fe 100644 --- a/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/MultiSelectTreeView.tsx @@ -7,20 +7,20 @@ export default function MultiSelectTreeView() { return ( - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js index ff2d73fb65c97..4bc39db3cf239 100644 --- a/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js +++ b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.js @@ -23,20 +23,20 @@ export default function TrackItemSelectionToggle() { - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx index f1fd58c9cdeca..ebd584ed1b692 100644 --- a/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx +++ b/docs/data/tree-view/simple-tree-view/selection/TrackItemSelectionToggle.tsx @@ -29,20 +29,20 @@ export default function TrackItemSelectionToggle() { - - - - + + + + - - - + + + - - + + - - + + diff --git a/docs/pages/x/api/tree-view/tree-item-2.json b/docs/pages/x/api/tree-view/tree-item-2.json index d75d891d15282..82e77380feb4c 100644 --- a/docs/pages/x/api/tree-view/tree-item-2.json +++ b/docs/pages/x/api/tree-view/tree-item-2.json @@ -1,6 +1,6 @@ { "props": { - "nodeId": { "type": { "name": "string" }, "required": true }, + "itemId": { "type": { "name": "string" }, "required": true }, "children": { "type": { "name": "node" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, "disabled": { "type": { "name": "bool" }, "default": "false" }, diff --git a/docs/pages/x/api/tree-view/tree-item.json b/docs/pages/x/api/tree-view/tree-item.json index 62886e2866d29..e51cc365b7d30 100644 --- a/docs/pages/x/api/tree-view/tree-item.json +++ b/docs/pages/x/api/tree-view/tree-item.json @@ -1,6 +1,6 @@ { "props": { - "nodeId": { "type": { "name": "string" }, "required": true }, + "itemId": { "type": { "name": "string" }, "required": true }, "children": { "type": { "name": "node" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, "ContentComponent": { diff --git a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json index 5fb4062e31c84..2278abc442beb 100644 --- a/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json +++ b/docs/translations/api-docs/tree-view/tree-item-2/tree-item-2.json @@ -3,10 +3,10 @@ "propDescriptions": { "children": { "description": "The content of the component." }, "classes": { "description": "Override or extend the styles applied to the component." }, - "disabled": { "description": "If true, the node is disabled." }, - "id": { "description": "The id attribute of the node. If not provided, it will be generated." }, - "label": { "description": "The label of the node." }, - "nodeId": { "description": "The id of the node. Must be unique." }, + "disabled": { "description": "If true, the item is disabled." }, + "id": { "description": "The id attribute of the item. If not provided, it will be generated." }, + "itemId": { "description": "The id of the item. Must be unique." }, + "label": { "description": "The label of the item." }, "onFocus": { "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a item's focus." }, diff --git a/docs/translations/api-docs/tree-view/tree-item/tree-item.json b/docs/translations/api-docs/tree-view/tree-item/tree-item.json index 4144332744dc9..24a2ba62082e6 100644 --- a/docs/translations/api-docs/tree-view/tree-item/tree-item.json +++ b/docs/translations/api-docs/tree-view/tree-item/tree-item.json @@ -9,8 +9,8 @@ }, "ContentProps": { "description": "Props applied to ContentComponent." }, "disabled": { "description": "If true, the item is disabled." }, + "itemId": { "description": "The id of the item." }, "label": { "description": "The tree item label." }, - "nodeId": { "description": "The id of the node." }, "onFocus": { "description": "This prop isn't supported. Use the onItemFocus callback on the tree if you need to monitor a item's focus." }, diff --git a/packages/x-codemod/README.md b/packages/x-codemod/README.md index f89dbad4e1f62..5894102fcca66 100644 --- a/packages/x-codemod/README.md +++ b/packages/x-codemod/README.md @@ -223,6 +223,7 @@ The list includes these transformers - [`rename-selection-props`](#rename-selection-props) - [`replace-transition-props-by-slot`](#replace-transition-props-by-slot) - [`rename-focus-callback`](#rename-focus-callback) +- [`rename-nodeid`](#rename-nodeid) #### `rename-tree-view-simple-tree-view` @@ -238,7 +239,7 @@ Renames the `TreeView` component to `SimpleTreeView` return ( - + - + - + ); @@ -253,8 +254,8 @@ Renames the `useTreeItem` hook to `useTreeItemState` +import { TreeItem, useTreeItemState } from '@mui/x-tree-view/TreeItem'; const CustomContent = React.forwardRef((props, ref) => { -- const { disabled } = useTreeItem(props.nodeId); -+ const { disabled } = useTreeItemState(props.nodeId); +- const { disabled } = useTreeItem(props.itemId); ++ const { disabled } = useTreeItemState(props.itemId); // Render some UI }); @@ -327,6 +328,16 @@ Replace the `onNodeFocus` callback with `onItemFocus`: /> ``` +#### `rename-nodeid` + +Rename nodeId to itemId + +```diff + + +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/expected.spec.js b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/expected.spec.js new file mode 100644 index 0000000000000..63624c90517bf --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/expected.spec.js @@ -0,0 +1,3 @@ + + +; diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/index.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/index.ts new file mode 100644 index 0000000000000..066e0f0e2bcfa --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/index.ts @@ -0,0 +1,18 @@ +import renameProps from '../../../util/renameProps'; +import type { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; + +export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { + const j = api.jscodeshift; + const root = j(file.source); + + const printOptions = options.printOptions; + + return renameProps({ + root, + componentNames: ['TreeView', 'SimpleTreeView'], + props: { + nodeId: 'itemId', + }, + j, + }).toSource(printOptions); +} diff --git a/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/rename-nodeid.test.ts b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/rename-nodeid.test.ts new file mode 100644 index 0000000000000..b7a26874e3bd5 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/tree-view/rename-nodeid/rename-nodeid.test.ts @@ -0,0 +1,27 @@ +import path from 'path'; +import { expect } from 'chai'; +import jscodeshift from 'jscodeshift'; +import transform from '.'; +import readFile from '../../../util/readFile'; + +function read(fileName) { + return readFile(path.join(__dirname, fileName)); +} + +describe('v7.0.0/tree-view', () => { + describe('rename-nodeid', () => { + it('transforms props as needed', () => { + const actual = transform({ source: read('./actual.spec.js') }, { jscodeshift }, {}); + + const expected = read('./expected.spec.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + + it('should be idempotent', () => { + const actual = transform({ source: read('./expected.spec.js') }, { jscodeshift }, {}); + + const expected = read('./expected.spec.js'); + expect(actual).to.equal(expected, 'The transformed version should be correct'); + }); + }); +}); diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx index b1131dec923b4..27f3df1b64cfc 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.tsx @@ -44,16 +44,16 @@ function WrappedTreeItem({ slotProps, label, id, - nodeId, + itemId, children, }: Pick, 'slots' | 'slotProps'> & - Pick & { label: string }) { + Pick & { label: string }) { const Item = slots?.item ?? TreeItem; const itemProps = useSlotProps({ elementType: Item, externalSlotProps: slotProps?.item, - additionalProps: { nodeId, id, label }, - ownerState: { nodeId, label }, + additionalProps: { itemId, id, label }, + ownerState: { itemId, label }, }); return {children}; @@ -116,7 +116,7 @@ const RichTreeView = React.forwardRef(function RichTreeView< const renderNode = ({ label, - nodeId, + itemId, id, children, }: ReturnType[number]) => { @@ -124,10 +124,10 @@ const RichTreeView = React.forwardRef(function RichTreeView< {children?.map(renderNode)} diff --git a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts index 5403d392a7fe3..2687be9a9d658 100644 --- a/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts +++ b/packages/x-tree-view/src/RichTreeView/RichTreeView.types.ts @@ -15,7 +15,7 @@ import { TreeViewItemId } from '../models'; import { TreeViewPublicAPI } from '../internals/models'; interface RichTreeViewItemSlotOwnerState { - nodeId: TreeViewItemId; + itemId: TreeViewItemId; label: string; } diff --git a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx index d60e248a558bd..69e2f4b8bccab 100644 --- a/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx +++ b/packages/x-tree-view/src/SimpleTreeView/SimpleTreeView.test.tsx @@ -25,7 +25,7 @@ describe('', () => { it('should warn when switching from controlled to uncontrolled of the expandedItems prop', () => { const { setProps } = render( - + , ); @@ -39,7 +39,7 @@ describe('', () => { it('should warn when switching from controlled to uncontrolled of the selectedItems prop', () => { const { setProps } = render( - + , ); @@ -53,8 +53,8 @@ describe('', () => { it('should not crash when shift clicking a clean tree', () => { render( - - + + , ); @@ -64,12 +64,12 @@ describe('', () => { it('should not crash when selecting multiple items in a deeply nested tree', () => { render( - - - + + + - + , ); fireEvent.click(screen.getByText('Item 1.1.1')); @@ -92,7 +92,7 @@ describe('', () => { it('should not crash when unmounting with duplicate ids', () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars function CustomTreeItem(props: any) { - return ; + return ; } function App() { const [isVisible, hideTreeView] = React.useReducer(() => false, true); @@ -104,8 +104,8 @@ describe('', () => { {isVisible && ( - - + + )} @@ -132,7 +132,7 @@ describe('', () => { const { getByRole } = render( - + , ); act(() => { @@ -146,12 +146,12 @@ describe('', () => { expect(handleKeyDown.callCount).to.equal(3); }); - it('should select node when Enter key is pressed ', () => { + it('should select item when Enter key is pressed ', () => { const handleKeyDown = spy(); const { getByRole, getByTestId } = render( - + , ); act(() => { @@ -169,7 +169,7 @@ describe('', () => { const handleFocus = spy(); const { getByRole } = render( - + , ); @@ -184,7 +184,7 @@ describe('', () => { const handleBlur = spy(); const { getByRole } = render( - + , ); @@ -206,8 +206,8 @@ describe('', () => { }; return ( - - + + ); @@ -241,8 +241,8 @@ describe('', () => { }; return ( - - + + ); } @@ -275,8 +275,8 @@ describe('', () => { onSelectedItemsChange={onSelectedItemsChange} multiSelect > - - + + ); } @@ -308,8 +308,8 @@ describe('', () => { }} id="tree" > - - + + ); @@ -347,7 +347,7 @@ describe('', () => { - {!hide && } + {!hide && } ); } @@ -363,10 +363,10 @@ describe('', () => { const { getByRole, getByTestId } = render( - - - - + + + + , ); @@ -392,7 +392,7 @@ describe('', () => { const focusSpy = spy(); const { getByRole } = render( - + , ); @@ -412,8 +412,8 @@ describe('', () => { const { getByText } = render( - - + + , ); @@ -429,8 +429,8 @@ describe('', () => { const { getByTestId } = render( -
}} nodeId="1" label="outer"> - +
}} itemId="1" label="outer"> + , ); @@ -448,8 +448,8 @@ describe('', () => { const { getByRole } = render( - - + + , ); @@ -465,8 +465,8 @@ describe('', () => { const { getByRole } = render( - - + + , ); @@ -482,10 +482,10 @@ describe('', () => { const { getByRole } = render( - - + + - + , ); @@ -501,10 +501,10 @@ describe('', () => { const { getByRole } = render( - - + + - + , ); @@ -520,10 +520,10 @@ describe('', () => { const { getByRole } = render( - - + + - + , ); @@ -534,7 +534,7 @@ describe('', () => { expect(onItemFocus.lastCall.lastArg).to.equal('1'); }); - it('should focus specific node using `apiRef`', () => { + it('should focus specific item using `apiRef`', () => { let apiRef: SimpleTreeViewApiRef; const onItemFocus = spy(); @@ -542,10 +542,10 @@ describe('', () => { apiRef = useTreeViewApiRef(); return ( - - + + - + ); } @@ -560,7 +560,7 @@ describe('', () => { expect(onItemFocus.lastCall.lastArg).to.equal('2'); }); - it('should not focus node if parent is collapsed', () => { + it('should not focus item if parent is collapsed', () => { let apiRef: SimpleTreeViewApiRef; const onItemFocus = spy(); @@ -568,10 +568,10 @@ describe('', () => { apiRef = useTreeViewApiRef(); return ( - - + + - + ); } diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index 4e0a49f2eda27..87728e407c48a 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -16,7 +16,7 @@ const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue isNodeExpanded: () => false, isNodeFocused: () => false, isNodeSelected: () => false, - isNodeDisabled: (nodeId: string | null): nodeId is string => !!nodeId, + isNodeDisabled: (itemId: string | null): itemId is string => !!itemId, getTreeItemId: () => '', mapFirstCharFromJSX: () => () => {}, } as any, @@ -39,7 +39,7 @@ const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue describe('', () => { const { render } = createRenderer(); - describeConformance(, () => ({ + describeConformance(, () => ({ classes, inheritComponent: 'li', wrapMount: (mount) => (node: React.ReactNode) => { @@ -71,7 +71,7 @@ describe('', () => { expect(() => { PropTypes.checkPropTypes( TreeItem.propTypes, - { nodeId: 'one', onFocus: () => {} }, + { itemId: 'one', onFocus: () => {} }, 'prop', 'TreeItem', ); @@ -82,7 +82,7 @@ describe('', () => { expect(() => { PropTypes.checkPropTypes( TreeItem.propTypes, - { nodeId: 'one', ContentComponent: () => {} }, + { itemId: 'one', ContentComponent: () => {} }, 'prop', 'TreeItem', ); @@ -95,7 +95,7 @@ describe('', () => { const { getByText } = render( - + , ); @@ -114,23 +114,23 @@ describe('', () => { }} defaultExpandedItems={['1']} > - - + +
}} />
}} /> - - + + , ); @@ -154,8 +154,8 @@ describe('', () => { Hide - - {!hide && } + + {!hide && } @@ -173,8 +173,8 @@ describe('', () => { it('should treat an empty array equally to no children', () => { const { getByTestId } = render( - - + + {[]} @@ -187,8 +187,8 @@ describe('', () => { it('should treat multiple empty conditional arrays as empty', () => { const { getByTestId } = render( - - + + {[].map((_, index) => ( a child ))} @@ -206,8 +206,8 @@ describe('', () => { it('should treat one conditional empty and one conditional with results as expandable', () => { const { getByTestId } = render( - - + + {[]} {[1].map((_, index) => ( a child @@ -223,8 +223,8 @@ describe('', () => { it('should handle edge case of nested array of array', () => { const { getByTestId } = render( - - + + {[[]]} @@ -239,8 +239,8 @@ describe('', () => { const { getByText } = render( - - + + , ); @@ -253,7 +253,7 @@ describe('', () => { it('should be able to use a custom id', () => { const { getByRole } = render( - + , ); @@ -268,7 +268,7 @@ describe('', () => { it('should have the role `treeitem`', () => { const { getByTestId } = render( - + , ); @@ -278,8 +278,8 @@ describe('', () => { it('should add the role `group` to a component containing children', () => { const { getByRole, getByText } = render( - - + + , ); @@ -291,8 +291,8 @@ describe('', () => { it('should have the attribute `aria-expanded=false` if collapsed', () => { const { getByTestId } = render( - - + + , ); @@ -303,8 +303,8 @@ describe('', () => { it('should have the attribute `aria-expanded={true}` if expanded', () => { const { getByTestId } = render( - - + + , ); @@ -315,7 +315,7 @@ describe('', () => { it('should not have the attribute `aria-expanded` if no children are present', () => { const { getByTestId } = render( - + , ); @@ -327,7 +327,7 @@ describe('', () => { it('should not have the attribute `aria-disabled` if disabled is false', () => { const { getByTestId } = render( - + , ); @@ -337,7 +337,7 @@ describe('', () => { it('should have the attribute `aria-disabled={true}` if disabled', () => { const { getByTestId } = render( - + , ); @@ -350,7 +350,7 @@ describe('', () => { it('should not have the attribute `aria-selected` if not selected', () => { const { getByTestId } = render( - + , ); @@ -360,7 +360,7 @@ describe('', () => { it('should have the attribute `aria-selected={true}` if selected', () => { const { getByTestId } = render( - + , ); @@ -372,7 +372,7 @@ describe('', () => { it('should have the attribute `aria-selected=false` if not selected', () => { const { getByTestId } = render( - + , ); @@ -382,7 +382,7 @@ describe('', () => { it('should have the attribute `aria-selected={true}` if selected', () => { const { getByTestId } = render( - + , ); @@ -392,7 +392,7 @@ describe('', () => { it('should have the attribute `aria-selected` if disableSelection is true', () => { const { getByTestId } = render( - + , ); @@ -405,9 +405,9 @@ describe('', () => { it('should focus the first node if none of the nodes are selected before the tree receives focus', () => { const { getByRole, getByTestId, queryAllByRole } = render( - - - + + + , ); @@ -423,9 +423,9 @@ describe('', () => { it('should focus the selected node if a node is selected before the tree receives focus', () => { const { getByTestId, getByRole } = render( - - - + + + , ); @@ -441,8 +441,8 @@ describe('', () => { it('should work with programmatic focus', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -473,9 +473,9 @@ describe('', () => { const { getByRole, getByTestId, getByText } = render( - - - + + + , ); @@ -503,8 +503,8 @@ describe('', () => { it('should focus on tree with scroll prevented', () => { const { getByRole, getByTestId } = render( - - + + , ); const focus = spy(getByRole('tree'), 'focus'); @@ -522,8 +522,8 @@ describe('', () => { it('should open the node and not move the focus if focus is on a closed node', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -542,8 +542,8 @@ describe('', () => { it('should move focus to the first child if focus is on an open item', () => { const { getByTestId, getByRole } = render( - - + + , ); @@ -561,8 +561,8 @@ describe('', () => { it('should do nothing if focus is on an end item', () => { const { getByRole, getByTestId, getByText } = render( - - + + , ); @@ -583,8 +583,8 @@ describe('', () => { it('should close the node if focus is on an open node', () => { render( - - + + , ); @@ -607,8 +607,8 @@ describe('', () => { it("should move focus to the item's parent item if focus is on a child that is an end node", () => { render( - - + + , ); @@ -632,9 +632,9 @@ describe('', () => { it("should move focus to the node's parent node if focus is on a child node that is closed", () => { render( - - - + + + , @@ -659,8 +659,8 @@ describe('', () => { it('should do nothing if focus is on a root node that is closed', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -677,7 +677,7 @@ describe('', () => { it('should do nothing if focus is on a root node that is an end node', () => { const { getByRole, getByTestId } = render( - + , ); @@ -694,8 +694,8 @@ describe('', () => { it('moves focus to a sibling node', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -710,8 +710,8 @@ describe('', () => { it('moves focus to a child item', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -741,11 +741,11 @@ describe('', () => { {!hide && ( - - + + )} - + ); @@ -770,10 +770,10 @@ describe('', () => { it("moves focus to a parent's sibling", () => { const { getByRole, getByTestId, getByText } = render( - - + + - + , ); @@ -796,8 +796,8 @@ describe('', () => { it('moves focus to a sibling node', () => { const { getByRole, getByTestId, getByText } = render( - - + + , ); @@ -816,8 +816,8 @@ describe('', () => { it('moves focus to a parent', () => { const { getByRole, getByTestId, getByText } = render( - - + + , ); @@ -839,10 +839,10 @@ describe('', () => { it("moves focus to a sibling's child", () => { const { getByRole, getByTestId, getByText } = render( - - + + - + , ); @@ -865,10 +865,10 @@ describe('', () => { it('moves focus to the first node in the tree', () => { const { getByRole, getByTestId, getByText } = render( - - - - + + + + , ); @@ -889,10 +889,10 @@ describe('', () => { it('moves focus to the last node in the tree without expanded items', () => { const { getByRole, getByTestId } = render( - - - - + + + + , ); @@ -910,12 +910,12 @@ describe('', () => { it('moves focus to the last item in the tree with expanded items', () => { const { getByRole, getByTestId } = render( - - - - - - + + + + + + , @@ -937,10 +937,10 @@ describe('', () => { it('moves focus to the next node with a name that starts with the typed character', () => { const { getByRole, getByTestId } = render( - - two} data-testid="two" /> - - + + two} data-testid="two" /> + + , ); @@ -966,10 +966,10 @@ describe('', () => { it('moves focus to the next node with the same starting character', () => { const { getByRole, getByTestId } = render( - - - - + + + + , ); @@ -995,10 +995,10 @@ describe('', () => { it('should not move focus when pressing a modifier key + letter', () => { const { getByRole, getByTestId } = render( - - - - + + + + , ); @@ -1030,9 +1030,9 @@ describe('', () => { Hide - {!hide && } - - + {!hide && } + + ); @@ -1062,18 +1062,18 @@ describe('', () => { const { getByRole, getByTestId } = render( - - + + - - + + - - - + + + - + , ); @@ -1103,8 +1103,8 @@ describe('', () => { it('expands a node with children', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -1123,8 +1123,8 @@ describe('', () => { it('collapses a node with children', () => { const { getByRole, getByTestId, getByText } = render( - - + + , ); @@ -1148,7 +1148,7 @@ describe('', () => { it('should select a node when space is pressed', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1166,7 +1166,7 @@ describe('', () => { it('should not deselect a node when space is pressed on a selected node', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1185,7 +1185,7 @@ describe('', () => { it('should not select a node when space is pressed and disableSelection', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1200,7 +1200,7 @@ describe('', () => { it('should select a node when Enter is pressed and the node is not selected', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1215,7 +1215,7 @@ describe('', () => { it('should not un-select a node when Enter is pressed and the node is selected', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1232,7 +1232,7 @@ describe('', () => { it('should select a node when click', () => { const { getByText, getByTestId } = render( - + , ); @@ -1244,7 +1244,7 @@ describe('', () => { it('should not deselect a node when clicking a selected node', () => { const { getByText, getByTestId } = render( - + , ); @@ -1256,7 +1256,7 @@ describe('', () => { it('should not select a node when click and disableSelection', () => { const { getByText, getByTestId } = render( - + , ); @@ -1272,8 +1272,8 @@ describe('', () => { specify('clicking a selected node holding ctrl should deselect the node', () => { const { getByText, getByTestId } = render( - - + + , ); @@ -1287,8 +1287,8 @@ describe('', () => { specify('clicking a selected node holding meta should deselect the node', () => { const { getByText, getByTestId } = render( - - + + , ); @@ -1304,8 +1304,8 @@ describe('', () => { it('clicking a selected node shout not deselect the node', () => { const { getByText, getByTestId } = render( - - + + , ); @@ -1320,7 +1320,7 @@ describe('', () => { it('should deselect the item when pressing space on a selected item', () => { const { getByTestId, getByRole } = render( - + , ); @@ -1339,11 +1339,11 @@ describe('', () => { specify('keyboard arrow', () => { const { getByRole, getByTestId, getByText, queryAllByRole } = render( - - - - - + + + + + , ); @@ -1392,11 +1392,11 @@ describe('', () => { specify('keyboard arrow does not select when selectionDisabled', () => { const { getByRole, getByTestId, queryAllByRole } = render( - - - - - + + + + + , ); @@ -1417,12 +1417,12 @@ describe('', () => { specify('keyboard arrow merge', () => { const { getByRole, getByTestId, getByText, queryAllByRole } = render( - - - - - - + + + + + + , ); @@ -1451,17 +1451,17 @@ describe('', () => { specify('keyboard space', () => { const { getByRole, getByTestId, getByText } = render( - - - - + + + + - - - + + + - - + + , ); const tree = getByRole('tree'); @@ -1498,17 +1498,17 @@ describe('', () => { specify('keyboard home and end', () => { const { getByRole, getByTestId } = render( - - - - + + + + - - - + + + - - + + , ); @@ -1548,17 +1548,17 @@ describe('', () => { specify('keyboard home and end do not select when selectionDisabled', () => { const { getByRole, getByText, queryAllByRole } = render( - - - - + + + + - - - + + + - - + + , ); @@ -1588,17 +1588,17 @@ describe('', () => { specify('mouse', () => { const { getByTestId, getByText } = render( - - - - + + + + - - - + + + - - + + , ); @@ -1620,11 +1620,11 @@ describe('', () => { it('mouse behavior after deselection', () => { const { getByTestId, getByText } = render( - - - - - + + + + + , ); @@ -1653,17 +1653,17 @@ describe('', () => { specify('mouse does not range select when selectionDisabled', () => { const { getByText, queryAllByRole } = render( - - - - + + + + - - - + + + - - + + , ); @@ -1677,8 +1677,8 @@ describe('', () => { specify('keyboard', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -1704,8 +1704,8 @@ describe('', () => { specify('keyboard holding ctrl', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -1731,8 +1731,8 @@ describe('', () => { specify('mouse', () => { const { getByText, getByTestId } = render( - - + + , ); @@ -1753,8 +1753,8 @@ describe('', () => { specify('mouse using ctrl', () => { const { getByTestId, getByText } = render( - - + + , ); @@ -1771,8 +1771,8 @@ describe('', () => { specify('mouse using meta', () => { const { getByTestId, getByText } = render( - - + + , ); @@ -1790,11 +1790,11 @@ describe('', () => { specify('ctrl + a selects all', () => { const { getByRole, queryAllByRole } = render( - - - - - + + + + + , ); @@ -1809,11 +1809,11 @@ describe('', () => { specify('ctrl + a does not select all when disableSelection', () => { const { getByRole, queryAllByRole } = render( - - - - - + + + + + , ); @@ -1833,7 +1833,7 @@ describe('', () => { it('should prevent selection by mouse', () => { const { getByText, getByTestId } = render( - + , ); @@ -1844,10 +1844,10 @@ describe('', () => { it('should prevent node triggering start of range selection', () => { const { getByText, getByTestId } = render( - - - - + + + + , ); @@ -1862,10 +1862,10 @@ describe('', () => { it('should prevent node being selected as part of range selection', () => { const { getByText, getByTestId } = render( - - - - + + + + , ); @@ -1880,10 +1880,10 @@ describe('', () => { it('should prevent node triggering end of range selection', () => { const { getByText, getByTestId } = render( - - - - + + + + , ); @@ -1901,7 +1901,7 @@ describe('', () => { it('should prevent selection by keyboard', () => { const { getByRole, getByTestId } = render( - + , ); @@ -1916,10 +1916,10 @@ describe('', () => { it('should not prevent next node being range selected by keyboard', () => { const { getByRole, getByTestId } = render( - - - - + + + + , ); @@ -1936,8 +1936,8 @@ describe('', () => { it('should prevent range selection by keyboard + arrow down', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -1956,9 +1956,9 @@ describe('', () => { it('should select the next non disabled node by keyboard + arrow down', () => { const { getByRole, getByTestId } = render( - - - + + + , ); @@ -1979,11 +1979,11 @@ describe('', () => { it('should prevent range selection by keyboard + space', () => { const { getByRole, getByTestId, getByText } = render( - - - - - + + + + + , ); const tree = getByRole('tree'); @@ -2007,11 +2007,11 @@ describe('', () => { it('should prevent selection by ctrl + a', () => { const { getByRole, queryAllByRole } = render( - - - - - + + + + + , ); @@ -2026,11 +2026,11 @@ describe('', () => { it('should prevent selection by keyboard end', () => { const { getByRole, getByTestId } = render( - - - - - + + + + + , ); @@ -2054,11 +2054,11 @@ describe('', () => { it('should prevent selection by keyboard home', () => { const { getByRole, getByTestId } = render( - - - - - + + + + + , ); @@ -2087,8 +2087,8 @@ describe('', () => { const focusSpy = spy(); const { getByText } = render( - - + + , ); @@ -2099,8 +2099,8 @@ describe('', () => { it('should not prevent programmatic focus', () => { const { getByTestId } = render( - - + + , ); @@ -2113,8 +2113,8 @@ describe('', () => { it('should not prevent focus by type-ahead', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -2129,8 +2129,8 @@ describe('', () => { it('should not prevent focus by arrow keys', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -2147,8 +2147,8 @@ describe('', () => { it('should be focused on tree focus', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -2165,8 +2165,8 @@ describe('', () => { const focusSpy = spy(); const { getByText } = render( - - + + , ); @@ -2177,8 +2177,8 @@ describe('', () => { it('should prevent programmatic focus', () => { const { getByTestId } = render( - - + + , ); @@ -2191,8 +2191,8 @@ describe('', () => { it('should prevent focus by type-ahead', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -2207,9 +2207,9 @@ describe('', () => { it('should be skipped on navigation with arrow keys', () => { const { getByRole, getByTestId } = render( - - - + + + , ); @@ -2226,8 +2226,8 @@ describe('', () => { it('should not be focused on tree focus', () => { const { getByRole, getByTestId } = render( - - + + , ); @@ -2245,9 +2245,9 @@ describe('', () => { it('should prevent expansion on enter', () => { const { getByRole, getByTestId } = render( - - - + + + , ); @@ -2264,9 +2264,9 @@ describe('', () => { it('should prevent expansion on right arrow', () => { const { getByRole, getByTestId } = render( - - - + + + , ); @@ -2283,9 +2283,9 @@ describe('', () => { it('should prevent collapse on left arrow', () => { const { getByRole, getByTestId } = render( - - - + + + , ); @@ -2303,8 +2303,8 @@ describe('', () => { it('should prevent expansion on click', () => { const { getByText, getByTestId } = render( - - + + , ); @@ -2320,7 +2320,7 @@ describe('', () => { const { getByText } = render( - + , ); @@ -2333,9 +2333,9 @@ describe('', () => { it('should disable child items when parent item is disabled', () => { const { getByTestId } = render( - - - + + + , ); @@ -2353,7 +2353,7 @@ describe('', () => { )); const { container } = render( - + , ); expect(container.textContent).to.equal('MOCK CONTENT COMPONENT'); @@ -2366,7 +2366,7 @@ describe('', () => { const { container } = render( @@ -2379,9 +2379,9 @@ describe('', () => { it('should be able to type in an child input', () => { const { getByRole } = render( - + @@ -2419,8 +2419,8 @@ describe('', () => { - - + + , ); diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx index 7501355e51370..46defc7f74c0e 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx @@ -171,7 +171,7 @@ export const TreeItem = React.forwardRef(function TreeItem( slotProps: inSlotProps, ContentComponent = TreeItemContent, ContentProps, - nodeId, + itemId, id, label, onClick, @@ -198,10 +198,10 @@ export const TreeItem = React.forwardRef(function TreeItem( return Boolean(reactChildren); }; const expandable = isExpandable(children); - const expanded = instance.isNodeExpanded(nodeId); - const focused = instance.isNodeFocused(nodeId); - const selected = instance.isNodeSelected(nodeId); - const disabled = instance.isNodeDisabled(nodeId); + const expanded = instance.isNodeExpanded(itemId); + const focused = instance.isNodeFocused(itemId); + const selected = instance.isNodeSelected(itemId); + const disabled = instance.isNodeDisabled(itemId); const ownerState: TreeItemOwnerState = { ...props, @@ -280,7 +280,7 @@ export const TreeItem = React.forwardRef(function TreeItem( /* single-selection trees unset aria-selected on un-selected items. * * If the tree does not support multiple selection, aria-selected - * is set to true for the selected node and it is not present on any other node in the tree. + * is set to true for the selected item and it is not present on any other item in the tree. * Source: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ */ ariaSelected = true; @@ -294,14 +294,14 @@ export const TreeItem = React.forwardRef(function TreeItem( const canBeFocused = !disabled || disabledItemsFocusable; if (!focused && canBeFocused && event.currentTarget === event.target) { - instance.focusItem(event, nodeId); + instance.focusItem(event, itemId); } } - const idAttribute = instance.getTreeItemId(nodeId, id); + const idAttribute = instance.getTreeItemId(itemId, id); return ( - + , */ label?: React.ReactNode; /** - * The id of the node. + * The id of the item. */ - nodeId: TreeViewItemId; + itemId: TreeViewItemId; /** * The system prop that allows defining system overrides as well as additional CSS styles. */ diff --git a/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx b/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx index 82619aa0a868b..467418e30e722 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItemContent.tsx @@ -19,29 +19,29 @@ export interface TreeItemContentProps extends React.HTMLAttributes focused: string; /** State class applied to the element when disabled. */ disabled: string; - /** Styles applied to the tree node icon and collapse/expand icon. */ + /** Styles applied to the tree item icon and collapse/expand icon. */ iconContainer: string; /** Styles applied to the label element. */ label: string; }; /** - * The tree node label. + * The tree item label. */ label?: React.ReactNode; /** - * The id of the node. + * The id of the item. */ - nodeId: string; + itemId: string; /** - * The icon to display next to the tree node's label. + * The icon to display next to the tree item's label. */ icon?: React.ReactNode; /** - * The icon to display next to the tree node's label. Either an expansion or collapse icon. + * The icon to display next to the tree item's label. Either an expansion or collapse icon. */ expansionIcon?: React.ReactNode; /** - * The icon to display next to the tree node's label. Either a parent or end icon. + * The icon to display next to the tree item's label. Either a parent or end icon. */ displayIcon?: React.ReactNode; } @@ -62,7 +62,7 @@ const TreeItemContent = React.forwardRef(function TreeItemContent( expansionIcon, icon: iconProp, label, - nodeId, + itemId, onClick, onMouseDown, ...other @@ -76,7 +76,7 @@ const TreeItemContent = React.forwardRef(function TreeItemContent( handleExpansion, handleSelection, preventSelection, - } = useTreeItemState(nodeId); + } = useTreeItemState(itemId); const icon = iconProp || expansionIcon || displayIcon; @@ -128,25 +128,25 @@ TreeItemContent.propTypes = { classes: PropTypes.object.isRequired, className: PropTypes.string, /** - * The icon to display next to the tree node's label. Either a parent or end icon. + * The icon to display next to the tree item's label. Either a parent or end icon. */ displayIcon: PropTypes.node, /** - * The icon to display next to the tree node's label. Either an expansion or collapse icon. + * The icon to display next to the tree item's label. Either an expansion or collapse icon. */ expansionIcon: PropTypes.node, /** - * The icon to display next to the tree node's label. + * The icon to display next to the tree item's label. */ icon: PropTypes.node, /** - * The tree node label. + * The id of the item. */ - label: PropTypes.node, + itemId: PropTypes.string.isRequired, /** - * The id of the node. + * The tree item label. */ - nodeId: PropTypes.string.isRequired, + label: PropTypes.node, } as any; export { TreeItemContent }; diff --git a/packages/x-tree-view/src/TreeItem/useTreeItemState.ts b/packages/x-tree-view/src/TreeItem/useTreeItemState.ts index ea80469e22b32..41085c0109882 100644 --- a/packages/x-tree-view/src/TreeItem/useTreeItemState.ts +++ b/packages/x-tree-view/src/TreeItem/useTreeItemState.ts @@ -2,29 +2,29 @@ import * as React from 'react'; import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; import { DefaultTreeViewPlugins } from '../internals/plugins'; -export function useTreeItemState(nodeId: string) { +export function useTreeItemState(itemId: string) { const { instance, selection: { multiSelect }, } = useTreeViewContext(); - const expandable = instance.isNodeExpandable(nodeId); - const expanded = instance.isNodeExpanded(nodeId); - const focused = instance.isNodeFocused(nodeId); - const selected = instance.isNodeSelected(nodeId); - const disabled = instance.isNodeDisabled(nodeId); + const expandable = instance.isNodeExpandable(itemId); + const expanded = instance.isNodeExpanded(itemId); + const focused = instance.isNodeFocused(itemId); + const selected = instance.isNodeSelected(itemId); + const disabled = instance.isNodeDisabled(itemId); const handleExpansion = (event: React.MouseEvent) => { if (!disabled) { if (!focused) { - instance.focusItem(event, nodeId); + instance.focusItem(event, itemId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); // If already expanded and trying to toggle selection don't close - if (expandable && !(multiple && instance.isNodeExpanded(nodeId))) { - instance.toggleNodeExpansion(event, nodeId); + if (expandable && !(multiple && instance.isNodeExpanded(itemId))) { + instance.toggleNodeExpansion(event, itemId); } } }; @@ -32,19 +32,19 @@ export function useTreeItemState(nodeId: string) { const handleSelection = (event: React.MouseEvent) => { if (!disabled) { if (!focused) { - instance.focusItem(event, nodeId); + instance.focusItem(event, itemId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); if (multiple) { if (event.shiftKey) { - instance.selectRange(event, { end: nodeId }); + instance.selectRange(event, { end: itemId }); } else { - instance.selectNode(event, nodeId, true); + instance.selectNode(event, itemId, true); } } else { - instance.selectNode(event, nodeId); + instance.selectNode(event, itemId); } } }; diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx index 45cddfc5efd6e..c0959fe8c69c8 100644 --- a/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx @@ -173,7 +173,7 @@ export const TreeItem2 = React.forwardRef(function TreeItem2( ) { const props = useThemeProps({ props: inProps, name: 'MuiTreeItem2' }); - const { id, nodeId, label, disabled, children, slots = {}, slotProps = {}, ...other } = props; + const { id, itemId, label, disabled, children, slots = {}, slotProps = {}, ...other } = props; const { getRootProps, @@ -184,7 +184,7 @@ export const TreeItem2 = React.forwardRef(function TreeItem2( status, } = useTreeItem2({ id, - nodeId, + itemId, children, label, disabled, @@ -252,7 +252,7 @@ export const TreeItem2 = React.forwardRef(function TreeItem2( }); return ( - + @@ -281,23 +281,23 @@ TreeItem2.propTypes = { classes: PropTypes.object, className: PropTypes.string, /** - * If `true`, the node is disabled. + * If `true`, the item is disabled. * @default false */ disabled: PropTypes.bool, /** - * The id attribute of the node. If not provided, it will be generated. + * The id attribute of the item. If not provided, it will be generated. */ id: PropTypes.string, /** - * The label of the node. + * The id of the item. + * Must be unique. */ - label: PropTypes.node, + itemId: PropTypes.string.isRequired, /** - * The id of the node. - * Must be unique. + * The label of the item. */ - nodeId: PropTypes.string.isRequired, + label: PropTypes.node, /** * This prop isn't supported. * Use the `onItemFocus` callback on the tree if you need to monitor a item's focus. diff --git a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx index 636641d35d75b..29e07517d822e 100644 --- a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx +++ b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.tsx @@ -3,10 +3,10 @@ import { TreeItem2ProviderProps } from './TreeItem2Provider.types'; import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext'; function TreeItem2Provider(props: TreeItem2ProviderProps) { - const { children, nodeId } = props; + const { children, itemId } = props; const { wrapItem } = useTreeViewContext<[]>(); - return wrapItem({ children, nodeId }); + return wrapItem({ children, itemId }); } TreeItem2Provider.propTypes = { @@ -15,7 +15,7 @@ TreeItem2Provider.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- children: PropTypes.node, - nodeId: PropTypes.string.isRequired, + itemId: PropTypes.string.isRequired, } as any; export { TreeItem2Provider }; diff --git a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts index 29cab33d51648..8ad31aca21978 100644 --- a/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts +++ b/packages/x-tree-view/src/TreeItem2Provider/TreeItem2Provider.types.ts @@ -3,5 +3,5 @@ import { TreeViewItemId } from '../models'; export interface TreeItem2ProviderProps { children: React.ReactNode; - nodeId: TreeViewItemId; + itemId: TreeViewItemId; } diff --git a/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx index e6db24c035627..020f82748e12b 100644 --- a/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx +++ b/packages/x-tree-view/src/hooks/useTreeItem2Utils/useTreeItem2Utils.tsx @@ -14,10 +14,10 @@ interface UseTreeItem2UtilsReturnValue { } export const useTreeItem2Utils = ({ - nodeId, + itemId, children, }: { - nodeId: string; + itemId: string; children: React.ReactNode; }): UseTreeItem2UtilsReturnValue => { const { @@ -27,10 +27,10 @@ export const useTreeItem2Utils = ({ const status: UseTreeItem2Status = { expandable: Boolean(Array.isArray(children) ? children.length : children), - expanded: instance.isNodeExpanded(nodeId), - focused: instance.isNodeFocused(nodeId), - selected: instance.isNodeSelected(nodeId), - disabled: instance.isNodeDisabled(nodeId), + expanded: instance.isNodeExpanded(itemId), + focused: instance.isNodeFocused(itemId), + selected: instance.isNodeSelected(itemId), + disabled: instance.isNodeDisabled(itemId), }; const handleExpansion = (event: React.MouseEvent) => { @@ -39,14 +39,14 @@ export const useTreeItem2Utils = ({ } if (!status.focused) { - instance.focusItem(event, nodeId); + instance.focusItem(event, itemId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); // If already expanded and trying to toggle selection don't close - if (status.expandable && !(multiple && instance.isNodeExpanded(nodeId))) { - instance.toggleNodeExpansion(event, nodeId); + if (status.expandable && !(multiple && instance.isNodeExpanded(itemId))) { + instance.toggleNodeExpansion(event, itemId); } }; @@ -56,19 +56,19 @@ export const useTreeItem2Utils = ({ } if (!status.focused) { - instance.focusItem(event, nodeId); + instance.focusItem(event, itemId); } const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey); if (multiple) { if (event.shiftKey) { - instance.selectRange(event, { end: nodeId }); + instance.selectRange(event, { end: itemId }); } else { - instance.selectNode(event, nodeId, true); + instance.selectNode(event, itemId, true); } } else { - instance.selectNode(event, nodeId); + instance.selectNode(event, itemId); } }; diff --git a/packages/x-tree-view/src/internals/models/plugin.ts b/packages/x-tree-view/src/internals/models/plugin.ts index aeacf008d090e..0587d6b19e2c6 100644 --- a/packages/x-tree-view/src/internals/models/plugin.ts +++ b/packages/x-tree-view/src/internals/models/plugin.ts @@ -144,7 +144,7 @@ export type TreeViewItemPlugin = ( ) => void | TreeViewItemPluginResponse; export type TreeItemWrapper = (params: { - nodeId: TreeViewItemId; + itemId: TreeViewItemId; children: React.ReactNode; }) => React.ReactNode; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts index 1b89304453640..a50f6ef3527c7 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.ts @@ -15,16 +15,16 @@ export const useTreeViewExpansion: TreeViewPlugin }; const isNodeExpanded = React.useCallback( - (nodeId: string) => { + (itemId: string) => { return Array.isArray(models.expandedItems.value) - ? models.expandedItems.value.indexOf(nodeId) !== -1 + ? models.expandedItems.value.indexOf(itemId) !== -1 : false; }, [models.expandedItems.value], ); const isNodeExpandable = React.useCallback( - (nodeId: string) => !!instance.getNode(nodeId)?.expandable, + (itemId: string) => !!instance.getNode(itemId)?.expandable, [instance], ); @@ -51,8 +51,8 @@ export const useTreeViewExpansion: TreeViewPlugin }, ); - const expandAllSiblings = (event: React.KeyboardEvent, nodeId: string) => { - const node = instance.getNode(nodeId); + const expandAllSiblings = (event: React.KeyboardEvent, itemId: string) => { + const node = instance.getNode(itemId); const siblings = instance.getChildrenIds(node.parentId); const diff = siblings.filter( @@ -63,8 +63,8 @@ export const useTreeViewExpansion: TreeViewPlugin if (diff.length > 0) { if (params.onItemExpansionToggle) { - diff.forEach((newlyExpandedNodeId) => { - params.onItemExpansionToggle!(event, newlyExpandedNodeId, true); + diff.forEach((newlyExpandedItemId) => { + params.onItemExpansionToggle!(event, newlyExpandedItemId, true); }); } diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts index 966b89a82bf14..54fff61870b76 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.types.ts @@ -3,10 +3,10 @@ import { DefaultizedProps, TreeViewPluginSignature } from '../../models'; import { UseTreeViewNodesSignature } from '../useTreeViewNodes'; export interface UseTreeViewExpansionInstance { - isNodeExpanded: (nodeId: string) => boolean; - isNodeExpandable: (nodeId: string) => boolean; + isNodeExpanded: (itemId: string) => boolean; + isNodeExpandable: (itemId: string) => boolean; toggleNodeExpansion: (event: React.SyntheticEvent, value: string) => void; - expandAllSiblings: (event: React.KeyboardEvent, nodeId: string) => void; + expandAllSiblings: (event: React.KeyboardEvent, itemId: string) => void; } export interface UseTreeViewExpansionParameters { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts index 0e90c9e787b3f..e0aa4229e38d2 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts @@ -17,10 +17,10 @@ export const useTreeViewFocus: TreeViewPlugin = ({ models, rootRef, }) => { - const setFocusedNodeId = useEventCallback((nodeId: React.SetStateAction) => { - const cleanNodeId = typeof nodeId === 'function' ? nodeId(state.focusedNodeId) : nodeId; - if (state.focusedNodeId !== cleanNodeId) { - setState((prevState) => ({ ...prevState, focusedNodeId: cleanNodeId })); + const setFocusedItemId = useEventCallback((itemId: React.SetStateAction) => { + const cleanItemId = typeof itemId === 'function' ? itemId(state.focusedNodeId) : itemId; + if (state.focusedNodeId !== cleanItemId) { + setState((prevState) => ({ ...prevState, focusedNodeId: cleanItemId })); } }); @@ -30,24 +30,24 @@ export const useTreeViewFocus: TreeViewPlugin = ({ ); const isNodeFocused = React.useCallback( - (nodeId: string) => state.focusedNodeId === nodeId && isTreeViewFocused(), + (itemId: string) => state.focusedNodeId === itemId && isTreeViewFocused(), [state.focusedNodeId, isTreeViewFocused], ); - const isNodeVisible = (nodeId: string) => { - const node = instance.getNode(nodeId); + const isNodeVisible = (itemId: string) => { + const node = instance.getNode(itemId); return node && (node.parentId == null || instance.isNodeExpanded(node.parentId)); }; - const focusItem = useEventCallback((event: React.SyntheticEvent, nodeId: string | null) => { - // if we receive a nodeId, and it is visible, the focus will be set to it - if (nodeId && isNodeVisible(nodeId)) { + const focusItem = useEventCallback((event: React.SyntheticEvent, itemId: string | null) => { + // if we receive an itemId, and it is visible, the focus will be set to it + if (itemId && isNodeVisible(itemId)) { if (!isTreeViewFocused()) { instance.focusRoot(); } - setFocusedNodeId(nodeId); + setFocusedItemId(itemId); if (params.onItemFocus) { - params.onItemFocus(event, nodeId); + params.onItemFocus(event, itemId); } } }); @@ -64,7 +64,7 @@ export const useTreeViewFocus: TreeViewPlugin = ({ nodeToFocusId = instance.getNavigableChildrenIds(null)[0]; } - setFocusedNodeId(nodeToFocusId); + setFocusedItemId(nodeToFocusId); if (params.onItemFocus) { params.onItemFocus(event, nodeToFocusId); } @@ -86,14 +86,14 @@ export const useTreeViewFocus: TreeViewPlugin = ({ }); useInstanceEventHandler(instance, 'removeNode', ({ id }) => { - setFocusedNodeId((oldFocusedNodeId) => { + setFocusedItemId((oldFocusedItemId) => { if ( - oldFocusedNodeId === id && + oldFocusedItemId === id && rootRef.current === ownerDocument(rootRef.current).activeElement ) { return instance.getChildrenIds(null)[0]; } - return oldFocusedNodeId; + return oldFocusedItemId; }); }); @@ -109,7 +109,7 @@ export const useTreeViewFocus: TreeViewPlugin = ({ const createHandleBlur = (otherHandlers: EventHandlers) => (event: React.FocusEvent) => { otherHandlers.onBlur?.(event); - setFocusedNodeId(null); + setFocusedItemId(null); }; const focusedNode = instance.getNode(state.focusedNodeId!); diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts index c9294f14d09db..f6013ad19ab7c 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.types.ts @@ -6,7 +6,7 @@ import type { UseTreeViewSelectionSignature } from '../useTreeViewSelection'; import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion'; export interface UseTreeViewFocusInstance { - isNodeFocused: (nodeId: string) => boolean; + isNodeFocused: (itemId: string) => boolean; focusItem: (event: React.SyntheticEvent, itemId: string | null) => void; focusDefaultNode: (event: React.SyntheticEvent) => void; focusRoot: () => void; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.ts index 71af0f6d6430d..2e97b58bd0dd4 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.ts @@ -8,7 +8,7 @@ export const useTreeViewId: TreeViewPlugin = ({ instance const treeId = useId(params.id); const getTreeItemId = React.useCallback( - (nodeId: string, idAttribute: string | undefined) => idAttribute ?? `${treeId}-${nodeId}`, + (itemId: string, idAttribute: string | undefined) => idAttribute ?? `${treeId}-${itemId}`, [treeId], ); diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.types.ts index 9d249a49da951..e7b84528bfe45 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewId/useTreeViewId.types.ts @@ -1,7 +1,7 @@ import { TreeViewPluginSignature } from '../../models'; export interface UseTreeViewIdInstance { - getTreeItemId: (nodeId: string, idAttribute: string | undefined) => string; + getTreeItemId: (itemId: string, idAttribute: string | undefined) => string; } export interface UseTreeViewIdParameters { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx index 51f8ca2acf193..7ac7809b31e26 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.tsx @@ -42,12 +42,12 @@ export const useTreeViewJSXNodes: TreeViewPlugin = }); }); - const removeJSXNode = useEventCallback((nodeId: string) => { + const removeJSXNode = useEventCallback((itemId: string) => { setState((prevState) => { const newNodeMap = { ...prevState.nodes.nodeMap }; const newItemMap = { ...prevState.nodes.itemMap }; - delete newNodeMap[nodeId]; - delete newItemMap[nodeId]; + delete newNodeMap[itemId]; + delete newItemMap[itemId]; return { ...prevState, nodes: { @@ -57,19 +57,19 @@ export const useTreeViewJSXNodes: TreeViewPlugin = }, }; }); - publishTreeViewEvent(instance, 'removeNode', { id: nodeId }); + publishTreeViewEvent(instance, 'removeNode', { id: itemId }); }); - const mapFirstCharFromJSX = useEventCallback((nodeId: string, firstChar: string) => { + const mapFirstCharFromJSX = useEventCallback((itemId: string, firstChar: string) => { instance.updateFirstCharMap((firstCharMap) => { - firstCharMap[nodeId] = firstChar; + firstCharMap[itemId] = firstChar; return firstCharMap; }); return () => { instance.updateFirstCharMap((firstCharMap) => { const newMap = { ...firstCharMap }; - delete newMap[nodeId]; + delete newMap[itemId]; return newMap; }); }; @@ -87,7 +87,7 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin { - const { children, disabled = false, label, nodeId, id } = props; + const { children, disabled = false, label, itemId, id } = props; const { instance } = useTreeViewContext<[UseTreeViewJSXNodesSignature]>(); @@ -109,9 +109,9 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin( () => ({ element: treeItemElement!, - id: nodeId, + id: itemId, }), - [nodeId, treeItemElement], + [itemId, treeItemElement], ); const { index, parentId } = useDescendant(descendant); @@ -120,7 +120,7 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin instance.removeJSXNode(nodeId); + return () => instance.removeJSXNode(itemId); } return undefined; - }, [instance, parentId, index, nodeId, expandable, disabled, id]); + }, [instance, parentId, index, itemId, expandable, disabled, id]); React.useEffect(() => { if (label) { return instance.mapFirstCharFromJSX( - nodeId, + itemId, (pluginContentRef.current?.textContent ?? '').substring(0, 1).toLowerCase(), ); } return undefined; - }, [instance, nodeId, label]); + }, [instance, itemId, label]); return { contentRef: handleContentRef, @@ -152,8 +152,8 @@ const useTreeViewJSXNodesItemPlugin: TreeViewItemPlugin ( - {children} +useTreeViewJSXNodes.wrapItem = ({ children, itemId }) => ( + {children} ); useTreeViewJSXNodes.params = {}; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.ts index e5aa62ec412b1..7567e90b10c44 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewJSXNodes/useTreeViewJSXNodes.types.ts @@ -4,8 +4,8 @@ import { UseTreeViewKeyboardNavigationSignature } from '../useTreeViewKeyboardNa export interface UseTreeViewNodesInstance { insertJSXNode: (node: TreeViewNode) => void; - removeJSXNode: (nodeId: string) => void; - mapFirstCharFromJSX: (nodeId: string, firstChar: string) => () => void; + removeJSXNode: (itemId: string) => void; + mapFirstCharFromJSX: (itemId: string, firstChar: string) => () => void; } export interface UseTreeViewNodesParameters {} diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts index 48acfb2c2e831..f5abd106a5761 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.ts @@ -50,12 +50,12 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< return; } - const newFirstCharMap: { [nodeId: string]: string } = {}; + const newFirstCharMap: { [itemId: string]: string } = {}; const processItem = (item: TreeViewBaseItem) => { const getItemId = params.getItemId; - const nodeId = getItemId ? getItemId(item) : (item as { id: string }).id; - newFirstCharMap[nodeId] = instance.getNode(nodeId).label!.substring(0, 1).toLowerCase(); + const itemId = getItemId ? getItemId(item) : (item as { id: string }).id; + newFirstCharMap[itemId] = instance.getNode(itemId).label!.substring(0, 1).toLowerCase(); item.children?.forEach(processItem); }; @@ -67,7 +67,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< updateFirstCharMap, }); - const getFirstMatchingNode = (nodeId: string, firstChar: string) => { + const getFirstMatchingItem = (itemId: string, firstChar: string) => { let start: number; let index: number; const lowercaseChar = firstChar.toLowerCase(); @@ -75,21 +75,21 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< const firstCharIds: string[] = []; const firstChars: string[] = []; // This really only works since the ids are strings - Object.keys(firstCharMap.current).forEach((mapNodeId) => { - const map = instance.getNode(mapNodeId); + Object.keys(firstCharMap.current).forEach((mapItemId) => { + const map = instance.getNode(mapItemId); const visible = map.parentId ? instance.isNodeExpanded(map.parentId) : true; const shouldBeSkipped = params.disabledItemsFocusable ? false - : instance.isNodeDisabled(mapNodeId); + : instance.isNodeDisabled(mapItemId); if (visible && !shouldBeSkipped) { - firstCharIds.push(mapNodeId); - firstChars.push(firstCharMap.current[mapNodeId]); + firstCharIds.push(mapItemId); + firstChars.push(firstCharMap.current[mapItemId]); } }); // Get start index for search based on position of currentItem - start = firstCharIds.indexOf(nodeId) + 1; + start = firstCharIds.indexOf(itemId) + 1; if (start >= firstCharIds.length) { start = 0; } @@ -110,11 +110,11 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< return null; }; - const canToggleNodeSelection = (nodeId: string) => - !params.disableSelection && !instance.isNodeDisabled(nodeId); + const canToggleItemSelection = (itemId: string) => + !params.disableSelection && !instance.isNodeDisabled(itemId); - const canToggleNodeExpansion = (nodeId: string) => { - return !instance.isNodeDisabled(nodeId) && instance.isNodeExpandable(nodeId); + const canToggleItemExpansion = (itemId: string) => { + return !instance.isNodeDisabled(itemId) && instance.isNodeExpandable(itemId); }; // ARIA specification: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/#keyboardinteraction @@ -138,7 +138,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // eslint-disable-next-line default-case switch (true) { // Select the node when pressing "Space" - case key === ' ' && canToggleNodeSelection(state.focusedNodeId): { + case key === ' ' && canToggleItemSelection(state.focusedNodeId): { event.preventDefault(); if (params.multiSelect && event.shiftKey) { instance.selectRange(event, { end: state.focusedNodeId }); @@ -153,10 +153,10 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // If the focused node has children, we expand it. // If the focused node has no children, we select it. case key === 'Enter': { - if (canToggleNodeExpansion(state.focusedNodeId)) { + if (canToggleItemExpansion(state.focusedNodeId)) { instance.toggleNodeExpansion(event, state.focusedNodeId); event.preventDefault(); - } else if (canToggleNodeSelection(state.focusedNodeId)) { + } else if (canToggleItemSelection(state.focusedNodeId)) { if (params.multiSelect) { event.preventDefault(); instance.selectNode(event, state.focusedNodeId, true); @@ -169,20 +169,20 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // Focus the next focusable node + // Focus the next focusable item case key === 'ArrowDown': { - const nextNode = getNextNode(instance, state.focusedNodeId); - if (nextNode) { + const nextItem = getNextNode(instance, state.focusedNodeId); + if (nextItem) { event.preventDefault(); - instance.focusItem(event, nextNode); + instance.focusItem(event, nextItem); // Multi select behavior when pressing Shift + ArrowDown - // Toggles the selection state of the next node - if (params.multiSelect && event.shiftKey && canToggleNodeSelection(nextNode)) { + // Toggles the selection state of the next item + if (params.multiSelect && event.shiftKey && canToggleItemSelection(nextItem)) { instance.selectRange( event, { - end: nextNode, + end: nextItem, current: state.focusedNodeId, }, true, @@ -193,20 +193,20 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // Focuses the previous focusable node + // Focuses the previous focusable item case key === 'ArrowUp': { - const previousNode = getPreviousNode(instance, state.focusedNodeId); - if (previousNode) { + const previousItem = getPreviousNode(instance, state.focusedNodeId); + if (previousItem) { event.preventDefault(); - instance.focusItem(event, previousNode); + instance.focusItem(event, previousItem); // Multi select behavior when pressing Shift + ArrowUp - // Toggles the selection state of the previous node - if (params.multiSelect && event.shiftKey && canToggleNodeSelection(previousNode)) { + // Toggles the selection state of the previous item + if (params.multiSelect && event.shiftKey && canToggleItemSelection(previousItem)) { instance.selectRange( event, { - end: previousNode, + end: previousItem, current: state.focusedNodeId, }, true, @@ -217,13 +217,13 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // If the focused node is expanded, we move the focus to its first child - // If the focused node is collapsed and has children, we expand it + // If the focused item is expanded, we move the focus to its first child + // If the focused item is collapsed and has children, we expand it case (key === 'ArrowRight' && !isRTL) || (key === 'ArrowLeft' && isRTL): { if (instance.isNodeExpanded(state.focusedNodeId)) { instance.focusItem(event, getNextNode(instance, state.focusedNodeId)); event.preventDefault(); - } else if (canToggleNodeExpansion(state.focusedNodeId)) { + } else if (canToggleItemExpansion(state.focusedNodeId)) { instance.toggleNodeExpansion(event, state.focusedNodeId); event.preventDefault(); } @@ -231,11 +231,11 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // If the focused node is expanded, we collapse it - // If the focused node is collapsed and has a parent, we move the focus to this parent + // If the focused item is expanded, we collapse it + // If the focused item is collapsed and has a parent, we move the focus to this parent case (key === 'ArrowLeft' && !isRTL) || (key === 'ArrowRight' && isRTL): { if ( - canToggleNodeExpansion(state.focusedNodeId) && + canToggleItemExpansion(state.focusedNodeId) && instance.isNodeExpanded(state.focusedNodeId) ) { instance.toggleNodeExpansion(event, state.focusedNodeId!); @@ -258,7 +258,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Multi select behavior when pressing Ctrl + Shift + Home // Selects the focused node and all nodes up to the first node. if ( - canToggleNodeSelection(state.focusedNodeId) && + canToggleItemSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey @@ -270,14 +270,14 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // Focuses the last node in the tree + // Focuses the last item in the tree case key === 'End': { instance.focusItem(event, getLastNode(instance)); // Multi select behavior when pressing Ctrl + Shirt + End - // Selects the focused node and all the nodes down to the last node. + // Selects the focused item and all the items down to the last item. if ( - canToggleNodeSelection(state.focusedNodeId) && + canToggleItemSelection(state.focusedNodeId) && params.multiSelect && ctrlPressed && event.shiftKey @@ -289,7 +289,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< break; } - // Expand all siblings that are at the same level as the focused node + // Expand all siblings that are at the same level as the focused item case key === '*': { instance.expandAllSiblings(event, state.focusedNodeId); event.preventDefault(); @@ -310,7 +310,7 @@ export const useTreeViewKeyboardNavigation: TreeViewPlugin< // Type-ahead // TODO: Support typing multiple characters case !ctrlPressed && !event.shiftKey && isPrintableCharacter(key): { - const matchingNode = getFirstMatchingNode(state.focusedNodeId, key); + const matchingNode = getFirstMatchingItem(state.focusedNodeId, key); if (matchingNode != null) { instance.focusItem(event, matchingNode); event.preventDefault(); diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.ts index a3502830bdcb9..9727caa58e113 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.types.ts @@ -18,4 +18,4 @@ export type UseTreeViewKeyboardNavigationSignature = TreeViewPluginSignature<{ ]; }>; -export type TreeViewFirstCharMap = { [nodeId: string]: string }; +export type TreeViewFirstCharMap = { [itemId: string]: string }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.test.tsx index af8da91e18ef4..28f3e2b1f398f 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.test.tsx +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.test.tsx @@ -46,8 +46,8 @@ describe('useTreeViewNodes', () => { render( - - + + , ), diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts index 54ca801486d61..15fa2ab2665b9 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.ts @@ -6,7 +6,7 @@ import { UseTreeViewNodesSignature, UseTreeViewNodesDefaultizedParameters, TreeViewNodeMap, - TreeViewNodeIdAndChildren, + TreeViewItemIdAndChildren, UseTreeViewNodesState, TreeViewItemMap, } from './useTreeViewNodes.types'; @@ -29,7 +29,7 @@ const updateNodesState = ({ item: TreeViewBaseItem, index: number, parentId: string | null, - ): TreeViewNodeIdAndChildren => { + ): TreeViewItemIdAndChildren => { const id: string = getItemId ? getItemId(item) : (item as any).id; if (id == null) { @@ -100,35 +100,35 @@ export const useTreeViewNodes: TreeViewPlugin = ({ setState, }) => { const getNode = React.useCallback( - (nodeId: string) => state.nodes.nodeMap[nodeId], + (itemId: string) => state.nodes.nodeMap[itemId], [state.nodes.nodeMap], ); const getItem = React.useCallback( - (nodeId: string) => state.nodes.itemMap[nodeId], + (itemId: string) => state.nodes.itemMap[itemId], [state.nodes.itemMap], ); const isNodeDisabled = React.useCallback( - (nodeId: string | null): nodeId is string => { - if (nodeId == null) { + (itemId: string | null): itemId is string => { + if (itemId == null) { return false; } - let node = instance.getNode(nodeId); + let item = instance.getNode(itemId); - // This can be called before the node has been added to the node map. - if (!node) { + // This can be called before the item has been added to the node map. + if (!item) { return false; } - if (node.disabled) { + if (item.disabled) { return true; } - while (node.parentId != null) { - node = instance.getNode(node.parentId); - if (node.disabled) { + while (item.parentId != null) { + item = instance.getNode(item.parentId); + if (item.disabled) { return true; } } @@ -138,18 +138,18 @@ export const useTreeViewNodes: TreeViewPlugin = ({ [instance], ); - const getChildrenIds = useEventCallback((nodeId: string | null) => + const getChildrenIds = useEventCallback((itemId: string | null) => Object.values(state.nodes.nodeMap) - .filter((node) => node.parentId === nodeId) + .filter((item) => item.parentId === itemId) .sort((a, b) => a.index - b.index) .map((child) => child.id), ); - const getNavigableChildrenIds = (nodeId: string | null) => { - let childrenIds = instance.getChildrenIds(nodeId); + const getNavigableChildrenIds = (itemId: string | null) => { + let childrenIds = instance.getChildrenIds(itemId); if (!params.disabledItemsFocusable) { - childrenIds = childrenIds.filter((node) => !instance.isNodeDisabled(node)); + childrenIds = childrenIds.filter((item) => !instance.isNodeDisabled(item)); } return childrenIds; }; @@ -181,20 +181,20 @@ export const useTreeViewNodes: TreeViewPlugin = ({ ]); const getNodesToRender = () => { - const getPropsFromNodeId = ({ + const getPropsFromItemId = ({ id, children, - }: TreeViewNodeIdAndChildren): ReturnType[number] => { + }: TreeViewItemIdAndChildren): ReturnType[number] => { const node = state.nodes.nodeMap[id]; return { label: node.label!, - nodeId: node.id, + itemId: node.id, id: node.idAttribute, - children: children?.map(getPropsFromNodeId), + children: children?.map(getPropsFromItemId), }; }; - return state.nodes.nodeTree.map(getPropsFromNodeId); + return state.nodes.nodeTree.map(getPropsFromItemId); }; populateInstance(instance, { diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.ts index 11fddd41fe577..55a5bc1c52c95 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewNodes/useTreeViewNodes.types.ts @@ -3,18 +3,18 @@ import { TreeViewItemId } from '../../../models'; interface TreeViewNodeProps { label: string; - nodeId: string; + itemId: string; id: string | undefined; children?: TreeViewNodeProps[]; } export interface UseTreeViewNodesInstance { - getNode: (nodeId: string) => TreeViewNode; - getItem: (nodeId: string) => R; + getNode: (itemId: string) => TreeViewNode; + getItem: (itemId: string) => R; getNodesToRender: () => TreeViewNodeProps[]; - getChildrenIds: (nodeId: string | null) => string[]; - getNavigableChildrenIds: (nodeId: string | null) => string[]; - isNodeDisabled: (nodeId: string | null) => nodeId is string; + getChildrenIds: (itemId: string | null) => string[]; + getNavigableChildrenIds: (itemId: string | null) => string[]; + isNodeDisabled: (itemId: string | null) => itemId is string; } export interface UseTreeViewNodesPublicAPI @@ -65,14 +65,14 @@ interface UseTreeViewNodesEventLookup { }; } -export interface TreeViewNodeIdAndChildren { +export interface TreeViewItemIdAndChildren { id: TreeViewItemId; - children?: TreeViewNodeIdAndChildren[]; + children?: TreeViewItemIdAndChildren[]; } export interface UseTreeViewNodesState { nodes: { - nodeTree: TreeViewNodeIdAndChildren[]; + nodeTree: TreeViewItemIdAndChildren[]; nodeMap: TreeViewNodeMap; itemMap: TreeViewItemMap; }; @@ -91,6 +91,6 @@ export type UseTreeViewNodesSignature = TreeViewPluginSignature<{ contextValue: UseTreeViewNodesContextValue; }>; -export type TreeViewNodeMap = { [nodeId: string]: TreeViewNode }; +export type TreeViewNodeMap = { [itemId: string]: TreeViewNode }; -export type TreeViewItemMap = { [nodeId: string]: R }; +export type TreeViewItemMap = { [itemId: string]: R }; diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts index 1d818fdf74eac..af9da8dffe5c7 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.ts @@ -55,12 +55,12 @@ export const useTreeViewSelection: TreeViewPlugin models.selectedItems.setControlledValue(newSelectedItems); }; - const isNodeSelected = (nodeId: string) => + const isNodeSelected = (itemId: string) => Array.isArray(models.selectedItems.value) - ? models.selectedItems.value.indexOf(nodeId) !== -1 - : models.selectedItems.value === nodeId; + ? models.selectedItems.value.indexOf(itemId) !== -1 + : models.selectedItems.value === itemId; - const selectNode = (event: React.SyntheticEvent, nodeId: string, multiple = false) => { + const selectNode = (event: React.SyntheticEvent, itemId: string, multiple = false) => { if (params.disableSelection) { return; } @@ -68,19 +68,19 @@ export const useTreeViewSelection: TreeViewPlugin if (multiple) { if (Array.isArray(models.selectedItems.value)) { let newSelected: string[]; - if (models.selectedItems.value.indexOf(nodeId) !== -1) { - newSelected = models.selectedItems.value.filter((id) => id !== nodeId); + if (models.selectedItems.value.indexOf(itemId) !== -1) { + newSelected = models.selectedItems.value.filter((id) => id !== itemId); } else { - newSelected = [nodeId].concat(models.selectedItems.value); + newSelected = [itemId].concat(models.selectedItems.value); } setSelectedItems(event, newSelected); } } else { - const newSelected = params.multiSelect ? [nodeId] : nodeId; + const newSelected = params.multiSelect ? [itemId] : itemId; setSelectedItems(event, newSelected); } - lastSelectedNode.current = nodeId; + lastSelectedNode.current = itemId; lastSelectionWasRange.current = false; currentRangeSelection.current = []; }; @@ -161,12 +161,12 @@ export const useTreeViewSelection: TreeViewPlugin lastSelectionWasRange.current = true; }; - const rangeSelectToFirst = (event: React.KeyboardEvent, nodeId: string) => { + const rangeSelectToFirst = (event: React.KeyboardEvent, itemId: string) => { if (!lastSelectedNode.current) { - lastSelectedNode.current = nodeId; + lastSelectedNode.current = itemId; } - const start = lastSelectionWasRange.current ? lastSelectedNode.current : nodeId; + const start = lastSelectionWasRange.current ? lastSelectedNode.current : itemId; instance.selectRange(event, { start, @@ -174,12 +174,12 @@ export const useTreeViewSelection: TreeViewPlugin }); }; - const rangeSelectToLast = (event: React.KeyboardEvent, nodeId: string) => { + const rangeSelectToLast = (event: React.KeyboardEvent, itemId: string) => { if (!lastSelectedNode.current) { - lastSelectedNode.current = nodeId; + lastSelectedNode.current = itemId; } - const start = lastSelectionWasRange.current ? lastSelectedNode.current : nodeId; + const start = lastSelectionWasRange.current ? lastSelectedNode.current : itemId; instance.selectRange(event, { start, diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts index 5ad17089bd114..e14cf7c8666d6 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.ts @@ -4,11 +4,11 @@ import { UseTreeViewNodesSignature } from '../useTreeViewNodes'; import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion'; export interface UseTreeViewSelectionInstance { - isNodeSelected: (nodeId: string) => boolean; - selectNode: (event: React.SyntheticEvent, nodeId: string, multiple?: boolean) => void; + isNodeSelected: (itemId: string) => boolean; + selectNode: (event: React.SyntheticEvent, itemId: string, multiple?: boolean) => void; selectRange: (event: React.SyntheticEvent, nodes: TreeViewItemRange, stacked?: boolean) => void; - rangeSelectToFirst: (event: React.KeyboardEvent, nodeId: string) => void; - rangeSelectToLast: (event: React.KeyboardEvent, nodeId: string) => void; + rangeSelectToFirst: (event: React.KeyboardEvent, itemId: string) => void; + rangeSelectToLast: (event: React.KeyboardEvent, itemId: string) => void; } type TreeViewSelectionValue = Multiple extends true diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts index 0fd10e4d56879..b8e847672800c 100644 --- a/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts +++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.ts @@ -141,10 +141,10 @@ export const useTreeView = plugin.wrapItem) .filter((wrapItem): wrapItem is TreeItemWrapper => !!wrapItem); - contextValue.wrapItem = ({ nodeId, children }) => { + contextValue.wrapItem = ({ itemId, children }) => { let finalChildren: React.ReactNode = children; itemWrappers.forEach((itemWrapper) => { - finalChildren = itemWrapper({ nodeId, children: finalChildren }); + finalChildren = itemWrapper({ itemId, children: finalChildren }); }); return finalChildren; diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.utils.ts b/packages/x-tree-view/src/internals/useTreeView/useTreeView.utils.ts index a960b0e3e6403..6e374cb568bb5 100644 --- a/packages/x-tree-view/src/internals/useTreeView/useTreeView.utils.ts +++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.utils.ts @@ -9,48 +9,48 @@ import type { UseTreeViewNodesSignature } from '../plugins/useTreeViewNodes'; export const getPreviousNode = ( instance: TreeViewInstance<[UseTreeViewNodesSignature, UseTreeViewExpansionSignature]>, - nodeId: string, + itemId: string, ) => { - const node = instance.getNode(nodeId); - const siblings = instance.getNavigableChildrenIds(node.parentId); - const nodeIndex = siblings.indexOf(nodeId); + const item = instance.getNode(itemId); + const siblings = instance.getNavigableChildrenIds(item.parentId); + const itemIndex = siblings.indexOf(itemId); - if (nodeIndex === 0) { - return node.parentId; + if (itemIndex === 0) { + return item.parentId; } - let currentNode: string = siblings[nodeIndex - 1]; + let currentItem: string = siblings[itemIndex - 1]; while ( - instance.isNodeExpanded(currentNode) && - instance.getNavigableChildrenIds(currentNode).length > 0 + instance.isNodeExpanded(currentItem) && + instance.getNavigableChildrenIds(currentItem).length > 0 ) { - currentNode = instance.getNavigableChildrenIds(currentNode).pop()!; + currentItem = instance.getNavigableChildrenIds(currentItem).pop()!; } - return currentNode; + return currentItem; }; export const getNextNode = ( instance: TreeViewInstance<[UseTreeViewExpansionSignature, UseTreeViewNodesSignature]>, - nodeId: string, + itemId: string, ) => { // If expanded get first child - if (instance.isNodeExpanded(nodeId) && instance.getNavigableChildrenIds(nodeId).length > 0) { - return instance.getNavigableChildrenIds(nodeId)[0]; + if (instance.isNodeExpanded(itemId) && instance.getNavigableChildrenIds(itemId).length > 0) { + return instance.getNavigableChildrenIds(itemId)[0]; } - let node = instance.getNode(nodeId); - while (node != null) { + let item = instance.getNode(itemId); + while (item != null) { // Try to get next sibling - const siblings = instance.getNavigableChildrenIds(node.parentId); - const nextSibling = siblings[siblings.indexOf(node.id) + 1]; + const siblings = instance.getNavigableChildrenIds(item.parentId); + const nextSibling = siblings[siblings.indexOf(item.id) + 1]; if (nextSibling) { return nextSibling; } // If the sibling does not exist, go up a level to the parent and try again. - node = instance.getNode(node.parentId!); + item = instance.getNode(item.parentId!); } return null; @@ -59,12 +59,12 @@ export const getNextNode = ( export const getLastNode = ( instance: TreeViewInstance<[UseTreeViewExpansionSignature, UseTreeViewNodesSignature]>, ) => { - let lastNode = instance.getNavigableChildrenIds(null).pop()!; + let lastItem = instance.getNavigableChildrenIds(null).pop()!; - while (instance.isNodeExpanded(lastNode)) { - lastNode = instance.getNavigableChildrenIds(lastNode).pop()!; + while (instance.isNodeExpanded(lastItem)) { + lastItem = instance.getNavigableChildrenIds(lastItem).pop()!; } - return lastNode; + return lastItem; }; export const getFirstNode = (instance: TreeViewInstance<[UseTreeViewNodesSignature]>) => diff --git a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts index e7f29833807e6..e90c0a9860797 100644 --- a/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts +++ b/packages/x-tree-view/src/themeAugmentation/themeAugmentation.spec.ts @@ -65,7 +65,7 @@ createTheme({ }, MuiTreeItem: { defaultProps: { - nodeId: '1', + itemId: '1', // @ts-expect-error invalid MuiTreeItem prop someRandomProp: true, }, @@ -84,7 +84,7 @@ createTheme({ }, MuiTreeItem2: { defaultProps: { - nodeId: '1', + itemId: '1', // @ts-expect-error invalid MuiTreeItem2 prop someRandomProp: true, }, diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts index 09e5d548d739e..a23ecf339d03e 100644 --- a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts +++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.ts @@ -26,11 +26,11 @@ export const useTreeItem2 = (); - const { id, nodeId, label, children, rootRef } = parameters; + const { id, itemId, label, children, rootRef } = parameters; const { rootRef: pluginRootRef, contentRef } = runItemPlugins(parameters); - const { interactions, status } = useTreeItem2Utils({ nodeId, children }); - const idAttribute = instance.getTreeItemId(nodeId, id); + const { interactions, status } = useTreeItem2Utils({ itemId, children }); + const idAttribute = instance.getTreeItemId(itemId, id); const handleRootRef = useForkRef(rootRef, pluginRootRef)!; const createRootHandleFocus = @@ -48,7 +48,7 @@ export const useTreeItem2 = ; From 150d3cea88cbebc2833b7a0670e60070befca3ab Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Fri, 15 Mar 2024 14:35:00 +0100 Subject: [PATCH 42/49] [docs] Fix nested cells alignment in the popular features demo (#12450) --- docs/data/data-grid/demo/PopularFeaturesDemo.js | 3 ++- docs/data/data-grid/demo/PopularFeaturesDemo.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.js b/docs/data/data-grid/demo/PopularFeaturesDemo.js index 3da89e7e926b2..d0ed4e20b0753 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.js +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.js @@ -521,7 +521,8 @@ export default function PopularFeaturesDemo() { }} sx={{ fontFamily: 'IBM Plex Sans', - [`& .${gridClasses.cell}`]: { + // Do not target cells in nested grids + [`& > div > div > div > div > div > .${gridClasses.cell}`]: { py: 1.5, }, [`& .${gridClasses.columnHeaderTitle}`]: { diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx index 89912c4f12c4b..907662d1d38a5 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx @@ -542,7 +542,8 @@ export default function PopularFeaturesDemo() { }} sx={{ fontFamily: 'IBM Plex Sans', - [`& .${gridClasses.cell}`]: { + // Do not target cells in nested grids + [`& > div > div > div > div > div > .${gridClasses.cell}`]: { py: 1.5, }, [`& .${gridClasses.columnHeaderTitle}`]: { From 6549091440f6d13a8c48d6a89d3bf9fe5a77bfa0 Mon Sep 17 00:00:00 2001 From: Jan Potoms <2109932+Janpot@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:23:28 +0100 Subject: [PATCH 43/49] [charts] Fix small typo in `CartesianContextProvider` (#12461) Signed-off-by: Jan Potoms <2109932+Janpot@users.noreply.github.com> --- packages/x-charts/src/context/CartesianContextProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx index f225db07b11e3..9a9cb62b88524 100644 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ b/packages/x-charts/src/context/CartesianContextProvider.tsx @@ -179,7 +179,7 @@ function CartesianContextProvider(props: CartesianContextProviderProps) { }; const allXAxis: AxisConfig[] = [ - ...(xAxis?.map((axis, index) => ({ id: `deaultized-x-axis-${index}`, ...axis })) ?? []), + ...(xAxis?.map((axis, index) => ({ id: `defaultized-x-axis-${index}`, ...axis })) ?? []), // Allows to specify an axis with id=DEFAULT_X_AXIS_KEY ...(xAxis === undefined || xAxis.findIndex(({ id }) => id === DEFAULT_X_AXIS_KEY) === -1 ? [{ id: DEFAULT_X_AXIS_KEY, scaleType: 'linear' } as AxisConfig] @@ -238,7 +238,7 @@ function CartesianContextProvider(props: CartesianContextProviderProps) { }); const allYAxis: AxisConfig[] = [ - ...(yAxis?.map((axis, index) => ({ id: `deaultized-y-axis-${index}`, ...axis })) ?? []), + ...(yAxis?.map((axis, index) => ({ id: `defaultized-y-axis-${index}`, ...axis })) ?? []), ...(yAxis === undefined || yAxis.findIndex(({ id }) => id === DEFAULT_Y_AXIS_KEY) === -1 ? [{ id: DEFAULT_Y_AXIS_KEY, scaleType: 'linear' } as AxisConfig] : []), From 300331419eaaab62139a08113334b0345a2d28f7 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Fri, 15 Mar 2024 22:49:44 +0100 Subject: [PATCH 44/49] [DataGridPremium] Add support for confirmation before clipboard paste (#12225) --- .../clipboard/ClipboardPasteEvents.js | 71 ++++++++++++++++++ .../clipboard/ClipboardPasteEvents.tsx | 72 +++++++++++++++++++ .../ClipboardPasteEvents.tsx.preview | 10 --- docs/data/data-grid/clipboard/clipboard.md | 17 ++++- .../x/api/data-grid/data-grid-premium.json | 4 ++ .../data-grid-premium/data-grid-premium.json | 4 ++ .../src/DataGridPremium/DataGridPremium.tsx | 8 +++ .../clipboard/useGridClipboardImport.ts | 20 +++++- .../src/models/dataGridPremiumProps.ts | 8 +++ 9 files changed, 201 insertions(+), 13 deletions(-) delete mode 100644 docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx.preview diff --git a/docs/data/data-grid/clipboard/ClipboardPasteEvents.js b/docs/data/data-grid/clipboard/ClipboardPasteEvents.js index 299a2ea33c05a..a755c662bc739 100644 --- a/docs/data/data-grid/clipboard/ClipboardPasteEvents.js +++ b/docs/data/data-grid/clipboard/ClipboardPasteEvents.js @@ -1,6 +1,12 @@ import * as React from 'react'; import { DataGridPremium } from '@mui/x-data-grid-premium'; import { useDemoData } from '@mui/x-data-grid-generator'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; export default function ClipboardPasteEvents() { const { data } = useDemoData({ @@ -28,6 +34,19 @@ export default function ClipboardPasteEvents() { }, }; + const confirm = useConfirm(); + const confirmPaste = React.useCallback(() => { + return new Promise((resolve, reject) => { + confirm.open((confirmed) => { + if (confirmed) { + resolve(); + } else { + reject(); + } + }); + }); + }, [confirm]); + return (
setLoading(true)} onClipboardPasteEnd={() => setLoading(false)} ignoreValueFormatterDuringExport + disableRowSelectionOnClick /> + + + {'Are you sure you want to paste?'} + + + + This will overwrite the selected cells. + + + + + + +
); } + +const useConfirm = () => { + const [isOpen, setIsOpen] = React.useState(false); + const callbackRef = React.useRef(null); + + const open = React.useCallback((callback) => { + setIsOpen(true); + callbackRef.current = callback; + }, []); + + const cancel = React.useCallback(() => { + setIsOpen(false); + callbackRef.current?.(false); + callbackRef.current = null; + }, []); + + const confirm = React.useCallback(() => { + setIsOpen(false); + callbackRef.current?.(true); + callbackRef.current = null; + }, []); + + return { + open, + isOpen, + cancel, + confirm, + }; +}; diff --git a/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx b/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx index 0139a1fc04293..3c3b16913c61c 100644 --- a/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx +++ b/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx @@ -1,6 +1,12 @@ import * as React from 'react'; import { DataGridPremium, DataGridPremiumProps } from '@mui/x-data-grid-premium'; import { useDemoData } from '@mui/x-data-grid-generator'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; +import Button from '@mui/material/Button'; export default function ClipboardPasteEvents() { const { data } = useDemoData({ @@ -30,6 +36,19 @@ export default function ClipboardPasteEvents() { }, }; + const confirm = useConfirm(); + const confirmPaste = React.useCallback<() => Promise>(() => { + return new Promise((resolve, reject) => { + confirm.open((confirmed) => { + if (confirmed) { + resolve(); + } else { + reject(); + } + }); + }); + }, [confirm]); + return (
setLoading(true)} onClipboardPasteEnd={() => setLoading(false)} ignoreValueFormatterDuringExport + disableRowSelectionOnClick /> + + + + {'Are you sure you want to paste?'} + + + + This will overwrite the selected cells. + + + + + + +
); } + +const useConfirm = () => { + const [isOpen, setIsOpen] = React.useState(false); + const callbackRef = React.useRef<((confirmed: boolean) => void) | null>(null); + + const open = React.useCallback((callback: (confirmed: boolean) => void) => { + setIsOpen(true); + callbackRef.current = callback; + }, []); + + const cancel = React.useCallback(() => { + setIsOpen(false); + callbackRef.current?.(false); + callbackRef.current = null; + }, []); + + const confirm = React.useCallback(() => { + setIsOpen(false); + callbackRef.current?.(true); + callbackRef.current = null; + }, []); + + return { + open, + isOpen, + cancel, + confirm, + }; +}; diff --git a/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx.preview b/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx.preview deleted file mode 100644 index 0c6f364b0155b..0000000000000 --- a/docs/data/data-grid/clipboard/ClipboardPasteEvents.tsx.preview +++ /dev/null @@ -1,10 +0,0 @@ - setLoading(true)} - onClipboardPasteEnd={() => setLoading(false)} - ignoreValueFormatterDuringExport -/> \ No newline at end of file diff --git a/docs/data/data-grid/clipboard/clipboard.md b/docs/data/data-grid/clipboard/clipboard.md index c9a922c10b10a..78f953a889869 100644 --- a/docs/data/data-grid/clipboard/clipboard.md +++ b/docs/data/data-grid/clipboard/clipboard.md @@ -87,7 +87,22 @@ For convenience, you can also listen to these events using their respective prop - `onClipboardPasteStart` - `onClipboardPasteEnd` -The demo below shows how to use these events to display a loading indicator while the clipboard paste operation is in progress: +Additionally, there is the `onBeforeClipboardPasteStart` prop, which is called before the clipboard paste operation starts +and can be used to cancel or confirm the paste operation: + +```tsx +const onBeforeClipboardPasteStart = async () => { + const confirmed = window.confirm('Are you sure you want to paste?'); + if (!confirmed) { + throw new Error('Paste operation cancelled'); + } +}; + +; +``` + +The demo below uses the [`Dialog`](/material-ui/react-dialog/) component for paste confirmation. +If confirmed, the Data Grid displays a loading indicator during the paste operation. {{"demo": "ClipboardPasteEvents.js", "bg": "inline"}} diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index ba181402628c8..559aa75b2e3f4 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -232,6 +232,10 @@ "describedArgs": ["model", "details"] } }, + "onBeforeClipboardPasteStart": { + "type": { "name": "func" }, + "signature": { "type": "function(params: object) => void", "describedArgs": ["params"] } + }, "onCellClick": { "type": { "name": "func" }, "signature": { diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 7f8c28d5b7175..96b941032c92d 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -249,6 +249,10 @@ "details": "Additional details for this callback." } }, + "onBeforeClipboardPasteStart": { + "description": "Callback fired before the clipboard paste operation starts. Use it to confirm or cancel the paste operation.", + "typeDescriptions": { "params": "Params passed to the callback." } + }, "onCellClick": { "description": "Callback fired when any cell is clicked.", "typeDescriptions": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index ad6815061d5c1..a411583a776c3 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -543,6 +543,14 @@ DataGridPremiumRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onAggregationModelChange: PropTypes.func, + /** + * Callback fired before the clipboard paste operation starts. + * Use it to confirm or cancel the paste operation. + * @param {object} params Params passed to the callback. + * @param {string[][]} params.data The raw pasted data split by rows and cells. + * @returns {Promise} A promise that resolves to confirm the paste operation, and rejects to cancel it. + */ + onBeforeClipboardPasteStart: PropTypes.func, /** * Callback fired when any cell is clicked. * @param {GridCellParams} params With all properties from [[GridCellParams]]. diff --git a/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts b/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts index fe95b6cc33d9c..be1527ee8797e 100644 --- a/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts +++ b/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts @@ -21,6 +21,7 @@ import { useGridRegisterPipeProcessor, getPublicApiRef, isPasteShortcut, + useGridLogger, } from '@mui/x-data-grid/internals'; import { GRID_DETAIL_PANEL_TOGGLE_FIELD, GRID_REORDER_COL_DEF } from '@mui/x-data-grid-pro'; import { unstable_debounce as debounce } from '@mui/utils'; @@ -318,6 +319,7 @@ export const useGridClipboardImport = ( | 'onClipboardPasteEnd' | 'splitClipboardPastedText' | 'disableClipboardPaste' + | 'onBeforeClipboardPasteStart' >, ): void => { const processRowUpdate = props.processRowUpdate; @@ -325,9 +327,12 @@ export const useGridClipboardImport = ( const getRowId = props.getRowId; const enableClipboardPaste = !props.disableClipboardPaste; const rootEl = apiRef.current.rootElementRef?.current; + const logger = useGridLogger(apiRef, 'useGridClipboardImport'); const splitClipboardPastedText = props.splitClipboardPastedText; + const { pagination, onBeforeClipboardPasteStart } = props; + const handlePaste = React.useCallback>( async (params, event) => { if (!enableClipboardPaste) { @@ -360,6 +365,15 @@ export const useGridClipboardImport = ( return; } + if (onBeforeClipboardPasteStart) { + try { + await onBeforeClipboardPasteStart({ data: pastedData }); + } catch (error) { + logger.debug('Clipboard paste operation cancelled'); + return; + } + } + const cellUpdater = new CellValueUpdater({ apiRef, processRowUpdate, @@ -377,7 +391,7 @@ export const useGridClipboardImport = ( updateCell: (...args) => { cellUpdater.updateCell(...args); }, - pagination: props.pagination, + pagination, }); cellUpdater.applyUpdates(); @@ -390,7 +404,9 @@ export const useGridClipboardImport = ( enableClipboardPaste, rootEl, splitClipboardPastedText, - props.pagination, + pagination, + onBeforeClipboardPasteStart, + logger, ], ); diff --git a/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts b/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts index add53ef3b59d4..ee3b030e670ea 100644 --- a/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts +++ b/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts @@ -169,6 +169,14 @@ export interface DataGridPremiumPropsWithoutDefaultValue void; + /** + * Callback fired before the clipboard paste operation starts. + * Use it to confirm or cancel the paste operation. + * @param {object} params Params passed to the callback. + * @param {string[][]} params.data The raw pasted data split by rows and cells. + * @returns {Promise} A promise that resolves to confirm the paste operation, and rejects to cancel it. + */ + onBeforeClipboardPasteStart?: (params: { data: string[][] }) => Promise; /** * Callback fired when the clipboard paste operation starts. */ From 0dc41910f86e11cef96d0f1db7b1af8cfbcc8a63 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 17 Mar 2024 01:27:41 +0100 Subject: [PATCH 45/49] [docs] Fix some Vale errors (#12469) --- .circleci/config.yml | 2 +- .github/styles/Blog/BrandName.yml | 24 ---------- .github/styles/Blog/ComponentNaming.yml | 24 ---------- .github/styles/Blog/ComposedWords.yml | 15 ------- .github/styles/Blog/NamingConventions.yml | 19 -------- .github/styles/Blog/NoCompanyName.yml | 14 ------ .github/styles/Blog/Typos.yml | 11 ----- .github/styles/Vocab/accept.txt | 0 .github/styles/Vocab/reject.txt | 0 .gitignore | 4 +- .vale.ini | 44 ++++++++----------- CHANGELOG.md | 22 +++++----- README.md | 4 +- changelogOld/CHANGELOG.v4.md | 20 ++++----- changelogOld/CHANGELOG.v5.md | 26 +++++------ docs/README.md | 3 +- docs/data/charts/gauge/gauge.md | 8 ++-- docs/data/charts/sparkline/sparkline.md | 2 +- docs/data/charts/tree-map/tree-map.md | 2 +- docs/data/data-grid/api-object/api-object.md | 2 +- .../column-definition/column-definition.md | 6 +-- .../column-pinning/column-pinning.md | 2 +- docs/data/data-grid/editing/editing.md | 2 +- docs/data/data-grid/events/events.md | 2 +- docs/data/data-grid/faq/faq.md | 6 +-- docs/data/data-grid/filtering/index.md | 2 +- .../data-grid/master-detail/master-detail.md | 2 +- docs/data/data-grid/overview/overview.md | 2 +- .../data-grid/row-grouping/row-grouping.md | 2 +- .../data-grid/row-ordering/row-ordering.md | 2 +- .../data/data-grid/row-updates/row-updates.md | 2 +- .../data-grid/server-side-data/aggregation.md | 2 +- docs/data/data-grid/server-side-data/index.md | 4 +- .../server-side-data/infinite-loading.md | 2 +- .../server-side-data/lazy-loading.md | 2 +- .../server-side-data/row-grouping.md | 2 +- .../data-grid/server-side-data/tree-data.md | 2 +- docs/data/data-grid/state/state.md | 4 +- .../virtualization/virtualization.md | 2 +- .../adapters-locale/FieldPlaceholder.js | 2 +- .../adapters-locale/FieldPlaceholder.tsx | 2 +- .../FieldPlaceholder.tsx.preview | 2 +- .../adapters-locale/adapters-locale.md | 4 +- .../base-concepts/base-concepts.md | 8 ++-- .../date-calendar/date-calendar.md | 2 +- .../date-pickers/date-picker/date-picker.md | 2 +- .../date-time-picker/date-time-picker.md | 2 +- .../digital-clock/digital-clock.md | 4 +- .../date-pickers/time-clock/time-clock.md | 2 +- .../introduction/installation/installation.md | 2 +- docs/data/introduction/licensing/licensing.md | 2 +- .../migration-data-grid-v4.md | 6 +-- .../migration-data-grid-v6.md | 2 +- .../migration-pickers-v6.md | 2 +- docs/public/_redirects | 2 +- docs/scripts/reportBrokenLinks.js | 2 +- docs/src/modules/components/DemoPropsForm.tsx | 2 +- docs/src/modules/utils/postProcessImport.ts | 2 +- .../date-calendar/date-calendar.json | 2 +- .../date-pickers/date-field/date-field.json | 2 +- .../date-pickers/date-picker/date-picker.json | 2 +- .../date-range-calendar.json | 2 +- .../date-range-picker/date-range-picker.json | 2 +- .../date-time-field/date-time-field.json | 2 +- .../date-time-picker/date-time-picker.json | 2 +- .../date-time-range-picker.json | 2 +- .../desktop-date-picker.json | 2 +- .../desktop-date-range-picker.json | 2 +- .../desktop-date-time-picker.json | 2 +- .../desktop-date-time-range-picker.json | 2 +- .../mobile-date-picker.json | 2 +- .../mobile-date-range-picker.json | 2 +- .../mobile-date-time-picker.json | 2 +- .../mobile-date-time-range-picker.json | 2 +- .../multi-input-date-range-field.json | 2 +- .../multi-input-date-time-range-field.json | 2 +- .../single-input-date-range-field.json | 2 +- .../single-input-date-time-range-field.json | 2 +- .../static-date-picker.json | 2 +- .../static-date-range-picker.json | 2 +- .../static-date-time-picker.json | 2 +- docsTech/processing.md | 16 +++---- .../clipboard/useGridClipboardImport.ts | 2 +- .../columnPinning/useGridColumnPinning.tsx | 6 +-- .../detailPanel/useGridDetailPanel.ts | 2 +- .../x-data-grid-pro/src/models/dataSource.ts | 2 +- .../tests/cellEditing.DataGridPro.test.tsx | 2 +- .../src/tests/components.DataGridPro.test.tsx | 4 +- .../src/components/cell/GridActionsCell.tsx | 2 +- .../filterPanel/GridFilterInputBoolean.tsx | 4 +- .../panel/filterPanel/GridFilterInputDate.tsx | 4 +- .../GridFilterInputSingleSelect.tsx | 4 +- .../filterPanel/GridFilterInputValue.tsx | 4 +- .../columnResize/useGridColumnResize.tsx | 2 +- .../features/editing/useGridCellEditing.ts | 2 +- .../features/editing/useGridRowEditing.ts | 2 +- .../hooks/features/filter/gridFilterUtils.ts | 2 +- .../src/models/api/gridRowsMetaApi.ts | 2 +- .../x-data-grid/src/utils/createSelector.ts | 2 +- packages/x-data-grid/src/utils/domUtils.ts | 2 +- .../x-data-grid/src/utils/keyboardUtils.ts | 2 +- .../DateRangeCalendar/DateRangeCalendar.tsx | 2 +- .../src/DateRangePicker/DateRangePicker.tsx | 2 +- .../DateTimeRangePicker.tsx | 2 +- .../DesktopDateRangePicker.tsx | 2 +- .../DesktopDateTimeRangePicker.tsx | 2 +- .../MobileDateRangePicker.tsx | 2 +- .../MobileDateTimeRangePicker.tsx | 2 +- .../MultiInputDateRangeField.tsx | 2 +- .../MultiInputDateTimeRangeField.tsx | 2 +- .../SingleInputDateRangeField.tsx | 2 +- .../SingleInputDateTimeRangeField.tsx | 2 +- .../StaticDateRangePicker.tsx | 2 +- .../src/internals/models/dateRange.ts | 2 +- .../src/DateCalendar/DateCalendar.tsx | 2 +- .../src/DateField/DateField.tsx | 2 +- .../src/DatePicker/DatePicker.tsx | 2 +- .../src/DateTimeField/DateTimeField.tsx | 2 +- .../src/DateTimePicker/DateTimePicker.tsx | 2 +- .../DesktopDatePicker/DesktopDatePicker.tsx | 2 +- .../DesktopDateTimePicker.tsx | 2 +- .../src/MobileDatePicker/MobileDatePicker.tsx | 2 +- .../MobileDateTimePicker.tsx | 2 +- .../src/StaticDatePicker/StaticDatePicker.tsx | 2 +- .../StaticDateTimePicker.tsx | 2 +- .../internals/components/PickersPopper.tsx | 2 +- .../src/internals/hooks/useOpenState.ts | 2 +- .../src/internals/models/validation.ts | 2 +- scripts/README.md | 2 +- scripts/l10n.ts | 2 +- 130 files changed, 204 insertions(+), 316 deletions(-) delete mode 100644 .github/styles/Blog/BrandName.yml delete mode 100644 .github/styles/Blog/ComponentNaming.yml delete mode 100644 .github/styles/Blog/ComposedWords.yml delete mode 100644 .github/styles/Blog/NamingConventions.yml delete mode 100644 .github/styles/Blog/NoCompanyName.yml delete mode 100644 .github/styles/Blog/Typos.yml delete mode 100644 .github/styles/Vocab/accept.txt delete mode 100644 .github/styles/Vocab/reject.txt diff --git a/.circleci/config.yml b/.circleci/config.yml index 49cb3d59e8a9b..200f447444cb4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,7 +52,7 @@ commands: browsers: type: boolean default: false - description: 'Set to true if you intend to any browser (e.g. with playwright).' + description: 'Set to true if you intend to any browser (for example with playwright).' steps: - run: diff --git a/.github/styles/Blog/BrandName.yml b/.github/styles/Blog/BrandName.yml deleted file mode 100644 index bfdb8da88c272..0000000000000 --- a/.github/styles/Blog/BrandName.yml +++ /dev/null @@ -1,24 +0,0 @@ -# Without a non-breaking space, brand names can be split in the middle -# with the start and end on two different lines. -# For example, Apple does this meticulously with their brand name: https://www.apple.com/macbook-air/. -# Also read https://www.chrisdpeters.com/blog/using-non-breaking-spaces-to-help-with-branding/ for why. -extends: substitution -message: Use a non-breaking space for brand name ('%s' instead of '%s') -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - Material UI: Material UI - MUI X: MUI X - Base UI: Base UI - MUI System: MUI System - MUI Store: MUI Store - MUI Core: MUI Core - MUI Toolpad: MUI Toolpad - MUI Connect: MUI Connect - Stack Overflow: Stack Overflow -# Don't forget to run the following command to generate the package writing-rules.zip file -# Vale uses that ZIP file and not the YAML files. -# -# pnpm docs:zipRules diff --git a/.github/styles/Blog/ComponentNaming.yml b/.github/styles/Blog/ComponentNaming.yml deleted file mode 100644 index 138f7c303a722..0000000000000 --- a/.github/styles/Blog/ComponentNaming.yml +++ /dev/null @@ -1,24 +0,0 @@ -extends: substitution -message: Unless you're referring to the general concept, consider using '%s' instead of '%s'. -level: suggestion -ignorecase: false -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - # Capitalize - data grid: Data Grid - date picker: Date Picker - Data grid: Data Grid - Date picker: Date Picker - Time picker: Time Picker - Date [t|T]ime picker: Date Time Picker - Date [r|R]ange picker: Date Range Picker - Time [r|R]ange picker: Time Range Picker - Date [t|T]ime [r|R]ange picker: Date Time Range Picker - time picker: Time Picker - date time picker: Date Time Picker - date range picker: Date Range Picker - time range picker: Time Range Picker - date time range picker: Date Time Range Picker - # use Data Grid instead of grid - the grid: the Data Grid diff --git a/.github/styles/Blog/ComposedWords.yml b/.github/styles/Blog/ComposedWords.yml deleted file mode 100644 index 1d931388bee34..0000000000000 --- a/.github/styles/Blog/ComposedWords.yml +++ /dev/null @@ -1,15 +0,0 @@ -extends: substitution -message: To be consistent with the rest of the documentation, consider using '%s' instead of '%s'. -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - sub-component: subcomponent - sub-components: subcomponents - 'sub component': subcomponent - 'sub components': subcomponents - 'use-case': 'use case' - 'usecase': 'use case' - "can't": 'cannot' - 'can not': 'cannot' diff --git a/.github/styles/Blog/NamingConventions.yml b/.github/styles/Blog/NamingConventions.yml deleted file mode 100644 index ac0e3668f9fc4..0000000000000 --- a/.github/styles/Blog/NamingConventions.yml +++ /dev/null @@ -1,19 +0,0 @@ -extends: substitution -message: To be consistent with capitalization, consider using '%s' instead of '%s'. -level: error -ignorecase: false -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - api: API - Api: API - typescript: TypeScript - Typescript: TypeScript - ts: TypeScript - TS: TypeScript - javascript: JavaScript - Javascript: JavaScript - css: CSS - Css: CSS - NPM: npm # https://css-tricks.com/start-sentence-npm/ - Github: GitHub diff --git a/.github/styles/Blog/NoCompanyName.yml b/.github/styles/Blog/NoCompanyName.yml deleted file mode 100644 index a60b3c3bc2d78..0000000000000 --- a/.github/styles/Blog/NoCompanyName.yml +++ /dev/null @@ -1,14 +0,0 @@ -extends: existence -message: We avoid referencing the company name '%s'. Instead you can reference a product or the team. -level: warning -ignorecase: false -tokens: - - 'MUI \w+' -exceptions: - - 'MUI X' - - 'MUI System' - - 'MUI Store' - - 'MUI Core' - - 'MUI Toolpad' - - 'MUI Connect' - - 'MUI organization' # valid use of a regular space diff --git a/.github/styles/Blog/Typos.yml b/.github/styles/Blog/Typos.yml deleted file mode 100644 index 9397f61532505..0000000000000 --- a/.github/styles/Blog/Typos.yml +++ /dev/null @@ -1,11 +0,0 @@ -extends: substitution -message: Do you mean '%s' instead of '%s'? -level: error -ignorecase: true -# swap maps tokens in form of bad: good -# for more information: https://vale.sh/docs/topics/styles/#substitution -swap: - bellow: below - eg: e.g. - eg.: e.g. - 'e.g ': 'e.g. ' diff --git a/.github/styles/Vocab/accept.txt b/.github/styles/Vocab/accept.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/.github/styles/Vocab/reject.txt b/.github/styles/Vocab/reject.txt deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/.gitignore b/.gitignore index a0f281bd18190..a10ee92e04cfc 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,7 @@ dist node_modules size-snapshot.json performance-snapshot.json +# vale downloaded config .github/styles/Google -.github/styles/write-good \ No newline at end of file +.github/styles/MUI +.github/styles/.vale-config diff --git a/.vale.ini b/.vale.ini index b22f6236cfd3a..217e3d4d501a7 100644 --- a/.vale.ini +++ b/.vale.ini @@ -1,33 +1,27 @@ -# Config vale. More information at https://docs.errata.ai/vale/config +# Vale config. More information at https://vale.sh/docs/topics/config/ StylesPath = .github/styles -MinAlertLevel = suggestion +MinAlertLevel = error -Packages = Google +# To update mui-vale package: +# 1. Go to the docs folder in the material-ui repo +# 2. Update/create YAML files +# 3. Run `pnpm docs:zipRules` to generate the zip files +# 4. You can test locally by replacing the url with the file path of the generated zip +Packages = Google, https://github.com/mui/material-ui/raw/HEAD/docs/mui-vale.zip [*.md] -# Ignore code injection which start with {{... +# Ignore code injections that start with {{... BlockIgnores = {{.* -# Custom syle -# BasedOnStyles = Blog +BasedOnStyles = MUI -Blog.ComposedWords = YES -Blog.NamingConventions = YES -Blog.Typos = YES -Blog.BrandName = YES -Blog.NoCompanyName = YES +# Google errors: +Google.GenderBias = YES # No Gender bias +# Google warings: +Google.FirstPerson = YES # Avoid first-person +Google.We = YES # Avoid first-person plural +Google.Will = YES # Avoid future tense +Google.OxfordComma = YES # Prefer Oxford comma -# Google: -Google.FirstPerson = YES # Avoid first-person pronouns such as I, me, ...'. -Google.GenderBias = YES # Avoid gendered profession -Google.OxfordComma = YES -Google.Quotes = YES # Commas and periods go inside quotation marks. -Google.Spelling = YES # In general, use American spelling (word ending with 'nised' or 'logue') -Google.We = YES # Try to avoid using first-person plural - -# Those rules are not repected a lot -# Google.Passive = YES # In general, use active voice instead of passive voice. -# Google.Will = YES # Avoid using will - -# False positives with "1st" nearly no use in our doc -# Google.Units = YES # Put a nonbreaking space between the number and the unit \ No newline at end of file +[CHANGELOG*.md] +MUI.CorrectReferenceAllCases = NO diff --git a/CHANGELOG.md b/CHANGELOG.md index f39c4060b14f1..b0499f734d2dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,7 +90,7 @@ The `onNodeFocus` callback has been renamed to `onItemFocus` for consistency: - [docs] Add `legacy` bundle drop mention in migration pages (#12424) @LukasTy - [docs] Add missing luxon `Info` import (#12427) @LukasTy - [docs] Improve slots definitions for charts (#12408) @alexfauquette -- [docs] Polish What's new in MUI X blog titles (#12309) @oliviertassinari +- [docs] Polish What's new in MUI X blog titles (#12309) @oliviertassinari - [docs] Replace `rel="noreferrer"` by `rel="noopener"` @oliviertassinari - [docs] Update `date-fns` `weekStarsOn` overriding example (#12416) @LukasTy @@ -252,7 +252,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0-beta.5`. ### Docs - [docs] Fix image layout shift when loading @oliviertassinari -- [docs] Match Material UI repo comment for redirections @oliviertassinari +- [docs] Match Material UI repo comment for redirections @oliviertassinari - [docs] Non breaking spaces @oliviertassinari - [docs] Polish the Date Picker playground (#11869) @zanivan - [docs] Standardize WAI-ARIA references @oliviertassinari @@ -263,9 +263,9 @@ Same changes as in `@mui/x-date-pickers@7.0.0-beta.5`. - [core] Remove grid folder from `getComponentInfo` RegExp (#12241) @flaviendelangle - [core] Remove `window.` reference for common globals @oliviertassinari - [core] Use runtime agnostic setTimeout type @oliviertassinari -- [docs-infra] Fix Stack Overflow breaking space @oliviertassinari +- [docs-infra] Fix Stack Overflow breaking space @oliviertassinari - [docs-infra] Fix missing non breaking spaces @oliviertassinari -- [github] Update `no-response` workflow (#12193) @MBilalShafi +- [infra] Update `no-response` workflow (#12193) @MBilalShafi - [infra] Fix missing permission reset @oliviertassinari ## 7.0.0-beta.4 @@ -282,7 +282,7 @@ We'd like to offer a big thanks to the 10 contributors who made this release pos ### Breaking changes -- The support for IE11 has been removed from all MUI X packages. The `legacy` bundle that used to support old browsers like IE11 is no longer included. +- The support for IE11 has been removed from all MUI X packages. The `legacy` bundle that used to support old browsers like IE11 is no longer included. ### Data Grid @@ -392,7 +392,7 @@ These components are no longer exported from `@mui/x-charts`: ### Docs - [docs] Add recipe for hiding separator on non-resizable columns (#12134) @michelengelen -- [docs] Add small improvements to the Gauge Chart page (#12076) @danilo-leal +- [docs] Add small improvements to the Gauge page (#12076) @danilo-leal - [docs] Add the 'point' scaleType to the axis documentation (#12179) @alexfauquette - [docs] Clarify Pickers 'Component composition' section (#12097) @LukasTy - [docs] Fix "Licensing" page link (#12156) @LukasTy @@ -975,7 +975,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0-alpha.9`. - [charts] Do not propagate `innerRadius` and `outerRadius` to the DOM (#11689) @alexfauquette - [charts] Fix default `stackOffset` for `LineChart` (#11647) @alexfauquette -- [charts] Remove a TS ignore (#11688) @alexfauquette +- [charts] Remove a TypeScript ignore (#11688) @alexfauquette ### Tree View @@ -1404,7 +1404,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0-alpha.7`. - [core] Fix release changelog (#11496) @romgrk - [core] Fix use of ::before & ::after (#11515) @oliviertassinari - [core] Localize the issue template to MUI X (#11511) @oliviertassinari -- [core] Regen api files (#11542) @flaviendelangle +- [core] Regenerate API files (#11542) @flaviendelangle - [core] Remove issue emoji @oliviertassinari - [core] Sync the release instructions with MUI Core @oliviertassinari - [core] Yaml format match most common convention @oliviertassinari @@ -1447,7 +1447,7 @@ We'd like to offer a big thanks to the 6 contributors who made this release poss - The `filterModel` now supports `Date` objects as values for `date` and `dateTime` column types. The `filterModel` still accepts strings as values for `date` and `dateTime` column types, - but all updates to the `filterModel` coming from the UI (e.g. filter panel) will set the value as a `Date` object. + but all updates to the `filterModel` coming from the UI (for example filter panel) will set the value as a `Date` object. #### `@mui/x-data-grid@7.0.0-alpha.6` @@ -1658,7 +1658,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0-alpha.5`, plus: ### Core - [core] Automate cherry-pick of PRs from `next` -> `master` (#11382) @MBilalShafi -- [github] Update `no-response` workflow (#11369) @MBilalShafi +- [infra] Update `no-response` workflow (#11369) @MBilalShafi - [test] Fix flaky screenshots (#11388) @cherniavskii ## 7.0.0-alpha.4 @@ -1896,7 +1896,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0-alpha.3`. - [charts] Adjusted `defaultizeValueFormatter` util to accept an optional `series.valueFormatter` value (#11144) @michelengelen - [charts] Apply `labelStyle` and `tickLabelStyle` props on `` (#11180) @akamfoad -- [charts] Fix TS config (#11259) @alexfauquette +- [charts] Fix TypeScript config (#11259) @alexfauquette - [charts] Fix error with empty dataset (#11063) @alexfauquette - [charts] Fix export strategy (#11235) @alexfauquette - [charts] Remove outdated prop-types (#11045) @alexfauquette diff --git a/README.md b/README.md index 5e751d42d69b6..3bbfeb09f669b 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ From community guidance to critical business support, we're here to help. Read t Read the [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes. Contributing to MUI X is about more than just issues and pull requests! -There are many other ways to [support MUI](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. +There are many other ways to [support MUI X](https://mui.com/material-ui/getting-started/faq/#mui-is-awesome-how-can-i-support-the-project) beyond contributing to the code base. ## Changelog @@ -103,7 +103,7 @@ The [changelog](https://github.com/mui/mui-x/releases) is regularly updated to r ## Roadmap -Future plans and high-priority features and enhancements can be found in our [roadmap](https://mui.com/x/introduction/roadmap/). +Future plans and high-priority features and enhancements can be found in the [roadmap](https://mui.com/x/introduction/roadmap/). ## Security diff --git a/changelogOld/CHANGELOG.v4.md b/changelogOld/CHANGELOG.v4.md index af9cc92f160ab..662652efd208d 100644 --- a/changelogOld/CHANGELOG.v4.md +++ b/changelogOld/CHANGELOG.v4.md @@ -310,8 +310,8 @@ Big thanks to the 8 contributors who made this release possible. Here are some h - [docs] Add missing API docs (#2167) @ZeeshanTamboli - [docs] Describe how to export custom rendered cells (#2194) @m4theushw -- [docs] Generate api doc for the GridExportCSVOptions interface (#2102) @flaviendelangle -- [docs] Handle generics in api doc generation (#2210) @flaviendelangle +- [docs] Generate API doc for the GridExportCSVOptions interface (#2102) @flaviendelangle +- [docs] Handle generics in API doc generation (#2210) @flaviendelangle ### Core @@ -1260,7 +1260,7 @@ Big thanks to the 8 contributors who made this release possible. Here are some h - [DataGrid] Rename `GridBaseComponentProps` type to `GridSlotComponentProps` (#1252) @DanailH - [DataGrid] Rename `useGridBaseComponentProps` hook to `useGridSlotComponentProps` (#1252) @DanailH - [DataGrid] Rename modules (#1292) @DanailH -- [DataGrid] Rename all events related to column reordering, e.g. `GRID_COL_REORDER_START` -> `GRID_COLUMN_REORDER_START` (#1299) @m4theushw +- [DataGrid] Rename all events related to column reordering, for example `GRID_COL_REORDER_START` -> `GRID_COLUMN_REORDER_START` (#1299) @m4theushw - [DataGrid] Methods `onColItemDragStart`, `onColHeaderDragOver`, `onColItemDragOver`, `onColItemDragEnter` removed from the grid API. Prefer listening to [column reordering events](https://mui.com/x/react-data-grid/column-ordering/) (#1299) @m4theushw - [DataGrid] Calling `apiRef.current.getColumnHeaderParams` returns a `GridColumnHeaderParams` instead of `GridColParams` (#1299) @m4theushw - [DataGrid] Events that follow the pattern `GRID_COLUMN_HEADER_xxx` will be called with a `GridColumnHeaderParams` instead of `GridColParams` (#1299) @m4theushw @@ -1475,7 +1475,7 @@ Big thanks to the 4 contributors who made this release possible. Here are some h - [DataGrid] Fix process is not defined (EXPERIMENTAL_ENABLED) (#1027) @leontastic - [DataGrid] Fix scrollbar size on windows (#1061) @dtassone - [DataGrid] Fix warning with v5 (#1038) @oliviertassinari -- [DataGrid] Resolve the api ref at the same time as any other ref (#990) @oliviertassinari +- [DataGrid] Resolve the apiRef at the same time as any other ref (#990) @oliviertassinari - [DataGrid] Use the disableDensitySelector to disable the DensitySelector (#1031) @DanailH - [DataGrid] Fix passing [] or undefined in sortModel prop (#1035) @dtassone - [XGrid] Fix server-side multi filters (#1029) @dtassone @@ -1501,7 +1501,7 @@ _Feb 5, 2021_ Big thanks to the 5 contributors who made this release possible. Here are some highlights ✨: - 🎁 Add getRowId prop (#972) @dtassone -- 🚀 Add streaming delete row api (#980) @dtassone +- 🚀 Add streaming delete row API (#980) @dtassone - 💅 Fix autoHeight (#940) @oliviertassinari - 🙌 Enable the data grid to work under strict mode (#933) @dtassone - ⚡️ Add component slots for toolbar and preference panel (#971) @DanailH @@ -1511,7 +1511,7 @@ Big thanks to the 5 contributors who made this release possible. Here are some h - [DataGrid] Add component slots for toolbar and preference panel (#971) @DanailH - [DataGrid] Add getRowId prop (#972) @dtassone -- [DataGrid] Add streaming delete row api (#980) @dtassone +- [DataGrid] Add streaming delete row API (#980) @dtassone - [DataGrid] Fix autoHeight (#940) @oliviertassinari - [DataGrid] Fix column reorder instability (#950) @dtassone - [DataGrid] Fix footer visual regression (#932) @dtassone @@ -1525,7 +1525,7 @@ Big thanks to the 5 contributors who made this release possible. Here are some h ### docs - [docs] Add sorting page in datagrid docs (#931) @dtassone -- [docs] Api page update with component slots (#969) @dtassone +- [docs] API page update with component slots (#969) @dtassone - [docs] Catch leaks ahread of time (#979) @oliviertassinari - [docs] Fix immutability with filter operator demos (#975) @dtassone - [docs] Improve docs of DataGrid about filter operators (#973) @SaskiaKeil @@ -1728,7 +1728,7 @@ Big thanks to the 4 contributors who made this release possible. Here are some h ### Docs -- [docs] Add missing props to DataGrid and XGrid api pages (#721) @DanailH +- [docs] Add missing props to DataGrid and XGrid API pages (#721) @DanailH - [docs] Fix wrong link anchor @oliviertassinari - [docs] Proxy production version @oliviertassinari @@ -1853,7 +1853,7 @@ _Nov 20, 2020_ ### Core -- [core] Prepare work for a future public state api (#533) @dtassone +- [core] Prepare work for a future public state API (#533) @dtassone - [core] Fix yarn prettier write @oliviertassinari - [test] Share karma setup (#576) @oliviertassinari @@ -1974,7 +1974,7 @@ You can find the documentation at this address: https://mui.com/x/react-data-gri ### @material-ui/x-grid@v4.0.0-alpha.1 / @material-ui/data-grid@v4.0.0-alpha.1 -- [DataGrid] Add api pages for data-grid and x-grid (#289) @dtassone +- [DataGrid] Add API pages for data-grid and x-grid (#289) @dtassone - [DataGrid] Add dark mode scrollbar (#282) @dtassone - [DataGrid] Better explain the limits of MIT vs commercial (#225) @oliviertassinari - [DataGrid] First v4 alpha version (#291) @dtassone diff --git a/changelogOld/CHANGELOG.v5.md b/changelogOld/CHANGELOG.v5.md index 62a6d292bf609..a544c6b2c162b 100644 --- a/changelogOld/CHANGELOG.v5.md +++ b/changelogOld/CHANGELOG.v5.md @@ -295,7 +295,7 @@ We'd like to offer a big thanks to the 4 contributors who made this release poss ### Docs -- [docs] Fix the nested import on the api pages (#7134) @flaviendelangle +- [docs] Fix the nested import on the API pages (#7134) @flaviendelangle - [docs] Keep track of the localization completion (#7099) @alexfauquette - [docs] Update localization doc to use existing locale (#7104) @LukasTy @@ -495,7 +495,7 @@ We'd like to offer a big thanks to the 5 contributors who made this release poss - [CalendarPicker] Don't move to closest enabled date when `props.date` contains a disabled date (#6537) @flaviendelangle - [DateRangePicker] Fix calendar day outside of month layout shifting on hover (pick #6448) (#6538) @alexfauquette -- [pickers] Fix Typescript issues (#6510) @flaviendelangle +- [pickers] Fix TypeScript issues (#6510) @flaviendelangle ### Docs @@ -599,7 +599,7 @@ We'd like to offer a big thanks to the 5 contributors who made this release poss - [pickers] Add Finnish (fi-FI) locale to pickers (#6219) (#6230) @PetroSilenius - [pickers] Add Persian (fa-IR) locale to the pickers (#6181) @fakhamatia -- [pickers] Fix usage with Typescript 4.8 (#6229) @flaviendelangle +- [pickers] Fix usage with TypeScript 4.8 (#6229) @flaviendelangle - [YearPicker] Scroll to the current year even with `autoFocus=false` (#6224) @alexfauquette ### Docs @@ -641,7 +641,7 @@ We'd like to offer a big thanks to the 6 contributors who made this release poss ### Core -- [core] Update to Typescript 4.8.3 (#6136) @flaviendelangle +- [core] Update to TypeScript 4.8.3 (#6136) @flaviendelangle - [core] Update RFC template (#6100) @bytasv ## 5.17.2 @@ -832,7 +832,7 @@ We'd like to offer a big thanks to the 11 contributors who made this release pos ### Core - [core] Clarify the scope of the license key used for tests and documentation (#5824) @oliviertassinari -- [core] Fix Typescript error on field hooks (#5892) @flaviendelangle +- [core] Fix TypeScript error on field hooks (#5892) @flaviendelangle - [core] Memoize `columns` in `useDemoData` hook (#5848) @cherniavskii - [core] Remove Firefox from the BrowserStack list (#5874) @DanailH - [core] Small changes to the release script (#5840) @m4theushw @@ -874,7 +874,7 @@ We'd like to offer a big thanks to the 9 contributors who made this release poss ### Core - [core] Upgrade monorepo (#5771, #5797) @cherniavskii -- [core] Various TS improvements (#5556) @flaviendelangle +- [core] Various TypeScript improvements (#5556) @flaviendelangle - [license] Give more context in the missing license (#5731) @oliviertassinari - [license] Only log an error type once (#5730) @oliviertassinari - [test] Increase timeout to take print screenshot (#5799) @m4theushw @@ -1140,7 +1140,7 @@ We'd like to offer a big thanks to the 10 contributors who made this release pos - [DataGrid] Add Romanian (ro-RO) locale (#5345) @rolule - [DataGrid] Export Norwegian (nb-NO) locale (#5407) @cherniavskii - [DataGrid] Fix broken "start editing" integration with Japanese (#5414) @mnajdova -- [DataGrid] Fix "stop editing" integration with IME e.g. Japanese (#5257) @Gumichocopengin8 +- [DataGrid] Fix "stop editing" integration with IME, for example Japanese (#5257) @Gumichocopengin8 - [DataGrid] Fix dimensions computation with `autoHeight` and scroll x (#5401) @flaviendelangle - [DataGrid] Improve Slovak (sk-SK) locale (#5332) @msidlo - [DataGrid] Mention Premium plan in error messages and docs warnings (#5328) @cherniavskii @@ -1390,7 +1390,7 @@ We'd like to offer a big thanks to the 15 contributors who made this release pos - [core] Avoid Order ID to refer to GitHub issues/PRs (#5005) @oliviertassinari - [core] Improve the workflow for incomplete issues (#5012) @mnajdova - [core] Remove dead code on row grouping tree creation (#4945) @flaviendelangle -- [core] Use new cache api for the row grouping last model tracking (#4980) @flaviendelangle +- [core] Use new cache API for the row grouping last model tracking (#4980) @flaviendelangle - [core] Ensure that PRs have atleast 1 label (#5011) @DanailH - [core] Fix trailing-space @oliviertassinari - [core] Stop Renovate PR updates when PR is on hold (#5020) @cherniavskii @@ -1624,7 +1624,7 @@ We'd like to offer a big thanks to the 15 contributors who made this release pos - [core] Fix naming collision (#4853) @alexfauquette - [core] Prevent out-of-memory when type-checking in CI (#4697) @flaviendelangle - [core] Remove `rowsCache` from state (#4480) @m4theushw -- [core] Rework `DayPicker` api (#4783) @flaviendelangle +- [core] Rework `DayPicker` API (#4783) @flaviendelangle - [core] Update `x-license-pro` license to handle premium package (#4315) @DanailH - [core] Update monorepo & version (#4789) @oliviertassinari - [core] Update monorepo (#4772) @flaviendelangle @@ -1743,7 +1743,7 @@ We'd like to offer a big thanks to the 9 contributors who made this release poss - [core] Fix npm page description mistake (#4364) @oliviertassinari - [core] Fix typos and JSDoc (#4406) @flaviendelangle - [core] Move away for the event system to trigger pipe processings (#4378) @flaviendelangle -- [core] Small fixes TS on pickers (#4461) @flaviendelangle +- [core] Small fixes TypeScript on pickers (#4461) @flaviendelangle - [core] Unify tests (#4368) @flaviendelangle - [core] Enforce `noImplicitAny` in `docs` folder (#4412) @cherniavskii @@ -1994,7 +1994,7 @@ We'd like to offer a big thanks to the 15 contributors who made this release pos - [DataGrid] Allow to navigate between cells with keyboard once inside an `actions` column (#3375) @m4theushw - [DataGrid] Fix desynchronization between rows and header when sorting (#4058) @alexfauquette - [DataGrid] Clean and document the columns selector (#4010) @flaviendelangle -- [DataGrid] Deprecate and stop typing the api params of `GridCellParams`/`GridValueGetterParams` and affiliated (#4089) @ flaviendelangle +- [DataGrid] Deprecate and stop typing the API params of `GridCellParams`/`GridValueGetterParams` and affiliated (#4089) @ flaviendelangle - [DataGrid] Differentiate the Pro and Community versions of `GridState`, `GridApi` and `GridApiRef` (#3648) @flaviendelangle - [DataGrid] Fix column selection for print export (#3917) @alexfauquette - [DataGrid] Fix horizontal scroll not working on empty grid (#3821) @cherniavskii @@ -3001,7 +3001,7 @@ A big thanks to the 5 contributors who made this release possible. Here are some - [DataGrid] Remove the `state` prop and use the `initialState` prop (#2848) @flaviendelangle Note that `initialState` only allows the `preferencePanel`, `filter.filterModel` and `sort.sortModel` keys. - To fully control the state, use the feature's model prop and change callback (e.g. `filterModel` and `onFilterModelChange`). + To fully control the state, use the feature's model prop and change callback (for example `filterModel` and `onFilterModelChange`). ```diff Treemap allows to display data with a hierarchical structure.

:::warning -The Treemap Chart component isn't available yet, but you can upvote [**this GitHub issue**](https://github.com/mui/mui-x/issues/7924) to see it arrive sooner. +The Treemap component isn't available yet, but you can upvote [**this GitHub issue**](https://github.com/mui/mui-x/issues/7924) to see it arrive sooner. Don't hesitate to leave a comment there to influence what gets built. Especially if you already have a use case for this component, or if you're facing a pain point with your current solution. diff --git a/docs/data/data-grid/api-object/api-object.md b/docs/data/data-grid/api-object/api-object.md index 9c0e091826eaa..1c436192c83e2 100644 --- a/docs/data/data-grid/api-object/api-object.md +++ b/docs/data/data-grid/api-object/api-object.md @@ -62,7 +62,7 @@ If you try to use it in the first render of the component, it will crash because ### Access the disabled column features -You can control the disabled features of a column (e.g. hiding, sorting, filtering, pinning, grouping, etc) programmatically using `initialState`, controlled models, or the API object. +You can control the disabled features of a column (for example hiding, sorting, filtering, pinning, grouping, etc) programmatically using `initialState`, controlled models, or the API object. In the example below, API object is used to build a custom sorting for the _firstName_ column which is not sortable by the default grid UI (i.e `colDef.sortable` property is set to `false`). diff --git a/docs/data/data-grid/column-definition/column-definition.md b/docs/data/data-grid/column-definition/column-definition.md index 0c1ae990eea6f..d869453225069 100644 --- a/docs/data/data-grid/column-definition/column-definition.md +++ b/docs/data/data-grid/column-definition/column-definition.md @@ -93,13 +93,13 @@ The value returned by `valueGetter` is used for: ### Value formatter The value formatter allows you to convert the value before displaying it. -Common use cases include converting a JavaScript `Date` object to a date string or a `Number` into a formatted number (e.g. "1,000.50"). +Common use cases include converting a JavaScript `Date` object to a date string or a `Number` into a formatted number (for example "1,000.50"). Note, that the value returned by `valueFormatter` is only used for rendering purposes. Filtering and sorting are based on the raw value (`row[field]`) or the value returned by [`valueGetter`](/x/react-data-grid/column-definition/#value-getter). -In the following demo, `valueGetter` is used to convert the tax rate (e.g. `0.2`) to a decimal value (e.g. `20`), -and `valueFormatter` is used to display it as a percentage (e.g. `20%`). +In the following demo, `valueGetter` is used to convert the tax rate (for example `0.2`) to a decimal value (for example `20`), +and `valueFormatter` is used to display it as a percentage (for example `20%`). {{"demo": "ValueFormatterGrid.js", "bg": "inline"}} diff --git a/docs/data/data-grid/column-pinning/column-pinning.md b/docs/data/data-grid/column-pinning/column-pinning.md index 462a666c6a832..40451599150ee 100644 --- a/docs/data/data-grid/column-pinning/column-pinning.md +++ b/docs/data/data-grid/column-pinning/column-pinning.md @@ -73,7 +73,7 @@ To disable the pinning of a single column, set the `pinnable` property in `GridC ### Pin non-pinnable columns programmatically -It may be desirable to allow one or more columns to be pinned or unpinned programmatically that cannot be pinned or unpinned on the UI (i.e. columns for which prop `disableColumnPinning = true` or `colDef.pinnable = false`). +It may be desirable to allow one or more columns to be pinned or unpinned programmatically that cannot be pinned or unpinned on the UI (that is columns for which prop `disableColumnPinning = true` or `colDef.pinnable = false`). This can be done in one of the following ways. - (A) Initializing the pinned columns diff --git a/docs/data/data-grid/editing/editing.md b/docs/data/data-grid/editing/editing.md index 2e74600d3fc90..fcf48175f9a40 100644 --- a/docs/data/data-grid/editing/editing.md +++ b/docs/data/data-grid/editing/editing.md @@ -41,7 +41,7 @@ To enable this behavior, set the `editMode` prop on the Data Grid to `"row"`. No ``` The following demo illustrates how row editing works. -The user can [start](#start-editing) and [stop](#stop-editing) editing a row using the same actions as those provided for cell editing (e.g. double-clicking a cell). +The user can [start](#start-editing) and [stop](#stop-editing) editing a row using the same actions as those provided for cell editing (for example double-clicking a cell). {{"demo": "BasicRowEditingGrid.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/docs/data/data-grid/events/events.md b/docs/data/data-grid/events/events.md index 5e380ee8b56fe..170c71790af73 100644 --- a/docs/data/data-grid/events/events.md +++ b/docs/data/data-grid/events/events.md @@ -48,7 +48,7 @@ useGridApiEventHandler(apiRef, 'rowClick', handleEvent); ``` :::warning -This hook can only be used inside the scope of the Data Grid (i.e. inside component slots or cell renderers). +This hook can only be used inside the scope of the Data Grid (that is inside component slots or cell renderers). ::: The following demo shows how to subscribe to the `rowClick` event using `useGridApiEventHandler`—try it out by clicking on any row: diff --git a/docs/data/data-grid/faq/faq.md b/docs/data/data-grid/faq/faq.md index aabf157af76cf..823b9a6d1f7d6 100644 --- a/docs/data/data-grid/faq/faq.md +++ b/docs/data/data-grid/faq/faq.md @@ -103,9 +103,9 @@ See more about the `renderCell` method in the [rendering cells](/x/react-data-gr It is a function that allows you to derive the cell value from the row data. It is the most performant way to customize the cell content. It is also the only way to customize the cell value without changing the row data. It should be used when you need to derive the cell value from the row data. Common use cases are: -- Transforming the value (e.g. convert a decimal value to a percentage value) -- Deriving the value from multiple fields (e.g. concatenating first name and last name) -- Deriving the value from a nested field (e.g. `user.address.city`) +- Transforming the value (for example convert a decimal value to a percentage value) +- Deriving the value from multiple fields (for example concatenating first name and last name) +- Deriving the value from a nested field (for example `user.address.city`) This value is also used internally in the Grid to filter, sort, and render (if no `renderCell` or `valueFormatter` is provided). You can learn more about it in the [value getter](/x/react-data-grid/column-definition/#value-getter) section. diff --git a/docs/data/data-grid/filtering/index.md b/docs/data/data-grid/filtering/index.md index c0787c6afdfae..48f73b927bd84 100644 --- a/docs/data/data-grid/filtering/index.md +++ b/docs/data/data-grid/filtering/index.md @@ -36,7 +36,7 @@ A filter item represents a filtering rule and is composed of several elements: - `filterItem.field`: the field on which the rule applies. - `filterItem.value`: the value to look for. -- `filterItem.operator`: name of the operator method to use (e.g. _contains_), matches the `value` key of the operator object. +- `filterItem.operator`: name of the operator method to use (for example _contains_), matches the `value` key of the operator object. - `filterItem.id` ([](/x/introduction/licensing/#pro-plan 'Pro plan')): required when multiple filter items are used. :::info diff --git a/docs/data/data-grid/master-detail/master-detail.md b/docs/data/data-grid/master-detail/master-detail.md index b897a7f8d5c68..ac3cec8269b80 100644 --- a/docs/data/data-grid/master-detail/master-detail.md +++ b/docs/data/data-grid/master-detail/master-detail.md @@ -111,7 +111,7 @@ If this is not sufficient, the entire toggle component can be overridden. To fully customize it, add another column with `field: GRID_DETAIL_PANEL_TOGGLE_FIELD` to your set of columns. The grid will detect that there is already a toggle column defined and it will not add another toggle in the default position. The new toggle component can be provided via [`renderCell`](/x/react-data-grid/column-definition/#rendering-cells) in the same as any other column. -By only setting the `field`, is up to you to configure the remaining options (e.g. disable the column menu, filtering, sorting). +By only setting the `field`, is up to you to configure the remaining options (for example disable the column menu, filtering, sorting). To already start with a few suggested options configured, spread `GRID_DETAIL_PANEL_TOGGLE_COL_DEF` when defining the column. ```tsx diff --git a/docs/data/data-grid/overview/overview.md b/docs/data/data-grid/overview/overview.md index 1d573d2e0e0d6..cec6d0fa1ed23 100644 --- a/docs/data/data-grid/overview/overview.md +++ b/docs/data/data-grid/overview/overview.md @@ -51,7 +51,7 @@ import { DataGridPro } from '@mui/x-data-grid-pro'; ### Premium plan [](/x/introduction/licensing/#premium-plan 'Premium plan') -The Premium version includes everything from Pro, as well as advanced features for data analysis and large dataset management, like row grouping with aggregation functions (e.g., Sum) and the ability to export to Excel files. +The Premium version includes everything from Pro, as well as advanced features for data analysis and large dataset management, like row grouping with aggregation functions (for example "Sum") and the ability to export to Excel files. You can visit the [showcase page](/x/react-data-grid/demo/) for a comprehensible overview of all features exclusive to this plan. diff --git a/docs/data/data-grid/row-grouping/row-grouping.md b/docs/data/data-grid/row-grouping/row-grouping.md index a8a5c8ae0b23a..bb7aca8496d32 100644 --- a/docs/data/data-grid/row-grouping/row-grouping.md +++ b/docs/data/data-grid/row-grouping/row-grouping.md @@ -305,7 +305,7 @@ If you are dynamically switching the `leafField` or `mainGroupingCriteria`, the ## Get the rows in a group You can use the `apiRef.current.getRowGroupChildren` method to get the id of all rows contained in a group. -It will not contain the autogenerated rows (i.e. the subgroup rows or the aggregation footers). +It will not contain the autogenerated rows (that is the subgroup rows or the aggregation footers). ```ts const rows: GridRowId[] = apiRef.current.getRowGroupChildren({ diff --git a/docs/data/data-grid/row-ordering/row-ordering.md b/docs/data/data-grid/row-ordering/row-ordering.md index 09e3922d1b2fc..b4cd6e75930f6 100644 --- a/docs/data/data-grid/row-ordering/row-ordering.md +++ b/docs/data/data-grid/row-ordering/row-ordering.md @@ -57,7 +57,7 @@ To change the icon used for the row reordering, you can provide a different comp Another way to customize is to add a column with `field: __reorder__` to your set of columns. That way, you can overwrite any of the properties from the `GRID_REORDER_COL_DEF` column. The grid will detect that there is already a reorder column defined and it will not add another one in the default position. -If you only set the `field`, then it is up to you to configure the remaining options (e.g. disable the column menu, filtering, sorting). +If you only set the `field`, then it is up to you to configure the remaining options (for example disable the column menu, filtering, sorting). To start with our suggested configuration, spread `GRID_REORDER_COL_DEF` when defining the column. ```tsx diff --git a/docs/data/data-grid/row-updates/row-updates.md b/docs/data/data-grid/row-updates/row-updates.md index 02d6e165843fa..e1b8e96802848 100644 --- a/docs/data/data-grid/row-updates/row-updates.md +++ b/docs/data/data-grid/row-updates/row-updates.md @@ -41,7 +41,7 @@ In addition, the area in which `onRowsScrollEnd` is called can be changed using {{"demo": "InfiniteLoadingGrid.js", "bg": "inline", "disableAd": true}} :::info -For sorting and filtering to work properly with the infinite loading, they should be applied on the server side. +For sorting and filtering to work properly with the infinite loading, they should be applied on the server-side. Otherwise, the sorting and filtering will only be applied to the subset of rows that have been loaded. ::: diff --git a/docs/data/data-grid/server-side-data/aggregation.md b/docs/data/data-grid/server-side-data/aggregation.md index fdbaf839ca6f2..99b226b777709 100644 --- a/docs/data/data-grid/server-side-data/aggregation.md +++ b/docs/data/data-grid/server-side-data/aggregation.md @@ -4,7 +4,7 @@ title: React Server-side row grouping # Data Grid - Server-side aggregation [](/x/introduction/licensing/#premium-plan 'Premium plan')🚧 -

Aggregation with server side data source.

+

Aggregation with server-side data source.

:::warning This feature isn't implemented yet. It's coming. diff --git a/docs/data/data-grid/server-side-data/index.md b/docs/data/data-grid/server-side-data/index.md index 13ee31d9b0053..a626f60d7c3c2 100644 --- a/docs/data/data-grid/server-side-data/index.md +++ b/docs/data/data-grid/server-side-data/index.md @@ -63,7 +63,7 @@ This example only scratches the surface with a lot of problems still unsolved li - Performance optimization - Caching data/deduping requests - More complex use-cases on the server like grouping, tree data, etc. -- Server side row editing +- Server-side row editing - Lazy loading of data - Handling updates to the data like row editing, row deletion, etc. - Refetching data on-demand @@ -182,7 +182,7 @@ interface GetRowsResponse { rows: GridRowModel[]; /** * To reflect updates in total `rowCount` (optional) - * Useful when the `rowCount` is inaccurate (e.g. when filtering) or not available upfront + * Useful when the `rowCount` is inaccurate (for example when filtering) or not available upfront */ rowCount?: number; /** diff --git a/docs/data/data-grid/server-side-data/infinite-loading.md b/docs/data/data-grid/server-side-data/infinite-loading.md index c317a2bb4fd25..d2bcc0e58d038 100644 --- a/docs/data/data-grid/server-side-data/infinite-loading.md +++ b/docs/data/data-grid/server-side-data/infinite-loading.md @@ -4,7 +4,7 @@ title: React Server-side infinite loading # Data Grid - Server-side infinite loading [](/x/introduction/licensing/#pro-plan 'Pro plan')🚧 -

Row infinite loading with server side data source.

+

Row infinite loading with server-side data source.

:::warning This feature isn't implemented yet. It's coming. diff --git a/docs/data/data-grid/server-side-data/lazy-loading.md b/docs/data/data-grid/server-side-data/lazy-loading.md index 849d0c9ba7212..6c66183ab0e65 100644 --- a/docs/data/data-grid/server-side-data/lazy-loading.md +++ b/docs/data/data-grid/server-side-data/lazy-loading.md @@ -4,7 +4,7 @@ title: React Server-side lazy loading # Data Grid - Server-side lazy loading [](/x/introduction/licensing/#pro-plan 'Pro plan')🚧 -

Row lazy-loading with server side data source.

+

Row lazy-loading with server-side data source.

:::warning This feature isn't implemented yet. It's coming. diff --git a/docs/data/data-grid/server-side-data/row-grouping.md b/docs/data/data-grid/server-side-data/row-grouping.md index 3dd487f34f27a..537ef8ad0d54c 100644 --- a/docs/data/data-grid/server-side-data/row-grouping.md +++ b/docs/data/data-grid/server-side-data/row-grouping.md @@ -4,7 +4,7 @@ title: React Server-side row grouping # Data Grid - Server-side row grouping [](/x/introduction/licensing/#pro-plan 'Pro plan')🚧 -

Lazy-loaded row grouping with server side data source.

+

Lazy-loaded row grouping with server-side data source.

:::warning This feature isn't implemented yet. It's coming. diff --git a/docs/data/data-grid/server-side-data/tree-data.md b/docs/data/data-grid/server-side-data/tree-data.md index 7ce5524f9c16a..ba9d93e367199 100644 --- a/docs/data/data-grid/server-side-data/tree-data.md +++ b/docs/data/data-grid/server-side-data/tree-data.md @@ -4,7 +4,7 @@ title: React Server-side tree data # Data Grid - Server-side tree data [](/x/introduction/licensing/#pro-plan 'Pro plan')🚧 -

Tree data lazy-loading with server side data source.

+

Tree data lazy-loading with server-side data source.

:::warning This feature isn't implemented yet. It's coming. diff --git a/docs/data/data-grid/state/state.md b/docs/data/data-grid/state/state.md index 8ba74653c21d7..8555125b10096 100644 --- a/docs/data/data-grid/state/state.md +++ b/docs/data/data-grid/state/state.md @@ -11,7 +11,7 @@ This prop has the same format as the returned value of `apiRef.current.exportSta The `initialState` can only be used to set the initial value of the state. The Data Grid will not react if you change the `initialState` value later on. -If you need to fully control specific models, use the control props instead (e.g. [`prop.filterModel`](/x/react-data-grid/filtering/#controlled-filters) or [`prop.sortModel`](https://mui.com/x/react-data-grid/sorting/#controlled-sort-model)). +If you need to fully control specific models, use the control props instead (for example [`prop.filterModel`](/x/react-data-grid/filtering/#controlled-filters) or [`prop.sortModel`](https://mui.com/x/react-data-grid/sorting/#controlled-sort-model)). You can find more information on the corresponding feature documentation page. ::: @@ -96,7 +96,7 @@ If you restore the page using `initialState` before the data is fetched, the Dat ### Save and restore the state from external storage -You can use `apiRef.current.exportState()` to save a snapshot of the state to an external storage (e.g. using local storage or redux). +You can use `apiRef.current.exportState()` to save a snapshot of the state to an external storage (for example using local storage or redux). This way the state can be persisted on refresh or navigating to another page. In the following demo, the state is saved to `localStorage` and restored when the page is refreshed. diff --git a/docs/data/data-grid/virtualization/virtualization.md b/docs/data/data-grid/virtualization/virtualization.md index 7ee018b63d752..201affbcfc9ff 100644 --- a/docs/data/data-grid/virtualization/virtualization.md +++ b/docs/data/data-grid/virtualization/virtualization.md @@ -27,7 +27,7 @@ By default, 2 columns are rendered outside of the viewport. You can change this {{"demo": "ColumnVirtualizationGrid.js", "bg": "inline"}} -You can disable column virtualization by setting the column buffer to a higher number than the number of rendered columns, e.g. with `columnBuffer={columns.length}` or `columnBuffer={Number.MAX_SAFE_INTEGER}`. +You can disable column virtualization by setting the column buffer to a higher number than the number of rendered columns, for example with `columnBuffer={columns.length}` or `columnBuffer={Number.MAX_SAFE_INTEGER}`. ## Disable virtualization diff --git a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.js b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.js index 3d6d7780f6305..75c82c1dfd6bc 100644 --- a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.js +++ b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.js @@ -19,7 +19,7 @@ export default function FieldPlaceholder() { dateAdapter={AdapterDayjs} // Define the date locale to have the right format `day.month.year`. adapterLocale="de" - // Define the translations to have the right placeholders (e.g. `JJJJ` for the year). + // Define the translations to have the right placeholders (for example `JJJJ` for the year). localeText={germanLocale} > diff --git a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx index 3d6d7780f6305..75c82c1dfd6bc 100644 --- a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx +++ b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx @@ -19,7 +19,7 @@ export default function FieldPlaceholder() { dateAdapter={AdapterDayjs} // Define the date locale to have the right format `day.month.year`. adapterLocale="de" - // Define the translations to have the right placeholders (e.g. `JJJJ` for the year). + // Define the translations to have the right placeholders (for example `JJJJ` for the year). localeText={germanLocale} > diff --git a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx.preview b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx.preview index 75dfaceb10638..eba9949fe496e 100644 --- a/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx.preview +++ b/docs/data/date-pickers/adapters-locale/FieldPlaceholder.tsx.preview @@ -7,7 +7,7 @@ dateAdapter={AdapterDayjs} // Define the date locale to have the right format `day.month.year`. adapterLocale="de" - // Define the translations to have the right placeholders (e.g. `JJJJ` for the year). + // Define the translations to have the right placeholders (for example `JJJJ` for the year). localeText={germanLocale} > diff --git a/docs/data/date-pickers/adapters-locale/adapters-locale.md b/docs/data/date-pickers/adapters-locale/adapters-locale.md index 6bf74e5efefaf..6ab1f2944f392 100644 --- a/docs/data/date-pickers/adapters-locale/adapters-locale.md +++ b/docs/data/date-pickers/adapters-locale/adapters-locale.md @@ -96,7 +96,7 @@ import 'moment/locale/de'; ## Meridiem — 12h/24h format -All the time and datetime components will automatically adjust to the locale's time setting, i.e. the 12-hour or 24-hour format. +All the time and datetime components will automatically adjust to the locale's time setting, that is the 12-hour or 24-hour format. You can override the default setting with the `ampm` prop: {{"demo": "AmPMCustomization.js"}} @@ -141,7 +141,7 @@ Here is the list of the currently supported formats: - The month - ✅ 1-based digit (e.g: `08`) - - ✅ Multi-letter values (e.g. `Aug`, `August`) + - ✅ Multi-letter values (for example `Aug`, `August`) - ❌ 1-letter values (e.g: `A`) because several months are represented with the same letter - The day of the month diff --git a/docs/data/date-pickers/base-concepts/base-concepts.md b/docs/data/date-pickers/base-concepts/base-concepts.md index 53befcef5c332..04ae6a33fa60b 100644 --- a/docs/data/date-pickers/base-concepts/base-concepts.md +++ b/docs/data/date-pickers/base-concepts/base-concepts.md @@ -65,12 +65,12 @@ The demo below shows each one of them using their field component: Each _Picker_ is available in a responsive, desktop and mobile variant: -- The responsive component (e.g. `DatePicker`) which renders the desktop component or the mobile one depending on the device it runs on. +- The responsive component (for example `DatePicker`) which renders the desktop component or the mobile one depending on the device it runs on. -- The desktop component (e.g. `DesktopDatePicker`) which works best for mouse devices and large screens. +- The desktop component (for example `DesktopDatePicker`) which works best for mouse devices and large screens. It renders the views inside a popover and allows editing values directly inside the field. -- The mobile component (e.g. `MobileDatePicker`) which works best for touch devices and small screens. +- The mobile component (for example `MobileDatePicker`) which works best for touch devices and small screens. It renders the view inside a modal and does not allow editing values directly inside the field. {{"demo": "ResponsivePickers.js"}} @@ -151,7 +151,7 @@ Importing it from `@mui/x-date-pickers-pro` is enough. ### Responsive components :::info -Some test environments (i.e. `jsdom`) do not support media query. In such cases, components will be rendered in desktop mode. To modify this behavior you can fake the `window.matchMedia`. +Some test environments (for example `jsdom`) do not support media query. In such cases, components will be rendered in desktop mode. To modify this behavior you can fake the `window.matchMedia`. ::: Be aware that running tests in headless browsers might not pass the default mediaQuery (`pointer: fine`). diff --git a/docs/data/date-pickers/date-calendar/date-calendar.md b/docs/data/date-pickers/date-calendar/date-calendar.md index f8dfa6e61c3aa..e17b9b09779e8 100644 --- a/docs/data/date-pickers/date-calendar/date-calendar.md +++ b/docs/data/date-pickers/date-calendar/date-calendar.md @@ -91,7 +91,7 @@ You can select the whole week using the `day` component slot: ## Dynamic data Sometimes it may be necessary to display additional info right in the calendar. -The following demo shows how to add a badge on some day based on server side data: +The following demo shows how to add a badge on some day based on server-side data: {{"demo": "DateCalendarServerRequest.js"}} diff --git a/docs/data/date-pickers/date-picker/date-picker.md b/docs/data/date-pickers/date-picker/date-picker.md index 24099daa4407d..3c56bd50b1279 100644 --- a/docs/data/date-pickers/date-picker/date-picker.md +++ b/docs/data/date-pickers/date-picker/date-picker.md @@ -88,7 +88,7 @@ Use the `openTo` prop to change this behavior: :::success The views will appear in the order defined by the `views` array. If the view defined in `openTo` is not the first view, then the views before will not be included in the default flow -(e.g. view the default behaviors, the `year` is only accessible when clicking on the toolbar). +(for example view the default behaviors, the `year` is only accessible when clicking on the toolbar). ::: ## Landscape orientation diff --git a/docs/data/date-pickers/date-time-picker/date-time-picker.md b/docs/data/date-pickers/date-time-picker/date-time-picker.md index 1ffbfa0388d26..075ec25dd1567 100644 --- a/docs/data/date-pickers/date-time-picker/date-time-picker.md +++ b/docs/data/date-pickers/date-time-picker/date-time-picker.md @@ -90,7 +90,7 @@ Use the `openTo` prop to change this behavior: :::success The views will appear in the order defined by the `views` array. If the view defined in `openTo` is not the first view, then the views before will not be included in the default flow -(e.g. view the default behaviors, the `year` is only accessible when clicking on the toolbar). +(for example view the default behaviors, the `year` is only accessible when clicking on the toolbar). ::: ## Landscape orientation diff --git a/docs/data/date-pickers/digital-clock/digital-clock.md b/docs/data/date-pickers/digital-clock/digital-clock.md index 535650aecf9c7..4e3f2f39061c7 100644 --- a/docs/data/date-pickers/digital-clock/digital-clock.md +++ b/docs/data/date-pickers/digital-clock/digital-clock.md @@ -52,7 +52,7 @@ Views will appear in the order they're included in the `views` array. ## 12h/24h format -The components use the hour format of the locale's time setting, i.e. the 12-hour or 24-hour format. +The components use the hour format of the locale's time setting, that is the 12-hour or 24-hour format. You can force a specific format using the `ampm` prop. @@ -77,7 +77,7 @@ The prop accepts: ## Skip rendering disabled options -With the `skipDisabled` prop, the components don't render options that are not available to the user (e.g. through `minTime`, `maxTime`, `shouldDisabledTime` etc.). +With the `skipDisabled` prop, the components don't render options that are not available to the user (for example through `minTime`, `maxTime`, `shouldDisabledTime` etc.). The following example combines these properties to customize which options are rendered. diff --git a/docs/data/date-pickers/time-clock/time-clock.md b/docs/data/date-pickers/time-clock/time-clock.md index 042a32a60af5a..9f545b257e100 100644 --- a/docs/data/date-pickers/time-clock/time-clock.md +++ b/docs/data/date-pickers/time-clock/time-clock.md @@ -46,7 +46,7 @@ Views will appear in the order they're included in the `views` array. ## 12h/24h format -The component uses the hour format of the locale's time setting, i.e. the 12-hour or 24-hour format. +The component uses the hour format of the locale's time setting, that is the 12-hour or 24-hour format. You can force a specific format using the `ampm` prop. diff --git a/docs/data/introduction/installation/installation.md b/docs/data/introduction/installation/installation.md index 46f58f64c82fa..2b47b9ea98020 100644 --- a/docs/data/introduction/installation/installation.md +++ b/docs/data/introduction/installation/installation.md @@ -11,6 +11,6 @@ MUI X components have a peer dependency on `@mui/material`: the installation [i ## Components -Note that you only need to install the packages corresponding to the components you're using—e.g., Data Grid users don't need to install the Date and Time Pickers. +Note that you only need to install the packages corresponding to the components you're using—for example Data Grid users don't need to install the Date and Time Pickers. {{"component": "modules/components/InstallationGrid.js"}} diff --git a/docs/data/introduction/licensing/licensing.md b/docs/data/introduction/licensing/licensing.md index 6764647c39533..13bd5aecba8cf 100644 --- a/docs/data/introduction/licensing/licensing.md +++ b/docs/data/introduction/licensing/licensing.md @@ -221,7 +221,7 @@ While we recommend hard-coding the license key in git, you can also use an envir This method is required if your codebase is "source-available" (to hide the license key), and can be preferred if you want to granularly share the license key with your licensed developers. ::: -The license key is validated on the server and client side so you must expose the environment variable to the browser. +The license key is validated on the server and client-side so you must expose the environment variable to the browser. To do this, you need to prefix the environment variables with `NEXT_PUBLIC_` as explained in the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser): ```tsx diff --git a/docs/data/migration/migration-data-grid-v4/migration-data-grid-v4.md b/docs/data/migration/migration-data-grid-v4/migration-data-grid-v4.md index 3f171622cafb2..c3c64e54780ce 100644 --- a/docs/data/migration/migration-data-grid-v4/migration-data-grid-v4.md +++ b/docs/data/migration/migration-data-grid-v4/migration-data-grid-v4.md @@ -31,8 +31,8 @@ Using Material UI v4 with v5 can be achieved with the following steps: import { createGenerateClassName } from '@material-ui/core/styles'; const generateClassName = createGenerateClassName({ - // By enabling this option, if you have non-MUI elements (e.g. `
`) - // using MUI classes (e.g. `.MuiButton`) they will lose styles. + // By enabling this option, if you have non-MUI elements (for example `
`) + // using MUI classes (for example `.MuiButton`) they will lose styles. // Make sure to convert them to use `styled()` or `` first. disableGlobal: true, // Class names will receive this seed to avoid name collisions. @@ -234,7 +234,7 @@ To use the v5 version of MUI X, you first need to update to the new package nam You can use the new `initialState` prop instead. Note that `initialState` only allows the `preferencePanel`, `filter.filterModel` and `sort.sortModel` keys. - To fully control the state, use the feature's model prop and change callback (e.g. `filterModel` and `onFilterModelChange`). + To fully control the state, use the feature's model prop and change callback (for example `filterModel` and `onFilterModelChange`). ```diff = { /** - * Name of the prop, e.g. 'children' + * Name of the prop, for example 'children' */ propName: Extract; /** diff --git a/docs/src/modules/utils/postProcessImport.ts b/docs/src/modules/utils/postProcessImport.ts index 9418831f99302..6366a0be16c04 100644 --- a/docs/src/modules/utils/postProcessImport.ts +++ b/docs/src/modules/utils/postProcessImport.ts @@ -21,7 +21,7 @@ export const ADAPTER_TO_LIBRARY: Record = { const PICKERS_ADAPTER_REGEX = /^@mui\/(lab|x-date-pickers(?:-pro)?)\/(?Adapter.*)/; export const postProcessImport = (importName: string): Record | null => { - // e.g. date-fns + // for example date-fns const dateAdapterMatch = PICKERS_ADAPTER_REGEX.exec(importName); if (dateAdapterMatch !== null) { /** diff --git a/docs/translations/api-docs/date-pickers/date-calendar/date-calendar.json b/docs/translations/api-docs/date-pickers/date-calendar/date-calendar.json index 7c2a6f4423f09..4fb2face3be80 100644 --- a/docs/translations/api-docs/date-pickers/date-calendar/date-calendar.json +++ b/docs/translations/api-docs/date-pickers/date-calendar/date-calendar.json @@ -78,7 +78,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/date-field/date-field.json b/docs/translations/api-docs/date-pickers/date-field/date-field.json index 8b90f9e6a9bf4..e023b91557cb4 100644 --- a/docs/translations/api-docs/date-pickers/date-field/date-field.json +++ b/docs/translations/api-docs/date-pickers/date-field/date-field.json @@ -89,7 +89,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/date-picker/date-picker.json b/docs/translations/api-docs/date-pickers/date-picker/date-picker.json index 3e108cea87188..b668d9cce6fad 100644 --- a/docs/translations/api-docs/date-pickers/date-picker/date-picker.json +++ b/docs/translations/api-docs/date-pickers/date-picker/date-picker.json @@ -116,7 +116,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/date-range-calendar/date-range-calendar.json b/docs/translations/api-docs/date-pickers/date-range-calendar/date-range-calendar.json index 818fb79cac8f5..f8ea0bbeb6f74 100644 --- a/docs/translations/api-docs/date-pickers/date-range-calendar/date-range-calendar.json +++ b/docs/translations/api-docs/date-pickers/date-range-calendar/date-range-calendar.json @@ -94,7 +94,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json b/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json index a13177b244d05..c611e6d21646d 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker/date-range-picker.json @@ -125,7 +125,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json b/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json index bcabb346acaa9..4e77982370d01 100644 --- a/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json +++ b/docs/translations/api-docs/date-pickers/date-time-field/date-time-field.json @@ -106,7 +106,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json b/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json index 2b5be2cd11918..ed02c1200ef99 100644 --- a/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-picker/date-time-picker.json @@ -136,7 +136,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json b/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json index 7a822c7ee04f0..0f72e517db20d 100644 --- a/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-range-picker/date-time-range-picker.json @@ -149,7 +149,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json index e97a6a69024fc..b79689614dc6f 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-picker/desktop-date-picker.json @@ -113,7 +113,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json index 619d7999ae43a..37f5382422fba 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker/desktop-date-range-picker.json @@ -122,7 +122,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json index 5b1bd9b0032a2..31eb2d4af4acd 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-picker/desktop-date-time-picker.json @@ -133,7 +133,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json index 84238b6d79e37..0ab25ea155fdf 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-range-picker/desktop-date-time-range-picker.json @@ -146,7 +146,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json index bec2bd8dfdde7..1e0eaf91dca0b 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-picker/mobile-date-picker.json @@ -113,7 +113,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json index 8f7d5c9375e2f..030601daceb30 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker/mobile-date-range-picker.json @@ -119,7 +119,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json index d5ba4d1d7dad4..0dec4b4e84de4 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-time-picker/mobile-date-time-picker.json @@ -133,7 +133,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json index b2f99f529dbcb..135c8a9594b24 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-time-range-picker/mobile-date-time-range-picker.json @@ -143,7 +143,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json index fde01a7e1b8d1..3ca6292ec5f9f 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-range-field/multi-input-date-range-field.json @@ -53,7 +53,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json index c06fd6bdb2613..f6be2a6546e27 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field/multi-input-date-time-range-field.json @@ -70,7 +70,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json index 04844904a07e3..238caa628da90 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-range-field/single-input-date-range-field.json @@ -89,7 +89,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json index 61340a97f9b64..191ca78e1814a 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field/single-input-date-time-range-field.json @@ -106,7 +106,7 @@ "description": "The currently selected sections. This prop accepts four formats: 1. If a number is provided, the section at this index will be selected. 2. If a string of type FieldSectionType is provided, the first section with that name will be selected. 3. If "all" is provided, all the sections will be selected. 4. If null is provided, no section will be selected. If not provided, the selected sections will be handled internally." }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json b/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json index 39559f2e8f027..b59bbd0f34537 100644 --- a/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-picker/static-date-picker.json @@ -88,7 +88,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json b/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json index 97198fdef5c17..5c312fa77d836 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker/static-date-range-picker.json @@ -93,7 +93,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "position": "The date to test, 'start' or 'end'.", diff --git a/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json b/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json index c50bd09bffb6e..cda54bc81038e 100644 --- a/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-time-picker/static-date-time-picker.json @@ -108,7 +108,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.
Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", + "description": "Disable specific date.
Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance.", "typeDescriptions": { "day": "The date to test.", "boolean": "If true the date will be disabled." diff --git a/docsTech/processing.md b/docsTech/processing.md index 600f63e9356d8..bc39ac111e58e 100644 --- a/docsTech/processing.md +++ b/docsTech/processing.md @@ -14,7 +14,7 @@ For each pattern, you will find a list of where such pattern is used, why it is - Pipe-processing - Plugin state enrichment - - Add custom behavior to an api method + - Add custom behavior to an API method - Feature limitation - Component children processing - Family-processing @@ -39,8 +39,8 @@ We can classify the pipe-processing into several categories: A few possible reasons could be to: -- Add some columns (eg: processor of the Selection plugin) -- Re-order the columns (eg: processor of the Column Pinning plugin). +- Add some columns (for example processor of the Selection plugin) +- Re-order the columns (for example processor of the Column Pinning plugin). **Example**: @@ -78,7 +78,7 @@ useGridRegisterPipeProcessor(apiRef, 'hydrateColumns', addCustomFeatureColumn); **Why register to this processing** -- Modify the base height of a row or add the height of some custom elements (eg: processor of the Detail Panel plugin increases the row height when the detail panel is open). +- Modify the base height of a row or add the height of some custom elements (for example processor of the Detail Panel plugin increases the row height when the detail panel is open). **Example**: @@ -107,7 +107,7 @@ useGridRegisterPipeProcessor(apiRef, 'rowHeight', addCustomFeatureHeight); **Publisher**: `useGridRows` plugin before updating `state.rows`. -**Why register to this processing**: Add some rows (eg: processor of the Aggregation plugin). +**Why register to this processing**: Add some rows (for example processor of the Aggregation plugin). **Example**: @@ -142,9 +142,9 @@ const addGroupFooterRows = React.useCallback>(( useGridRegisterPipeProcessor(apiRef, 'hydrateRows', addGroupFooterRows); ``` -### Add custom behavior to an api method +### Add custom behavior to an API method -**Goal**: To add some data on the value returned by an api method (eg: `exportState`) or to apply some custom behavior based on the input value of an api method (eg: `restoreState`) +**Goal**: To add some data on the value returned by an API method (for example `exportState`) or to apply some custom behavior based on the input value of an API method (for example `restoreState`) #### List @@ -238,7 +238,7 @@ useGridRegisterPipeProcessor(apiRef, 'scrollToIndexes', calculateScrollLeft); ### Feature limitation -**Goal**: To block the application of another plugin (eg: `canBeReorder`) +**Goal**: To block the application of another plugin (for example `canBeReorder`) #### List diff --git a/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts b/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts index be1527ee8797e..0c06460e0e3ad 100644 --- a/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts +++ b/packages/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts @@ -31,7 +31,7 @@ import type { DataGridPremiumProcessedProps } from '../../../models/dataGridPrem const missingOnProcessRowUpdateErrorWarning = buildWarning( [ 'MUI X: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', - 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. ` ...} />`.', + 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, for example ` ...} />`.', 'For more detail, see https://mui.com/x/react-data-grid/editing/#server-side-persistence.', ], 'error', diff --git a/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx index feb20560b0f69..d64f7916d68fd 100644 --- a/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -278,16 +278,16 @@ export const useGridColumnPinning = ( * on must be moved to left or right to make room for it. The ^^^ below represents the column * which gave space to receive X. * - * | X | B | C | D | -> | B | C | D | X | (e.g. X moved to after D, so delta=1) + * | X | B | C | D | -> | B | C | D | X | (for example X moved to after D, so delta=1) * ^^^ ^^^ * - * | A | B | C | X | -> | X | A | B | C | (e.g. X moved before A, so delta=-1) + * | A | B | C | X | -> | X | A | B | C | (for example X moved before A, so delta=-1) * ^^^ ^^^ * * If column P is pinned, it will not move to provide space. However, it will jump to the next * non-pinned column. * - * | X | B | P | D | -> | B | D | P | X | (e.g. X moved to after D, with P pinned) + * | X | B | P | D | -> | B | D | P | X | (for example X moved to after D, with P pinned) * ^^^ ^^^ */ const siblingField = latestColumnFields[targetIndex - delta]; diff --git a/packages/x-data-grid-pro/src/hooks/features/detailPanel/useGridDetailPanel.ts b/packages/x-data-grid-pro/src/hooks/features/detailPanel/useGridDetailPanel.ts index b7e8fc2256cb6..f618ffe20a7f2 100644 --- a/packages/x-data-grid-pro/src/hooks/features/detailPanel/useGridDetailPanel.ts +++ b/packages/x-data-grid-pro/src/hooks/features/detailPanel/useGridDetailPanel.ts @@ -303,7 +303,7 @@ export const useGridDetailPanel = ( const heightCache = gridDetailPanelExpandedRowsHeightCacheSelector(apiRef); - initialValue.detail = heightCache[row.id] ?? 0; // Fallback to zero because the cache might not be ready yet (e.g. page was changed) + initialValue.detail = heightCache[row.id] ?? 0; // Fallback to zero because the cache might not be ready yet (for example page was changed) return initialValue; }, [apiRef, expandedRowIds, updateCachesIfNeeded], diff --git a/packages/x-data-grid-pro/src/models/dataSource.ts b/packages/x-data-grid-pro/src/models/dataSource.ts index 3818c77a72011..53bb071e6b4f7 100644 --- a/packages/x-data-grid-pro/src/models/dataSource.ts +++ b/packages/x-data-grid-pro/src/models/dataSource.ts @@ -37,7 +37,7 @@ interface GetRowsResponse { rows: GridRowModel[]; /** * To reflect updates in total `rowCount` (optional). - * Useful when the `rowCount` is inaccurate (e.g. when filtering) or not available upfront. + * Useful when the `rowCount` is inaccurate (for example when filtering) or not available upfront. */ rowCount?: number; /** diff --git a/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx index d8f8c1505cd95..6c5ccec2e3392 100644 --- a/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx @@ -873,7 +873,7 @@ describe(' - Cell editing', () => { apiRef.current.subscribeEvent('cellEditStart', listener); const cell = getCell(0, 1); userEvent.mousePress(cell); - fireEvent.keyDown(cell, { key: 'a', [key]: true }); // e.g. Ctrl + A, copy + fireEvent.keyDown(cell, { key: 'a', [key]: true }); // for example Ctrl + A, copy expect(listener.callCount).to.equal(0); }); }); diff --git a/packages/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx index 8827d24988aef..2b354aba0ec37 100644 --- a/packages/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx @@ -62,7 +62,7 @@ describe(' - Components', () => { const eventToFire = prop.replace(/^on([A-Z])/, (match) => match.slice(2).toLowerCase(), - ) as EventType; // e.g. onDoubleClick -> doubleClick + ) as EventType; // for example onDoubleClick -> doubleClick const cell = getCell(0, 0); if (event !== 'cellMouseUp') { @@ -111,7 +111,7 @@ describe(' - Components', () => { const eventToFire = prop.replace(/^on([A-Z])/, (match) => match.slice(2).toLowerCase(), - ) as EventType; // e.g. onDoubleClick -> doubleClick + ) as EventType; // for example onDoubleClick -> doubleClick fireEvent[eventToFire](getRow(0)); expect(propHandler.callCount).to.equal(1); diff --git a/packages/x-data-grid/src/components/cell/GridActionsCell.tsx b/packages/x-data-grid/src/components/cell/GridActionsCell.tsx index c3a64881b58a0..9030408f0339e 100644 --- a/packages/x-data-grid/src/components/cell/GridActionsCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridActionsCell.tsx @@ -171,7 +171,7 @@ function GridActionsCell(props: GridActionsCellProps) { if (newIndex !== focusedButtonIndex) { event.preventDefault(); // Prevent scrolling - event.stopPropagation(); // Don't stop propagation for other keys, e.g. ArrowUp + event.stopPropagation(); // Don't stop propagation for other keys, for example ArrowUp setFocusedButtonIndex(newIndex); } }; diff --git a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx index e9bdf86d06ccf..1dc95b6953136 100644 --- a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx +++ b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputBoolean.tsx @@ -11,7 +11,7 @@ export type GridFilterInputBooleanProps = GridFilterInputValueProps & clearButton?: React.ReactNode | null; /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive?: boolean; }; @@ -133,7 +133,7 @@ GridFilterInputBoolean.propTypes = { focusElementRef: refType, /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive: PropTypes.bool, item: PropTypes.shape({ diff --git a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx index dcfef282d86ba..a613c573a082f 100644 --- a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx +++ b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputDate.tsx @@ -13,7 +13,7 @@ export type GridFilterInputDateProps = GridFilterInputValueProps & clearButton?: React.ReactNode | null; /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive?: boolean; }; @@ -135,7 +135,7 @@ GridFilterInputDate.propTypes = { ]), /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive: PropTypes.bool, item: PropTypes.shape({ diff --git a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx index 5ee0b5e7226a4..6836f735cb51d 100644 --- a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx +++ b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputSingleSelect.tsx @@ -60,7 +60,7 @@ export type GridFilterInputSingleSelectProps = GridFilterInputValueProps & clearButton?: React.ReactNode | null; /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive?: boolean; type?: 'singleSelect'; @@ -184,7 +184,7 @@ GridFilterInputSingleSelect.propTypes = { ]), /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive: PropTypes.bool, item: PropTypes.shape({ diff --git a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx index 6535b9420cfaa..4ee1baf459bf9 100644 --- a/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx +++ b/packages/x-data-grid/src/components/panel/filterPanel/GridFilterInputValue.tsx @@ -13,7 +13,7 @@ export type GridTypeFilterInputValueProps = GridFilterInputValueProps & clearButton?: React.ReactNode | null; /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive?: boolean; }; @@ -115,7 +115,7 @@ GridFilterInputValue.propTypes = { ]), /** * It is `true` if the filter either has a value or an operator with no value - * required is selected (e.g. `isEmpty`) + * required is selected (for example `isEmpty`) */ isFilterActive: PropTypes.bool, item: PropTypes.shape({ diff --git a/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx index 9f6ed9c817b6c..c547c07b93e75 100644 --- a/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/x-data-grid/src/hooks/features/columnResize/useGridColumnResize.tsx @@ -193,7 +193,7 @@ function excludeOutliers(inputValues: number[], factor: number) { const iqr = q3 - q1; // We make a small adjustment if `iqr < 5` for the cases where the IQR is - // very small (e.g. zero) due to very close by values in the input data. + // very small (for example zero) due to very close by values in the input data. // Otherwise, with an IQR of `0`, anything outside that would be considered // an outlier, but it makes more sense visually to allow for this 5px variance // rather than showing a cropped cell. diff --git a/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts b/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts index 79f51c6d850b5..954b96ba8ee02 100644 --- a/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts +++ b/packages/x-data-grid/src/hooks/features/editing/useGridCellEditing.ts @@ -43,7 +43,7 @@ import { const missingOnProcessRowUpdateErrorWarning = buildWarning( [ 'MUI X: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', - 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. ` ...} />`.', + 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, for example ` ...} />`.', 'For more detail, see https://mui.com/x/react-data-grid/editing/#server-side-persistence.', ], 'error', diff --git a/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts b/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts index e8180dd510b20..5fe365be8a4e9 100644 --- a/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts +++ b/packages/x-data-grid/src/hooks/features/editing/useGridRowEditing.ts @@ -50,7 +50,7 @@ import { GRID_ACTIONS_COLUMN_TYPE } from '../../../colDef'; const missingOnProcessRowUpdateErrorWarning = buildWarning( [ 'MUI X: A call to `processRowUpdate` threw an error which was not handled because `onProcessRowUpdateError` is missing.', - 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, e.g. ` ...} />`.', + 'To handle the error pass a callback to the `onProcessRowUpdateError` prop, for example ` ...} />`.', 'For more detail, see https://mui.com/x/react-data-grid/editing/#server-side-persistence.', ], 'error', diff --git a/packages/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index 20d021178b453..e2988a3188d33 100644 --- a/packages/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -258,7 +258,7 @@ const buildAggregatedFilterItemsApplier = ( } // We generate a new function with `new Function()` to avoid expensive patterns for JS engines - // such as a dynamic object assignment, e.g. `{ [dynamicKey]: value }`. + // such as a dynamic object assignment, for example `{ [dynamicKey]: value }`. const filterItemCore = new Function( 'appliers', 'row', diff --git a/packages/x-data-grid/src/models/api/gridRowsMetaApi.ts b/packages/x-data-grid/src/models/api/gridRowsMetaApi.ts index 66c11093ada74..77e744b599aa5 100644 --- a/packages/x-data-grid/src/models/api/gridRowsMetaApi.ts +++ b/packages/x-data-grid/src/models/api/gridRowsMetaApi.ts @@ -54,7 +54,7 @@ export interface GridRowsMetaPrivateApi { rowHasAutoHeight: (id: GridRowId) => boolean; /** * Returns the index of the last row measured. - * The value considers only the rows reachable by scroll, e.g. first row has index=0 in all pages. + * The value considers only the rows reachable by scroll, for example first row has index=0 in all pages. * @returns {number} The index of the last measured row. */ getLastMeasuredRowIndex: () => number; diff --git a/packages/x-data-grid/src/utils/createSelector.ts b/packages/x-data-grid/src/utils/createSelector.ts index 582c6fa696b69..c325c007ad197 100644 --- a/packages/x-data-grid/src/utils/createSelector.ts +++ b/packages/x-data-grid/src/utils/createSelector.ts @@ -40,7 +40,7 @@ const cache = new WeakMap>(); const missingInstanceIdWarning = buildWarning([ 'MUI X: A selector was called without passing the instance ID, which may impact the performance of the grid.', - 'To fix, call it with `apiRef`, e.g. `mySelector(apiRef)`, or pass the instance ID explicitly, e.g. `mySelector(state, apiRef.current.instanceId)`.', + 'To fix, call it with `apiRef`, for example `mySelector(apiRef)`, or pass the instance ID explicitly, for example `mySelector(state, apiRef.current.instanceId)`.', ]); function checkIsAPIRef(value: any) { diff --git a/packages/x-data-grid/src/utils/domUtils.ts b/packages/x-data-grid/src/utils/domUtils.ts index 29dc7bf8df3c6..cbf4071ea77a7 100644 --- a/packages/x-data-grid/src/utils/domUtils.ts +++ b/packages/x-data-grid/src/utils/domUtils.ts @@ -244,7 +244,7 @@ export function findGridCells(api: GridPrivateApiCommunity, field: string) { function queryRows(api: GridPrivateApiCommunity) { return api.virtualScrollerRef.current!.querySelectorAll( - // Use > to ignore rows from nested data grids (e.g. in detail panel) + // Use > to ignore rows from nested data grids (for example in detail panel) `:scope > div > div > .${gridClasses.row}`, ); } diff --git a/packages/x-data-grid/src/utils/keyboardUtils.ts b/packages/x-data-grid/src/utils/keyboardUtils.ts index 88e60807ddace..358005ebf3063 100644 --- a/packages/x-data-grid/src/utils/keyboardUtils.ts +++ b/packages/x-data-grid/src/utils/keyboardUtils.ts @@ -10,7 +10,7 @@ export const isEscapeKey = (key: string): boolean => key === 'Escape'; */ export const isTabKey = (key: string): boolean => key === 'Tab'; -// Non printable keys have a name, e.g. "ArrowRight", see the whole list: +// Non printable keys have a name, for example "ArrowRight", see the whole list: // https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values // So event.key.length === 1 is often enough. // diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 5657e124d0397..328bd4358a57d 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -762,7 +762,7 @@ DateRangeCalendar.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx index 30bcf5403022a..679515dd3ab2a 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx @@ -296,7 +296,7 @@ DateRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx index 2f9d4dedd783b..60b321c28d0f0 100644 --- a/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateTimeRangePicker/DateTimeRangePicker.tsx @@ -331,7 +331,7 @@ DateTimeRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx index 53b40e695207e..c3ae2ac5605da 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx @@ -331,7 +331,7 @@ DesktopDateRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx index 3d189efa0a049..0d6c6fa6d85cf 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateTimeRangePicker/DesktopDateTimeRangePicker.tsx @@ -489,7 +489,7 @@ DesktopDateTimeRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx index fbb6cc4ec78cd..c65509df1c359 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx @@ -327,7 +327,7 @@ MobileDateRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx index 5346acf574457..4bce95d92d918 100644 --- a/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateTimeRangePicker/MobileDateTimeRangePicker.tsx @@ -484,7 +484,7 @@ MobileDateTimeRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx index 4a4e615be45df..e8097c7106430 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx @@ -314,7 +314,7 @@ MultiInputDateRangeField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx index cd0a2bc5e8f0e..30283e239e2d7 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx @@ -345,7 +345,7 @@ MultiInputDateTimeRangeField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx index d758ed6ea40fc..a576e897b46e1 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx @@ -279,7 +279,7 @@ SingleInputDateRangeField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx index 7b86fc27045bb..86c93f642d88b 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx @@ -312,7 +312,7 @@ SingleInputDateTimeRangeField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx index edc98f0a5ef85..bb3c4aa34ed7d 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx @@ -237,7 +237,7 @@ StaticDateRangePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers-pro/src/internals/models/dateRange.ts b/packages/x-date-pickers-pro/src/internals/models/dateRange.ts index f1ac7d14580ba..b9404d00d6322 100644 --- a/packages/x-date-pickers-pro/src/internals/models/dateRange.ts +++ b/packages/x-date-pickers-pro/src/internals/models/dateRange.ts @@ -13,7 +13,7 @@ export interface DayRangeValidationProps { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx index 069f668edd366..b14728935e28f 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx @@ -549,7 +549,7 @@ DateCalendar.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DateField/DateField.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx index 61abeceed5ca6..131d6f5ed9874 100644 --- a/packages/x-date-pickers/src/DateField/DateField.tsx +++ b/packages/x-date-pickers/src/DateField/DateField.tsx @@ -276,7 +276,7 @@ DateField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx index e84f5887a9c48..65f36dbf7537a 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx @@ -285,7 +285,7 @@ DatePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx index ef7fe6f7229c3..7f9cf35580d1f 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx @@ -309,7 +309,7 @@ DateTimeField.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx index 0056962d249ad..68edc264b00fb 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx @@ -323,7 +323,7 @@ DateTimePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx index 922a22b3f23ca..9f75443212d83 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx @@ -330,7 +330,7 @@ DesktopDatePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index 65ab6975bd82d..785d032f92a3f 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -418,7 +418,7 @@ DesktopDateTimePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx index 33c402bfe9c33..24a8915df648b 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx @@ -327,7 +327,7 @@ MobileDatePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx index 0573711c48938..4291fcc318934 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx @@ -377,7 +377,7 @@ MobileDateTimePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx index a52fa76e97363..b0e540e898795 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx @@ -228,7 +228,7 @@ StaticDatePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx index 7c86a187e96da..1262583a39fec 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx +++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx @@ -277,7 +277,7 @@ StaticDateTimePicker.propTypes = { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx index 993294c4157c4..5e472471459b5 100644 --- a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx @@ -246,7 +246,7 @@ function useClickAwayListener( // TODO This behavior is not tested automatically // It's unclear whether this is due to different update semantics in test (batched in act() vs discrete on click). // Or if this is a timing related issues due to different Transition components - // Once we get rid of all the manual scheduling (e.g. setTimeout(update, 0)) we can revisit this code+test. + // Once we get rid of all the manual scheduling (for example setTimeout(update, 0)) we can revisit this code+test. if (active) { const doc = ownerDocument(nodeRef.current); diff --git a/packages/x-date-pickers/src/internals/hooks/useOpenState.ts b/packages/x-date-pickers/src/internals/hooks/useOpenState.ts index f733e60b7d28e..558814e5be267 100644 --- a/packages/x-date-pickers/src/internals/hooks/useOpenState.ts +++ b/packages/x-date-pickers/src/internals/hooks/useOpenState.ts @@ -11,7 +11,7 @@ export const useOpenState = ({ open, onOpen, onClose }: OpenStateProps) => { const [openState, setIsOpenState] = React.useState(false); // It is required to update inner state in useEffect in order to avoid situation when - // Our component is not mounted yet, but `open` state is set to `true` (e.g. initially opened) + // Our component is not mounted yet, but `open` state is set to `true` (for example initially opened) React.useEffect(() => { if (isControllingOpenProp) { if (typeof open !== 'boolean') { diff --git a/packages/x-date-pickers/src/internals/models/validation.ts b/packages/x-date-pickers/src/internals/models/validation.ts index aee8f2a3833e9..2488416d7848e 100644 --- a/packages/x-date-pickers/src/internals/models/validation.ts +++ b/packages/x-date-pickers/src/internals/models/validation.ts @@ -76,7 +76,7 @@ export interface DayValidationProps { /** * Disable specific date. * - * Warning: This function can be called multiple times (e.g. when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. + * Warning: This function can be called multiple times (for example when rendering date calendar, checking if focus can be moved to a certain date, etc.). Expensive computations can impact performance. * * @template TDate * @param {TDate} day The date to test. diff --git a/scripts/README.md b/scripts/README.md index 3af2512675780..e810994e0d2bd 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -23,7 +23,7 @@ yarn release:changelog --release The branch to release (default: master) ``` -You can also provide the github token by setting `process.env.GITHUB_TOKEN` variable. +You can also provide the GitHub token by setting `process.env.GITHUB_TOKEN` variable. In case of a problem, another method to generate the changelog is available at the end of this page. diff --git a/scripts/l10n.ts b/scripts/l10n.ts index bd21115264581..50a8db8786507 100644 --- a/scripts/l10n.ts +++ b/scripts/l10n.ts @@ -142,7 +142,7 @@ function extractTranslations(translationsPath: string): [TranslationsByGroup, Tr (property.key as babelTypes.Identifier).name || `'${(property.key as babelTypes.StringLiteral).value}'`; - // Ignore translations for MUI Core components, e.g. MuiTablePagination + // Ignore translations for MUI Core components, for example MuiTablePagination if (key.startsWith('Mui')) { return; } From 398eec2e67ab132ad02bda455ac06810ee3d7016 Mon Sep 17 00:00:00 2001 From: Olivier Tassinari Date: Sun, 17 Mar 2024 17:31:07 +0100 Subject: [PATCH 46/49] [core] Use Circle CI context Same as https://github.com/mui/material-ui/pull/41532 --- .circleci/config.yml | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 200f447444cb4..9936d6febdf72 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -18,7 +18,7 @@ parameters: type: string default: '' -defaults: &defaults +default-job: &default-job parameters: react-dist-tag: description: The dist-tag of react to be used @@ -46,6 +46,10 @@ defaults: &defaults # restore_cache: # key: v1-repo-{{ .Branch }}-{{ .Revision }} +default-context: &default-context + context: + - org-global + commands: install_js: parameters: @@ -114,7 +118,7 @@ commands: jobs: checkout: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -125,7 +129,7 @@ jobs: name: Check for duplicated packages command: yarn deduplicate test_unit: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -149,7 +153,7 @@ jobs: chmod +x codecov ./codecov -t ${CODECOV_TOKEN} -Z -F "$REACT_DIST_TAG-jsdom" test_lint: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -163,7 +167,7 @@ jobs: name: Lint Markdown command: yarn markdownlint test_static: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -194,7 +198,7 @@ jobs: yarn docs:link-check git add -A && git diff --exit-code --staged test_browser: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.41.2-focal environment: @@ -211,7 +215,7 @@ jobs: path: /tmp/_karma_webpack_ destination: artifact-file test_types: - <<: *defaults + <<: *default-job steps: - checkout - install_js @@ -227,7 +231,7 @@ jobs: environment: NODE_OPTIONS: --max-old-space-size=3072 test_e2e: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.41.2-focal environment: @@ -240,7 +244,7 @@ jobs: name: Run e2e tests command: yarn test:e2e test_e2e_website: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.41.2-focal environment: @@ -255,7 +259,7 @@ jobs: environment: PLAYWRIGHT_TEST_BASE_URL: << parameters.e2e-base-url >> test_regressions: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.41.2-focal environment: @@ -271,7 +275,7 @@ jobs: name: Upload screenshots to Argos CI command: yarn test:argos run_danger: - <<: *defaults + <<: *default-job docker: - image: mcr.microsoft.com/playwright:v1.41.2-focal environment: @@ -291,36 +295,46 @@ workflows: when: equal: [pipeline, << pipeline.parameters.workflow >>] jobs: - - checkout + - checkout: + <<: *default-context - test_unit: + <<: *default-context requires: - checkout - test_lint: + <<: *default-context requires: - checkout - test_static: + <<: *default-context requires: - checkout - test_browser: + <<: *default-context requires: - checkout - test_types: + <<: *default-context requires: - checkout - test_e2e: + <<: *default-context requires: - checkout - test_regressions: + <<: *default-context requires: - checkout - run_danger: + <<: *default-context requires: - checkout e2e-website: when: equal: [e2e-website, << pipeline.parameters.workflow >>] jobs: - - checkout + - checkout: + <<: *default-context - test_e2e_website: requires: - checkout From bae7c103ecfee7b183503c77961236f841370ca4 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Mon, 18 Mar 2024 11:05:06 +0100 Subject: [PATCH 47/49] [DataGridPremium] Fix single grouping column sorting (#9679) --- ...owGroupingFilteringSingleGroupingColDef.js} | 2 +- ...wGroupingFilteringSingleGroupingColDef.tsx} | 2 +- .../data-grid/row-grouping/row-grouping.md | 9 ++++++--- .../rowGrouping/createGroupingColDef.tsx | 6 +++--- .../tests/rowGrouping.DataGridPremium.test.tsx | 18 +++++++++--------- 5 files changed, 20 insertions(+), 17 deletions(-) rename docs/data/data-grid/row-grouping/{RowGroupingSortingSingleGroupingColDef.js => RowGroupingFilteringSingleGroupingColDef.js} (96%) rename docs/data/data-grid/row-grouping/{RowGroupingSortingSingleGroupingColDef.tsx => RowGroupingFilteringSingleGroupingColDef.tsx} (96%) diff --git a/docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.js b/docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.js similarity index 96% rename from docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.js rename to docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.js index 9274284cb3d09..1b61c77665195 100644 --- a/docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.js +++ b/docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.js @@ -11,7 +11,7 @@ import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; import Select from '@mui/material/Select'; -export default function RowGroupingSortingSingleGroupingColDef() { +export default function RowGroupingFilteringSingleGroupingColDef() { const data = useMovieData(); const [mainGroupingCriteria, setMainGroupingCriteria] = React.useState('undefined'); diff --git a/docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.tsx b/docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.tsx similarity index 96% rename from docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.tsx rename to docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.tsx index bc0cf2b01f876..ec2ffa02b495a 100644 --- a/docs/data/data-grid/row-grouping/RowGroupingSortingSingleGroupingColDef.tsx +++ b/docs/data/data-grid/row-grouping/RowGroupingFilteringSingleGroupingColDef.tsx @@ -11,7 +11,7 @@ import FormControl from '@mui/material/FormControl'; import InputLabel from '@mui/material/InputLabel'; import Select from '@mui/material/Select'; -export default function RowGroupingSortingSingleGroupingColDef() { +export default function RowGroupingFilteringSingleGroupingColDef() { const data = useMovieData(); const [mainGroupingCriteria, setMainGroupingCriteria] = React.useState('undefined'); diff --git a/docs/data/data-grid/row-grouping/row-grouping.md b/docs/data/data-grid/row-grouping/row-grouping.md index bb7aca8496d32..08ef4281d6cfd 100644 --- a/docs/data/data-grid/row-grouping/row-grouping.md +++ b/docs/data/data-grid/row-grouping/row-grouping.md @@ -271,17 +271,20 @@ To change the default cell indent, you can use the `--DataGrid-cellOffsetMultipl ### Single grouping column -When using `rowGroupingColumnMode = "single"`, the default behavior is to apply the `sortComparator` and `filterOperators` of the top-level grouping criteria. +When using `rowGroupingColumnMode = "single"`, the default behavior is to: + +- sort each grouping criteria using the `sortComparator` of the column +- apply the `filterOperators` of the top-level grouping criteria If you are rendering leaves with the `leafField` property of `groupingColDef`, the sorting and filtering will be applied on the leaves based on the `sortComparator` and `filterOperators` of their original column. -In both cases, you can force the sorting and filtering to be applied on another grouping criteria with the `mainGroupingCriteria` property of `groupingColDef` +You can force the filtering to be applied on another grouping criteria with the `mainGroupingCriteria` property of `groupingColDef` :::warning This feature is not yet compatible with `sortingMode = "server"` and `filteringMode = "server"`. ::: -{{"demo": "RowGroupingSortingSingleGroupingColDef.js", "bg": "inline", "defaultCodeOpen": false}} +{{"demo": "RowGroupingFilteringSingleGroupingColDef.js", "bg": "inline", "defaultCodeOpen": false}} ### Multiple grouping columns diff --git a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx index fd0573c80f842..e82a47871fc52 100644 --- a/packages/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx +++ b/packages/x-data-grid-premium/src/hooks/features/rowGrouping/createGroupingColDef.tsx @@ -88,11 +88,11 @@ const getGroupingCriteriaProperties = (groupedByColDef: GridColDef, applyHeaderN // We only want to sort the groups of the current grouping criteria if ( cellParams1.rowNode.type === 'group' && - cellParams1.rowNode.groupingField === groupedByColDef.field && cellParams2.rowNode.type === 'group' && - cellParams2.rowNode.groupingField === groupedByColDef.field + cellParams1.rowNode.groupingField === cellParams2.rowNode.groupingField ) { - return groupedByColDef.sortComparator!(v1, v2, cellParams1, cellParams2); + const colDef = cellParams1.api.getColumn(cellParams1.rowNode.groupingField); + return colDef.sortComparator(v1, v2, cellParams1, cellParams2); } return groupingFieldIndexComparator(v1, v2, cellParams1, cellParams2); diff --git a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index 1a9740d208265..601df54088683 100644 --- a/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -1852,7 +1852,7 @@ describe(' - Row grouping', () => { clock.withFakeTimers(); describe('prop: rowGroupingColumnMode = "single"', () => { - it('should use the top level grouping criteria for sorting if mainGroupingCriteria and leafField are not defined', async () => { + it('should use each grouping criteria for sorting if leafField are not defined', async () => { render( - Row grouping', () => { 'Cat 1 (1)', '', 'Cat A (3)', - 'Cat 1 (1)', - '', 'Cat 2 (2)', '', '', + 'Cat 1 (1)', + '', ]); }); - it('should use the column grouping criteria for sorting if mainGroupingCriteria is one of the grouping criteria and leaf field is defined', () => { + it('should sort leaves if leaf field is defined', () => { render( - Row grouping', () => { />, ); expect(getColumnValues(0)).to.deep.equal([ + 'Cat B (2)', + 'Cat 2 (1)', + '3', + 'Cat 1 (1)', + '4', 'Cat A (3)', 'Cat 2 (2)', '1', '2', 'Cat 1 (1)', '0', - 'Cat B (2)', - 'Cat 2 (1)', - '3', - 'Cat 1 (1)', - '4', ]); }); From b438784f6a2138c74a3177791b820e069fc1225c Mon Sep 17 00:00:00 2001 From: Flavien DELANGLE Date: Mon, 18 Mar 2024 11:47:44 +0100 Subject: [PATCH 48/49] [TreeView] Update JSDoc of the `ContentComponent` prop to avoid using the word "node" (#12476) --- docs/translations/api-docs/tree-view/tree-item/tree-item.json | 2 +- packages/x-tree-view/src/TreeItem/TreeItem.tsx | 2 +- packages/x-tree-view/src/TreeItem/TreeItem.types.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/translations/api-docs/tree-view/tree-item/tree-item.json b/docs/translations/api-docs/tree-view/tree-item/tree-item.json index 24a2ba62082e6..e1cf3bc289491 100644 --- a/docs/translations/api-docs/tree-view/tree-item/tree-item.json +++ b/docs/translations/api-docs/tree-view/tree-item/tree-item.json @@ -4,7 +4,7 @@ "children": { "description": "The content of the component." }, "classes": { "description": "Override or extend the styles applied to the component." }, "ContentComponent": { - "description": "The component used for the content node.", + "description": "The component used to render the content of the item.", "requiresRef": true }, "ContentProps": { "description": "Props applied to ContentComponent." }, diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.tsx index 46defc7f74c0e..17450737dbe01 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.tsx @@ -362,7 +362,7 @@ TreeItem.propTypes = { classes: PropTypes.object, className: PropTypes.string, /** - * The component used for the content node. + * The component used to render the content of the item. * @default TreeItemContent */ ContentComponent: elementTypeAcceptingRef, diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts index 631781fb6ddfe..1c141c12e6abf 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.types.ts +++ b/packages/x-tree-view/src/TreeItem/TreeItem.types.ts @@ -61,7 +61,7 @@ export interface TreeItemProps extends Omit, */ slotProps?: TreeItemSlotProps; /** - * The component used for the content node. + * The component used to render the content of the item. * @default TreeItemContent */ ContentComponent?: React.JSXElementConstructor; From 83de93f39d8f9224e020623fa411e6c6ff661e21 Mon Sep 17 00:00:00 2001 From: Andrew Cherniavskii Date: Mon, 18 Mar 2024 12:17:52 +0100 Subject: [PATCH 49/49] [core] Fix `no-restricted-imports` ESLint rule not working for data grid packages (#12477) --- .eslintrc.js | 8 ++++---- .../components/headerFiltering/GridHeaderFilterCell.tsx | 4 +--- .../features/infiniteLoader/useGridInfiniteLoader.tsx | 3 +-- packages/x-data-grid-pro/src/models/gridApiPro.ts | 3 +-- packages/x-data-grid/src/internals/index.ts | 2 ++ packages/x-data-grid/src/models/api/index.ts | 2 +- scripts/x-data-grid-premium.exports.json | 1 - scripts/x-data-grid-pro.exports.json | 1 - scripts/x-data-grid.exports.json | 1 - 9 files changed, 10 insertions(+), 15 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index cfd1113f822de..99d1462f8f7d4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -157,10 +157,10 @@ module.exports = { }, }, buildPackageRestrictedImports('@mui/x-charts', 'x-charts'), - buildPackageRestrictedImports('@mui/x-data-grid', 'grid/x-data-grid'), - buildPackageRestrictedImports('@mui/x-data-grid-pro', 'grid/x-data-grid-pro'), - buildPackageRestrictedImports('@mui/x-data-grid-premium', 'grid/x-data-grid-premium'), - buildPackageRestrictedImports('@mui/x-data-grid-generator', 'grid/x-data-grid-generator'), + buildPackageRestrictedImports('@mui/x-data-grid', 'x-data-grid'), + buildPackageRestrictedImports('@mui/x-data-grid-pro', 'x-data-grid-pro'), + buildPackageRestrictedImports('@mui/x-data-grid-premium', 'x-data-grid-premium'), + buildPackageRestrictedImports('@mui/x-data-grid-generator', 'x-data-grid-generator'), buildPackageRestrictedImports('@mui/x-pickers', 'x-pickers'), buildPackageRestrictedImports('@mui/x-pickers-pro', 'x-pickers-pro'), buildPackageRestrictedImports('@mui/x-license', 'x-license'), diff --git a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index 16d4e3618500a..68833b01e116f 100644 --- a/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -25,11 +25,9 @@ import { gridHeaderFilteringEditFieldSelector, gridHeaderFilteringMenuSelector, isNavigationKey, -} from '@mui/x-data-grid/internals'; -import { shouldCellShowLeftBorder, shouldCellShowRightBorder, -} from '@mui/x-data-grid/utils/cellBorderUtils'; +} from '@mui/x-data-grid/internals'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { DataGridProProcessedProps } from '../../models/dataGridProProps'; import { GridHeaderFilterMenuContainer } from './GridHeaderFilterMenuContainer'; diff --git a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx index 6b8372a7393aa..1558c773857ae 100644 --- a/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx +++ b/packages/x-data-grid-pro/src/hooks/features/infiniteLoader/useGridInfiniteLoader.tsx @@ -6,10 +6,9 @@ import { useGridApiMethod, gridDimensionsSelector, } from '@mui/x-data-grid'; -import { useGridVisibleRows } from '@mui/x-data-grid/internals'; +import { useGridVisibleRows, GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/internals'; import useEventCallback from '@mui/utils/useEventCallback'; import { styled } from '@mui/system'; -import type { GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/models/api/gridInfiniteLoaderApi'; import { GridRowScrollEndParams } from '../../../models'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; diff --git a/packages/x-data-grid-pro/src/models/gridApiPro.ts b/packages/x-data-grid-pro/src/models/gridApiPro.ts index d70437eeb15a2..33e5f884a0d42 100644 --- a/packages/x-data-grid-pro/src/models/gridApiPro.ts +++ b/packages/x-data-grid-pro/src/models/gridApiPro.ts @@ -4,8 +4,7 @@ import { GridRowMultiSelectionApi, GridRowProApi, } from '@mui/x-data-grid'; -import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals'; -import type { GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/models/api/gridInfiniteLoaderApi'; +import { GridPrivateOnlyApiCommon, GridInfiniteLoaderPrivateApi } from '@mui/x-data-grid/internals'; import { GridInitialStatePro, GridStatePro } from './gridStatePro'; import type { GridColumnPinningApi, diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 1a61da047e6f3..1380dbf4d4c06 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -151,7 +151,9 @@ export * from '../utils/fastMemo'; export { buildWarning } from '../utils/warning'; export { exportAs } from '../utils/exportAs'; export * from '../utils/getPublicApiRef'; +export * from '../utils/cellBorderUtils'; export type { GridPrivateOnlyApiCommon } from '../models/api/gridApiCommon'; +export type { GridInfiniteLoaderPrivateApi } from '../models/api/gridInfiniteLoaderApi'; export { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext'; export * from '../hooks/utils'; diff --git a/packages/x-data-grid/src/models/api/index.ts b/packages/x-data-grid/src/models/api/index.ts index 709885eea44ad..16cae502d7ec8 100644 --- a/packages/x-data-grid/src/models/api/index.ts +++ b/packages/x-data-grid/src/models/api/index.ts @@ -18,7 +18,7 @@ export * from './gridPreferencesPanelApi'; export * from './gridPrintExportApi'; export * from './gridCallbackDetails'; export * from './gridScrollApi'; -export * from './gridVirtualizationApi'; +export type { GridVirtualizationApi } from './gridVirtualizationApi'; export type { GridApiCommon } from './gridApiCommon'; export type { GridEditingApi, GridCellModesModel, GridRowModesModel } from './gridEditingApi'; diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 227740915c9d6..b0cddf8620c36 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -598,7 +598,6 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, - { "name": "GridVirtualizationPrivateApi", "kind": "Interface" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index f9e4d9888c0e0..1946f832da008 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -552,7 +552,6 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, - { "name": "GridVirtualizationPrivateApi", "kind": "Interface" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 5464d87a7253d..484adb49bc66c 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -511,7 +511,6 @@ { "name": "GridVirtualizationApi", "kind": "Interface" }, { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" }, { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" }, - { "name": "GridVirtualizationPrivateApi", "kind": "Interface" }, { "name": "gridVirtualizationSelector", "kind": "Variable" }, { "name": "GridVirtualizationState", "kind": "TypeAlias" }, { "name": "GridVisibilityOffIcon", "kind": "Variable" },