diff --git a/.circleci/config.yml b/.circleci/config.yml index 7d2dbe00b7094..4b43745318f49 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -195,7 +195,7 @@ jobs: test_browser: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.37.1-focal + - image: mcr.microsoft.com/playwright:v1.39.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -228,7 +228,7 @@ jobs: test_e2e: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.37.1-focal + - image: mcr.microsoft.com/playwright:v1.39.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -241,7 +241,7 @@ jobs: test_e2e_website: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.37.1-focal + - image: mcr.microsoft.com/playwright:v1.39.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -256,7 +256,7 @@ jobs: test_regressions: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.37.1-focal + - image: mcr.microsoft.com/playwright:v1.39.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: @@ -269,29 +269,21 @@ jobs: - run: name: Upload screenshots to Argos CI command: yarn test:argos - test_performance: + run_danger: <<: *defaults docker: - - image: mcr.microsoft.com/playwright:v1.37.1-focal + - image: mcr.microsoft.com/playwright:v1.39.0-focal environment: NODE_ENV: development # Needed if playwright is in `devDependencies` steps: - checkout - install_js: browsers: true - - run: - name: Run performance tests - command: xvfb-run yarn test:performance - - store_artifacts: - name: Persist performance snapshot as pipeline artifact - path: performance-snapshot.json - destination: performance-snapshot.json - run: name: Run danger on PRs command: yarn danger ci --fail-on-errors environment: DANGER_DISABLE_TRANSPILATION: 'true' - DANGER_COMMAND: 'reportPerformance' workflows: version: 2 pipeline: @@ -320,7 +312,7 @@ workflows: - test_regressions: requires: - checkout - - test_performance: + - run_danger: requires: - checkout e2e-website: diff --git a/.github/ISSUE_TEMPLATE/1.bug.yml b/.github/ISSUE_TEMPLATE/1.bug.yml index 2a8b32b1dce92..a71ee74c028b1 100644 --- a/.github/ISSUE_TEMPLATE/1.bug.yml +++ b/.github/ISSUE_TEMPLATE/1.bug.yml @@ -1,6 +1,6 @@ name: Bug report 🐛 description: Create a bug report for MUI X. -labels: ['status: needs triage'] +labels: ['status: waiting for maintainer'] body: - type: markdown attributes: @@ -8,13 +8,14 @@ body: Please provide a searchable summary of the issue in the title above ⬆️. Thanks for contributing by creating an issue! ❤️ - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/mui-x/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: checkboxes attributes: label: Latest version @@ -22,15 +23,17 @@ body: options: - label: I have tested the latest version required: true + - type: textarea attributes: - label: Steps to reproduce 🕹 + label: Steps to reproduce description: | - **⚠️ Issues that we can't reproduce will be closed.** - - Please provide a link to a live example and an unambiguous set of steps to reproduce this bug. As a starting point, we recommend you [browse the documentation](https://mui.com/x/introduction/), and [select](https://mui.com/static/docs/forking-an-example.png) the closest example to your use case. Or you can use a [basic template](https://mui.com/r/x-issue-template) to build your reproduction case. + **⚠️ Please provide a live example for your report⚠️** + If you don't have one, you can use one of these options: + - [DataGrid codesandbox template](https://codesandbox.io/s/github/mui/mui-x/tree/master/templates/x-data-grid?file=/src/demo.tsx) + - Fork any of the examples in our [documentation](https://mui.com/x/introduction/) by [clicking on the codesandbox or stackblitz icon](https://mui.com/static/docs/forking-an-example.png) value: | - Link to live example: + Link to live example: (required) Steps: 1. @@ -38,19 +41,19 @@ body: 3. - type: textarea attributes: - label: Current behavior 😯 + label: Current behavior description: Describe what happens instead of the expected behavior. - type: textarea attributes: - label: Expected behavior 🤔 + label: Expected behavior description: Describe what should happen. - type: textarea attributes: - label: Context 🔦 + label: Context description: What are you trying to accomplish? How has this issue affected you? Providing context helps us come up with a solution that is more useful in the real world. - type: textarea attributes: - label: Your environment 🌎 + label: Your environment description: Run `npx @mui/envinfo` and post the results. If you encounter issues with TypeScript please include the used tsconfig. value: |
diff --git a/.github/ISSUE_TEMPLATE/2.feature.yml b/.github/ISSUE_TEMPLATE/2.feature.yml index 66e35f24201b1..2bdf5faee9c81 100644 --- a/.github/ISSUE_TEMPLATE/2.feature.yml +++ b/.github/ISSUE_TEMPLATE/2.feature.yml @@ -1,6 +1,6 @@ name: Feature request 💄 description: Suggest a new idea for MUI X. -labels: ['status: needs triage'] +labels: ['status: waiting for maintainer'] body: - type: markdown attributes: @@ -8,13 +8,14 @@ body: Please provide a searchable summary of the issue in the title above ⬆️. Thanks for contributing by creating an issue! ❤️ - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/mui-x/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: checkboxes attributes: label: Latest version @@ -22,6 +23,7 @@ body: options: - label: I have tested the latest version required: true + - type: textarea attributes: label: Summary 💡 @@ -37,6 +39,6 @@ body: - type: input id: contact attributes: - label: Order ID 💳 (optional) + label: Order ID or Support key 💳 (optional) description: The [Pro plan](https://mui.com/pricing/) comes with priority over the Community plan. Providing your order ID might give your problem more attention. placeholder: 'e.g. 11111' diff --git a/.github/ISSUE_TEMPLATE/3.pro-support.yml b/.github/ISSUE_TEMPLATE/3.pro-support.yml index d78b1f202a16d..7942854803204 100644 --- a/.github/ISSUE_TEMPLATE/3.pro-support.yml +++ b/.github/ISSUE_TEMPLATE/3.pro-support.yml @@ -1,7 +1,7 @@ name: 'Pro plan: question ❔' description: I'm a Pro plan user, I can't find a solution to my problem with MUI X. title: '[question] ' -labels: ['status: needs triage', 'pro plan', 'support: commercial'] +labels: ['status: waiting for maintainer', 'pro plan', 'support: commercial'] body: - type: markdown attributes: @@ -9,21 +9,23 @@ body: Please provide a searchable summary of the issue in the title above ⬆️. ⚠️ **Don't use this form if the problem is [a bug](https://github.com/mui/mui-x/issues/new?assignees=&labels=status%3A+needs+triage&template=1.bug.yml) or a [feature request](https://github.com/mui/mui-x/issues/new?assignees=&labels=status%3A+needs+triage&template=2.feature.yml), use the dedicated form instead.** + - type: input id: contact attributes: - label: Order ID or Support key 💳 (optional) + label: Order ID or Support key 💳 description: The order ID of the purchased Pro plan. Community users can [learn more about support](https://mui.com/getting-started/support/) in the documentation. placeholder: 'e.g. 11111' validations: required: true - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/mui-x/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: checkboxes attributes: label: Latest version @@ -31,9 +33,15 @@ body: options: - label: I have tested the latest version required: true + - type: textarea attributes: label: The problem in depth 🔍 + description: | + **If applicable, please provide a live example to explain your problem.** + If you don't have one, you can use one of these options: + - [DataGrid codesandbox template](https://codesandbox.io/s/github/mui/mui-x/tree/master/templates/x-data-grid?file=/src/demo.tsx) + - Fork any of the examples in our [documentation](https://mui.com/x/introduction/) by [clicking on the codesandbox or stackblitz icon](https://mui.com/static/docs/forking-an-example.png) - type: textarea attributes: label: Your environment 🌎 diff --git a/.github/ISSUE_TEMPLATE/4.premium-support.yml b/.github/ISSUE_TEMPLATE/4.premium-support.yml index 14820c37b7f12..fabb0defcb778 100644 --- a/.github/ISSUE_TEMPLATE/4.premium-support.yml +++ b/.github/ISSUE_TEMPLATE/4.premium-support.yml @@ -1,7 +1,7 @@ name: 'Premium plan: question ❔' description: I'm a Premium plan user, I can't find a solution to my problem with MUI X. title: '[question] ' -labels: ['status: needs triage', 'Premium plan', 'support: commercial'] +labels: ['status: waiting for maintainer', 'Premium plan', 'support: commercial'] body: - type: markdown attributes: @@ -9,21 +9,23 @@ body: Please provide a searchable summary of the issue in the title above ⬆️. ⚠️ **Don't use this form if the problem is [a bug](https://github.com/mui/mui-x/issues/new?assignees=&labels=status%3A+needs+triage&template=1.bug.yml) or a [feature request](https://github.com/mui/mui-x/issues/new?assignees=&labels=status%3A+needs+triage&template=2.feature.yml), use the dedicated form instead.** + - type: input id: contact attributes: - label: Order ID + label: Order ID or Support key 💳 description: The order ID of the purchased Premium plan. Community users can [learn more about support](https://mui.com/getting-started/support/) in the documentation. placeholder: 'e.g. 11111' validations: required: true - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/mui-x/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: checkboxes attributes: label: Latest version @@ -31,9 +33,15 @@ body: options: - label: I have tested the latest version required: true + - type: textarea attributes: label: The problem in depth 🔍 + description: | + **If applicable, please provide a live example to explain your problem.** + If you don't have one, you can use one of these options: + - [DataGrid codesandbox template](https://codesandbox.io/s/github/mui/mui-x/tree/master/templates/x-data-grid?file=/src/demo.tsx) + - Fork any of the examples in our [documentation](https://mui.com/x/introduction/) by [clicking on the codesandbox or stackblitz icon](https://mui.com/static/docs/forking-an-example.png) - type: textarea attributes: label: Your environment 🌎 diff --git a/.github/ISSUE_TEMPLATE/5.priority-support.yml b/.github/ISSUE_TEMPLATE/5.priority-support.yml index b015f08c4c6fe..49b685d8449dc 100644 --- a/.github/ISSUE_TEMPLATE/5.priority-support.yml +++ b/.github/ISSUE_TEMPLATE/5.priority-support.yml @@ -1,19 +1,20 @@ -name: 'Priority support: SLA ⏰' +name: 'Priority Support: SLA ⏰' description: I'm an MUI X Premium user and we have purchased the Priority Support add-on. I can't find a solution to my problem with MUI X. title: '[question] ' -labels: ['status: needs triage', 'support: commercial', 'support: unknown'] +labels: ['status: waiting for maintainer', 'support: commercial', 'support: unknown'] body: - type: markdown attributes: value: | Please provide a searchable summary of the issue in the title above ⬆️. - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/mui-x/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: checkboxes attributes: label: Latest version @@ -21,9 +22,15 @@ body: options: - label: I have tested the latest version required: true + - type: textarea attributes: label: The problem in depth 🔍 + description: | + **If you're reporting a bug, please provide a live example for your report.** + If you don't have one, you can use one of these options: + - [DataGrid codesandbox template](https://codesandbox.io/s/github/mui/mui-x/tree/master/templates/x-data-grid?file=/src/demo.tsx) + - Fork any of the examples in our [documentation](https://mui.com/x/introduction/) by [clicking on the codesandbox or stackblitz icon](https://mui.com/static/docs/forking-an-example.png) - type: textarea attributes: label: Your environment 🌎 diff --git a/.github/ISSUE_TEMPLATE/6.docs-feedback.yml b/.github/ISSUE_TEMPLATE/6.docs-feedback.yml index 88fac0f863eb2..9c5dcfd8cb1c1 100644 --- a/.github/ISSUE_TEMPLATE/6.docs-feedback.yml +++ b/.github/ISSUE_TEMPLATE/6.docs-feedback.yml @@ -1,6 +1,6 @@ name: Docs feedback description: Improve documentation about MUI Core. -labels: ['status: needs triage', 'support: docs-feedback'] +labels: ['status: waiting for maintainer', 'support: docs-feedback'] title: '[docs] ' body: - type: markdown @@ -9,13 +9,14 @@ body: Please provide a searchable summary of the issue in the title above ⬆️. Thanks for contributing by creating an issue! ❤️ - - type: checkboxes + + - type: input attributes: - label: Duplicates - description: Please [search the history](https://github.com/mui/material-ui/issues) to see if an issue already exists for the same problem. - options: - - label: I have searched the existing issues - required: true + label: Search keywords + description: Your issue may have already been reported! List the keywords you've used to search the [existing issues](https://github.com/mui/mui-x/issues). This will also make your issue searchable for others. + placeholder: e.g. datagrid column resizing + validations: + required: true - type: input id: page-url diff --git a/.github/ISSUE_TEMPLATE/7.rfc.yml b/.github/ISSUE_TEMPLATE/7.rfc.yml index bedf972a27100..a5eef95af73fa 100644 --- a/.github/ISSUE_TEMPLATE/7.rfc.yml +++ b/.github/ISSUE_TEMPLATE/7.rfc.yml @@ -1,7 +1,7 @@ name: RFC 💬 description: Request for comments for your proposal. title: '[RFC] ' -labels: ['status: needs triage', 'RFC'] +labels: ['status: waiting for maintainer', 'RFC'] body: - type: markdown attributes: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9ade02e1d0386..0c2d282f6a906 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -16,10 +16,10 @@ jobs: security-events: write steps: - name: Checkout repository - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5 + uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 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@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5 + uses: github/codeql-action/analyze@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 diff --git a/.github/workflows/ensure-triage-label.yml b/.github/workflows/ensure-triage-label.yml index c4753ccbd3ef4..b88106efdd148 100644 --- a/.github/workflows/ensure-triage-label.yml +++ b/.github/workflows/ensure-triage-label.yml @@ -30,6 +30,6 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - labels: ['status: needs triage'] + labels: ['status: waiting for maintainer'] }) } diff --git a/.github/workflows/issue-cleanup.yaml b/.github/workflows/issue-cleanup.yaml deleted file mode 100644 index 14f0ed51d3246..0000000000000 --- a/.github/workflows/issue-cleanup.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Cleanup issue comment - -on: - issues: - types: - - opened - -permissions: {} - -jobs: - issue_cleanup: - runs-on: ubuntu-latest - permissions: - issues: write - steps: - - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6 - with: - script: | - const issue = await github.rest.issues.get({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - }); - - const text = - '### Duplicates\r\n\r\n- [X] I have searched the existing issues\r\n\r\n### Latest version\r\n\r\n- [X] I have tested the latest version\r\n\r\n'; - - const body = issue.data.body.replace(text, ''); - - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: context.issue.number, - body, - }) diff --git a/.github/workflows/issue-cleanup.yml b/.github/workflows/issue-cleanup.yml new file mode 100644 index 0000000000000..dab93c5d505e3 --- /dev/null +++ b/.github/workflows/issue-cleanup.yml @@ -0,0 +1,52 @@ +name: Cleanup issue comment + +on: + issues: + types: + - opened + +permissions: {} + +jobs: + issue_cleanup: + runs-on: ubuntu-latest + permissions: + issues: write + steps: + - uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6 + with: + script: | + const issue = await github.rest.issues.get({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + }) + + const lines = issue.data.body.split('\n') + + const _ = extractInputSection(lines, 'Latest version') + const searchKeywords = extractInputSection(lines, 'Search keywords') + const orderID = extractInputSection(lines, 'Order ID or Support key') + + lines.push('') + lines.push('**Search keywords**: ' + searchKeywords) + if (orderID !== '' && orderID !== '_No response_') { + lines.push('**Order ID**: ' + orderID) + } + + const body = lines.join('\n') + + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + body, + }) + + function extractInputSection(lines, title) { + const index = lines.findIndex(line => line.startsWith('###') && line.includes(title)) + if (index === -1) { + return '' + } + return lines.splice(index, 4)[2].trim() + } diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml index f63adf221853e..38e190c28e9bc 100644 --- a/.github/workflows/l10n.yml +++ b/.github/workflows/l10n.yml @@ -17,9 +17,9 @@ jobs: issues: write steps: - run: echo "${{ github.actor }}" - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Use Node.js 18.x - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18 cache: 'yarn' # https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-dependencies diff --git a/.github/workflows/mark-duplicate.yml b/.github/workflows/mark-duplicate.yml index eb9a755966387..15e82efe76c2b 100644 --- a/.github/workflows/mark-duplicate.yml +++ b/.github/workflows/mark-duplicate.yml @@ -19,5 +19,5 @@ jobs: actions: 'mark-duplicate' token: ${{ secrets.GITHUB_TOKEN }} duplicate-labels: 'duplicate' - remove-labels: 'status: incomplete,status: needs triage' + remove-labels: 'status: incomplete,status: waiting for maintainer' close-issue: true diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml index b169265335f30..3664edfcc0f87 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/no-response.yml @@ -18,13 +18,15 @@ jobs: contents: read issues: write steps: - - uses: lee-dohm/no-response@9bb0a4b5e6a45046f00353d5de7d90fb8bd773bb # v0.5.0 + - uses: MBilalShafi/no-response-add-label@6291e5d1a4eaffe530b2ec434991f258641a8599 with: token: ${{ secrets.GITHUB_TOKEN }} # Number of days of inactivity before an Issue is closed for lack of response daysUntilClose: 7 # Label requiring a response - responseRequiredLabel: 'status: waiting for follow-up' + responseRequiredLabel: 'status: waiting for author' + # Label to add back when required label is removed + optionalFollowupLabel: 'status: waiting for maintainer' # Comment to post when closing an Issue for lack of response. Set to `false` to disable closeComment: > The issue has been inactive for 7 days and has been automatically closed. diff --git a/.github/workflows/priority-support-validation-prompt.yml b/.github/workflows/priority-support-validation-prompt.yml index e8198db8aadbd..cfb435293944a 100644 --- a/.github/workflows/priority-support-validation-prompt.yml +++ b/.github/workflows/priority-support-validation-prompt.yml @@ -25,7 +25,7 @@ jobs: - name: Create comment if: ${{ steps.findComment.outputs.comment-id == '' && contains(github.event.label.name, 'unknown') }} - uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2 + uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3.1.0 with: issue-number: ${{ github.event.issue.number }} body: | @@ -35,11 +35,11 @@ jobs: Do not share your support key in this issue! - Priority support is only provided to verified customers. Once you have verified your support key, we will remove the `support: unknown` label and add the `support: priority` label to this issue. Only then the time for the SLA will start counting. + Priority Support is only provided to verified customers. Once you have verified your support key, we will remove the `support: unknown` label and add the `support: priority` label to this issue. Only then the time for the SLA will start counting. - name: Update comment if: ${{ steps.findComment.outputs.comment-id != '' && contains(github.event.label.name, 'priority') }} - uses: peter-evans/create-or-update-comment@c6c9a1a66007646a28c153e2a8580a5bad27bcfa # v3.0.2 + uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3.1.0 with: comment-id: ${{ steps.findComment.outputs.comment-id }} body: | diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 163045780d775..f7dab7dc7b5aa 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -23,12 +23,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - name: Run analysis - uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: results.sarif results_format: sarif @@ -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@00e563ead9f72a8461b24876bee2d0c2e8bd2ee8 # v2.21.5 + uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 with: sarif_file: results.sarif diff --git a/.github/workflows/vale-action.yml b/.github/workflows/vale-action.yml index d145911760a69..61078d7162eef 100644 --- a/.github/workflows/vale-action.yml +++ b/.github/workflows/vale-action.yml @@ -12,7 +12,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: errata-ai/vale-action@c4213d4de3d5f718b8497bd86161531c78992084 # v2.0.1 with: reporter: github-pr-review diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a24c7212add..287579f178bde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,529 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 6.18.0 + +_Nov 3, 2023_ + +We'd like to offer a big thanks to the 7 contributors who made this release possible. Here are some highlights ✨: + +- 🎁 The Charts package is now officially stable! +- 🥧 Pie charts are now animated. +- 📈 Line charts now support partial data, and can interpolate missing data. + +line charts with partial data + +- ✨ Allow to ignore [diacritics](https://en.wikipedia.org/wiki/Diacritic) when filtering +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.18.0` + +- [DataGrid] Allow to ignore [diacritics](https://en.wikipedia.org/wiki/Diacritic) when filtering (#10569) @cherniavskii +- [DataGrid] Fix a typo in `gridFilterApi` (#10786) @vu-dao-93 +- [DataGrid] Fix `undefined` row id (#10670) @romgrk +- [DataGrid] Make column autosizing work with dynamic row height (#10693) @cherniavskii +- [l10n] Allow to customize sorting label per column (#10839) @JerryWu1234 + +#### `@mui/x-data-grid-pro@6.18.0` [![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@6.18.0`. + +#### `@mui/x-data-grid-premium@6.18.0` [![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@6.18.0`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.18.0` + +- [pickers] Add reference links to calendar components (#10644) @michelengelen + +#### `@mui/x-date-pickers-pro@6.18.0` [![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.18.0`. + +### Charts / `@mui/x-charts@6.18.0` + +- [charts] Add animation on pie chart (#10782) @alexfauquette +- [charts] Add reference links to shared/misc chart components (#10660) @michelengelen +- [charts] Allows to connect nulls (#10803) @alexfauquette +- [charts] Fix axis highlight in dark mode (#10820) @LukasTy + +### Docs + +- [docs] Add a data grid recipe for autosizing columns after fetching row-data (#10822) @michelengelen +- [docs] Add a data grid recipe showing how to remove cell outline on `focus` (#10843) @michelengelen +- [docs] Add demo about how to use charts margin (#10886) @alexfauquette +- [docs] Improve custom field input demos readability (#10559) @LukasTy + +### Core + +- [core] Generate `slot` API descriptions based on `slots` or `components` (#10879) @LukasTy + +## 6.17.0 + +_Oct 27, 2023_ + +We'd like to offer a big thanks to the 9 contributors who made this release possible. Here are some highlights ✨: + +- 🎁 The Tree View package is now officially stable! + +![tree-view-example](https://github.com/mui/mui-x/assets/550141/77d1fe66-d912-49ba-b38f-b853fb90446a) + +- ✨ Improve the handling of non-numeric values by Data Grid aggregation +- 🚀 Support lines with different domains on the line charts +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.17.0` + +- [DataGrid] Allow custom debounce time for row positions calculation (#10708) @cherniavskii +- [DataGrid] Persist stable row index for focused row (#10674) @cherniavskii + +#### `@mui/x-data-grid-pro@6.17.0` [![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@6.17.0`, plus: + +- [DataGridPro] Fix `undefined` values passed to `valueFormatter` for tree leaf nodes (#10748) @cherniavskii + +#### `@mui/x-data-grid-premium@6.17.0` [![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@6.17.0`, plus: + +- [DataGridPremium] Fix `avg` aggregation to ignore non-numeric values (#10787) @cherniavskii +- [DataGridPremium] Fix `size` aggregation to ignore `undefined` values (#10745) @cherniavskii +- [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 + +#### `@mui/x-date-pickers@6.17.0` + +- [fields] POC: Use `contentEditable` on `FakeTextField` (#10779) @flaviendelangle +- [pickers] Fix weekday label localization (#10809) @LukasTy + +#### `@mui/x-date-pickers-pro@6.17.0` [![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.17.0`. + +### Charts / `@mui/x-charts@6.0.0-alpha.17` + +- [charts] Fix text position in Safari (#10815) @lhilgert9 +- [charts] Support lines with different domains (#10801) @alexfauquette + +### Tree View / `@mui/x-tree-view@6.17.0` + +No change + +### Docs + +- [docs] Correct editing related props' description (#10798) @MBilalShafi +- [docs] Fix RTL data grid demo (#10728) @oliviertassinari +- [docs] Fix unclosed warning (#10796) @flaviendelangle +- [docs] Improve performance of `Save and restore the state from external storage` recipe (#10811) @michelengelen + +- [test] Add missing type on `cleanText` utility function (#10780) @flaviendelangle + +## 6.16.3 + +_Oct 20, 2023_ + +We'd like to offer a big thanks to the 7 contributors who made this release possible. Here are some highlights ✨: + +- 🎁 Add a Data Grid recipe for saving & restoring state +- 💫 Support animations on the bar chart +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.16.3` + +- [DataGrid] Allow passing readonly arrays to `columns` and `sortingOrder` props (#10686) @pcorpet + +#### `@mui/x-data-grid-pro@6.16.3` [![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@6.16.3`. + +#### `@mui/x-data-grid-premium@6.16.3` [![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@6.16.3`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.16.3` + +- [fields] Correctly respect leading zeroes on seconds section (#10713) @flaviendelangle +- [fields] Use `onChange` instead of `onKeyPress` for Backspace editing (#10494) @flaviendelangle +- [pickers] Add reference links to DatePicker components (#10626) @michelengelen +- [pickers] Add reference links to clock components (#10645) @michelengelen +- [pickers] Add reference links to misc picker components (#10647) @michelengelen +- [pickers] Add reference links to toolbar components (#10646) @michelengelen +- [pickers] POC: Change the props received by the `FakeTextField` component (#10687) @flaviendelangle + +#### `@mui/x-date-pickers-pro@6.16.3` [![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.16.3`, plus: + +- [DateRangePicker] Fix touch based range dragging (#10664) @michelengelen + +### Charts / `@mui/x-charts@6.0.0-alpha.16` + +- [charts] Add reference links to area + bar chart components (#10652) @michelengelen +- [charts] Add reference links to line chart + sparkline components (#10650) @michelengelen +- [charts] Add reference links to pie + scatter chart components (#10653) @michelengelen +- [charts] Render only when `width` and `height` are resolved (#10714) @alexfauquette +- [charts] Support animation on `BarChart` (#9926) @alexfauquette +- [charts] Use new text component to avoid tick label overflow on x-axis (#10648) @alexfauquette + +### Docs + +- [docs] Add a recipe for saving and restoring `state` externally (#10722) @michelengelen +- [docs] Add example about how to add an axis (#10709) @alexfauquette +- [docs] Customization Playground - fix DesktopDatePicker sx props and styled examples (#10665) @noraleonte +- [docs] Improve meta description @oliviertassinari +- [docs] Make overview demo work in codesandbox (#10661) @alexfauquette + +### Core + +- [core] Update React renovate group with `@types` (#10723) @LukasTy +- [core] Update `styled-components` (#10733) @LukasTy + +## 6.16.2 + +_Oct 12, 2023_ + +We'd like to offer a big thanks to the 12 contributors who made this release possible. Here are some highlights ✨: + +- 📊 Chart's legend text management has been reworked and contains breaking changes (#10138) @alexfauquette +- 📝 Add [Bulk editing](https://mui.com/x/react-data-grid/recipes-editing/#bulk-editing) demo (#10333) @cherniavskii +- 🚀 Column grouping now works smoothly with column pinning (#10518) @MBilalShafi +- 🌍 Improve Arabic (ar-SD) and Spanish (es-ES) locales +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.16.2` + +- [DataGrid] Fix `LazyLoading` demo crash (#10621) @MBilalShafi +- [DataGrid] Fix cells overlapping the scrollbar in iOS Safari (#10633) @cherniavskii +- [DataGrid] Fix `getRowId is not defined` error (#10613) @romgrk +- [DataGrid] Get quick filter to work OOTB with `date` and `dateTime` fields (#10636) @MBilalShafi +- [DataGrid] Make cursor for selectable cells to be `default` unless editable (#9997) @gitstart +- [DataGrid] Remove unnecessary syntax in JSDoc (#10567) @Lev-Shapiro +- [DataGrid] Update row hover behavior to match native hover (#10623) @cherniavskii +- [l10n] Improve Arabic (ar-SD) locale (#10625) @alabenyahia + +#### `@mui/x-data-grid-pro@6.16.2` [![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@6.16.2`, plus: + +- [DataGridPro] Improve column grouping and column pinning friendship (#10518) @MBilalShafi + +#### `@mui/x-data-grid-premium@6.16.2` [![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@6.16.2`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.16.2` + +- [DateTimePicker] Add support for `DigitalClock` view renderer (#10624) @LukasTy +- [fields] Bootstrap the multi-HTML input component (#10638) @flaviendelangle +- [pickers] Fix timezone `UTC` false positive (#10586) @alexfauquette +- [l10n] Improve Spanish (es-ES) locale (#10588) @eduardodallmann + +#### `@mui/x-date-pickers-pro@6.16.2` [![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.16.2`. + +### Charts / `@mui/x-charts@6.0.0-alpha.15` + +#### Breaking changes + +The charts have a new text display mechanism. +It adds line break support and avoids overlapping text in the legend. +This comes with some breaking changes. + +- The DOM structure is modified. An intermediary `` element has been added. This can impact how your style is applied. + ```diff + - The label + + The label + ``` + +- The top margin has been reduced from 100 to 50 to benefit from the denser legend. + +- To accurately compute the text size and then place it, styling should be provided as a JS object. For example, to set the legend font size, you should do: + ```jsx + + ``` + Support for other text elements (axis labels and tick labels) will be implemented in follow-up PR. + +#### Changes + +- [charts] Fix typo between internal/external variable (#10640) @alexfauquette +- [charts] Improve the management of the text (#10138) @alexfauquette + +### Docs + +- [docs] Add bulk editing demo (#10333) @cherniavskii +- [docs] Add reference links to DateRangePicker components (#10629) @michelengelen +- [docs] Add reference links to DateTimePicker components (#10628) @michelengelen +- [docs] Add reference links to picker field components (#10631) @michelengelen +- [docs] Added reference links to TimePicker components (#10627) @michelengelen +- [docs] Avoid Pickers playground error due to empty views (#10654) @LukasTy +- [docs] Fix DataGrid[Pro/Premium] reference links (#10620) @michelengelen + +### Core + +- [core] Bump monorepo (#10619) @alexfauquette +- [core] Update `no-response` workflow (#10491) @MBilalShafi +- [core] Update the issue templates to reflect the new support workflow (#10651) @MBilalShafi +- [test] Fix `testEval` not invoking test assertions (#10587) @cherniavskii +- [test] Fix dev mode warning (#10610) @oliviertassinari +- [test] Set UUID chance seed in visual tests (#10609) @oliviertassinari + +## 6.16.1 + +_Oct 6, 2023_ + +We'd like to offer a big thanks to the 10 contributors who made this release possible. Here are some highlights ✨: + +- 🥧 Support interaction with pie chart +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.16.1` + +- [DataGrid] Add a new demo with sparklines (#9228) @flaviendelangle +- [DataGrid] Fix autosize missing a few pixels (#10471) @romgrk +- [DataGrid] Make `disableColumnSelector` demo idempotent (#10548) @MBilalShafi + +#### `@mui/x-data-grid-pro@6.16.1` [![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@6.16.1`. + +#### `@mui/x-data-grid-premium@6.16.1` [![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@6.16.1`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.16.1` + +- [pickers] Avoid calendar layout shifting when changing views (#10541) @LukasTy +- [pickers] Fix clearable behavior when disabled (#10542) @noraleonte +- [pickers] Improve customization playground examples (#10544) @noraleonte + +#### `@mui/x-date-pickers-pro@6.16.1` [![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.16.1`, plus: + +- [DateRangePicker] Fix `InputProps` propagation in multi input (#10564) @alexfauquette + +### Charts / `@mui/x-charts@6.0.0-alpha.14` + +- [charts] Display cursor pointer for pie chart only if `onClick` is provided (#10551) @giladappsforce +- [charts] Add `onClick` prop to PieChart (#10506) @giladappsforce +- [charts] Support `slots`/`slotProps` for the tooltip (#10515) @alexfauquette + +### Docs + +- [docs] Add `DateRangePicker` example with a `Button` trigger (#10485) @LukasTy +- [docs] Add section about disabling columns panel (#10328) @MBilalShafi +- [docs] Add section about overriding slots to base concepts (#10421) @noraleonte +- [docs] Add "What's new" page listing all release announcements (#9727) @joserodolfofreitas +- [docs] Update RTL Support section of the grid localization docs (#10561) @MBilalShafi + +### Core + +- [core] Fix casing consistency with legal and marketing content @oliviertassinari +- [core] Revert the link in the priority support ticket description (#10517) @michelengelen +- [changelog] Polish image @oliviertassinari + +## 6.16.0 + +_Sep 29, 2023_ + +We'd like to offer a big thanks to the 9 contributors who made this release possible. Here are some highlights ✨: + +- 🎁 Add a clearable behavior to all the single input pickers and fields (#9095) @noraleonte + + The pickers and fields now have an out-of-the box implementation for clearing the field value. You can see the documentation for this behavior on the [Date Picker documentation](https://mui.com/x/react-date-pickers/date-picker/#clearing-the-value). + + Clearable behavior + +- 💫 Add Date Picker customization playground (#9581) @noraleonte + + You can play around with style customization options on the [Date Picker documentation](https://mui.com/x/react-date-pickers/date-picker/#customization). + + We are thrilled to hear your feedback about this functionality! + +- 🚀 Fix header filters menu auto closing on render (#10483) @MBilalShafi +- 🎯 Fix column headers scroll when theme scoping is used (#10437) @cherniavskii +- 🌍 Improve Russian (ru-RU) locale on the data grid +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.16.0` + +- [DataGrid] Fix column headers scroll when theme scoping is used (#10437) @cherniavskii +- [DataGrid] Rename `global` to `globalScope` due to Jest issue (#10470) @romgrk +- [l10n] Improve Russian (ru-RU) locale (#10464 and #10407) @NKodos + +#### `@mui/x-data-grid-pro@6.16.0` [![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@6.16.0`, plus: + +- [DataGridPro] Fix header filters menu auto closing on render (#10483) @MBilalShafi + +#### `@mui/x-data-grid-premium@6.16.0` [![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@6.16.0`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.16.0` + +- [pickers] Add warning to `shouldDisableDate` validation (#10502) @michelengelen +- [pickers] Implement `clearable` field behavior (#9095) @noraleonte +- [pickers] Refactor `dayOfWeekFormatter` (#10345) @michelengelen + +#### `@mui/x-date-pickers-pro@6.16.0` [![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.16.0`. + +### Charts / `@mui/x-charts@6.0.0-alpha.13` + +- [charts] Share upfront future Pro features (#10465) @oliviertassinari + +### Tree View / `@mui/x-tree-view@6.0.0-beta.0` + +- [TreeView] Do not try to focus a collapsed node when re-focusing the TreeView (#10422) @flaviendelangle +- [TreeView] Fix the typing of the `Multiple` generic (#10478) @flaviendelangle + +### Docs + +- [docs] Correct the typo in data grid api docs (#10477) @MBilalShafi +- [docs] Add customization playground (#9581) @noraleonte +- [docs] Fix Tree View product ID (#10428) @oliviertassinari +- [docs] Fix demo crashing when all rows are deleted (#10438) @cherniavskii +- [docs] Fix mobile scrollbar column resize (#10455) @oliviertassinari +- [docs] Fix usage of `GridRenderCellParams` interface (#10435) @cherniavskii + +### Core + +- [core] Fix typo in header data grid quick filter @oliviertassinari +- [core] Group D3 renovate PRs (#10480) @flaviendelangle +- [core] Link the priority support page (#10495) @michelengelen +- [core] Move the pickers describes to the test utils folder (#10490) @flaviendelangle +- [core] Priority Support casing normalization @oliviertassinari +- [core] Remove automated DataGrid performance tests (#10414) @romgrk +- [core] Sync `prism-okaidia.css` with docs-infra @oliviertassinari +- [core] Update issue actions & templates (#10375) @romgrk +- [core] Update release guide (#10468) @DanailH + +## 6.15.0 + +_Sep 22, 2023_ + +We'd like to offer a big thanks to the 9 contributors who made this release possible. Here are some highlights ✨: + +- 🚀 Implement columns auto-sizing (#10180) @romgrk +- 🎁 Add support for `getRowsToExport` option to print export on the data grid (#10084) @zreecespieces +- 🌍 Improve Finnish (fi-FI) locale +- 🐞 Bugfixes +- 📚 Documentation improvements + +### Data Grid + +#### `@mui/x-data-grid@6.15.0` + +- [DataGrid] Add support for `getRowsToExport` option to print export (#10084) @zreecespieces +- [DataGrid] Fix dev warning about `InputLabelProps` (#10413) @romgrk +- [DataGrid] Refactor `GridMenu` prop `onClickAway` to `onClose` (#10411) @romgrk +- [DataGrid] Restore focus after `GridMenu` closes (#10412) @romgrk +- [DataGrid] Fix typing of `GridActionsCellItem` (#10344) @romgrk +- [DataGrid] Hide `eval` from bundlers (#10329) @romgrk +- [DataGrid] Add `border: 0` to unmounted focused cell to avoid layout shifts in that row (#10318) @lauri865 + +#### `@mui/x-data-grid-pro@6.15.0` [![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@6.15.0`, plus: + +- [DataGridPro] Implement columns auto-sizing (#10180) @romgrk +- [DataGridPro] Fix keyboard navigation issue in header filters (#10358) @MBilalShafi +- [DataGridPro] Add missing row hover styles (#10252) @cherniavskii +- [DataGridPro] Make default filter items have stable references in header filters (#10338) @MBilalShafi + +#### `@mui/x-data-grid-premium@6.15.0` [![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@6.15.0`. + +### Date Pickers + +#### `@mui/x-date-pickers@6.15.0` + +- [pickers] Support tokens without spaces (#10185) @alexfauquette +- [l10n] Improve Finnish (fi-FI) locale (#10346) @samijouppila + +#### `@mui/x-date-pickers-pro@6.15.0` [![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.15.0`. + +### Charts / `@mui/x-charts@6.0.0-alpha.12` + +- [charts] Fix sparkline scale and rendering (#10402) @alexfauquette +- [charts] Remove components from `@mui/material` (#10115) @alexfauquette + +### Tree View / `@mui/x-tree-view@6.0.0-alpha.4` + +- [TreeView] Split features into plugins to prepare for Pro version (#10123) @flaviendelangle + +### Docs + +- [docs] Add charts documentation pages to complete pricing table (#10394) @alexfauquette +- [docs] Add missing MIT packages on the Licensing page (#10348) @flaviendelangle +- [docs] Clearer component pattern @oliviertassinari +- [docs] Easier to understand demo (#10370) @oliviertassinari +- [docs] Fix `301` to Material UI @oliviertassinari +- [docs] Improve the column visibility section (#10327) @MBilalShafi +- [docs] Improve the documentation section `rowIdentifier` (#10326) @MBilalShafi +- [docs] Improve pickers localization documentation (#10202) @flaviendelangle +- [docs] Polish typescript ref usage (#10359) @oliviertassinari +- [docs] Improve charts tooltip wording (#10406) @alexfauquette + +### Core + +- [core] Cleanup GitHub issues template (#10372) @romgrk +- [core] Fix Circle CI OOM (#10385) @romgrk +- [core] Improve sleep test helper @oliviertassinari +- [core] Remove unwanted prefixes @oliviertassinari +- [core] Remove duplicate label @oliviertassinari +- [core] Simplify source @oliviertassinari +- [core] Upgrade monorepo (#10425) @cherniavskii +- [core] Upgrade monorepo to have the new typescript-to-proptype (#10224) @flaviendelangle +- [test] Do not use deprecated adapter methods (#10416) @flaviendelangle +- [test] Name test suites according to sentence case (#10429) @alexfauquette + ## 6.14.0 _Sep 14, 2023_ @@ -626,7 +1149,7 @@ Same changes as in `@mui/x-date-pickers@6.10.1`. ### Core - [core] Add `validate` command (#9714) @romgrk -- [CHANGELOG] Update generator to new format @oliviertassinari +- [changelog] Update generator to new format @oliviertassinari ## 6.10.0 @@ -684,7 +1207,7 @@ Same changes as in `@mui/x-date-pickers@6.10.0`. - [core] Disambiguate eslint plugin name @oliviertassinari - [core] Update priority support issue template and prompt (#9574) @DanailH -- [CHANGELOG] Clarify each plan (#9446) @oliviertassinari +- [changelog] Clarify each plan (#9446) @oliviertassinari - [license] Fix error terminology (#9614) @oliviertassinari ## 6.9.2 @@ -833,8 +1356,8 @@ Same changes as in `@mui/x-date-pickers@6.9.1`. - [core] Fix priority support prompt action (#9472) @DanailH - [core] Update `uses` for priority support action (#9480) @DanailH - [core] Bumb update monorepo (#9476) @alexfauquette -- [CHANGELOG] Fix media quality (#9439) @oliviertassinari -- [CHANGELOG] Remove height img attribute @oliviertassinari +- [changelog] Fix media quality (#9439) @oliviertassinari +- [changelog] Remove height img attribute @oliviertassinari - [test] Skip flaky row pinning tests in JSDOM (#9511) @cherniavskii ## 6.9.0 diff --git a/babel.config.js b/babel.config.js index 71b20890c43a8..b89d55e5b95b6 100644 --- a/babel.config.js +++ b/babel.config.js @@ -23,6 +23,9 @@ const defaultAlias = { '@mui-internal/docs-utilities': resolveAliasPath( './node_modules/@mui/monorepo/packages/docs-utilities', ), + '@mui-internal/test-utils': resolveAliasPath( + './node_modules/@mui/monorepo/packages/test-utils/src', + ), 'typescript-to-proptypes': resolveAliasPath( './node_modules/@mui/monorepo/packages/typescript-to-proptypes', ), diff --git a/changelogOld/CHANGELOG.v4.md b/changelogOld/CHANGELOG.v4.md index b5559f2089792..47a7da8d933fd 100644 --- a/changelogOld/CHANGELOG.v4.md +++ b/changelogOld/CHANGELOG.v4.md @@ -1615,7 +1615,7 @@ Big thanks to the 5 contributors who made this release possible. Here are some h - [docs] Fix imports for x-grid-data-generator (#887) @DanailH - [docs] Skip download of playwright for docs @oliviertassinari -- [CHANGELOG] Polish @oliviertassinari +- [changelog] Polish @oliviertassinari ### core @@ -1674,7 +1674,7 @@ Big thanks to the 2 contributors who made this release possible. Here are some h - [core] Update peer dependencies for React 17 (#814) @DanailH - [core] Batch small changes (#800) @oliviertassinari -- [CHANGELOG] Use the format of the main repository @oliviertassinari +- [changelog] Use the format of the main repository @oliviertassinari ## [4.0.0-alpha.14](https://github.com/mui/mui-x/compare/v4.0.0-alpha.13...v4.0.0-alpha.14) diff --git a/dangerfile.js b/dangerfile.js index 29eaf8da32943..a3726af581bf7 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -1,39 +1,4 @@ const { danger, markdown } = require('danger'); -const fse = require('fs-extra'); -const path = require('path'); -const prettier = require('prettier'); - -const dangerCommand = process.env.DANGER_COMMAND; - -async function reportBundleSize() { - const snapshotPath = path.join(__dirname, './performance-snapshot.json'); - const prettierConfigPath = path.join(__dirname, './prettier.config.js'); - const snapshot = await fse.readJSON(snapshotPath); - - const headers = ` -| Test case | Unit | Min | Max | Median | Mean | σ | -| --------- | ---- | --- | --- | ------ | ---- | - |`; - - let text = `These are the results for the performance tests: -${headers}\n`; - - const formatter = new Intl.NumberFormat('en'); - - Object.entries(snapshot).forEach(([name, values]) => { - const min = formatter.format(values.min); - const max = formatter.format(values.max); - const mean = formatter.format(values.mean); - const median = formatter.format(values.median); - const stdDev = formatter.format(values.stdDev); - text += `| ${name} | ms | ${min} | ${max} | ${median} | ${mean} | ${stdDev} |\n`; - }); - - const prettierConfig = prettier.resolveConfig.sync(snapshotPath, { - config: prettierConfigPath, - }); - - markdown(prettier.format(text, { prettierConfig, parser: 'markdown' })); -} function addDeployPreviewUrls() { /** @@ -59,24 +24,21 @@ function addDeployPreviewUrls() { .filter((file) => file.startsWith('docs/data') && file.endsWith('.md')) .slice(0, 5); - markdown(` -## Netlify deploy preview + markdown(`**Deploy preview:** ${netlifyPreview}`); -Netlify deploy preview: ${netlifyPreview} + if (docs.length) { + markdown(` -### Updated pages +**Updated pages:** -${ - docs.length - ? docs - .map((docsPath) => { - const formattedUrl = formatFileToLink(docsPath); - return `- [${docsPath}](${netlifyPreview}${formattedUrl})`; - }) - .join('\n') - : 'No updates.' -} +${docs + .map((docsPath) => { + const formattedUrl = formatFileToLink(docsPath); + return `- [${docsPath}](${netlifyPreview}${formattedUrl})`; + }) + .join('\n')} `); + } } function addL10nHelpMessage() { @@ -112,14 +74,6 @@ function addL10nHelpMessage() { async function run() { addL10nHelpMessage(); addDeployPreviewUrls(); - - switch (dangerCommand) { - case 'reportPerformance': - await reportBundleSize(); - break; - default: - throw new TypeError(`Unrecognized danger command '${dangerCommand}'`); - } } run().catch((error) => { diff --git a/docs/.link-check-errors.txt b/docs/.link-check-errors.txt index 8435836baf7ce..7d8878c20dfd0 100644 --- a/docs/.link-check-errors.txt +++ b/docs/.link-check-errors.txt @@ -15,4 +15,3 @@ Broken links found by `yarn docs:link-check` that exist: - https://mui.com/x/api/data-grid/data-grid/#props - https://mui.com/x/api/data-grid/data-grid/#slots - https://mui.com/x/api/date-pickers/date-picker/#slots -- https://mui.com/x/react-data-grid/migration-v4/ diff --git a/docs/config.js b/docs/config.js index 49a7ebd2de7f4..d8033570bf45b 100644 --- a/docs/config.js +++ b/docs/config.js @@ -1 +1 @@ -module.exports = require('@mui/monorepo/docs/config'); +module.exports = require('docs/config'); diff --git a/docs/data/charts-component-api-pages.ts b/docs/data/charts-component-api-pages.ts index f10b505b16bb1..d08cd143d52f2 100644 --- a/docs/data/charts-component-api-pages.ts +++ b/docs/data/charts-component-api-pages.ts @@ -1,4 +1,4 @@ -import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import type { MuiPage } from 'docs/src/MuiPage'; export default [ { pathname: '/x/api/charts/area-element', title: 'AreaElement' }, @@ -9,6 +9,8 @@ export default [ { pathname: '/x/api/charts/charts-axis', title: 'ChartsAxis' }, { pathname: '/x/api/charts/charts-axis-highlight', title: 'ChartsAxisHighlight' }, { pathname: '/x/api/charts/charts-clip-path', title: 'ChartsClipPath' }, + { pathname: '/x/api/charts/charts-legend', title: 'ChartsLegend' }, + { pathname: '/x/api/charts/charts-reference-line', title: 'ChartsReferenceLine' }, { pathname: '/x/api/charts/charts-tooltip', title: 'ChartsTooltip' }, { pathname: '/x/api/charts/charts-x-axis', title: 'ChartsXAxis' }, { pathname: '/x/api/charts/charts-y-axis', title: 'ChartsYAxis' }, diff --git a/docs/data/charts/areas-demo/AreaChartConnectNulls.js b/docs/data/charts/areas-demo/AreaChartConnectNulls.js new file mode 100644 index 0000000000000..157246f54383d --- /dev/null +++ b/docs/data/charts/areas-demo/AreaChartConnectNulls.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { LineChart } from '@mui/x-charts/LineChart'; + +const data = [4000, 3000, 2000, null, 1890, 2390, 3490]; +const xData = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G']; + +export default function AreaChartConnectNulls() { + return ( + + + + + ); +} diff --git a/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx b/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx new file mode 100644 index 0000000000000..157246f54383d --- /dev/null +++ b/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { LineChart } from '@mui/x-charts/LineChart'; + +const data = [4000, 3000, 2000, null, 1890, 2390, 3490]; +const xData = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G']; + +export default function AreaChartConnectNulls() { + return ( + + + + + ); +} diff --git a/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx.preview b/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx.preview new file mode 100644 index 0000000000000..436f3df1d5541 --- /dev/null +++ b/docs/data/charts/areas-demo/AreaChartConnectNulls.tsx.preview @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/docs/data/charts/areas-demo/areas-demo.md b/docs/data/charts/areas-demo/areas-demo.md index 4f513a43ddb35..81c8db85b2022 100644 --- a/docs/data/charts/areas-demo/areas-demo.md +++ b/docs/data/charts/areas-demo/areas-demo.md @@ -21,3 +21,7 @@ title: Charts - Areas demonstration ## PercentAreaChart {{"demo": "PercentAreaChart.js"}} + +## AreaChartConnectNulls + +{{"demo": "AreaChartConnectNulls.js"}} diff --git a/docs/data/charts/axis/AxisCustomizationNoSnap.js b/docs/data/charts/axis/AxisCustomizationNoSnap.js index fda8f64eb8019..f33f0352d4813 100644 --- a/docs/data/charts/axis/AxisCustomizationNoSnap.js +++ b/docs/data/charts/axis/AxisCustomizationNoSnap.js @@ -16,8 +16,7 @@ const defaultXAxis = { disableLine: false, disableTicks: false, fontSize: 12, - label: 'my axis', - labelFontSize: 14, + label: 'My axis', tickSize: 6, }; export default function AxisCustomizationNoSnap() { @@ -27,9 +26,7 @@ export default function AxisCustomizationNoSnap() { data={[ { propName: 'disableLine', knob: 'switch', defaultValue: false }, { propName: 'disableTicks', knob: 'switch', defaultValue: false }, - { propName: 'fontSize', knom: 'number', defaultValue: 12 }, { propName: 'label', knob: 'input', defaultValue: 'my axis' }, - { propName: 'labelFontSize', knom: 'number', defaultValue: 14 }, { propName: 'tickSize', knob: 'number', defaultValue: 6 }, ]} renderDemo={(props) => ( diff --git a/docs/data/charts/axis/AxisTextCustomizationNoSnap.js b/docs/data/charts/axis/AxisTextCustomizationNoSnap.js new file mode 100644 index 0000000000000..9f8cc6aa3d6b3 --- /dev/null +++ b/docs/data/charts/axis/AxisTextCustomizationNoSnap.js @@ -0,0 +1,170 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { BarChart } from '@mui/x-charts/BarChart'; + +const chartSetting = { + height: 300, +}; +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`; + +export default function AxisTextCustomizationNoSnap() { + return ( + ( + + + + )} + getCode={({ props }) => + [ + `import { ScatterChart } from '@mui/x-charts/ScatterChart';`, + '', + `', + ].join('\n') + } + /> + ); +} diff --git a/docs/data/charts/axis/AxisWithComposition.js b/docs/data/charts/axis/AxisWithComposition.js index d8280e0c0e0c8..71a2a507fe90f 100644 --- a/docs/data/charts/axis/AxisWithComposition.js +++ b/docs/data/charts/axis/AxisWithComposition.js @@ -1,13 +1,11 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { - ResponsiveChartContainer, - BarPlot, - LinePlot, - ChartsXAxis, - ChartsYAxis, - axisClasses, -} from '@mui/x-charts'; +import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer'; +import { LinePlot } 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 { axisClasses } from '@mui/x-charts/ChartsAxis'; export default function AxisWithComposition() { return ( @@ -46,10 +44,10 @@ export default function AxisWithComposition() { margin={{ left: 70, right: 70 }} sx={{ [`.${axisClasses.left} .${axisClasses.label}`]: { - transform: 'rotate(-90deg) translate(0px, -20px)', + transform: 'translate(-25px, 0)', }, [`.${axisClasses.right} .${axisClasses.label}`]: { - transform: 'rotate(90deg) translate(0px, -25px)', + transform: 'translate(30px, 0)', }, }} > diff --git a/docs/data/charts/axis/AxisWithComposition.tsx b/docs/data/charts/axis/AxisWithComposition.tsx index d8280e0c0e0c8..71a2a507fe90f 100644 --- a/docs/data/charts/axis/AxisWithComposition.tsx +++ b/docs/data/charts/axis/AxisWithComposition.tsx @@ -1,13 +1,11 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { - ResponsiveChartContainer, - BarPlot, - LinePlot, - ChartsXAxis, - ChartsYAxis, - axisClasses, -} from '@mui/x-charts'; +import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer'; +import { LinePlot } 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 { axisClasses } from '@mui/x-charts/ChartsAxis'; export default function AxisWithComposition() { return ( @@ -46,10 +44,10 @@ export default function AxisWithComposition() { margin={{ left: 70, right: 70 }} sx={{ [`.${axisClasses.left} .${axisClasses.label}`]: { - transform: 'rotate(-90deg) translate(0px, -20px)', + transform: 'translate(-25px, 0)', }, [`.${axisClasses.right} .${axisClasses.label}`]: { - transform: 'rotate(90deg) translate(0px, -25px)', + transform: 'translate(30px, 0)', }, }} > diff --git a/docs/data/charts/axis/HidingAxis.js b/docs/data/charts/axis/HidingAxis.js new file mode 100644 index 0000000000000..98fd4dc12ecd2 --- /dev/null +++ b/docs/data/charts/axis/HidingAxis.js @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { BarChart } from '@mui/x-charts/BarChart'; + +export default function HidingAxis() { + return ( + + ); +} diff --git a/docs/data/charts/axis/HidingAxis.tsx b/docs/data/charts/axis/HidingAxis.tsx new file mode 100644 index 0000000000000..98fd4dc12ecd2 --- /dev/null +++ b/docs/data/charts/axis/HidingAxis.tsx @@ -0,0 +1,14 @@ +import * as React from 'react'; +import { BarChart } from '@mui/x-charts/BarChart'; + +export default function HidingAxis() { + return ( + + ); +} diff --git a/docs/data/charts/axis/HidingAxis.tsx.preview b/docs/data/charts/axis/HidingAxis.tsx.preview new file mode 100644 index 0000000000000..6976af3c2bcca --- /dev/null +++ b/docs/data/charts/axis/HidingAxis.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/charts/axis/ReferenceLine.js b/docs/data/charts/axis/ReferenceLine.js new file mode 100644 index 0000000000000..844f9781c3f40 --- /dev/null +++ b/docs/data/charts/axis/ReferenceLine.js @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer'; +import { LinePlot } from '@mui/x-charts/LineChart'; + +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; +import { ChartsReferenceLine } from '@mui/x-charts/ChartsReferenceLine'; + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 12), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 4), +]; + +const y1 = [5, 5, 10, 90, 85, 70, 30, 25, 25]; +const y2 = [90, 85, 70, 25, 23, 40, 45, 40, 50]; + +const config = { + series: [ + { type: 'line', data: y1 }, + { type: 'line', data: y2 }, + ], + height: 400, + xAxis: [ + { + data: timeData, + scaleType: 'time', + valueFormatter: (date) => + date.getHours() === 0 + ? date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }) + : date.toLocaleTimeString('fr-FR', { + hour: '2-digit', + }), + }, + ], +}; + +export default function ReferenceLine() { + return ( + + + + + + + + + + ); +} diff --git a/docs/data/charts/axis/ReferenceLine.tsx b/docs/data/charts/axis/ReferenceLine.tsx new file mode 100644 index 0000000000000..bef07764db741 --- /dev/null +++ b/docs/data/charts/axis/ReferenceLine.tsx @@ -0,0 +1,66 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer'; +import { LinePlot } from '@mui/x-charts/LineChart'; +import { LineSeriesType } from '@mui/x-charts/models'; +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; +import { ChartsReferenceLine } from '@mui/x-charts/ChartsReferenceLine'; + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 12), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 4), +]; + +const y1 = [5, 5, 10, 90, 85, 70, 30, 25, 25]; +const y2 = [90, 85, 70, 25, 23, 40, 45, 40, 50]; + +const config = { + series: [ + { type: 'line', data: y1 }, + { type: 'line', data: y2 }, + ] as LineSeriesType[], + height: 400, + xAxis: [ + { + data: timeData, + scaleType: 'time', + valueFormatter: (date: Date) => + date.getHours() === 0 + ? date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }) + : date.toLocaleTimeString('fr-FR', { + hour: '2-digit', + }), + } as const, + ], +}; + +export default function ReferenceLine() { + return ( + + + + + + + + + + ); +} diff --git a/docs/data/charts/axis/ReferenceLine.tsx.preview b/docs/data/charts/axis/ReferenceLine.tsx.preview new file mode 100644 index 0000000000000..17c9fe4ec5d2e --- /dev/null +++ b/docs/data/charts/axis/ReferenceLine.tsx.preview @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/docs/data/charts/axis/TickLabelPosition.js b/docs/data/charts/axis/TickLabelPosition.js new file mode 100644 index 0000000000000..ea95912dce0a4 --- /dev/null +++ b/docs/data/charts/axis/TickLabelPosition.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickLabelPosition() { + return ( + + [0, 12].includes(time.getHours()), + tickLabelInterval: (time) => time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; + +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params) => { + const { position } = params; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +}; diff --git a/docs/data/charts/axis/TickLabelPosition.tsx b/docs/data/charts/axis/TickLabelPosition.tsx new file mode 100644 index 0000000000000..ce2ad07ff5dac --- /dev/null +++ b/docs/data/charts/axis/TickLabelPosition.tsx @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ShowMarkParams } from '@mui/x-charts/models'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickLabelPosition() { + return ( + + [0, 12].includes(time.getHours()), + tickLabelInterval: (time) => time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date: Date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params: ShowMarkParams) => { + const { position } = params as ShowMarkParams; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +} as const; diff --git a/docs/data/charts/axis/TickNumber.js b/docs/data/charts/axis/TickNumber.js index 4a475f1ffc8b2..dc3c5a63f698e 100644 --- a/docs/data/charts/axis/TickNumber.js +++ b/docs/data/charts/axis/TickNumber.js @@ -29,7 +29,7 @@ const valueFormatter = (date) => const config = { series: [{ data: y1 }, { data: y2 }], - height: 400, + height: 300, topAxis: 'half days', leftAxis: null, }; diff --git a/docs/data/charts/axis/TickNumber.tsx b/docs/data/charts/axis/TickNumber.tsx index 265a7fa020237..24792c3df9425 100644 --- a/docs/data/charts/axis/TickNumber.tsx +++ b/docs/data/charts/axis/TickNumber.tsx @@ -29,7 +29,7 @@ const valueFormatter = (date: Date) => const config = { series: [{ data: y1 }, { data: y2 }], - height: 400, + height: 300, topAxis: 'half days', leftAxis: null, }; diff --git a/docs/data/charts/axis/TickPosition.js b/docs/data/charts/axis/TickPosition.js new file mode 100644 index 0000000000000..e4ee1e8160d41 --- /dev/null +++ b/docs/data/charts/axis/TickPosition.js @@ -0,0 +1,96 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickPosition() { + return ( + + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; + +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params) => { + const { position } = params; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +}; diff --git a/docs/data/charts/axis/TickPosition.tsx b/docs/data/charts/axis/TickPosition.tsx new file mode 100644 index 0000000000000..c18b8674dbfe3 --- /dev/null +++ b/docs/data/charts/axis/TickPosition.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { ShowMarkParams } from '@mui/x-charts/models'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function TickPosition() { + return ( + + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} + /> + + ); +} + +const valueFormatter = (date: Date) => + date.toLocaleDateString('fr-FR', { + month: '2-digit', + day: '2-digit', + }); + +const timeData = [ + new Date(2023, 7, 31), + new Date(2023, 7, 31, 3), + new Date(2023, 7, 31, 6), + new Date(2023, 7, 31, 9), + new Date(2023, 7, 31, 12), + new Date(2023, 7, 31, 15), + new Date(2023, 7, 31, 18), + new Date(2023, 8, 1), + new Date(2023, 8, 1, 3), + new Date(2023, 8, 1, 6), + new Date(2023, 8, 1, 9), + new Date(2023, 8, 1, 12), + new Date(2023, 8, 1, 15), + new Date(2023, 8, 1, 18), + new Date(2023, 8, 2), + new Date(2023, 8, 2, 3), + new Date(2023, 8, 2, 6), + new Date(2023, 8, 2, 9), + new Date(2023, 8, 2, 12), + new Date(2023, 8, 2, 15), + new Date(2023, 8, 2, 18), + new Date(2023, 8, 3), + new Date(2023, 8, 3, 3), + new Date(2023, 8, 3, 6), + new Date(2023, 8, 3, 9), + new Date(2023, 8, 3, 12), + new Date(2023, 8, 3, 15), + new Date(2023, 8, 3, 18), + new Date(2023, 8, 4), +]; + +const y1 = [ + 5, 5.5, 5.3, 4.9, 5, 6.2, 8.9, 10, 15, 30, 80, 90, 94, 93, 85, 86, 75, 70, 68, 50, + 20, 30, 35, 28, 25, 27, 30, 28, 25, +]; +const y2 = [ + 90, 93, 89, 84, 85, 83, 73, 70, 63, 32, 30, 25, 18, 19, 23, 30, 32, 36, 40, 40, 42, + 45, 46, 42, 39, 40, 41, 43, 50, +]; + +const showMark = (params: ShowMarkParams) => { + const { position } = params as ShowMarkParams; + return position.getHours() === 0; +}; + +const config = { + series: [ + { data: y1, showMark }, + { data: y2, showMark }, + ], + height: 300, + topAxis: 'topAxis', + bottomAxis: 'bottomAxis', + leftAxis: null, +}; +const xAxisCommon = { + data: timeData, + scaleType: 'time', + valueFormatter, +} as const; diff --git a/docs/data/charts/axis/TickPosition.tsx.preview b/docs/data/charts/axis/TickPosition.tsx.preview new file mode 100644 index 0000000000000..660569fb3b5c1 --- /dev/null +++ b/docs/data/charts/axis/TickPosition.tsx.preview @@ -0,0 +1,16 @@ + time.getHours() === 0, + }, + { + ...xAxisCommon, + id: 'topAxis', + scaleType: 'point', + }, + ]} + {...config} +/> \ No newline at end of file diff --git a/docs/data/charts/axis/axis.md b/docs/data/charts/axis/axis.md index 5530233612569..050f714cd97b1 100644 --- a/docs/data/charts/axis/axis.md +++ b/docs/data/charts/axis/axis.md @@ -67,7 +67,9 @@ xAxis={[{ min: 10, max: 50, }]} {{"demo": "MinMaxExample.js"}} -### Ticks positions +## Tick position + +### Automatic tick position You can customize the number of ticks with the property `tickNumber`. @@ -86,6 +88,43 @@ Here the top axis has a `tickMinStep` of half a day, and the bottom axis a `tick {{"demo": "TickNumber.js"}} +### Fixed tick positions + +If you want more control over the tick position, you can use the `tickInterval` property. + +This property accepts an array of values. +Ticks will be placed at those values. + +For axis with scale type `'point'`, the `tickInterval` property can be a filtering function of the type `(value, index) => boolean`. + +In the next demo, both axes are with `scaleType='point'`. +The top axis displays the default behavior. +It shows a tick for each point. +The bottom axis uses a filtering function to only display a tick at the beginning of a day. + +{{"demo": "TickPosition.js"}} + +### Filtering ticks label + +You can display labels only on a subset of ticks with the `tickLabelInterval` property. +It's a filtering function in the `(value, index) => boolean` form. + +For example `tickLabelInterval: (value, index) => index % 2 === 0` will show the label every two ticks. + +:::warning +The `value` and `index` arguments are those of the ticks, not the axis data. +::: + +By default, ticks are filtered such that their labels do not overlap. +You can override this behavior with `tickLabelInterval: () => true` which forces showing the tick label for each tick. + +In this example, the top axis is a reference for the default behavior. +Notice that tick labels do not overflow. + +At the bottom, you can see one tick for the beginning and the middle of the day but the tick label is only displayed for the beginning of the day. + +{{"demo": "TickLabelPosition.js"}} + ## Axis customization You can further customize the axis rendering besides the axis definition. @@ -101,13 +140,26 @@ Those pros can accept three type of value: {{"demo": "ModifyAxisPosition.js"}} +### Hiding axis + +To hide an axis, set it to `null`. +For example `leftAxis={null}` hides the left axis. + +{{"demo": "HidingAxis.js"}} + ### Rendering Axes rendering can be further customized. Below is an interactive demonstration of the axis props. {{"demo": "AxisCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} -### Composition +### Text customization + +To customize the text elements (ticks label and the axis label) use the `tickLabelStyle` and `labelStyle` properties of the axis configuration. + +{{"demo": "AxisTextCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} + +## Composition If you are using composition, you have to provide the axis settings in the `` by using `xAxis` and `yAxis` props. @@ -118,3 +170,14 @@ You can choose their position with `position` props which accept `'top'`/`'botto Other props are similar to the ones defined in the [previous section](/x/react-charts/axis/#rendering). {{"demo": "AxisWithComposition.js"}} + +### Reference line + +The `` component add a reference line to the charts. +You can provide an `x` or `y` prop to get a vertical or horizontal line respectively at this value. + +You can add a `label` to this reference line. +It can be placed with `labelAlign` prop which accepts `'start'`, `'middle'`, and `'end'` values. +Elements can be styled with `lineStyle` and `labelStyle` props. + +{{"demo": "ReferenceLine.js"}} diff --git a/docs/data/charts/bars/BarAnimation.js b/docs/data/charts/bars/BarAnimation.js new file mode 100644 index 0000000000000..fc02054c2216c --- /dev/null +++ b/docs/data/charts/bars/BarAnimation.js @@ -0,0 +1,146 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Slider from '@mui/material/Slider'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { BarChart } from '@mui/x-charts/BarChart'; + +export default function BarAnimation() { + const [seriesNb, setSeriesNb] = React.useState(2); + const [itemNb, setItemNb] = React.useState(5); + const [skipAnimation, setSkipAnimation] = React.useState(false); + + const handleItemNbChange = (event, newValue) => { + if (typeof newValue !== 'number') { + return; + } + setItemNb(newValue); + }; + const handleSeriesNbChange = (event, newValue) => { + if (typeof newValue !== 'number') { + return; + } + setSeriesNb(newValue); + }; + + return ( + + ({ ...s, data: s.data.slice(0, itemNb) }))} + skipAnimation={skipAnimation} + /> + setSkipAnimation(event.target.checked)} /> + } + label="skipAnimation" + labelPlacement="end" + /> + + Number of items + + + + Number of series + + + + ); +} + +const highlightScope = { + highlighted: 'series', + faded: 'global', +}; + +const series = [ + { + label: 'series 1', + data: [ + 2423, 2210, 764, 1879, 1478, 1373, 1891, 2171, 620, 1269, 724, 1707, 1188, + 1879, 626, 1635, 2177, 516, 1793, 1598, + ], + }, + { + label: 'series 2', + data: [ + 2362, 2254, 1962, 1336, 586, 1069, 2194, 1629, 2173, 2031, 1757, 862, 2446, + 910, 2430, 2300, 805, 1835, 1684, 2197, + ], + }, + { + label: 'series 3', + data: [ + 1145, 1214, 975, 2266, 1768, 2341, 747, 1282, 1780, 1766, 2115, 1720, 1057, + 2000, 1716, 2253, 619, 1626, 1209, 1786, + ], + }, + { + label: 'series 4', + data: [ + 2361, 979, 2430, 1768, 1913, 2342, 1868, 1319, 1038, 2139, 1691, 935, 2262, + 1580, 692, 1559, 1344, 1442, 1593, 1889, + ], + }, + { + label: 'series 5', + data: [ + 968, 1371, 1381, 1060, 1327, 934, 1779, 1361, 878, 1055, 1737, 2380, 875, 2408, + 1066, 1802, 1442, 1567, 1552, 1742, + ], + }, + { + label: 'series 6', + data: [ + 2316, 1845, 2057, 1479, 1859, 1015, 1569, 1448, 1354, 1007, 799, 1748, 1454, + 1968, 1129, 1196, 2158, 540, 1482, 880, + ], + }, + { + label: 'series 7', + data: [ + 2140, 2082, 708, 2032, 554, 1365, 2121, 1639, 2430, 2440, 814, 1328, 883, 1811, + 2322, 1743, 700, 2131, 1473, 957, + ], + }, + { + label: 'series 8', + data: [ + 1074, 744, 2487, 823, 2252, 2317, 2139, 1818, 2256, 1769, 1123, 1461, 672, + 1335, 960, 1871, 2305, 1231, 2005, 908, + ], + }, + { + label: 'series 9', + data: [ + 1792, 886, 2472, 1546, 2164, 2323, 2435, 1268, 2368, 2158, 2200, 1316, 552, + 1874, 1771, 1038, 1838, 2029, 1793, 1117, + ], + }, + { + label: 'series 10', + data: [ + 1433, 1161, 1107, 1517, 1410, 1058, 676, 1280, 1936, 1774, 698, 1721, 1421, + 785, 1752, 800, 990, 1809, 1985, 665, + ], + }, +].map((s) => ({ ...s, highlightScope })); diff --git a/docs/data/charts/bars/BarAnimation.tsx b/docs/data/charts/bars/BarAnimation.tsx new file mode 100644 index 0000000000000..882a33e02afcf --- /dev/null +++ b/docs/data/charts/bars/BarAnimation.tsx @@ -0,0 +1,146 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Slider from '@mui/material/Slider'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { BarChart } from '@mui/x-charts/BarChart'; + +export default function BarAnimation() { + const [seriesNb, setSeriesNb] = React.useState(2); + const [itemNb, setItemNb] = React.useState(5); + const [skipAnimation, setSkipAnimation] = React.useState(false); + + const handleItemNbChange = (event: Event, newValue: number | number[]) => { + if (typeof newValue !== 'number') { + return; + } + setItemNb(newValue); + }; + const handleSeriesNbChange = (event: Event, newValue: number | number[]) => { + if (typeof newValue !== 'number') { + return; + } + setSeriesNb(newValue); + }; + + return ( + + ({ ...s, data: s.data.slice(0, itemNb) }))} + skipAnimation={skipAnimation} + /> + setSkipAnimation(event.target.checked)} /> + } + label="skipAnimation" + labelPlacement="end" + /> + + Number of items + + + + Number of series + + + + ); +} + +const highlightScope = { + highlighted: 'series', + faded: 'global', +} as const; + +const series = [ + { + label: 'series 1', + data: [ + 2423, 2210, 764, 1879, 1478, 1373, 1891, 2171, 620, 1269, 724, 1707, 1188, + 1879, 626, 1635, 2177, 516, 1793, 1598, + ], + }, + { + label: 'series 2', + data: [ + 2362, 2254, 1962, 1336, 586, 1069, 2194, 1629, 2173, 2031, 1757, 862, 2446, + 910, 2430, 2300, 805, 1835, 1684, 2197, + ], + }, + { + label: 'series 3', + data: [ + 1145, 1214, 975, 2266, 1768, 2341, 747, 1282, 1780, 1766, 2115, 1720, 1057, + 2000, 1716, 2253, 619, 1626, 1209, 1786, + ], + }, + { + label: 'series 4', + data: [ + 2361, 979, 2430, 1768, 1913, 2342, 1868, 1319, 1038, 2139, 1691, 935, 2262, + 1580, 692, 1559, 1344, 1442, 1593, 1889, + ], + }, + { + label: 'series 5', + data: [ + 968, 1371, 1381, 1060, 1327, 934, 1779, 1361, 878, 1055, 1737, 2380, 875, 2408, + 1066, 1802, 1442, 1567, 1552, 1742, + ], + }, + { + label: 'series 6', + data: [ + 2316, 1845, 2057, 1479, 1859, 1015, 1569, 1448, 1354, 1007, 799, 1748, 1454, + 1968, 1129, 1196, 2158, 540, 1482, 880, + ], + }, + { + label: 'series 7', + data: [ + 2140, 2082, 708, 2032, 554, 1365, 2121, 1639, 2430, 2440, 814, 1328, 883, 1811, + 2322, 1743, 700, 2131, 1473, 957, + ], + }, + { + label: 'series 8', + data: [ + 1074, 744, 2487, 823, 2252, 2317, 2139, 1818, 2256, 1769, 1123, 1461, 672, + 1335, 960, 1871, 2305, 1231, 2005, 908, + ], + }, + { + label: 'series 9', + data: [ + 1792, 886, 2472, 1546, 2164, 2323, 2435, 1268, 2368, 2158, 2200, 1316, 552, + 1874, 1771, 1038, 1838, 2029, 1793, 1117, + ], + }, + { + label: 'series 10', + data: [ + 1433, 1161, 1107, 1517, 1410, 1058, 676, 1280, 1936, 1774, 698, 1721, 1421, + 785, 1752, 800, 990, 1809, 1985, 665, + ], + }, +].map((s) => ({ ...s, highlightScope })); diff --git a/docs/data/charts/bars/BarsDataset.js b/docs/data/charts/bars/BarsDataset.js index 9030ae12be240..619a54cf3d8ec 100644 --- a/docs/data/charts/bars/BarsDataset.js +++ b/docs/data/charts/bars/BarsDataset.js @@ -12,7 +12,7 @@ const chartSetting = { height: 300, sx: { [`.${axisClasses.left} .${axisClasses.label}`]: { - transform: 'rotate(-90deg) translate(0px, -20px)', + transform: 'translate(-20px, 0)', }, }, }; diff --git a/docs/data/charts/bars/BarsDataset.tsx b/docs/data/charts/bars/BarsDataset.tsx index f893035ba52e1..5f4fa4f9f90b2 100644 --- a/docs/data/charts/bars/BarsDataset.tsx +++ b/docs/data/charts/bars/BarsDataset.tsx @@ -12,7 +12,7 @@ const chartSetting = { height: 300, sx: { [`.${axisClasses.left} .${axisClasses.label}`]: { - transform: 'rotate(-90deg) translate(0px, -20px)', + transform: 'translate(-20px, 0)', }, }, }; diff --git a/docs/data/charts/bars/StackBars.js b/docs/data/charts/bars/StackBars.js index 14b530048c5be..8ebf29b218502 100644 --- a/docs/data/charts/bars/StackBars.js +++ b/docs/data/charts/bars/StackBars.js @@ -5,11 +5,11 @@ export default function StackBars() { return ( + +// For a composed chart + + + +``` + +{{"demo": "BarAnimation.js"}} diff --git a/docs/data/charts/funnel/funnel.md b/docs/data/charts/funnel/funnel.md index ad7fe7bf0bdf9..28b5e4ee42fa8 100644 --- a/docs/data/charts/funnel/funnel.md +++ b/docs/data/charts/funnel/funnel.md @@ -2,7 +2,7 @@ title: React Funnel chart --- -# Charts - Funnel 🚧 +# Charts - Funnel 🚧[](/x/introduction/licensing/#pro-plan 'Pro plan')

Funnel charts allows to express quantity evolution along a process, such as audience engagement.

diff --git a/docs/data/charts/gantt/gantt.md b/docs/data/charts/gantt/gantt.md index 1823bab2adc08..30de78d4010dd 100644 --- a/docs/data/charts/gantt/gantt.md +++ b/docs/data/charts/gantt/gantt.md @@ -2,7 +2,7 @@ title: React Gantt chart --- -# Charts - Gantt 🚧 +# Charts - Gantt 🚧[](/x/introduction/licensing/#pro-plan 'Pro plan')

Gantt charts can illustrate a product schedule and the relationships between its various activities.

diff --git a/docs/data/charts/legend/BasicLegend.js b/docs/data/charts/legend/BasicLegend.js index ed40377966311..1822fe5a7fb7d 100644 --- a/docs/data/charts/legend/BasicLegend.js +++ b/docs/data/charts/legend/BasicLegend.js @@ -13,8 +13,8 @@ export default function BasicLegend() { return ( ({ - x: chance.floating({ min: -20, max: 20 }), - y: chance.floating({ min: -20, max: 20 }), -})).map((d, index) => ({ ...d, id: index })); - -const cssVarToKey = { - '--ChartsLegend-itemWidth': 'item width', - '--ChartsLegend-itemMarkSize': 'item mark size', - '--ChartsLegend-labelSpacing': 'label spacing', - '--ChartsLegend-rootSpacing': 'root spacing', -}; -export default function DimensionsNoSnap() { - return ( - ( - - )} - getCode={({ props }) => { - return [ - `import { ScatterChart } from '@mui/x-charts/ScatterChart';`, - '', - ` typeof props[cssVarToKey[cssVar]] === 'number') - .map((cssVar) => ` '${cssVar}': ${props[cssVarToKey[cssVar]]}px,`), - ' }}', - '/>', - ].join('\n'); - }} - /> - ); -} diff --git a/docs/data/charts/legend/HiddenLegend.js b/docs/data/charts/legend/HiddenLegend.js new file mode 100644 index 0000000000000..80879da5f9bf5 --- /dev/null +++ b/docs/data/charts/legend/HiddenLegend.js @@ -0,0 +1,38 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const series = [ + { + data: [ + { id: 0, value: 10, label: 'series A' }, + { id: 1, value: 15, label: 'series B' }, + { id: 2, value: 20, label: 'series C' }, + ], + }, +]; + +export default function HiddenLegend() { + const [isHidden, setIsHidden] = React.useState(false); + + return ( + + setIsHidden(event.target.checked)} /> + } + label="hide the legend" + labelPlacement="end" + /> + + + ); +} diff --git a/docs/data/charts/legend/HiddenLegend.tsx b/docs/data/charts/legend/HiddenLegend.tsx new file mode 100644 index 0000000000000..80879da5f9bf5 --- /dev/null +++ b/docs/data/charts/legend/HiddenLegend.tsx @@ -0,0 +1,38 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const series = [ + { + data: [ + { id: 0, value: 10, label: 'series A' }, + { id: 1, value: 15, label: 'series B' }, + { id: 2, value: 20, label: 'series C' }, + ], + }, +]; + +export default function HiddenLegend() { + const [isHidden, setIsHidden] = React.useState(false); + + return ( + + setIsHidden(event.target.checked)} /> + } + label="hide the legend" + labelPlacement="end" + /> + + + ); +} diff --git a/docs/data/charts/legend/HiddenLegend.tsx.preview b/docs/data/charts/legend/HiddenLegend.tsx.preview new file mode 100644 index 0000000000000..52019cf368ffa --- /dev/null +++ b/docs/data/charts/legend/HiddenLegend.tsx.preview @@ -0,0 +1,14 @@ + setIsHidden(event.target.checked)} /> + } + label="hide the legend" + labelPlacement="end" +/> + \ No newline at end of file diff --git a/docs/data/charts/legend/LegendCustomizationNoSnap.js b/docs/data/charts/legend/LegendCustomizationNoSnap.js deleted file mode 100644 index ac46041150c3f..0000000000000 --- a/docs/data/charts/legend/LegendCustomizationNoSnap.js +++ /dev/null @@ -1,108 +0,0 @@ -import * as React from 'react'; -import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; -import { ScatterChart } from '@mui/x-charts/ScatterChart'; -import { Chance } from 'chance'; - -const chance = new Chance(42); - -const data = Array.from({ length: 50 }, () => ({ - x: chance.floating({ min: -20, max: 20 }), - y: chance.floating({ min: -20, max: 20 }), -})).map((d, index) => ({ ...d, id: index })); - -export default function LegendCustomizationNoSnap() { - return ( - ( - - )} - getCode={({ props }) => { - return [ - `import { ScatterChart } from '@mui/x-charts/ScatterChart';`, - '', - `', - ].join('\n'); - }} - /> - ); -} diff --git a/docs/data/charts/legend/LegendDimensionNoSnap.js b/docs/data/charts/legend/LegendDimensionNoSnap.js new file mode 100644 index 0000000000000..2581092596b58 --- /dev/null +++ b/docs/data/charts/legend/LegendDimensionNoSnap.js @@ -0,0 +1,94 @@ +import * as React from 'react'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const data = [ + { id: 0, value: 10, label: 'Series A' }, + { id: 1, value: 15, label: 'Series B' }, + { id: 2, value: 20, label: 'Series C' }, + { id: 3, value: 10, label: 'Series D' }, + { id: 4, value: 15, label: 'Series E' }, + { id: 5, value: 20, label: 'Series F' }, + { id: 6, value: 10, label: 'Series G' }, + { id: 7, value: 15, label: 'Series H' }, +]; + +const itemsNumber = 15; + +export default function LegendDimensionNoSnap() { + return ( + ( + + )} + getCode={({ props }) => { + return [ + `import { PieChart } from '@mui/x-charts/PieChart';`, + '', + `', + ].join('\n'); + }} + /> + ); +} diff --git a/docs/data/charts/legend/LegendPositionNoSnap.js b/docs/data/charts/legend/LegendPositionNoSnap.js new file mode 100644 index 0000000000000..ac23970b9c43e --- /dev/null +++ b/docs/data/charts/legend/LegendPositionNoSnap.js @@ -0,0 +1,102 @@ +import * as React from 'react'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const data = [ + { id: 0, value: 10, label: 'Series A' }, + { id: 1, value: 15, label: 'Series B' }, + { id: 2, value: 20, label: 'Series C' }, + { id: 3, value: 10, label: 'Series D' }, + { id: 4, value: 15, label: 'Series E' }, + { id: 5, value: 20, label: 'Series F' }, + { id: 6, value: 10, label: 'Series G' }, + { id: 7, value: 15, label: 'Series H' }, + { id: 8, value: 20, label: 'Series I' }, + { id: 9, value: 10, label: 'Series J' }, + { id: 10, value: 15, label: 'Series K' }, + { id: 11, value: 20, label: 'Series L' }, + { id: 12, value: 10, label: 'Series M' }, + { id: 13, value: 15, label: 'Series N' }, + { id: 14, value: 20, label: 'Series O' }, +]; + +export default function LegendPositionNoSnap() { + return ( + ( + + )} + getCode={({ props }) => { + return [ + `import { PieChart } from '@mui/x-charts/PieChart';`, + '', + `', + ].join('\n'); + }} + /> + ); +} diff --git a/docs/data/charts/legend/LegendTextStylingNoSnap.js b/docs/data/charts/legend/LegendTextStylingNoSnap.js new file mode 100644 index 0000000000000..9af402d6c16be --- /dev/null +++ b/docs/data/charts/legend/LegendTextStylingNoSnap.js @@ -0,0 +1,84 @@ +import * as React from 'react'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const data = [ + { id: 0, value: 10, label: 'Series A' }, + { id: 1, value: 15, label: 'Series B' }, + { id: 2, value: 20, label: 'Series C' }, + { id: 3, value: 10, label: 'Series D' }, + { id: 4, value: 15, label: 'Series E' }, + { id: 5, value: 20, label: 'Series F' }, + { id: 6, value: 10, label: 'Series G' }, + { id: 7, value: 15, label: 'Series H' }, +]; + +const itemsNumber = 15; + +export default function LegendTextStylingNoSnap() { + return ( + ( + ({ + ...item, + label: item.label.replace(' ', props.breakLine ? '\n' : ' '), + })), + }, + ]} + slotProps={{ + legend: { + labelStyle: { + fontSize: props.fontSize, + fill: props.fill, + }, + }, + }} + margin={{ + top: 10, + bottom: 10, + left: 10, + right: 200, + }} + width={400} + height={400} + /> + )} + getCode={({ props }) => { + return [ + `import { PieChart } from '@mui/x-charts/PieChart';`, + '', + `', + ].join('\n'); + }} + /> + ); +} diff --git a/docs/data/charts/legend/legend.md b/docs/data/charts/legend/legend.md index 3f686306aa4df..063e315f53f03 100644 --- a/docs/data/charts/legend/legend.md +++ b/docs/data/charts/legend/legend.md @@ -12,28 +12,48 @@ In chart components, the legend links series with `label` properties and their c {{"demo": "BasicLegend.js"}} -## Placement +## Customization + +### Position The legend can either be displayed in a `'column'` or `'row'` layout controlled with the `direction` property. -It can also be moved by a combination of `position: { vertical, horizontal }` properties and the legend offset values. -The `position` places the legend just next to the drawing area, and offset values move it relative to this base position. +It can also be moved with the `position: { vertical, horizontal }` property which defines how the legend aligns within the SVG. - `vertical` can be `'top'`, `'middle'`, or `'bottom'`. - `horizontal` can be `'left'`, `'middle'`, or `'right'`. -- offsets are set with CSS variables `--ChartsLegend-rootOffsetX` and `--ChartsLegend-rootOffsetY`. -Defaults are such that the legend is placed on top of the charts. +You can add spacing to a given `position` with the `padding` property, which can be either a number or an object `{ top, bottom, left, right }`. +This `padding` will add space between the SVG borders and the legend. + +By default, the legend is placed above the charts. + +{{"demo": "LegendPositionNoSnap.js", "hideToolbar": true, "bg": "inline"}} + +### Hidding + +You can hide the legend with the property `slotProps.legend.hidden`. + +{{"demo": "HiddenLegend.js"}} + +### Dimensions + +Inside the legend, you can customize the pixel value of the width and height of the mark with the `itemMarkWidth` and `itemMarkHeight` props. + +You can also access the `markGap` prop to change the gap between the mark and its label, or the `itemGap` to change the gap between two legend items. +Both props impact the values defined in pixels. -{{"demo": "LegendCustomizationNoSnap.js", "hideToolbar": true, "bg": "inline"}} +{{"demo": "LegendDimensionNoSnap.js", "hideToolbar": true, "bg": "inline"}} -## Dimensions +### Label styling -The dimension of the legend is defined by some CSS variables: +To break lines in legend labels, use the special `\n` character. To customize the label style, you should not use CSS. +Instead, pass a styling object to the `labelStyle` property. -- `--ChartsLegend-itemWidth`: The width of one series (including the mark and the label). -- `--ChartsLegend-itemMarkSize`: The size of the mark square. -- `--ChartsLegend-labelSpacing`: The space between the mark and the label. -- `--ChartsLegend-rootSpacing`: The space between two series. +{{"demo": "LegendTextStylingNoSnap.js", "hideToolbar": true, "bg": "inline"}} -{{"demo": "DimensionsNoSnap.js", "hideToolbar": true, "bg": "inline"}} +:::info +The `labelStyle` property is needed to measure text size, and then place legend items at the correct position. +Style applied by other means will not be taken into account. +Which can lead to label overflow. +::: diff --git a/docs/data/charts/line-demo/LineChartConnectNulls.js b/docs/data/charts/line-demo/LineChartConnectNulls.js new file mode 100644 index 0000000000000..6c9a5e32de469 --- /dev/null +++ b/docs/data/charts/line-demo/LineChartConnectNulls.js @@ -0,0 +1,24 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { LineChart } from '@mui/x-charts/LineChart'; + +const data = [4000, 3000, 2000, null, 1890, 2390, 3490]; +const xData = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G']; +export default function LineChartConnectNulls() { + return ( + + + + + ); +} diff --git a/docs/data/charts/line-demo/LineChartConnectNulls.tsx b/docs/data/charts/line-demo/LineChartConnectNulls.tsx new file mode 100644 index 0000000000000..6c9a5e32de469 --- /dev/null +++ b/docs/data/charts/line-demo/LineChartConnectNulls.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import { LineChart } from '@mui/x-charts/LineChart'; + +const data = [4000, 3000, 2000, null, 1890, 2390, 3490]; +const xData = ['Page A', 'Page B', 'Page C', 'Page D', 'Page E', 'Page F', 'Page G']; +export default function LineChartConnectNulls() { + return ( + + + + + ); +} diff --git a/docs/data/charts/line-demo/LineChartConnectNulls.tsx.preview b/docs/data/charts/line-demo/LineChartConnectNulls.tsx.preview new file mode 100644 index 0000000000000..ccda3551ec64c --- /dev/null +++ b/docs/data/charts/line-demo/LineChartConnectNulls.tsx.preview @@ -0,0 +1,12 @@ + + \ No newline at end of file diff --git a/docs/data/charts/line-demo/LineChartWithReferenceLines.js b/docs/data/charts/line-demo/LineChartWithReferenceLines.js new file mode 100644 index 0000000000000..b991babc95b19 --- /dev/null +++ b/docs/data/charts/line-demo/LineChartWithReferenceLines.js @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { ChartContainer } from '@mui/x-charts/ChartContainer'; +import { ChartsReferenceLine } from '@mui/x-charts'; +import { LinePlot, MarkPlot } from '@mui/x-charts/LineChart'; +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; + +const uData = [4000, 3000, 2000, 2780, 1890, 2390, 3490]; +const pData = [2400, 1398, 9800, 3908, 4800, 3800, 4300]; +const xLabels = [ + 'Page A', + 'Page B', + 'Page C', + 'Page D', + 'Page E', + 'Page F', + 'Page G', +]; + +export default function LineChartWithReferenceLines() { + return ( + + + + + + + + + ); +} diff --git a/docs/data/charts/line-demo/LineChartWithReferenceLines.tsx b/docs/data/charts/line-demo/LineChartWithReferenceLines.tsx new file mode 100644 index 0000000000000..b991babc95b19 --- /dev/null +++ b/docs/data/charts/line-demo/LineChartWithReferenceLines.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { ChartContainer } from '@mui/x-charts/ChartContainer'; +import { ChartsReferenceLine } from '@mui/x-charts'; +import { LinePlot, MarkPlot } from '@mui/x-charts/LineChart'; +import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis'; +import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis'; + +const uData = [4000, 3000, 2000, 2780, 1890, 2390, 3490]; +const pData = [2400, 1398, 9800, 3908, 4800, 3800, 4300]; +const xLabels = [ + 'Page A', + 'Page B', + 'Page C', + 'Page D', + 'Page E', + 'Page F', + 'Page G', +]; + +export default function LineChartWithReferenceLines() { + return ( + + + + + + + + + ); +} diff --git a/docs/data/charts/line-demo/line-demo.md b/docs/data/charts/line-demo/line-demo.md index bbb6525d96912..e79bd9d6ad06f 100644 --- a/docs/data/charts/line-demo/line-demo.md +++ b/docs/data/charts/line-demo/line-demo.md @@ -21,3 +21,11 @@ title: Charts - Line demonstration ## BiaxialLineChart {{"demo": "BiaxialLineChart.js"}} + +## LineChartWithReferenceLines + +{{"demo": "LineChartWithReferenceLines.js"}} + +## LineChartConnectNulls + +{{"demo": "LineChartConnectNulls.js"}} diff --git a/docs/data/charts/lines/CSSCustomization.js b/docs/data/charts/lines/CSSCustomization.js index 256220f5f71cb..480e020b89566 100644 --- a/docs/data/charts/lines/CSSCustomization.js +++ b/docs/data/charts/lines/CSSCustomization.js @@ -71,7 +71,7 @@ export default function CSSCustomization() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/lines/CSSCustomization.tsx b/docs/data/charts/lines/CSSCustomization.tsx index 256220f5f71cb..480e020b89566 100644 --- a/docs/data/charts/lines/CSSCustomization.tsx +++ b/docs/data/charts/lines/CSSCustomization.tsx @@ -71,7 +71,7 @@ export default function CSSCustomization() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ diff --git a/docs/data/charts/lines/ConnectNulls.js b/docs/data/charts/lines/ConnectNulls.js new file mode 100644 index 0000000000000..1a543aeecda35 --- /dev/null +++ b/docs/data/charts/lines/ConnectNulls.js @@ -0,0 +1,37 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function ConnectNulls() { + const [connectNulls, setConnectNulls] = React.useState(true); + + return ( + + setConnectNulls(event.target.checked)} /> + } + label="connectNulls" + labelPlacement="end" + /> + + + ); +} diff --git a/docs/data/charts/lines/ConnectNulls.tsx b/docs/data/charts/lines/ConnectNulls.tsx new file mode 100644 index 0000000000000..1a543aeecda35 --- /dev/null +++ b/docs/data/charts/lines/ConnectNulls.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function ConnectNulls() { + const [connectNulls, setConnectNulls] = React.useState(true); + + return ( + + setConnectNulls(event.target.checked)} /> + } + label="connectNulls" + labelPlacement="end" + /> + + + ); +} diff --git a/docs/data/charts/lines/DifferentLength.js b/docs/data/charts/lines/DifferentLength.js new file mode 100644 index 0000000000000..39a0a48481df1 --- /dev/null +++ b/docs/data/charts/lines/DifferentLength.js @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function DifferentLength() { + return ( + (value == null ? 'NaN' : value.toString()), + }, + { + data: [null, null, null, null, 5.5, 2, 8.5, 1.5, 5], + }, + { + data: [7, 8, 5, 4, null, null, 2, 5.5, 1], + valueFormatter: (value) => (value == null ? '?' : value.toString()), + }, + ]} + height={200} + margin={{ top: 10, bottom: 20 }} + /> + ); +} diff --git a/docs/data/charts/lines/DifferentLength.tsx b/docs/data/charts/lines/DifferentLength.tsx new file mode 100644 index 0000000000000..39a0a48481df1 --- /dev/null +++ b/docs/data/charts/lines/DifferentLength.tsx @@ -0,0 +1,25 @@ +import * as React from 'react'; +import { LineChart } from '@mui/x-charts/LineChart'; + +export default function DifferentLength() { + return ( + (value == null ? 'NaN' : value.toString()), + }, + { + data: [null, null, null, null, 5.5, 2, 8.5, 1.5, 5], + }, + { + data: [7, 8, 5, 4, null, null, 2, 5.5, 1], + valueFormatter: (value) => (value == null ? '?' : value.toString()), + }, + ]} + height={200} + margin={{ top: 10, bottom: 20 }} + /> + ); +} diff --git a/docs/data/charts/lines/StackedAreas.js b/docs/data/charts/lines/StackedAreas.js index fb68774ba13be..5a895f8a4b44d 100644 --- a/docs/data/charts/lines/StackedAreas.js +++ b/docs/data/charts/lines/StackedAreas.js @@ -62,7 +62,7 @@ export default function StackedAreas() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ @@ -91,9 +91,6 @@ export default function StackedAreas() { showMark: false, }, ]} - sx={{ - '--ChartsLegend-itemWidth': '200px', - }} width={600} height={400} margin={{ left: 70 }} diff --git a/docs/data/charts/lines/StackedAreas.tsx b/docs/data/charts/lines/StackedAreas.tsx index fb68774ba13be..5a895f8a4b44d 100644 --- a/docs/data/charts/lines/StackedAreas.tsx +++ b/docs/data/charts/lines/StackedAreas.tsx @@ -62,7 +62,7 @@ export default function StackedAreas() { id: 'Years', data: years, scaleType: 'time', - valueFormatter: (date) => date.getFullYear(), + valueFormatter: (date) => date.getFullYear().toString(), }, ]} series={[ @@ -91,9 +91,6 @@ export default function StackedAreas() { showMark: false, }, ]} - sx={{ - '--ChartsLegend-itemWidth': '200px', - }} width={600} height={400} margin={{ left: 70 }} diff --git a/docs/data/charts/lines/lines.md b/docs/data/charts/lines/lines.md index 9a0cafc30e635..b2edf045f3e0a 100644 --- a/docs/data/charts/lines/lines.md +++ b/docs/data/charts/lines/lines.md @@ -52,6 +52,43 @@ By default, they are stacked in the order you defined them, with positive values For more information, see [stacking docs](/x/react-charts/stacking/). +## Partial data + +### Skip missing points + +Line series can have less data than the axis. +You can handle lines with partial data or data starting at different points by providing `null` values. + +By default, the tooltip does not show series if they have no value. +To override this behavior, use the `valueFormatter` to return a string if the value is `null` or `undefined`. + +{{"demo": "DifferentLength.js"}} + +:::info +When series data length is smaller than the axis one, overflowing values are `undefined` and not `null`. + +The following code plots a line for x between 2 and 4. + +- For x<2, values are set to `null` and then not shown. +- For x>4, values are set to `undefined` and then not shown. + +```jsx + +``` + +::: + +### Connect missing points + +Line series accepts a `connectNulls` property which will continue the interpolation across points with a `null` value. +This property can link two sets of points, with `null` data between them. +However, it can not extrapolate the curve before the first non-null data point or after the last one. + +{{"demo": "ConnectNulls.js"}} + ## Styling ### Interpolation diff --git a/docs/data/charts/overview/overview.md b/docs/data/charts/overview/overview.md index b4a85c857b241..f4220baa29a7d 100644 --- a/docs/data/charts/overview/overview.md +++ b/docs/data/charts/overview/overview.md @@ -8,10 +8,6 @@ packageName: '@mui/x-charts'

A fast and extendable library of react chart components for data visualization.

-:::warning -This library is in the alpha phase. This means it might receive some breaking changes if they are needed to improve the components. -::: - {{"component": "modules/components/ComponentLinkHeader.js", "design": false}} ## Overview @@ -29,19 +25,23 @@ To modify the styling of charts you can rely on all the MUI styling tools, such ## Getting started +:::warning +The `next` tag is used to download the latest v7 **pre-release** version. +::: + To install this library, run ```bash npm -npm install @mui/x-charts +npm install @mui/x-charts@next ``` ```bash yarn -yarn add @mui/x-charts +yarn add @mui/x-charts@next ``` ```bash pnpm -pnpm add @mui/x-charts +pnpm add @mui/x-charts@next ``` diff --git a/docs/data/charts/pie-demo/OnSeriesItemClick.js b/docs/data/charts/pie-demo/OnSeriesItemClick.js new file mode 100644 index 0000000000000..9d87000810266 --- /dev/null +++ b/docs/data/charts/pie-demo/OnSeriesItemClick.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { PieChart } from '@mui/x-charts/PieChart'; + +import { Typography, Stack } from '@mui/material'; + +const items = [ + { value: 10, label: 'Series A ( no Id )' }, + { id: 'id_B', value: 15, label: 'Series B' }, + { id: 'id_C', value: 20, label: 'Series C' }, +]; + +const formatObject = (obj) => { + if (obj === null) { + return ' undefined'; + } + return JSON.stringify(obj, null, 2) + .split('\n') + .map((l) => ` ${l}`) + .join('\n'); +}; +export default function OnSeriesItemClick() { + const [identifier, setIdentifier] = React.useState(null); + const [id, setId] = React.useState(undefined); + + const handleClick = (event, itemIdentifier, item) => { + setId(item.id); + setIdentifier(itemIdentifier); + }; + + return ( + + + {`item id: ${id ?? 'undefined'} + +item identifier: +${formatObject(identifier)}`} + + + + + ); +} diff --git a/docs/data/charts/pie-demo/OnSeriesItemClick.tsx b/docs/data/charts/pie-demo/OnSeriesItemClick.tsx new file mode 100644 index 0000000000000..69a1aa4b7246d --- /dev/null +++ b/docs/data/charts/pie-demo/OnSeriesItemClick.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { PieChart } from '@mui/x-charts/PieChart'; +import { PieItemIdentifier, DefaultizedPieValueType } from '@mui/x-charts/models'; +import { Typography, Stack } from '@mui/material'; + +const items = [ + { value: 10, label: 'Series A ( no Id )' }, + { id: 'id_B', value: 15, label: 'Series B' }, + { id: 'id_C', value: 20, label: 'Series C' }, +]; + +const formatObject = (obj: null | PieItemIdentifier) => { + if (obj === null) { + return ' undefined'; + } + return JSON.stringify(obj, null, 2) + .split('\n') + .map((l) => ` ${l}`) + .join('\n'); +}; +export default function OnSeriesItemClick() { + const [identifier, setIdentifier] = React.useState(null); + const [id, setId] = React.useState(undefined); + + const handleClick = ( + event: React.MouseEvent, + itemIdentifier: PieItemIdentifier, + item: DefaultizedPieValueType, + ) => { + setId(item.id); + setIdentifier(itemIdentifier); + }; + + return ( + + + {`item id: ${id ?? 'undefined'} + +item identifier: +${formatObject(identifier)}`} + + + + + ); +} diff --git a/docs/data/charts/pie-demo/PieChartWithPaddingAngle.js b/docs/data/charts/pie-demo/PieChartWithPaddingAngle.js index 8b7af53fd2377..f0606ec0e296c 100644 --- a/docs/data/charts/pie-demo/PieChartWithPaddingAngle.js +++ b/docs/data/charts/pie-demo/PieChartWithPaddingAngle.js @@ -40,7 +40,9 @@ export default function PieChartWithPaddingAngle() { margin={{ right: 5 }} width={200} height={200} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); diff --git a/docs/data/charts/pie-demo/PieChartWithPaddingAngle.tsx b/docs/data/charts/pie-demo/PieChartWithPaddingAngle.tsx index 8b7af53fd2377..f0606ec0e296c 100644 --- a/docs/data/charts/pie-demo/PieChartWithPaddingAngle.tsx +++ b/docs/data/charts/pie-demo/PieChartWithPaddingAngle.tsx @@ -40,7 +40,9 @@ export default function PieChartWithPaddingAngle() { margin={{ right: 5 }} width={200} height={200} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); diff --git a/docs/data/charts/pie-demo/TwoLevelPieChart.js b/docs/data/charts/pie-demo/TwoLevelPieChart.js index a62c14bc9f839..7a409b13b88a3 100644 --- a/docs/data/charts/pie-demo/TwoLevelPieChart.js +++ b/docs/data/charts/pie-demo/TwoLevelPieChart.js @@ -39,7 +39,9 @@ export default function TwoLevelPieChart() { ]} width={400} height={300} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); } diff --git a/docs/data/charts/pie-demo/TwoLevelPieChart.tsx b/docs/data/charts/pie-demo/TwoLevelPieChart.tsx index 54475f4f2cb45..2a8db580d08b3 100644 --- a/docs/data/charts/pie-demo/TwoLevelPieChart.tsx +++ b/docs/data/charts/pie-demo/TwoLevelPieChart.tsx @@ -38,7 +38,9 @@ export default function TwoLevelPieChart() { ]} width={400} height={300} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); } diff --git a/docs/data/charts/pie-demo/TwoSimplePieChart.js b/docs/data/charts/pie-demo/TwoSimplePieChart.js index 40b156bd19ad6..7cbc1c6f3e928 100644 --- a/docs/data/charts/pie-demo/TwoSimplePieChart.js +++ b/docs/data/charts/pie-demo/TwoSimplePieChart.js @@ -36,7 +36,9 @@ export default function TwoSimplePieChart() { }, ]} height={300} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); } diff --git a/docs/data/charts/pie-demo/TwoSimplePieChart.tsx b/docs/data/charts/pie-demo/TwoSimplePieChart.tsx index bd2b7e2a30b04..bb88d77dfd8b4 100644 --- a/docs/data/charts/pie-demo/TwoSimplePieChart.tsx +++ b/docs/data/charts/pie-demo/TwoSimplePieChart.tsx @@ -37,7 +37,9 @@ export default function TwoSimplePieChart() { }, ]} height={300} - legend={{ hidden: true }} + slotProps={{ + legend: { hidden: true }, + }} /> ); } diff --git a/docs/data/charts/pie-demo/pie-demo.md b/docs/data/charts/pie-demo/pie-demo.md index 009bba9372242..22034943883f9 100644 --- a/docs/data/charts/pie-demo/pie-demo.md +++ b/docs/data/charts/pie-demo/pie-demo.md @@ -29,3 +29,7 @@ title: Charts - Pie demonstration ## PieChartWithCenterLabel {{"demo": "PieChartWithCenterLabel.js"}} + +## OnSeriesItemClick + +{{"demo": "OnSeriesItemClick.js"}} diff --git a/docs/data/charts/pie/PieActiveArc.js b/docs/data/charts/pie/PieActiveArc.js index 293730d328b41..4c967f452fc0a 100644 --- a/docs/data/charts/pie/PieActiveArc.js +++ b/docs/data/charts/pie/PieActiveArc.js @@ -1,5 +1,5 @@ import * as React from 'react'; -import { PieChart, pieArcClasses } from '@mui/x-charts/PieChart'; +import { PieChart } from '@mui/x-charts/PieChart'; const data = [ { id: 0, value: 10, label: 'series A' }, @@ -14,14 +14,9 @@ export default function PieActiveArc() { { data, highlightScope: { faded: 'global', highlighted: 'item' }, - faded: { innerRadius: 30, additionalRadius: -30 }, + faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' }, }, ]} - sx={{ - [`& .${pieArcClasses.faded}`]: { - fill: 'gray', - }, - }} height={200} /> ); diff --git a/docs/data/charts/pie/PieActiveArc.tsx b/docs/data/charts/pie/PieActiveArc.tsx index 293730d328b41..4c967f452fc0a 100644 --- a/docs/data/charts/pie/PieActiveArc.tsx +++ b/docs/data/charts/pie/PieActiveArc.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { PieChart, pieArcClasses } from '@mui/x-charts/PieChart'; +import { PieChart } from '@mui/x-charts/PieChart'; const data = [ { id: 0, value: 10, label: 'series A' }, @@ -14,14 +14,9 @@ export default function PieActiveArc() { { data, highlightScope: { faded: 'global', highlighted: 'item' }, - faded: { innerRadius: 30, additionalRadius: -30 }, + faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' }, }, ]} - sx={{ - [`& .${pieArcClasses.faded}`]: { - fill: 'gray', - }, - }} height={200} /> ); diff --git a/docs/data/charts/pie/PieActiveArc.tsx.preview b/docs/data/charts/pie/PieActiveArc.tsx.preview index d70e759ba08d2..23a3a41ec8384 100644 --- a/docs/data/charts/pie/PieActiveArc.tsx.preview +++ b/docs/data/charts/pie/PieActiveArc.tsx.preview @@ -3,13 +3,8 @@ { data, highlightScope: { faded: 'global', highlighted: 'item' }, - faded: { innerRadius: 30, additionalRadius: -30 }, + faded: { innerRadius: 30, additionalRadius: -30, color: 'gray' }, }, ]} - sx={{ - [`& .${pieArcClasses.faded}`]: { - fill: 'gray', - }, - }} height={200} /> \ No newline at end of file diff --git a/docs/data/charts/pie/PieAnimation.js b/docs/data/charts/pie/PieAnimation.js new file mode 100644 index 0000000000000..2b6fdaf562c38 --- /dev/null +++ b/docs/data/charts/pie/PieAnimation.js @@ -0,0 +1,94 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Slider from '@mui/material/Slider'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const data1 = [ + { label: 'Group A', value: 400 }, + { label: 'Group B', value: 300 }, + { label: 'Group C', value: 300 }, + { label: 'Group D', value: 200 }, +]; + +const data2 = [ + { label: '1', value: 100 }, + { label: '2', value: 300 }, + { label: '3', value: 100 }, + { label: '4', value: 80 }, + { label: '5', value: 40 }, + { label: '6', value: 30 }, + { label: '7', value: 50 }, + { label: '8', value: 100 }, + { label: '9', value: 200 }, + { label: '10', value: 150 }, + { label: '11', value: 50 }, +]; + +export default function PieAnimation() { + const [radius, setRadius] = React.useState(50); + const [itemNb, setItemNb] = React.useState(5); + const [skipAnimation, setSkipAnimation] = React.useState(false); + + const handleItemNbChange = (event, newValue) => { + if (typeof newValue !== 'number') { + return; + } + setItemNb(newValue); + }; + const handleRadius = (event, newValue) => { + if (typeof newValue !== 'number') { + return; + } + setRadius(newValue); + }; + + return ( + + params.label ?? '', + }, + ]} + skipAnimation={skipAnimation} + /> + setSkipAnimation(event.target.checked)} /> + } + label="skipAnimation" + labelPlacement="end" + /> + + Number of items + + + + Radius + + + + ); +} diff --git a/docs/data/charts/pie/PieAnimation.tsx b/docs/data/charts/pie/PieAnimation.tsx new file mode 100644 index 0000000000000..f25df849a2ad3 --- /dev/null +++ b/docs/data/charts/pie/PieAnimation.tsx @@ -0,0 +1,93 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Slider from '@mui/material/Slider'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Checkbox from '@mui/material/Checkbox'; +import { PieChart } from '@mui/x-charts/PieChart'; + +const data1 = [ + { label: 'Group A', value: 400 }, + { label: 'Group B', value: 300 }, + { label: 'Group C', value: 300 }, + { label: 'Group D', value: 200 }, +]; +const data2 = [ + { label: '1', value: 100 }, + { label: '2', value: 300 }, + { label: '3', value: 100 }, + { label: '4', value: 80 }, + { label: '5', value: 40 }, + { label: '6', value: 30 }, + { label: '7', value: 50 }, + { label: '8', value: 100 }, + { label: '9', value: 200 }, + { label: '10', value: 150 }, + { label: '11', value: 50 }, +]; + +export default function PieAnimation() { + const [radius, setRadius] = React.useState(50); + const [itemNb, setItemNb] = React.useState(5); + const [skipAnimation, setSkipAnimation] = React.useState(false); + + const handleItemNbChange = (event: Event, newValue: number | number[]) => { + if (typeof newValue !== 'number') { + return; + } + setItemNb(newValue); + }; + const handleRadius = (event: Event, newValue: number | number[]) => { + if (typeof newValue !== 'number') { + return; + } + setRadius(newValue); + }; + + return ( + + params.label ?? '', + }, + ]} + skipAnimation={skipAnimation} + /> + setSkipAnimation(event.target.checked)} /> + } + label="skipAnimation" + labelPlacement="end" + /> + + Number of items + + + + Radius + + + + ); +} diff --git a/docs/data/charts/pie/pie.md b/docs/data/charts/pie/pie.md index 8401e7ed7ea69..5b765a4b6b578 100644 --- a/docs/data/charts/pie/pie.md +++ b/docs/data/charts/pie/pie.md @@ -82,3 +82,22 @@ If you do not want to provide absolute values, you can use `additionalRadius` wh This value can be negative to reduce arc size. {{"demo": "PieActiveArc.js"}} + +## Animation + +To skip animation at the creation and update of your chart you can use the `skipAnimation` prop. +When set to `true` it skips animation powered by `@react-spring/web`. + +Charts containers already use the `useReducedMotion` from `@react-spring/web` to skip animation [according to user preferences](https://react-spring.dev/docs/utilities/use-reduced-motion#why-is-it-important). + +```jsx +// For a single component chart + + +// For a composed chart + + + +``` + +{{"demo": "PieAnimation.js"}} diff --git a/docs/data/charts/sankey/sankey.md b/docs/data/charts/sankey/sankey.md index d6ff95f072cb9..29f4e174758ae 100644 --- a/docs/data/charts/sankey/sankey.md +++ b/docs/data/charts/sankey/sankey.md @@ -2,7 +2,7 @@ title: React Sankey chart --- -# Charts - Sankey 🚧 +# Charts - Sankey 🚧[](/x/introduction/licensing/#pro-plan 'Pro plan')

Chart lines can express flows between different entities.

diff --git a/docs/data/charts/scatter/BasicScatter.js b/docs/data/charts/scatter/BasicScatter.js index 773b9d9e7022b..07ddcab5eb8b9 100644 --- a/docs/data/charts/scatter/BasicScatter.js +++ b/docs/data/charts/scatter/BasicScatter.js @@ -172,11 +172,11 @@ export default function BasicScatter() { height={300} series={[ { - label: 'series A', + label: 'Series A', data: data.map((v) => ({ x: v.x1, y: v.y1, id: v.id })), }, { - label: 'series B', + label: 'Series B', data: data.map((v) => ({ x: v.x1, y: v.y2, id: v.id })), }, ]} diff --git a/docs/data/charts/scatter/BasicScatter.tsx b/docs/data/charts/scatter/BasicScatter.tsx index 773b9d9e7022b..07ddcab5eb8b9 100644 --- a/docs/data/charts/scatter/BasicScatter.tsx +++ b/docs/data/charts/scatter/BasicScatter.tsx @@ -172,11 +172,11 @@ export default function BasicScatter() { height={300} series={[ { - label: 'series A', + label: 'Series A', data: data.map((v) => ({ x: v.x1, y: v.y1, id: v.id })), }, { - label: 'series B', + label: 'Series B', data: data.map((v) => ({ x: v.x1, y: v.y2, id: v.id })), }, ]} diff --git a/docs/data/charts/scatter/BasicScatter.tsx.preview b/docs/data/charts/scatter/BasicScatter.tsx.preview index 8834fd9f4aad6..36dff4f51a156 100644 --- a/docs/data/charts/scatter/BasicScatter.tsx.preview +++ b/docs/data/charts/scatter/BasicScatter.tsx.preview @@ -3,11 +3,11 @@ height={300} series={[ { - label: 'series A', + label: 'Series A', data: data.map((v) => ({ x: v.x1, y: v.y1, id: v.id })), }, { - label: 'series B', + label: 'Series B', data: data.map((v) => ({ x: v.x1, y: v.y2, id: v.id })), }, ]} diff --git a/docs/data/charts/stacking/BasicStacking.js b/docs/data/charts/stacking/BasicStacking.js index eb9fdd1313691..e7173a92ecfdc 100644 --- a/docs/data/charts/stacking/BasicStacking.js +++ b/docs/data/charts/stacking/BasicStacking.js @@ -3,15 +3,15 @@ import { BarChart } from '@mui/x-charts/BarChart'; const seriesA = { data: [2, 3, 1, 4, 5], - label: 'series A', + label: 'Series A', }; const seriesB = { data: [3, 1, 4, 2, 1], - label: 'series B', + label: 'Series B', }; const seriesC = { data: [3, 2, 4, 5, 1], - label: 'series C', + label: 'Series C', }; export default function BasicStacking() { return ( diff --git a/docs/data/charts/stacking/BasicStacking.tsx b/docs/data/charts/stacking/BasicStacking.tsx index eb9fdd1313691..e7173a92ecfdc 100644 --- a/docs/data/charts/stacking/BasicStacking.tsx +++ b/docs/data/charts/stacking/BasicStacking.tsx @@ -3,15 +3,15 @@ import { BarChart } from '@mui/x-charts/BarChart'; const seriesA = { data: [2, 3, 1, 4, 5], - label: 'series A', + label: 'Series A', }; const seriesB = { data: [3, 1, 4, 2, 1], - label: 'series B', + label: 'Series B', }; const seriesC = { data: [3, 2, 4, 5, 1], - label: 'series C', + label: 'Series C', }; export default function BasicStacking() { return ( diff --git a/docs/data/charts/stacking/StackOrderDemo.js b/docs/data/charts/stacking/StackOrderDemo.js index 55f6c37ace0da..849e21d887f4e 100644 --- a/docs/data/charts/stacking/StackOrderDemo.js +++ b/docs/data/charts/stacking/StackOrderDemo.js @@ -4,8 +4,6 @@ import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import { BarChart } from '@mui/x-charts/BarChart'; -import { axisClasses } from '@mui/x-charts/ChartsAxis'; - // Data comming from https://www.insee.fr/fr/statistiques/5013868 const commonTransportation = [ 6.5, 12.5, 17.2, 19.6, 20.1, 20.0, 19.5, 18.8, 18.2, 17.3, 16.4, 15.9, 15.2, 14.7, @@ -110,23 +108,22 @@ export default function StackOrderDemo() { diff --git a/docs/data/charts/stacking/StackOrderDemo.tsx b/docs/data/charts/stacking/StackOrderDemo.tsx index 61cf990d81d31..97449aeadfac0 100644 --- a/docs/data/charts/stacking/StackOrderDemo.tsx +++ b/docs/data/charts/stacking/StackOrderDemo.tsx @@ -4,7 +4,6 @@ import TextField from '@mui/material/TextField'; import MenuItem from '@mui/material/MenuItem'; import { BarChart } from '@mui/x-charts/BarChart'; import { StackOrderType } from '@mui/x-charts/models'; -import { axisClasses } from '@mui/x-charts/ChartsAxis'; // Data comming from https://www.insee.fr/fr/statistiques/5013868 const commonTransportation = [ @@ -105,23 +104,22 @@ export default function StackOrderDemo() { diff --git a/docs/data/charts/styling/BasicColor.js b/docs/data/charts/styling/BasicColor.js index ab1e89b0dbc6d..49bb1619c9a45 100644 --- a/docs/data/charts/styling/BasicColor.js +++ b/docs/data/charts/styling/BasicColor.js @@ -35,7 +35,7 @@ export default function BasicColor() { series={[ { data: [15, 23, 18, 19, 13], - label: 'example', + label: 'Example', color, }, ]} diff --git a/docs/data/charts/styling/BasicColor.tsx b/docs/data/charts/styling/BasicColor.tsx index 2f47d169c6e65..2edbb887288f3 100644 --- a/docs/data/charts/styling/BasicColor.tsx +++ b/docs/data/charts/styling/BasicColor.tsx @@ -35,7 +35,7 @@ export default function BasicColor() { series={[ { data: [15, 23, 18, 19, 13], - label: 'example', + label: 'Example', color, }, ]} diff --git a/docs/data/charts/styling/ColorTemplate.js b/docs/data/charts/styling/ColorTemplate.js index 707a718a85d2b..44ca949fde962 100644 --- a/docs/data/charts/styling/ColorTemplate.js +++ b/docs/data/charts/styling/ColorTemplate.js @@ -26,12 +26,15 @@ function getGaussianSeriesData(mean, stdev = [0.3, 0.4], N = 50) { } const legendPlacement = { - legend: { - position: { - vertical: 'middle', - horizontal: 'right', + slotProps: { + legend: { + position: { + vertical: 'middle', + horizontal: 'right', + }, + direction: 'column', + itemGap: 2, }, - direction: 'column', }, margin: { top: 20, @@ -40,19 +43,19 @@ const legendPlacement = { }, }; const series = [ - { label: 'series 1', data: getGaussianSeriesData([-5, 0]) }, - { label: 'series 2', data: getGaussianSeriesData([-4, 0]) }, - { label: 'series 3', data: getGaussianSeriesData([-3, 0]) }, - { label: 'series 4', data: getGaussianSeriesData([-2, 0]) }, - { label: 'series 5', data: getGaussianSeriesData([-1, 0]) }, - { label: 'series 6', data: getGaussianSeriesData([0, 0]) }, - { label: 'series 7', data: getGaussianSeriesData([1, 0]) }, - { label: 'series 8', data: getGaussianSeriesData([2, 0]) }, - { label: 'series 9', data: getGaussianSeriesData([3, 0]) }, - { label: 'series 10', data: getGaussianSeriesData([4, 0]) }, - { label: 'series 11', data: getGaussianSeriesData([5, 0]) }, - { label: 'series 12', data: getGaussianSeriesData([6, 0]) }, - { label: 'series 13', data: getGaussianSeriesData([7, 0]) }, + { label: 'Series 1', data: getGaussianSeriesData([-5, 0]) }, + { label: 'Series 2', data: getGaussianSeriesData([-4, 0]) }, + { label: 'Series 3', data: getGaussianSeriesData([-3, 0]) }, + { label: 'Series 4', data: getGaussianSeriesData([-2, 0]) }, + { label: 'Series 5', data: getGaussianSeriesData([-1, 0]) }, + { label: 'Series 6', data: getGaussianSeriesData([0, 0]) }, + { label: 'Series 7', data: getGaussianSeriesData([1, 0]) }, + { label: 'Series 8', data: getGaussianSeriesData([2, 0]) }, + { label: 'Series 9', data: getGaussianSeriesData([3, 0]) }, + { label: 'Series 10', data: getGaussianSeriesData([4, 0]) }, + { label: 'Series 11', data: getGaussianSeriesData([5, 0]) }, + { label: 'Series 12', data: getGaussianSeriesData([6, 0]) }, + { label: 'Series 13', data: getGaussianSeriesData([7, 0]) }, ].map((s) => ({ ...s, valueFormatter: (v) => `(${v.x.toFixed(1)}, ${v.y.toFixed(1)})`, diff --git a/docs/data/charts/styling/ColorTemplate.tsx b/docs/data/charts/styling/ColorTemplate.tsx index b28898d303d6b..28a7404dcfec5 100644 --- a/docs/data/charts/styling/ColorTemplate.tsx +++ b/docs/data/charts/styling/ColorTemplate.tsx @@ -30,12 +30,15 @@ function getGaussianSeriesData( } const legendPlacement = { - legend: { - position: { - vertical: 'middle', - horizontal: 'right', + slotProps: { + legend: { + position: { + vertical: 'middle', + horizontal: 'right', + }, + direction: 'column', + itemGap: 2, }, - direction: 'column', }, margin: { top: 20, @@ -44,19 +47,19 @@ const legendPlacement = { }, } as const; const series = [ - { label: 'series 1', data: getGaussianSeriesData([-5, 0]) }, - { label: 'series 2', data: getGaussianSeriesData([-4, 0]) }, - { label: 'series 3', data: getGaussianSeriesData([-3, 0]) }, - { label: 'series 4', data: getGaussianSeriesData([-2, 0]) }, - { label: 'series 5', data: getGaussianSeriesData([-1, 0]) }, - { label: 'series 6', data: getGaussianSeriesData([0, 0]) }, - { label: 'series 7', data: getGaussianSeriesData([1, 0]) }, - { label: 'series 8', data: getGaussianSeriesData([2, 0]) }, - { label: 'series 9', data: getGaussianSeriesData([3, 0]) }, - { label: 'series 10', data: getGaussianSeriesData([4, 0]) }, - { label: 'series 11', data: getGaussianSeriesData([5, 0]) }, - { label: 'series 12', data: getGaussianSeriesData([6, 0]) }, - { label: 'series 13', data: getGaussianSeriesData([7, 0]) }, + { label: 'Series 1', data: getGaussianSeriesData([-5, 0]) }, + { label: 'Series 2', data: getGaussianSeriesData([-4, 0]) }, + { label: 'Series 3', data: getGaussianSeriesData([-3, 0]) }, + { label: 'Series 4', data: getGaussianSeriesData([-2, 0]) }, + { label: 'Series 5', data: getGaussianSeriesData([-1, 0]) }, + { label: 'Series 6', data: getGaussianSeriesData([0, 0]) }, + { label: 'Series 7', data: getGaussianSeriesData([1, 0]) }, + { label: 'Series 8', data: getGaussianSeriesData([2, 0]) }, + { label: 'Series 9', data: getGaussianSeriesData([3, 0]) }, + { label: 'Series 10', data: getGaussianSeriesData([4, 0]) }, + { label: 'Series 11', data: getGaussianSeriesData([5, 0]) }, + { label: 'Series 12', data: getGaussianSeriesData([6, 0]) }, + { label: 'Series 13', data: getGaussianSeriesData([7, 0]) }, ].map((s) => ({ ...s, valueFormatter: (v: ScatterValueType) => `(${v.x.toFixed(1)}, ${v.y.toFixed(1)})`, diff --git a/docs/data/charts/styling/MarginNoSnap.js b/docs/data/charts/styling/MarginNoSnap.js new file mode 100644 index 0000000000000..edd531d9b5d5c --- /dev/null +++ b/docs/data/charts/styling/MarginNoSnap.js @@ -0,0 +1,58 @@ +import * as React from 'react'; +import ChartsUsageDemo from 'docsx/src/modules/components/ChartsUsageDemo'; +import { BarChart } from '@mui/x-charts/BarChart'; +import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '@mui/x-charts/constants'; + +const data = ['left', 'right', 'top', 'bottom'].map((propName) => ({ + propName, + knob: 'number', + defaultValue: 80, + step: 1, + min: 0, + max: 200, +})); +export default function MarginNoSnap() { + return ( + ( + + )} + getCode={({ props }) => { + return [ + `import { BarChart } from '@mui/x-charts/BarChart';`, + '', + `', + ].join('\n'); + }} + /> + ); +} diff --git a/docs/data/charts/styling/MuiColorTemplate.js b/docs/data/charts/styling/MuiColorTemplate.js index c24c727549a55..cc0c5455fe33e 100644 --- a/docs/data/charts/styling/MuiColorTemplate.js +++ b/docs/data/charts/styling/MuiColorTemplate.js @@ -36,12 +36,15 @@ function getGaussianSeriesData(mean, stdev = [0.3, 0.4], N = 50) { } const legendPlacement = { - legend: { - position: { - vertical: 'middle', - horizontal: 'right', + slotProps: { + legend: { + position: { + vertical: 'middle', + horizontal: 'right', + }, + direction: 'column', + itemGap: 2, }, - direction: 'column', }, margin: { top: 20, @@ -50,19 +53,19 @@ const legendPlacement = { }; const series = [ - { label: 'series 1', data: getGaussianSeriesData([-5, 0]) }, - { label: 'series 2', data: getGaussianSeriesData([-4, 0]) }, - { label: 'series 3', data: getGaussianSeriesData([-3, 0]) }, - { label: 'series 4', data: getGaussianSeriesData([-2, 0]) }, - { label: 'series 5', data: getGaussianSeriesData([-1, 0]) }, - { label: 'series 6', data: getGaussianSeriesData([0, 0]) }, - { label: 'series 7', data: getGaussianSeriesData([1, 0]) }, - { label: 'series 8', data: getGaussianSeriesData([2, 0]) }, - { label: 'series 9', data: getGaussianSeriesData([3, 0]) }, - { label: 'series 10', data: getGaussianSeriesData([4, 0]) }, - { label: 'series 11', data: getGaussianSeriesData([5, 0]) }, - { label: 'series 12', data: getGaussianSeriesData([6, 0]) }, - { label: 'series 13', data: getGaussianSeriesData([7, 0]) }, + { label: 'Series 1', data: getGaussianSeriesData([-5, 0]) }, + { label: 'Series 2', data: getGaussianSeriesData([-4, 0]) }, + { label: 'Series 3', data: getGaussianSeriesData([-3, 0]) }, + { label: 'Series 4', data: getGaussianSeriesData([-2, 0]) }, + { label: 'Series 5', data: getGaussianSeriesData([-1, 0]) }, + { label: 'Series 6', data: getGaussianSeriesData([0, 0]) }, + { label: 'Series 7', data: getGaussianSeriesData([1, 0]) }, + { label: 'Series 8', data: getGaussianSeriesData([2, 0]) }, + { label: 'Series 9', data: getGaussianSeriesData([3, 0]) }, + { label: 'Series 10', data: getGaussianSeriesData([4, 0]) }, + { label: 'Series 11', data: getGaussianSeriesData([5, 0]) }, + { label: 'Series 12', data: getGaussianSeriesData([6, 0]) }, + { label: 'Series 13', data: getGaussianSeriesData([7, 0]) }, ].map((s) => ({ ...s, valueFormatter: (v) => `(${v.x.toFixed(1)}, ${v.y.toFixed(1)})`, diff --git a/docs/data/charts/styling/MuiColorTemplate.tsx b/docs/data/charts/styling/MuiColorTemplate.tsx index c18eff6cf07a8..78d2415ffdcf2 100644 --- a/docs/data/charts/styling/MuiColorTemplate.tsx +++ b/docs/data/charts/styling/MuiColorTemplate.tsx @@ -40,12 +40,15 @@ function getGaussianSeriesData( } const legendPlacement = { - legend: { - position: { - vertical: 'middle', - horizontal: 'right', + slotProps: { + legend: { + position: { + vertical: 'middle', + horizontal: 'right', + }, + direction: 'column', + itemGap: 2, }, - direction: 'column', }, margin: { top: 20, @@ -54,19 +57,19 @@ const legendPlacement = { } as const; const series = [ - { label: 'series 1', data: getGaussianSeriesData([-5, 0]) }, - { label: 'series 2', data: getGaussianSeriesData([-4, 0]) }, - { label: 'series 3', data: getGaussianSeriesData([-3, 0]) }, - { label: 'series 4', data: getGaussianSeriesData([-2, 0]) }, - { label: 'series 5', data: getGaussianSeriesData([-1, 0]) }, - { label: 'series 6', data: getGaussianSeriesData([0, 0]) }, - { label: 'series 7', data: getGaussianSeriesData([1, 0]) }, - { label: 'series 8', data: getGaussianSeriesData([2, 0]) }, - { label: 'series 9', data: getGaussianSeriesData([3, 0]) }, - { label: 'series 10', data: getGaussianSeriesData([4, 0]) }, - { label: 'series 11', data: getGaussianSeriesData([5, 0]) }, - { label: 'series 12', data: getGaussianSeriesData([6, 0]) }, - { label: 'series 13', data: getGaussianSeriesData([7, 0]) }, + { label: 'Series 1', data: getGaussianSeriesData([-5, 0]) }, + { label: 'Series 2', data: getGaussianSeriesData([-4, 0]) }, + { label: 'Series 3', data: getGaussianSeriesData([-3, 0]) }, + { label: 'Series 4', data: getGaussianSeriesData([-2, 0]) }, + { label: 'Series 5', data: getGaussianSeriesData([-1, 0]) }, + { label: 'Series 6', data: getGaussianSeriesData([0, 0]) }, + { label: 'Series 7', data: getGaussianSeriesData([1, 0]) }, + { label: 'Series 8', data: getGaussianSeriesData([2, 0]) }, + { label: 'Series 9', data: getGaussianSeriesData([3, 0]) }, + { label: 'Series 10', data: getGaussianSeriesData([4, 0]) }, + { label: 'Series 11', data: getGaussianSeriesData([5, 0]) }, + { label: 'Series 12', data: getGaussianSeriesData([6, 0]) }, + { label: 'Series 13', data: getGaussianSeriesData([7, 0]) }, ].map((s) => ({ ...s, valueFormatter: (v: ScatterValueType) => `(${v.x.toFixed(1)}, ${v.y.toFixed(1)})`, diff --git a/docs/data/charts/styling/styling.md b/docs/data/charts/styling/styling.md index 5aad402e5af5e..585be102da600 100644 --- a/docs/data/charts/styling/styling.md +++ b/docs/data/charts/styling/styling.md @@ -52,16 +52,18 @@ Those will fix the chart's size to the given value (in px). ### Placement -At the core of charts layout is the drawing area which corresponds to the space available to represent data. +At the core of chart layout is the drawing area which corresponds to the space available to represent data. -This space can be fined with the `margin` prop and its properties `top`, `bottom`, `left`, and `right`. +This space can be defined with the `margin` prop and its properties `top`, `bottom`, `left`, and `right`. Those values define the space between the SVG border and the drawing area. -You might want to modify those values to let more space for your axis ticks or reduce them to provide more space for the data. +You might want to modify those values to leave more space for your axis ticks or reduce them to provide more space for the data. + +{{"demo": "MarginNoSnap.js"}} ### CSS Since the library relies on SVG for rendering, you can customize them as you do with other MUI components with CSS overriding. Chart components accept the `sx` props. -And you can target any sub elements with its class name. +From here, you can target any subcomponents with its class name. diff --git a/docs/data/charts/tooltip/BandHighlight.js b/docs/data/charts/tooltip/BandHighlight.js index fbbfd6b04caca..e7f08e2c2d5ab 100644 --- a/docs/data/charts/tooltip/BandHighlight.js +++ b/docs/data/charts/tooltip/BandHighlight.js @@ -16,9 +16,9 @@ const barChartsParams = { }, ], series: [ - { data: [2, 5, 3, 4, 1], stack: '1', label: 'series x' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series y' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series z' }, + { data: [2, 5, 3, 4, 1], stack: '1', label: 'Series x' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series y' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series z' }, ], margin: { top: 10, right: 10 }, sx: { diff --git a/docs/data/charts/tooltip/BandHighlight.tsx b/docs/data/charts/tooltip/BandHighlight.tsx index a74e0bf1e326b..e080a99ba9946 100644 --- a/docs/data/charts/tooltip/BandHighlight.tsx +++ b/docs/data/charts/tooltip/BandHighlight.tsx @@ -16,9 +16,9 @@ const barChartsParams = { }, ], series: [ - { data: [2, 5, 3, 4, 1], stack: '1', label: 'series x' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series y' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series z' }, + { data: [2, 5, 3, 4, 1], stack: '1', label: 'Series x' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series y' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series z' }, ], margin: { top: 10, right: 10 }, sx: { diff --git a/docs/data/charts/tooltip/Formatting.js b/docs/data/charts/tooltip/Formatting.js index 089275bdd6eb8..759307e1c79fb 100644 --- a/docs/data/charts/tooltip/Formatting.js +++ b/docs/data/charts/tooltip/Formatting.js @@ -72,9 +72,6 @@ const lineChartsParams = { showMark: false, }, ], - sx: { - '--ChartsLegend-itemWidth': '200px', - }, width: 600, height: 400, }; diff --git a/docs/data/charts/tooltip/Formatting.tsx b/docs/data/charts/tooltip/Formatting.tsx index d990c0714e921..ab975cdd909fb 100644 --- a/docs/data/charts/tooltip/Formatting.tsx +++ b/docs/data/charts/tooltip/Formatting.tsx @@ -72,10 +72,6 @@ const lineChartsParams = { showMark: false, }, ], - - sx: { - '--ChartsLegend-itemWidth': '200px', - }, width: 600, height: 400, }; diff --git a/docs/data/charts/tooltip/Interaction.js b/docs/data/charts/tooltip/Interaction.js index 8f8fbf2e56d22..a7e6142713d69 100644 --- a/docs/data/charts/tooltip/Interaction.js +++ b/docs/data/charts/tooltip/Interaction.js @@ -10,12 +10,17 @@ const barChartsParams = { }, ], series: [ - { data: [2, 5, 3, 4, 1], stack: '1', label: 'series x' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series y' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series z' }, + { data: [2, 5, 3, 4, 1], stack: '1', label: 'Series x' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series y' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series z' }, ], margin: { top: 10, right: 10 }, height: 200, + slotProps: { + legend: { + hidden: true, + }, + }, }; export default function Interaction() { return ( diff --git a/docs/data/charts/tooltip/Interaction.tsx b/docs/data/charts/tooltip/Interaction.tsx index 69ffd37b6f169..2920b6d6b2e24 100644 --- a/docs/data/charts/tooltip/Interaction.tsx +++ b/docs/data/charts/tooltip/Interaction.tsx @@ -10,12 +10,17 @@ const barChartsParams = { }, ], series: [ - { data: [2, 5, 3, 4, 1], stack: '1', label: 'series x' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series y' }, - { data: [10, 3, 1, 2, 10], stack: '1', label: 'series z' }, + { data: [2, 5, 3, 4, 1], stack: '1', label: 'Series x' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series y' }, + { data: [10, 3, 1, 2, 10], stack: '1', label: 'Series z' }, ], margin: { top: 10, right: 10 }, height: 200, + slotProps: { + legend: { + hidden: true, + }, + }, }; export default function Interaction() { return ( diff --git a/docs/data/charts/tooltip/tooltip.md b/docs/data/charts/tooltip/tooltip.md index db56575ea1242..155711681a1b0 100644 --- a/docs/data/charts/tooltip/tooltip.md +++ b/docs/data/charts/tooltip/tooltip.md @@ -82,7 +82,9 @@ It will remove the header showing the x-axis value from the tooltip. ## Composition If you're using composition, by default the axis will be listening for mouse events to get its current x/y values. -If you don't need it, because you don't use highlights, and the tooltip is triggered by an item, you can disable those listeners with the `disableAxisListener` prop. +If you don't need it, you can disable those listeners with the `disableAxisListener` prop. + +You need those listerne if you are using [axes highlight](/x/react-charts/tooltip/#highlighting-axis) or you have a tooltip [triggered by axis](/x/react-charts/tooltip/#interactions). ```jsx diff --git a/docs/data/data-grid-component-api-pages.ts b/docs/data/data-grid-component-api-pages.ts index c7a3a5d06dbf1..437f0e7dfe829 100644 --- a/docs/data/data-grid-component-api-pages.ts +++ b/docs/data/data-grid-component-api-pages.ts @@ -1,4 +1,4 @@ -import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import type { MuiPage } from 'docs/src/MuiPage'; export default [ { pathname: '/x/api/data-grid/data-grid', title: 'DataGrid' }, diff --git a/docs/data/data-grid/column-definition/column-definition.md b/docs/data/data-grid/column-definition/column-definition.md index 24e0ccb3237b8..1c66b0e72c4ff 100644 --- a/docs/data/data-grid/column-definition/column-definition.md +++ b/docs/data/data-grid/column-definition/column-definition.md @@ -121,7 +121,7 @@ const columns: GridColDef[] = [ { field: 'date', headerName: 'Year', - renderCell: (params: GridRenderCellParams) => ( + renderCell: (params: GridRenderCellParams) => ( {params.value.getFullYear()} + setIncludeHeaders(ev.target.checked)} + /> + } + label="Include headers" + /> + setExcludeOutliers(event.target.checked)} + /> + } + label="Include outliers" + /> + setOutliersFactor(ev.target.value)} + sx={{ width: '12ch' }} + /> + setExpand(ev.target.checked)} + /> + } + label="Expand" + /> + +
+ +
+ + ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx b/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx new file mode 100644 index 0000000000000..5f813676e7ee8 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx @@ -0,0 +1,137 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import Checkbox from '@mui/material/Checkbox'; +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, + GridApiPro, + DEFAULT_GRID_AUTOSIZE_OPTIONS, +} from '@mui/x-data-grid-pro'; +import { randomRating, randomTraderName } from '@mui/x-data-grid-generator'; + +function renderRating(params: any) { + return ; +} + +function useData(length: number) { + return React.useMemo(() => { + const names = [ + 'Nike', + 'Adidas', + 'Puma', + 'Reebok', + 'Fila', + 'Lululemon Athletica Clothing', + 'Varley', + ]; + const rows = Array.from({ length }).map((_, id) => ({ + id, + brand: names[id % names.length], + rep: randomTraderName(), + rating: randomRating(), + })); + + const columns = [ + { field: 'id', headerName: 'Brand ID' }, + { field: 'brand', headerName: 'Brand name' }, + { field: 'rep', headerName: 'Representative' }, + { field: 'rating', headerName: 'Rating', renderCell: renderRating }, + ]; + + return { rows, columns }; + }, [length]); +} + +export default function ColumnAutosizing() { + const apiRef = useGridApiRef(); + const data = useData(100); + + const [includeHeaders, setIncludeHeaders] = React.useState( + DEFAULT_GRID_AUTOSIZE_OPTIONS.includeHeaders, + ); + const [includeOutliers, setExcludeOutliers] = React.useState( + DEFAULT_GRID_AUTOSIZE_OPTIONS.includeOutliers, + ); + const [outliersFactor, setOutliersFactor] = React.useState( + String(DEFAULT_GRID_AUTOSIZE_OPTIONS.outliersFactor), + ); + const [expand, setExpand] = React.useState(DEFAULT_GRID_AUTOSIZE_OPTIONS.expand); + + const autosizeOptions = { + includeHeaders, + includeOutliers, + outliersFactor: Number.isNaN(parseFloat(outliersFactor)) + ? 1 + : parseFloat(outliersFactor), + expand, + }; + + return ( +
+ + + setIncludeHeaders(ev.target.checked)} + /> + } + label="Include headers" + /> + setExcludeOutliers(event.target.checked)} + /> + } + label="Include outliers" + /> + setOutliersFactor(ev.target.value)} + sx={{ width: '12ch' }} + /> + setExpand(ev.target.checked)} + /> + } + label="Expand" + /> + +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.js b/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.js new file mode 100644 index 0000000000000..01490fbfceb23 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.js @@ -0,0 +1,101 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import Rating from '@mui/material/Rating'; +import Stack from '@mui/material/Stack'; +import { useGridApiRef } from '@mui/x-data-grid'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { + randomInt, + randomRating, + randomTraderName, +} from '@mui/x-data-grid-generator'; +import * as ReactDOM from 'react-dom'; + +const columns = [ + { field: 'id', headerName: 'Brand ID' }, + { field: 'brand', headerName: 'Brand name' }, + { field: 'rep', headerName: 'Representative' }, + { field: 'rating', headerName: 'Rating', renderCell: renderRating }, +]; + +function renderRating(params) { + return ; +} + +function getFakeData(length) { + return new Promise((resolve) => { + setTimeout(() => { + const names = [ + 'Nike', + 'Adidas', + 'Puma', + 'Reebok', + 'Fila', + 'Lululemon Athletica Clothing', + 'Varley', + ]; + + const rows = Array.from({ length }).map((_, id) => ({ + id, + brand: names[randomInt(0, names.length - 1)], + rep: randomTraderName(), + rating: randomRating(), + })); + + resolve({ rows }); + }, 1000); + }); +} + +export default function ColumnAutosizingAsync() { + const apiRef = useGridApiRef(); + const [isLoading, setIsLoading] = React.useState(false); + const [rows] = React.useState([]); + + const fetchData = React.useCallback(() => { + setIsLoading(true); + getFakeData(100) + .then((data) => { + return ReactDOM.flushSync(() => { + setIsLoading(false); + apiRef.current.updateRows(data.rows); + }); + }) + .then(() => + apiRef.current.autosizeColumns({ + includeHeaders: true, + includeOutliers: true, + }), + ); + }, [apiRef]); + + React.useEffect(() => { + fetchData(); + }, [fetchData]); + + return ( +
+ + + +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.tsx b/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.tsx new file mode 100644 index 0000000000000..2140a5e5ae3b0 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizingAsync.tsx @@ -0,0 +1,101 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import Rating from '@mui/material/Rating'; +import Stack from '@mui/material/Stack'; +import { useGridApiRef } from '@mui/x-data-grid'; +import { DataGridPro, GridApiPro } from '@mui/x-data-grid-pro'; +import { + randomInt, + randomRating, + randomTraderName, +} from '@mui/x-data-grid-generator'; +import * as ReactDOM from 'react-dom'; +import { GridData } from 'docsx/data/data-grid/virtualization/ColumnVirtualizationGrid'; + +const columns = [ + { field: 'id', headerName: 'Brand ID' }, + { field: 'brand', headerName: 'Brand name' }, + { field: 'rep', headerName: 'Representative' }, + { field: 'rating', headerName: 'Rating', renderCell: renderRating }, +]; + +function renderRating(params: any) { + return ; +} + +function getFakeData(length: number): Promise<{ rows: GridData['rows'] }> { + return new Promise((resolve) => { + setTimeout(() => { + const names = [ + 'Nike', + 'Adidas', + 'Puma', + 'Reebok', + 'Fila', + 'Lululemon Athletica Clothing', + 'Varley', + ]; + const rows = Array.from({ length }).map((_, id) => ({ + id, + brand: names[randomInt(0, names.length - 1)], + rep: randomTraderName(), + rating: randomRating(), + })); + + resolve({ rows }); + }, 1000); + }); +} + +export default function ColumnAutosizingAsync() { + const apiRef = useGridApiRef(); + const [isLoading, setIsLoading] = React.useState(false); + const [rows] = React.useState([]); + + const fetchData = React.useCallback(() => { + setIsLoading(true); + getFakeData(100) + .then((data) => { + return ReactDOM.flushSync(() => { + setIsLoading(false); + apiRef.current.updateRows(data.rows); + }); + }) + .then(() => + apiRef.current.autosizeColumns({ + includeHeaders: true, + includeOutliers: true, + }), + ); + }, [apiRef]); + + React.useEffect(() => { + fetchData(); + }, [fetchData]); + + return ( +
+ + + +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js new file mode 100644 index 0000000000000..4101807c03e92 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.js @@ -0,0 +1,64 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import { DataGridPro, gridClasses, useGridApiRef } from '@mui/x-data-grid-pro'; +import { randomInt, randomArrayItem } from '@mui/x-data-grid-generator'; + +const lines = [ + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'Aliquam dapibus, lorem vel mattis aliquet, purus lorem tincidunt mauris, in blandit quam risus sed ipsum.', + 'Maecenas non felis venenatis, porta velit quis, consectetur elit.', + 'Vestibulum commodo et odio a laoreet.', + 'Nullam cursus tincidunt auctor.', + 'Sed feugiat venenatis nulla, sit amet dictum nulla convallis sit amet.', + 'Nulla venenatis justo non felis vulputate, eu mollis metus ornare.', + 'Nam ullamcorper ligula id consectetur auctor.', + 'Phasellus et ultrices dui.', + 'Fusce facilisis egestas massa, et eleifend magna imperdiet et.', + 'Pellentesque ac metus velit.', + 'Vestibulum in massa nibh.', + 'Vestibulum pulvinar aliquam turpis, ac faucibus risus varius a.', +]; + +const columns = [{ field: 'id' }, { field: 'bio', width: 400 }]; + +const rows = []; + +for (let i = 0; i < 200; i += 1) { + const bio = []; + + for (let j = 0; j < randomInt(1, 5); j += 1) { + bio.push(randomArrayItem(lines)); + } + + rows.push({ id: i, bio: bio.join(' ') }); +} + +const autosizeOptions = { + includeOutliers: true, +}; + +export default function ColumnAutosizingDynamicRowHeight() { + const apiRef = useGridApiRef(); + + return ( +
+ +
+ 'auto'} + autosizeOptions={autosizeOptions} + sx={{ + [`& .${gridClasses.cell}`]: { + py: 0.5, + }, + }} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.tsx b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.tsx new file mode 100644 index 0000000000000..d54772abc10b3 --- /dev/null +++ b/docs/data/data-grid/column-dimensions/ColumnAutosizingDynamicRowHeight.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import Button from '@mui/material/Button'; +import { + DataGridPro, + GridColDef, + gridClasses, + useGridApiRef, + GridAutosizeOptions, +} from '@mui/x-data-grid-pro'; +import { randomInt, randomArrayItem } from '@mui/x-data-grid-generator'; + +const lines = [ + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', + 'Aliquam dapibus, lorem vel mattis aliquet, purus lorem tincidunt mauris, in blandit quam risus sed ipsum.', + 'Maecenas non felis venenatis, porta velit quis, consectetur elit.', + 'Vestibulum commodo et odio a laoreet.', + 'Nullam cursus tincidunt auctor.', + 'Sed feugiat venenatis nulla, sit amet dictum nulla convallis sit amet.', + 'Nulla venenatis justo non felis vulputate, eu mollis metus ornare.', + 'Nam ullamcorper ligula id consectetur auctor.', + 'Phasellus et ultrices dui.', + 'Fusce facilisis egestas massa, et eleifend magna imperdiet et.', + 'Pellentesque ac metus velit.', + 'Vestibulum in massa nibh.', + 'Vestibulum pulvinar aliquam turpis, ac faucibus risus varius a.', +]; + +const columns: GridColDef[] = [{ field: 'id' }, { field: 'bio', width: 400 }]; + +const rows: object[] = []; + +for (let i = 0; i < 200; i += 1) { + const bio = []; + + for (let j = 0; j < randomInt(1, 5); j += 1) { + bio.push(randomArrayItem(lines)); + } + + rows.push({ id: i, bio: bio.join(' ') }); +} + +const autosizeOptions: GridAutosizeOptions = { + includeOutliers: true, +}; + +export default function ColumnAutosizingDynamicRowHeight() { + const apiRef = useGridApiRef(); + + return ( +
+ +
+ 'auto'} + autosizeOptions={autosizeOptions} + sx={{ + [`& .${gridClasses.cell}`]: { + py: 0.5, + }, + }} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/column-dimensions/column-dimensions.md b/docs/data/data-grid/column-dimensions/column-dimensions.md index bded63f3da53f..a1aa0720548ab 100644 --- a/docs/data/data-grid/column-dimensions/column-dimensions.md +++ b/docs/data/data-grid/column-dimensions/column-dimensions.md @@ -58,6 +58,61 @@ 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') + +`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. + +Autosizing can be used by one of the following methods: + +- Adding the `autosizeOnMount` prop, +- Double-clicking a column header separator on the grid, +- Calling the `apiRef.current.autosizeColumns(options)` API method. + +You can pass options directly to the API method when you call it. To configure autosize for the other two methods, provide the options in the `autosizeOptions` prop. + +Note that for the separator double-click method, the `autosizeOptions.columns` will be replaced by the respective column user double-clicked on. + +In all the cases, the `colDef.minWidth` and `colDef.maxWidth` options will be respected. + +```tsx + +``` + +{{"demo": "ColumnAutosizing.js", "disableAd": true, "bg": "inline"}} + +:::warning +The data grid can only autosize based on the currently rendered cells. + +DOM access is required to accurately calculate dimensions, so unmounted cells (when [virtualization](/x/react-data-grid/virtualization/) is on) cannot be sized. If you need a bigger row sample, [open an issue](https://github.com/mui/mui-x/issues) to discuss it further. +::: + +### Autosizing asynchronously [](/x/introduction/licensing/#pro-plan 'Pro plan') + +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. + +{{"demo": "ColumnAutosizingAsync.js", "disableAd": true, "bg": "inline"}} + +:::warning +This example uses `ReactDOM.flushSync`. If used incorrectly it can hurt the performance of your application. Please refer to the official [React docs](https://react.dev/reference/react-dom/flushSync) for further information. +::: + +### Autosizing with dynamic row height + +Column autosizing is compatible with the [Dynamic row height](/x/react-data-grid/row-height/#dynamic-row-height) feature. + +{{"demo": "ColumnAutosizingDynamicRowHeight.js", "disableAd": true, "bg": "inline"}} + +:::warning +When autosizing columns with long content, consider setting the `maxWidth` for the column to avoid it becoming too wide. +::: + ## API - [DataGrid](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.js b/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.js new file mode 100644 index 0000000000000..ff98ae3194f14 --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.js @@ -0,0 +1,67 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import InputLabel from '@mui/material/InputLabel'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const UserType = { + Regular: 0, + Admin: 1, +}; + +export default function ColumnSelectorDisabledGrid() { + const [userType, setUserType] = React.useState(UserType.Regular); + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 7, + }); + + const columnVisibilityModel = React.useMemo(() => { + if (userType === UserType.Admin) { + return { + quantity: true, + filledQuantity: true, + id: true, + }; + } + return { + quantity: false, + filledQuantity: false, + id: false, + }; + }, [userType]); + + return ( + + + User Type + + +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.tsx b/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.tsx new file mode 100644 index 0000000000000..ca148f604b889 --- /dev/null +++ b/docs/data/data-grid/column-visibility/ColumnSelectorDisabledGrid.tsx @@ -0,0 +1,67 @@ +import * as React from 'react'; +import Stack from '@mui/material/Stack'; +import FormControl from '@mui/material/FormControl'; +import Select, { SelectChangeEvent } from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import InputLabel from '@mui/material/InputLabel'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const UserType = { + Regular: 0, + Admin: 1, +}; + +export default function ColumnSelectorDisabledGrid() { + const [userType, setUserType] = React.useState(UserType.Regular); + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 10, + maxColumns: 7, + }); + + const columnVisibilityModel = React.useMemo(() => { + if (userType === UserType.Admin) { + return { + quantity: true, + filledQuantity: true, + id: true, + }; + } + return { + quantity: false, + filledQuantity: false, + id: false, + }; + }, [userType]); + + return ( + + + User Type + + +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/column-visibility/column-visibility.md b/docs/data/data-grid/column-visibility/column-visibility.md index e93e57c4d32eb..15f5fb50a2f92 100644 --- a/docs/data/data-grid/column-visibility/column-visibility.md +++ b/docs/data/data-grid/column-visibility/column-visibility.md @@ -66,6 +66,19 @@ The user can then choose which columns are visible using the _Columns_ button. {{"demo": "ColumnSelectorGrid.js", "bg": "inline"}} +### Disable the column visibility panel + +Sometimes, the intention is to disable the columns panel or control the visible columns programmatically based on the application state. +To disable the column visibility panel, set the prop `disableColumnSelector={true}` and use the [`columnVisibilityModel`](#controlled-visible-columns) prop to control the visible columns. + +```tsx + +``` + +In the following demo, the columns panel is disabled, and access to columns `id`, `quantity`, and `filledQuantity` is only allowed for the `Admin` type user. + +{{"demo": "ColumnSelectorDisabledGrid.js", "bg": "inline"}} + ### Customize the list of columns in panel To show or hide specific columns in the column visibility panel, use the `slotProps.columnsPanel.getTogglableColumns` prop. It should return an array of column field names. @@ -110,10 +123,6 @@ To disable `Hide all` or `Show all` buttons in the column visibility panel, pass /> ``` -:::info -To hide the column visibility panel from the toolbar, set the prop `disableColumnSelector={true}`. -::: - ## API - [DataGrid](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/components/components.md b/docs/data/data-grid/components/components.md index c64f8c20442ee..6665ba18c9216 100644 --- a/docs/data/data-grid/components/components.md +++ b/docs/data/data-grid/components/components.md @@ -5,7 +5,7 @@ ## Overriding components As part of the customization API, the Data Grid allows you to override internal components with the `slots` prop. -The prop accepts an object of type [`UncapitalizedGridSlotsComponent`](/x/api/data-grid/data-grid/#slots). +The prop accepts an object of type [`GridSlotsComponent`](/x/api/data-grid/data-grid/#slots). If you wish to pass additional props in a component slot, you can do it using the `slotProps` prop. This prop is of type `GridSlotsComponentsProps`. @@ -25,18 +25,6 @@ As an example, you could override the column menu and pass additional props as b /> ``` -:::warning -The `components/componentsProps` API is deprecated and `slots/slotProps` API is preferred. - -Note that the `components` prop used _PascalCase_ for the slot names, while the `slots` prop uses _camelCase_. - -```tsx - // Deprecated - -``` - -::: - ### Interacting with the data grid The grid exposes two hooks to help you to access the data grid data while overriding component slots. diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.js b/docs/data/data-grid/custom-columns/CustomColumnTypesGrid.js similarity index 100% rename from docs/data/data-grid/column-definition/CustomColumnTypesGrid.js rename to docs/data/data-grid/custom-columns/CustomColumnTypesGrid.js diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx b/docs/data/data-grid/custom-columns/CustomColumnTypesGrid.tsx similarity index 100% rename from docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx rename to docs/data/data-grid/custom-columns/CustomColumnTypesGrid.tsx diff --git a/docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview b/docs/data/data-grid/custom-columns/CustomColumnTypesGrid.tsx.preview similarity index 100% rename from docs/data/data-grid/column-definition/CustomColumnTypesGrid.tsx.preview rename to docs/data/data-grid/custom-columns/CustomColumnTypesGrid.tsx.preview diff --git a/docs/data/data-grid/recipes-editing/EditingWithDatePickers.js b/docs/data/data-grid/custom-columns/EditingWithDatePickers.js similarity index 98% rename from docs/data/data-grid/recipes-editing/EditingWithDatePickers.js rename to docs/data/data-grid/custom-columns/EditingWithDatePickers.js index 9649d2b1bdc1a..975cfe98b71e5 100644 --- a/docs/data/data-grid/recipes-editing/EditingWithDatePickers.js +++ b/docs/data/data-grid/custom-columns/EditingWithDatePickers.js @@ -29,7 +29,7 @@ function buildApplyDateFilterFn(filterItem, compareFn, showTime = false) { const filterValueMs = filterValueCopy.getTime(); - return ({ value }) => { + return (value) => { if (!value) { return false; } @@ -125,7 +125,7 @@ function getDateFilterOperators(showTime = false) { { value: 'isEmpty', getApplyFilterFn: () => { - return ({ value }) => { + return (value) => { return value == null; }; }, @@ -134,7 +134,7 @@ function getDateFilterOperators(showTime = false) { { value: 'isNotEmpty', getApplyFilterFn: () => { - return ({ value }) => { + return (value) => { return value != null; }; }, diff --git a/docs/data/data-grid/recipes-editing/EditingWithDatePickers.tsx b/docs/data/data-grid/custom-columns/EditingWithDatePickers.tsx similarity index 97% rename from docs/data/data-grid/recipes-editing/EditingWithDatePickers.tsx rename to docs/data/data-grid/custom-columns/EditingWithDatePickers.tsx index 7e22c3e3cebb3..6036f41eafbb3 100644 --- a/docs/data/data-grid/recipes-editing/EditingWithDatePickers.tsx +++ b/docs/data/data-grid/custom-columns/EditingWithDatePickers.tsx @@ -8,9 +8,10 @@ import { GRID_DATE_COL_DEF, GRID_DATETIME_COL_DEF, GridFilterItem, - GridCellParams, GridColTypeDef, GridFilterInputValueProps, + GetApplyFilterFn, + GridFilterOperator, } from '@mui/x-data-grid'; import { randomCreatedDate, @@ -30,7 +31,7 @@ function buildApplyDateFilterFn( filterItem: GridFilterItem, compareFn: (value1: number, value2: number) => boolean, showTime: boolean = false, -) { +): ReturnType> { if (!filterItem.value) { return null; } @@ -41,7 +42,7 @@ function buildApplyDateFilterFn( const filterValueMs = filterValueCopy.getTime(); - return ({ value }: GridCellParams): boolean => { + return (value) => { if (!value) { return false; } @@ -62,7 +63,7 @@ function buildApplyDateFilterFn( function getDateFilterOperators( showTime: boolean = false, -): GridColTypeDef['filterOperators'] { +): GridFilterOperator[] { return [ { value: 'is', @@ -139,7 +140,7 @@ function getDateFilterOperators( { value: 'isEmpty', getApplyFilterFn: () => { - return ({ value }): boolean => { + return (value): boolean => { return value == null; }; }, @@ -148,7 +149,7 @@ function getDateFilterOperators( { value: 'isNotEmpty', getApplyFilterFn: () => { - return ({ value }): boolean => { + return (value): boolean => { return value != null; }; }, diff --git a/docs/data/data-grid/recipes-editing/EditingWithDatePickers.tsx.preview b/docs/data/data-grid/custom-columns/EditingWithDatePickers.tsx.preview similarity index 100% rename from docs/data/data-grid/recipes-editing/EditingWithDatePickers.tsx.preview rename to docs/data/data-grid/custom-columns/EditingWithDatePickers.tsx.preview diff --git a/docs/data/data-grid/custom-columns/SparklineColumn.js b/docs/data/data-grid/custom-columns/SparklineColumn.js new file mode 100644 index 0000000000000..9a98fe66bec15 --- /dev/null +++ b/docs/data/data-grid/custom-columns/SparklineColumn.js @@ -0,0 +1,193 @@ +import * as React from 'react'; +import { DataGrid, GRID_STRING_COL_DEF } from '@mui/x-data-grid'; +import { SparkLineChart } from '@mui/x-charts/SparkLineChart'; + +function GridSparklineCell(props) { + if (props.value == null) { + return null; + } + + return ( + + ); +} + +const sparklineColumnType = { + ...GRID_STRING_COL_DEF, + resizable: false, + filterable: false, + sortable: false, + editable: false, + groupable: false, + renderCell: (params) => , +}; + +const columns = [ + { field: 'name', headerName: 'Package name', width: 180 }, + { + field: 'monthlyDownloads', + ...sparklineColumnType, + headerName: 'Monthly DLs (line)', + width: 150, + }, + { + field: 'monthlyDownloadsBar', + ...sparklineColumnType, + headerName: 'Monthly DLs (bar)', + renderCell: (params) => , + width: 150, + valueGetter: (params) => params.row.monthlyDownloads, + }, + { + field: 'lastMonthDownloads', + headerName: 'Last month DLs', + type: 'number', + valueGetter: (params) => + params.row.monthlyDownloads[params.row.monthlyDownloads.length - 1], + width: 150, + }, +]; + +export default function SparklineColumn() { + return ( +
+ +
+ ); +} + +const rows = [ + { + name: 'react-datepicker', + monthlyDownloads: [ + 469172, 488506, 592287, 617401, 640374, 632751, 668638, 807246, 749198, 944863, + 911787, 844815, 992022, 1143838, 1446926, 1267886, 1362511, 1348746, 1560533, + 1670690, 1695142, 1916613, 1823306, 1683646, 2025965, 2529989, 3263473, + 3296541, 3041524, 2599497, 2719473, 2610351, 2912067, 3079330, 2871077, + 2684197, 2852679, 3227844, 3867488, 3582735, 3454135, 3799207, 3813848, + 3839009, 4054869, 4319042, 4388671, 4149743, 4519686, 4810266, 5621007, + 5260194, 5563038, 5837767, 5342797, 6427653, 6611624, 6532709, 6886198, + 6071253, 6730371, 7097963, 8001343, 6867713, 7688481, + ], + id: 0, + }, + { + name: '@mui/x-date-pickers', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 557488, 1341471, 2044561, 2206438, 2682543, 2772941, 2987705, 3264829, 2972821, + 3489759, 3698191, 4410492, 4201780, 4892509, + ], + id: 1, + }, + { + name: 'flatpickr', + monthlyDownloads: [ + 166896, 190041, 248686, 226746, 261744, 271890, 332176, 381123, 396435, 495620, + 520278, 460839, 704158, 559134, 681089, 712384, 765381, 771374, 851314, 907947, + 903675, 1049642, 1003160, 881573, 1072283, 1139115, 1382701, 1395655, 1355040, + 1381571, 1495175, 1513409, 1673240, 1772826, 1712051, 1641944, 1718714, + 1849475, 2226745, 2104910, 1967886, 2096636, 1991424, 2155674, 2263360, + 2261195, 2324734, 2075858, 2297512, 2368925, 2886678, 2543833, 2835623, + 2916036, 2638289, 3050516, 2950882, 3042688, 3290024, 2790747, 3196521, + 3146755, 3562973, 3082832, 3477021, + ], + id: 2, + }, + { + name: 'react-day-picker', + monthlyDownloads: [ + 264651, 311845, 436558, 439385, 520413, 533380, 562363, 533793, 558029, 791126, + 649082, 566792, 723451, 737827, 890859, 935554, 1044397, 1022973, 1129827, + 1145309, 1195630, 1358925, 1373160, 1172679, 1340106, 1396974, 1623641, + 1687545, 1581634, 1550291, 1718864, 1578447, 1618394, 1697784, 1564166, + 1400088, 1471853, 1730190, 1994936, 1786010, 1713263, 1839730, 1714299, + 1753411, 1885780, 1902870, 1970994, 1762571, 1989425, 2043994, 2476663, + 2151717, 2243360, 2371687, 2046381, 2468153, 2514297, 2660758, 2887687, + 2337575, 2700261, 2873230, 3323961, 3026604, 3244131, + ], + id: 3, + }, + { + name: 'react-dates', + monthlyDownloads: [ + 251871, 262216, 402383, 396459, 378793, 406720, 447538, 451451, 457111, 589821, + 640744, 504879, 626099, 662007, 754576, 768231, 833019, 851537, 972306, + 1014831, 1027570, 1189068, 1119099, 987244, 1197954, 1310721, 1480816, 1577547, + 1854053, 1791831, 1817336, 1757624, 1859245, 1814024, 1925249, 1867506, + 1892138, 2001963, 2538000, 2327809, 2277795, 2335804, 2278370, 2258587, + 2314794, 2376233, 2315449, 1948923, 2114500, 2208357, 2471023, 2172957, + 2349786, 2481612, 2283701, 2534949, 2351510, 2074785, 2170915, 1882137, + 2087930, 2423606, 3085474, 2656079, 2861712, + ], + id: 4, + }, + { + name: '@material-ui/pickers', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 21003, 112544, 223356, 357258, + 427403, 592436, 643442, 652000, 851649, 997585, 1237884, 1323019, 1329075, + 1446751, 1603441, 1605489, 1770242, 1926553, 1957029, 1917431, 2047824, + 2342019, 2952485, 2850314, 2905856, 3145594, 3162610, 3356708, 3574777, + 3581429, 3588626, 3215994, 3209791, 3229263, 3577594, 2982893, 3072732, + 3083998, 2802316, 3345024, 3224987, 2853866, 2931270, 2419496, 2624119, + 2614166, 3072423, 2550430, 2605515, + ], + id: 5, + }, + { + name: 'react-calendar', + monthlyDownloads: [ + 13671, 16918, 27272, 34315, 42212, 56369, 64241, 77857, 70680, 91093, 108306, + 94734, 132289, 133860, 147706, 158504, 192578, 207173, 220052, 233496, 250091, + 285557, 280329, 262382, 330528, 337111, 398561, 452800, 432857, 452775, 541950, + 481764, 537173, 585916, 573412, 552463, 582320, 665610, 757420, 733958, 731212, + 786886, 793785, 836271, 899076, 950749, 981813, 913076, 1037772, 1111379, + 1372103, 1316354, 1353646, 1436614, 1349791, 1542007, 1549215, 1576125, + 1701436, 1477188, 1756447, 1804657, 2024066, 1802328, 1975321, + ], + id: 6, + }, + { + name: 'react-datetime', + monthlyDownloads: [ + 474506, 514529, 624998, 634955, 693156, 762051, 822194, 999794, 1028527, + 1264039, 1074500, 874769, 945614, 841453, 859657, 822025, 886668, 810302, + 849949, 872377, 783857, 887114, 789091, 698810, 800283, 789543, 919445, + 1026095, 1130903, 1021922, 971668, 922021, 875551, 849529, 891653, 806460, + 740611, 804504, 1008750, 1080174, 917512, 886872, 874670, 853764, 862825, + 894367, 919854, 807459, 858222, 858151, 967551, 897111, 902405, 944057, 879880, + 1090124, 1081206, 1026493, 1002294, 832895, 955662, 972831, 1166432, 1042367, + 1025499, + ], + id: 7, + }, + { + name: 'react-date-picker', + monthlyDownloads: [ + 49274, 48553, 64322, 58823, 59113, 66912, 70695, 74530, 66425, 84803, 86193, + 69178, 94987, 89205, 105340, 98078, 112268, 111998, 122224, 127661, 133198, + 138867, 128836, 120011, 158852, 154510, 175291, 197496, 224817, 194683, 220130, + 210720, 233037, 252119, 240970, 233944, 256490, 298853, 340486, 318831, 317291, + 335995, 336665, 343706, 356435, 376861, 379366, 355358, 408157, 425652, 499923, + 471759, 512219, 511044, 470863, 531581, 534128, 531059, 613792, 527997, 594540, + 637346, 788377, 721212, 644692, + ], + id: 8, + }, + { + name: '@react-spectrum/datepicker', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 691, 402, + 1239, 1536, 1853, 2163, 4151, 9644, 15667, 16426, 17786, 21804, 21358, 24062, + 30870, 34053, 35400, 37834, + ], + id: 9, + }, +]; diff --git a/docs/data/data-grid/custom-columns/SparklineColumn.tsx b/docs/data/data-grid/custom-columns/SparklineColumn.tsx new file mode 100644 index 0000000000000..3d74d9ff01b74 --- /dev/null +++ b/docs/data/data-grid/custom-columns/SparklineColumn.tsx @@ -0,0 +1,206 @@ +import * as React from 'react'; +import { + DataGrid, + GridColDef, + GridRowsProp, + GridColTypeDef, + GridRenderCellParams, + GRID_STRING_COL_DEF, +} from '@mui/x-data-grid'; +import { SparkLineChart } from '@mui/x-charts/SparkLineChart'; + +type SparkLineChartProps = React.ComponentProps; + +function GridSparklineCell( + props: GridRenderCellParams & { + plotType?: SparkLineChartProps['plotType']; + }, +) { + if (props.value == null) { + return null; + } + + return ( + + ); +} + +const sparklineColumnType: GridColTypeDef = { + ...GRID_STRING_COL_DEF, + resizable: false, + filterable: false, + sortable: false, + editable: false, + groupable: false, + renderCell: (params) => , +}; + +const columns: GridColDef[] = [ + { field: 'name', headerName: 'Package name', width: 180 }, + { + field: 'monthlyDownloads', + ...sparklineColumnType, + headerName: 'Monthly DLs (line)', + width: 150, + }, + { + field: 'monthlyDownloadsBar', + ...sparklineColumnType, + headerName: 'Monthly DLs (bar)', + renderCell: (params) => , + width: 150, + valueGetter: (params) => params.row.monthlyDownloads, + }, + { + field: 'lastMonthDownloads', + headerName: 'Last month DLs', + type: 'number', + valueGetter: (params) => + params.row.monthlyDownloads[params.row.monthlyDownloads.length - 1], + width: 150, + }, +]; + +export default function SparklineColumn() { + return ( +
+ +
+ ); +} + +const rows: GridRowsProp = [ + { + name: 'react-datepicker', + monthlyDownloads: [ + 469172, 488506, 592287, 617401, 640374, 632751, 668638, 807246, 749198, 944863, + 911787, 844815, 992022, 1143838, 1446926, 1267886, 1362511, 1348746, 1560533, + 1670690, 1695142, 1916613, 1823306, 1683646, 2025965, 2529989, 3263473, + 3296541, 3041524, 2599497, 2719473, 2610351, 2912067, 3079330, 2871077, + 2684197, 2852679, 3227844, 3867488, 3582735, 3454135, 3799207, 3813848, + 3839009, 4054869, 4319042, 4388671, 4149743, 4519686, 4810266, 5621007, + 5260194, 5563038, 5837767, 5342797, 6427653, 6611624, 6532709, 6886198, + 6071253, 6730371, 7097963, 8001343, 6867713, 7688481, + ], + id: 0, + }, + { + name: '@mui/x-date-pickers', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 557488, 1341471, 2044561, 2206438, 2682543, 2772941, 2987705, 3264829, 2972821, + 3489759, 3698191, 4410492, 4201780, 4892509, + ], + id: 1, + }, + { + name: 'flatpickr', + monthlyDownloads: [ + 166896, 190041, 248686, 226746, 261744, 271890, 332176, 381123, 396435, 495620, + 520278, 460839, 704158, 559134, 681089, 712384, 765381, 771374, 851314, 907947, + 903675, 1049642, 1003160, 881573, 1072283, 1139115, 1382701, 1395655, 1355040, + 1381571, 1495175, 1513409, 1673240, 1772826, 1712051, 1641944, 1718714, + 1849475, 2226745, 2104910, 1967886, 2096636, 1991424, 2155674, 2263360, + 2261195, 2324734, 2075858, 2297512, 2368925, 2886678, 2543833, 2835623, + 2916036, 2638289, 3050516, 2950882, 3042688, 3290024, 2790747, 3196521, + 3146755, 3562973, 3082832, 3477021, + ], + id: 2, + }, + { + name: 'react-day-picker', + monthlyDownloads: [ + 264651, 311845, 436558, 439385, 520413, 533380, 562363, 533793, 558029, 791126, + 649082, 566792, 723451, 737827, 890859, 935554, 1044397, 1022973, 1129827, + 1145309, 1195630, 1358925, 1373160, 1172679, 1340106, 1396974, 1623641, + 1687545, 1581634, 1550291, 1718864, 1578447, 1618394, 1697784, 1564166, + 1400088, 1471853, 1730190, 1994936, 1786010, 1713263, 1839730, 1714299, + 1753411, 1885780, 1902870, 1970994, 1762571, 1989425, 2043994, 2476663, + 2151717, 2243360, 2371687, 2046381, 2468153, 2514297, 2660758, 2887687, + 2337575, 2700261, 2873230, 3323961, 3026604, 3244131, + ], + id: 3, + }, + { + name: 'react-dates', + monthlyDownloads: [ + 251871, 262216, 402383, 396459, 378793, 406720, 447538, 451451, 457111, 589821, + 640744, 504879, 626099, 662007, 754576, 768231, 833019, 851537, 972306, + 1014831, 1027570, 1189068, 1119099, 987244, 1197954, 1310721, 1480816, 1577547, + 1854053, 1791831, 1817336, 1757624, 1859245, 1814024, 1925249, 1867506, + 1892138, 2001963, 2538000, 2327809, 2277795, 2335804, 2278370, 2258587, + 2314794, 2376233, 2315449, 1948923, 2114500, 2208357, 2471023, 2172957, + 2349786, 2481612, 2283701, 2534949, 2351510, 2074785, 2170915, 1882137, + 2087930, 2423606, 3085474, 2656079, 2861712, + ], + id: 4, + }, + { + name: '@material-ui/pickers', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 21003, 112544, 223356, 357258, + 427403, 592436, 643442, 652000, 851649, 997585, 1237884, 1323019, 1329075, + 1446751, 1603441, 1605489, 1770242, 1926553, 1957029, 1917431, 2047824, + 2342019, 2952485, 2850314, 2905856, 3145594, 3162610, 3356708, 3574777, + 3581429, 3588626, 3215994, 3209791, 3229263, 3577594, 2982893, 3072732, + 3083998, 2802316, 3345024, 3224987, 2853866, 2931270, 2419496, 2624119, + 2614166, 3072423, 2550430, 2605515, + ], + id: 5, + }, + { + name: 'react-calendar', + monthlyDownloads: [ + 13671, 16918, 27272, 34315, 42212, 56369, 64241, 77857, 70680, 91093, 108306, + 94734, 132289, 133860, 147706, 158504, 192578, 207173, 220052, 233496, 250091, + 285557, 280329, 262382, 330528, 337111, 398561, 452800, 432857, 452775, 541950, + 481764, 537173, 585916, 573412, 552463, 582320, 665610, 757420, 733958, 731212, + 786886, 793785, 836271, 899076, 950749, 981813, 913076, 1037772, 1111379, + 1372103, 1316354, 1353646, 1436614, 1349791, 1542007, 1549215, 1576125, + 1701436, 1477188, 1756447, 1804657, 2024066, 1802328, 1975321, + ], + id: 6, + }, + { + name: 'react-datetime', + monthlyDownloads: [ + 474506, 514529, 624998, 634955, 693156, 762051, 822194, 999794, 1028527, + 1264039, 1074500, 874769, 945614, 841453, 859657, 822025, 886668, 810302, + 849949, 872377, 783857, 887114, 789091, 698810, 800283, 789543, 919445, + 1026095, 1130903, 1021922, 971668, 922021, 875551, 849529, 891653, 806460, + 740611, 804504, 1008750, 1080174, 917512, 886872, 874670, 853764, 862825, + 894367, 919854, 807459, 858222, 858151, 967551, 897111, 902405, 944057, 879880, + 1090124, 1081206, 1026493, 1002294, 832895, 955662, 972831, 1166432, 1042367, + 1025499, + ], + id: 7, + }, + { + name: 'react-date-picker', + monthlyDownloads: [ + 49274, 48553, 64322, 58823, 59113, 66912, 70695, 74530, 66425, 84803, 86193, + 69178, 94987, 89205, 105340, 98078, 112268, 111998, 122224, 127661, 133198, + 138867, 128836, 120011, 158852, 154510, 175291, 197496, 224817, 194683, 220130, + 210720, 233037, 252119, 240970, 233944, 256490, 298853, 340486, 318831, 317291, + 335995, 336665, 343706, 356435, 376861, 379366, 355358, 408157, 425652, 499923, + 471759, 512219, 511044, 470863, 531581, 534128, 531059, 613792, 527997, 594540, + 637346, 788377, 721212, 644692, + ], + id: 8, + }, + { + name: '@react-spectrum/datepicker', + monthlyDownloads: [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 691, 402, + 1239, 1536, 1853, 2163, 4151, 9644, 15667, 16426, 17786, 21804, 21358, 24062, + 30870, 34053, 35400, 37834, + ], + id: 9, + }, +]; diff --git a/docs/data/data-grid/custom-columns/SparklineColumn.tsx.preview b/docs/data/data-grid/custom-columns/SparklineColumn.tsx.preview new file mode 100644 index 0000000000000..074afd47c4411 --- /dev/null +++ b/docs/data/data-grid/custom-columns/SparklineColumn.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/custom-columns/custom-columns.md b/docs/data/data-grid/custom-columns/custom-columns.md new file mode 100644 index 0000000000000..b876943dcea21 --- /dev/null +++ b/docs/data/data-grid/custom-columns/custom-columns.md @@ -0,0 +1,51 @@ +# Data Grid - Custom columns + +

Create custom column types.

+ +You can extend the [built-in column types](/x/react-data-grid/column-definition/#column-types) with your own by simply spreading the necessary properties. + +The demo below defines a new column type: `usdPrice` that extends the native `number` column type. + +```ts +const usdPrice: GridColTypeDef = { + type: 'number', + width: 130, + valueFormatter: ({ value }) => valueFormatter.format(Number(value)), + cellClassName: 'font-tabular-nums', +}; +``` + +{{"demo": "CustomColumnTypesGrid.js", "bg": "inline"}} + +:::info +If an unsupported column type is used, the `string` column type will be used instead. +::: + +## Sparkline + +Sparkline charts can be useful as an overview of data trends. + +In the demo below, we create a custom column type using the `GridColTypeDef` interface and use the [Sparkline](/x/react-charts/sparkline/) component from [`@mui/x-charts`](/x/react-charts/) package in the [`renderCell`](/x/react-data-grid/column-definition/#rendering-cells) property. + +{{"demo": "SparklineColumn.js", "bg": "inline"}} + +## Date pickers + +By default, the data grid uses native browser inputs for editing `date` and `dateTime` columns. + +While [MUI X Date / Time Pickers](/x/react-date-pickers/getting-started/) are not supported by the data grid out of the box yet, it is easy to integrate them by creating [custom edit components](/x/react-data-grid/editing/#create-your-own-edit-component) and [custom filter operators](/x/react-data-grid/filtering/customization/#create-a-custom-operator). + +The example below uses `@mui/x-date-pickers` for both `date` and `dateTime` column types: + +{{"demo": "EditingWithDatePickers.js", "bg": "inline", "defaultCodeOpen": false }} + +:::warning +You can change date format by importing different locale (`en-US` locale is used in the example above). +See [Localization](/x/react-date-pickers/localization/) for more information. +::: + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) +- [DataGridPremium](/x/api/data-grid/data-grid-premium/) diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.js b/docs/data/data-grid/demo/PopularFeaturesDemo.js index 2ebb71910fb3e..650a75f6b272e 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.js +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.js @@ -28,7 +28,7 @@ import ColumnVirtualizationGrid from '../virtualization/ColumnVirtualizationGrid import FullFeaturedDemo from './FullFeaturedDemo'; import LazyLoadingGrid from '../row-updates/LazyLoadingGrid'; import BasicGroupingDemo from '../column-groups/BasicGroupingDemo'; -import EditingWithDatePickers from '../recipes-editing/EditingWithDatePickers'; +import EditingWithDatePickers from '../custom-columns/EditingWithDatePickers'; import CellSelectionGrid from '../cell-selection/CellSelectionRangeStyling'; import AddNewColumnMenuGrid from '../column-menu/AddNewColumnMenuGrid'; import HeaderFilteringDataGridPro from '../filtering/HeaderFilteringDataGridPro'; diff --git a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx index 52e9484d115e7..a1785921e1a0c 100644 --- a/docs/data/data-grid/demo/PopularFeaturesDemo.tsx +++ b/docs/data/data-grid/demo/PopularFeaturesDemo.tsx @@ -33,7 +33,7 @@ import ColumnVirtualizationGrid from '../virtualization/ColumnVirtualizationGrid import FullFeaturedDemo from './FullFeaturedDemo'; import LazyLoadingGrid from '../row-updates/LazyLoadingGrid'; import BasicGroupingDemo from '../column-groups/BasicGroupingDemo'; -import EditingWithDatePickers from '../recipes-editing/EditingWithDatePickers'; +import EditingWithDatePickers from '../custom-columns/EditingWithDatePickers'; import CellSelectionGrid from '../cell-selection/CellSelectionRangeStyling'; import AddNewColumnMenuGrid from '../column-menu/AddNewColumnMenuGrid'; import HeaderFilteringDataGridPro from '../filtering/HeaderFilteringDataGridPro'; diff --git a/docs/data/data-grid/editing/editing.md b/docs/data/data-grid/editing/editing.md index 193fad2a047c4..ae28abf66188d 100644 --- a/docs/data/data-grid/editing/editing.md +++ b/docs/data/data-grid/editing/editing.md @@ -466,7 +466,13 @@ Instead, use the provided interactions to exit edit mode. ## Advanced use cases -See [Editing recipes](/x/react-data-grid/recipes-editing/) for more advanced use cases. +The [Editing recipes](/x/react-data-grid/recipes-editing/) page covers more advanced use cases, such as: + +- [Multiline editing](/x/react-data-grid/recipes-editing/#multiline-editing) +- [Single click editing](/x/react-data-grid/recipes-editing/#single-click-editing) +- [Bulk editing](/x/react-data-grid/recipes-editing/#bulk-editing) +- [Conditional validation](/x/react-data-grid/recipes-editing/#conditional-validation) +- [Linked fields](/x/react-data-grid/recipes-editing/#linked-fields) ## apiRef diff --git a/docs/data/data-grid/export/ExcelExportWithWebWorker.js b/docs/data/data-grid/export/ExcelExportWithWebWorker.js index b40c0900c7fe9..43d5b3f78e176 100644 --- a/docs/data/data-grid/export/ExcelExportWithWebWorker.js +++ b/docs/data/data-grid/export/ExcelExportWithWebWorker.js @@ -49,7 +49,7 @@ export default function ExcelExportWithWebWorker() { loading={data.rows.length === 0} rowHeight={38} checkboxSelection - components={{ Toolbar: CustomToolbar }} + slots={{ toolbar: CustomToolbar }} onExcelExportStateChange={(newState) => setInProgress(newState === 'pending') } diff --git a/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx b/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx index 73e118d07c18f..bb445408ec51f 100644 --- a/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx +++ b/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx @@ -49,7 +49,7 @@ export default function ExcelExportWithWebWorker() { loading={data.rows.length === 0} rowHeight={38} checkboxSelection - components={{ Toolbar: CustomToolbar }} + slots={{ toolbar: CustomToolbar }} onExcelExportStateChange={(newState) => setInProgress(newState === 'pending') } diff --git a/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx.preview b/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx.preview index 6eed24db1fdc6..190fdbb5c6fc9 100644 --- a/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx.preview +++ b/docs/data/data-grid/export/ExcelExportWithWebWorker.tsx.preview @@ -8,7 +8,7 @@ loading={data.rows.length === 0} rowHeight={38} checkboxSelection - components={{ Toolbar: CustomToolbar }} + slots={{ toolbar: CustomToolbar }} onExcelExportStateChange={(newState) => setInProgress(newState === 'pending') } diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.js b/docs/data/data-grid/export/PrintExportSelectedRows.js deleted file mode 100644 index 2b50951c8158c..0000000000000 --- a/docs/data/data-grid/export/PrintExportSelectedRows.js +++ /dev/null @@ -1,39 +0,0 @@ -import * as React from 'react'; -import { useDemoData } from '@mui/x-data-grid-generator'; -import { - DataGrid, - GridToolbar, - gridFilteredSortedRowIdsSelector, - selectedGridRowsSelector, -} from '@mui/x-data-grid'; - -const getSelectedRowsToExport = ({ apiRef }) => { - const selectedRowIds = selectedGridRowsSelector(apiRef); - if (selectedRowIds.size > 0) { - return Array.from(selectedRowIds.keys()); - } - - return gridFilteredSortedRowIdsSelector(apiRef); -}; - -export default function PrintExportSelectedRows() { - const { data, loading } = useDemoData({ - dataSet: 'Commodity', - rowLength: 10, - maxColumns: 6, - }); - - return ( -
- -
- ); -} diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.tsx b/docs/data/data-grid/export/PrintExportSelectedRows.tsx deleted file mode 100644 index 5a0ca2049f126..0000000000000 --- a/docs/data/data-grid/export/PrintExportSelectedRows.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from 'react'; -import { useDemoData } from '@mui/x-data-grid-generator'; -import { - DataGrid, - GridToolbar, - GridPrintGetRowsToExportParams, - gridFilteredSortedRowIdsSelector, - selectedGridRowsSelector, - GridRowId, -} from '@mui/x-data-grid'; - -const getSelectedRowsToExport = ({ - apiRef, -}: GridPrintGetRowsToExportParams): GridRowId[] => { - const selectedRowIds = selectedGridRowsSelector(apiRef); - if (selectedRowIds.size > 0) { - return Array.from(selectedRowIds.keys()); - } - - return gridFilteredSortedRowIdsSelector(apiRef); -}; - -export default function PrintExportSelectedRows() { - const { data, loading } = useDemoData({ - dataSet: 'Commodity', - rowLength: 10, - maxColumns: 6, - }); - - return ( -
- -
- ); -} diff --git a/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview b/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview deleted file mode 100644 index 11e312d983798..0000000000000 --- a/docs/data/data-grid/export/PrintExportSelectedRows.tsx.preview +++ /dev/null @@ -1,9 +0,0 @@ - \ No newline at end of file diff --git a/docs/data/data-grid/export/export.md b/docs/data/data-grid/export/export.md index 65dee9725b4a8..9c3b94fed0724 100644 --- a/docs/data/data-grid/export/export.md +++ b/docs/data/data-grid/export/export.md @@ -88,15 +88,6 @@ There are a few ways to include or hide other columns. ## Exported rows -### Print export - -The print export always prints all rows regardless of whether or not some rows are selected. -To export only selected rows via print you can use the `getRowsToExport` function. - -{{"demo": "PrintExportSelectedRows.js", "bg": "inline", "defaultCodeOpen": false}} - -### CSV and Excel export - By default, the data grid exports the selected rows if there are any. If not, it exports all rows except the footers (filtered and sorted rows, according to active rules), including the collapsed ones. diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js new file mode 100644 index 0000000000000..53ee4312c7d4c --- /dev/null +++ b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function UseNonNativeSelect() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx new file mode 100644 index 0000000000000..53ee4312c7d4c --- /dev/null +++ b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx @@ -0,0 +1,26 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function UseNonNativeSelect() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview new file mode 100644 index 0000000000000..f042b7c7db386 --- /dev/null +++ b/docs/data/data-grid/filtering-recipes/UseNonNativeSelect.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering-recipes/filtering-recipes.md b/docs/data/data-grid/filtering-recipes/filtering-recipes.md index 4f74be0656555..b9c7e71005100 100644 --- a/docs/data/data-grid/filtering-recipes/filtering-recipes.md +++ b/docs/data/data-grid/filtering-recipes/filtering-recipes.md @@ -6,10 +6,16 @@ title: Data Grid - Filtering customization recipes

Advanced filtering customization recipes.

-## Quck filter outside of the grid +## Quick filter outside of the grid Currently if you want to use the [Quick filter](/x/react-data-grid/filtering/quick-filter/) feature you need to use from the toolbar component slot. A common use case is to have certain components positioned outside of the grid. Because of the way the grid context works this might not be a straining forward thing to do. The example below illustrates how this use case can be achieved. {{"demo": "QuickFilterOutsideOfGrid.js", "bg": "inline", "defaultCodeOpen": false}} + +## Use non-native select in filter panel + +If you do not want to use the native select in the filtering panel you can switch it to the `@mui/material/Select` component by using the `slotProps` property. + +{{"demo": "UseNonNativeSelect.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.js b/docs/data/data-grid/filtering/CustomMultiValueOperator.js index 75e3549d6a239..e2b6603975dc3 100644 --- a/docs/data/data-grid/filtering/CustomMultiValueOperator.js +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.js @@ -90,8 +90,7 @@ const quantityOnlyOperators = [ if (filterItem.value[0] == null || filterItem.value[1] == null) { return null; } - - return ({ value }) => { + return (value) => { return ( value !== null && filterItem.value[0] <= value && diff --git a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx index 2532d64d3c881..2245dc2ced32c 100644 --- a/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx +++ b/docs/data/data-grid/filtering/CustomMultiValueOperator.tsx @@ -4,7 +4,6 @@ import TextField, { TextFieldProps } from '@mui/material/TextField'; import { GridFilterInputValueProps, DataGrid, - GridFilterItem, GridFilterModel, GridFilterOperator, useGridRootProps, @@ -88,19 +87,18 @@ function InputNumberInterval(props: GridFilterInputValueProps) { ); } -const quantityOnlyOperators: GridFilterOperator[] = [ +const quantityOnlyOperators: GridFilterOperator[] = [ { label: 'Between', value: 'between', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFn: (filterItem) => { if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) { return null; } if (filterItem.value[0] == null || filterItem.value[1] == null) { return null; } - - return ({ value }) => { + return (value) => { return ( value !== null && filterItem.value[0] <= value && diff --git a/docs/data/data-grid/filtering/CustomRatingOperator.js b/docs/data/data-grid/filtering/CustomRatingOperator.js index 75258d6fc0e56..0c4fb634628f5 100644 --- a/docs/data/data-grid/filtering/CustomRatingOperator.js +++ b/docs/data/data-grid/filtering/CustomRatingOperator.js @@ -4,6 +4,14 @@ import Rating from '@mui/material/Rating'; import { DataGrid, GridToolbarFilterButton } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; +function Toolbar() { + return ( +
+ +
+ ); +} + function RatingInputValue(props) { const { item, applyValue, focusElementRef } = props; @@ -50,9 +58,8 @@ const ratingOnlyOperators = [ if (!filterItem.field || !filterItem.value || !filterItem.operator) { return null; } - - return (params) => { - return Number(params.value) >= Number(filterItem.value); + return (value) => { + return Number(value) >= Number(filterItem.value); }; }, InputComponent: RatingInputValue, @@ -89,7 +96,7 @@ export default function CustomRatingOperator() { {...data} columns={columns} slots={{ - toolbar: GridToolbarFilterButton, + toolbar: Toolbar, }} initialState={{ ...data.initialState, diff --git a/docs/data/data-grid/filtering/CustomRatingOperator.tsx b/docs/data/data-grid/filtering/CustomRatingOperator.tsx index 4e90a173f00d6..0eec4ca4651ed 100644 --- a/docs/data/data-grid/filtering/CustomRatingOperator.tsx +++ b/docs/data/data-grid/filtering/CustomRatingOperator.tsx @@ -4,12 +4,19 @@ import Rating, { RatingProps } from '@mui/material/Rating'; import { GridFilterInputValueProps, DataGrid, - GridFilterItem, GridFilterOperator, GridToolbarFilterButton, } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; +function Toolbar() { + return ( +
+ +
+ ); +} + function RatingInputValue(props: GridFilterInputValueProps) { const { item, applyValue, focusElementRef } = props; @@ -48,17 +55,16 @@ function RatingInputValue(props: GridFilterInputValueProps) { ); } -const ratingOnlyOperators: GridFilterOperator[] = [ +const ratingOnlyOperators: GridFilterOperator[] = [ { label: 'Above', value: 'above', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFn: (filterItem) => { if (!filterItem.field || !filterItem.value || !filterItem.operator) { return null; } - - return (params): boolean => { - return Number(params.value) >= Number(filterItem.value); + return (value) => { + return Number(value) >= Number(filterItem.value); }; }, InputComponent: RatingInputValue, @@ -95,7 +101,7 @@ export default function CustomRatingOperator() { {...data} columns={columns} slots={{ - toolbar: GridToolbarFilterButton, + toolbar: Toolbar, }} initialState={{ ...data.initialState, diff --git a/docs/data/data-grid/filtering/CustomSelectionOperator.js b/docs/data/data-grid/filtering/CustomSelectionOperator.js index 2fb64da233211..73b628a03644f 100644 --- a/docs/data/data-grid/filtering/CustomSelectionOperator.js +++ b/docs/data/data-grid/filtering/CustomSelectionOperator.js @@ -53,12 +53,13 @@ export default function CustomSelectionOperator() { return innerFilterFn; } - return (params) => { - if (rowSelectionModelLookupRef.current[params.id]) { + return (value, row, col, apiRef) => { + const rowId = apiRef.current.getRowId(row); + if (rowSelectionModelLookupRef.current[rowId]) { return true; } - return innerFilterFn(params); + return innerFilterFn(value, row, col, apiRef); }; }; diff --git a/docs/data/data-grid/filtering/CustomSelectionOperator.tsx b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx index c16f407942f8c..34ce0ad8b4a12 100644 --- a/docs/data/data-grid/filtering/CustomSelectionOperator.tsx +++ b/docs/data/data-grid/filtering/CustomSelectionOperator.tsx @@ -5,7 +5,6 @@ import { GridFilterModel, GridRowId, GridFilterOperator, - GridCellParams, getGridDefaultColumnTypes, DEFAULT_GRID_COL_TYPE_KEY, } from '@mui/x-data-grid'; @@ -69,12 +68,13 @@ export default function CustomSelectionOperator() { return innerFilterFn; } - return (params: GridCellParams) => { - if (rowSelectionModelLookupRef.current[params.id]) { + return (value, row, col, apiRef) => { + const rowId = apiRef.current.getRowId(row); + if (rowSelectionModelLookupRef.current[rowId]) { return true; } - return innerFilterFn(params); + return innerFilterFn(value, row, col, apiRef); }; }; diff --git a/docs/data/data-grid/filtering/DisableActionButtonsDataGridPro.js b/docs/data/data-grid/filtering/DisableActionButtonsDataGridPro.js index a50239e89e01b..b268af383b0b0 100644 --- a/docs/data/data-grid/filtering/DisableActionButtonsDataGridPro.js +++ b/docs/data/data-grid/filtering/DisableActionButtonsDataGridPro.js @@ -15,7 +15,7 @@ export default function DisableActionButtonsDataGridPro() {
{ +const getApplyQuickFilterFnSameYear = (value) => { if (!value || value.length !== 4 || !/\d{4}/.test(value)) { // If the value is not a 4 digit string, it can not be a year so applying this filter is useless return null; } - return (params) => { - if (params.value instanceof Date) { - return params.value.getFullYear() === Number(value); + return (cellValue) => { + if (cellValue instanceof Date) { + return cellValue.getFullYear() === Number(value); } return false; }; @@ -47,7 +47,7 @@ export default function QuickFilteringCustomLogic() { if (column.field === 'dateCreated') { return { ...column, - getApplyQuickFilterFn: getApplyFilterFnSameYear, + getApplyQuickFilterFn: getApplyQuickFilterFnSameYear, }; } if (column.field === 'name') { diff --git a/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx index 75c64f2defc11..930e29b1c161c 100644 --- a/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx +++ b/docs/data/data-grid/filtering/QuickFilteringCustomLogic.tsx @@ -1,6 +1,10 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { DataGrid, GridCellParams, GridToolbarQuickFilter } from '@mui/x-data-grid'; +import { + DataGrid, + GetApplyQuickFilterFn, + GridToolbarQuickFilter, +} from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; function QuickSearchToolbar() { @@ -18,14 +22,16 @@ function QuickSearchToolbar() { const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; -const getApplyFilterFnSameYear = (value: string) => { +const getApplyQuickFilterFnSameYear: GetApplyQuickFilterFn = ( + value, +) => { if (!value || value.length !== 4 || !/\d{4}/.test(value)) { // If the value is not a 4 digit string, it can not be a year so applying this filter is useless return null; } - return (params: GridCellParams): boolean => { - if (params.value instanceof Date) { - return params.value.getFullYear() === Number(value); + return (cellValue) => { + if (cellValue instanceof Date) { + return cellValue.getFullYear() === Number(value); } return false; }; @@ -47,7 +53,7 @@ export default function QuickFilteringCustomLogic() { if (column.field === 'dateCreated') { return { ...column, - getApplyQuickFilterFn: getApplyFilterFnSameYear, + getApplyQuickFilterFn: getApplyQuickFilterFnSameYear, }; } if (column.field === 'name') { diff --git a/docs/data/data-grid/filtering/QuickFilteringDiacritics.js b/docs/data/data-grid/filtering/QuickFilteringDiacritics.js new file mode 100644 index 0000000000000..72466794f2003 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringDiacritics.js @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { DataGrid, GridToolbar } from '@mui/x-data-grid'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Switch from '@mui/material/Switch'; + +const dateFormatter = new Intl.DateTimeFormat('fr-FR', { + day: 'numeric', + month: 'long', + year: 'numeric', +}); + +const rows = [ + { id: 0, string: 'Café', date: new Date(2023, 1, 1), singleSelect: 'Jalapeño' }, +]; + +const columns = [ + { field: 'string', width: 100 }, + { + field: 'date', + type: 'date', + width: 150, + valueFormatter: (params) => dateFormatter.format(params.value), + }, + { + field: 'singleSelect', + type: 'singleSelect', + valueOptions: ['Jalapeño'], + }, +]; + +export default function QuickFilteringDiacritics() { + const [filterModel, setFilterModel] = React.useState({ + items: [], + quickFilterValues: ['cafe'], + }); + const [ignoreDiacritics, setIgnoreDiacritics] = React.useState(true); + + return ( +
+ setIgnoreDiacritics(event.target.checked)} + control={} + label="Ignore diacritics" + /> +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringDiacritics.preview b/docs/data/data-grid/filtering/QuickFilteringDiacritics.preview new file mode 100644 index 0000000000000..b79084cb80037 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringDiacritics.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/filtering/QuickFilteringDiacritics.tsx b/docs/data/data-grid/filtering/QuickFilteringDiacritics.tsx new file mode 100644 index 0000000000000..26197a90dbad9 --- /dev/null +++ b/docs/data/data-grid/filtering/QuickFilteringDiacritics.tsx @@ -0,0 +1,69 @@ +import * as React from 'react'; +import { + DataGrid, + GridToolbar, + GridColDef, + GridFilterModel, +} from '@mui/x-data-grid'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import Switch from '@mui/material/Switch'; + +const dateFormatter = new Intl.DateTimeFormat('fr-FR', { + day: 'numeric', + month: 'long', + year: 'numeric', +}); + +const rows = [ + { id: 0, string: 'Café', date: new Date(2023, 1, 1), singleSelect: 'Jalapeño' }, +]; +const columns: GridColDef[] = [ + { field: 'string', width: 100 }, + { + field: 'date', + type: 'date', + width: 150, + valueFormatter: (params) => dateFormatter.format(params.value), + }, + { + field: 'singleSelect', + type: 'singleSelect', + valueOptions: ['Jalapeño'], + }, +]; + +export default function QuickFilteringDiacritics() { + const [filterModel, setFilterModel] = React.useState({ + items: [], + quickFilterValues: ['cafe'], + }); + const [ignoreDiacritics, setIgnoreDiacritics] = React.useState(true); + + return ( +
+ + setIgnoreDiacritics((event.target as HTMLInputElement).checked) + } + control={} + label="Ignore diacritics" + /> +
+ +
+
+ ); +} diff --git a/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.js b/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.js index f49a7c71784d3..f2dfefdc34c61 100644 --- a/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.js +++ b/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.js @@ -41,7 +41,7 @@ export default function QuickFilteringExcludeHiddenColumns() { onChange={(event) => setColumnVisibilityModel(() => ({ id: event.target.checked })) } - control={} + control={} label="Show ID column" /> } + control={} label="Exclude hidden columns" /> diff --git a/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.tsx b/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.tsx index 0c193459cc6fe..46597a6ecba94 100644 --- a/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.tsx +++ b/docs/data/data-grid/filtering/QuickFilteringExcludeHiddenColumns.tsx @@ -49,7 +49,7 @@ export default function QuickFilteringExcludeHiddenColumns() { onChange={(event) => setColumnVisibilityModel(() => ({ id: (event.target as any).checked })) } - control={} + control={} label="Show ID column" /> } + control={} label="Exclude hidden columns" /> diff --git a/docs/data/data-grid/filtering/customization.md b/docs/data/data-grid/filtering/customization.md index 7b9e6c499043f..83566d25d21ee 100644 --- a/docs/data/data-grid/filtering/customization.md +++ b/docs/data/data-grid/filtering/customization.md @@ -34,16 +34,16 @@ When applying the filters, the data grid will call this function with the filter This function must return another function that takes the cell value as an input and return `true` if it satisfies the operator condition. ```ts -const operator: GridFilterOperator = { +const operator: GridFilterOperator = { label: 'From', value: 'from', - getApplyFilterFn: (filterItem: GridFilterItem, column: GridColDef) => { + getApplyFilterFn: (filterItem, column) => { if (!filterItem.field || !filterItem.value || !filterItem.operator) { return null; } - return (params: GridCellParams): boolean => { - return Number(params.value) >= Number(filterItem.value); + return (value, row, column, apiRef) => { + return Number(value) >= Number(filterItem.value); }; }, InputComponent: RatingInputValue, @@ -84,22 +84,24 @@ The filtering function `getApplyFilterFn` must be adapted to handle `filterItem. Below is an example for a "between" operator, applied on the "Quantity" column. ```ts -{ +const operator: GridFilterOperator = { label: 'Between', value: 'between', - getApplyFilterFn: (filterItem: GridFilterItem) => { + getApplyFilterFn: (filterItem) => { if (!Array.isArray(filterItem.value) || filterItem.value.length !== 2) { return null; } if (filterItem.value[0] == null || filterItem.value[1] == null) { return null; } - return ({ value }): boolean => { - return value != null && filterItem.value[0] <= value && value <= filterItem.value[1]; + return (value) => { + return ( + value != null && filterItem.value[0] <= value && value <= filterItem.value[1] + ); }; }, InputComponent: InputNumberInterval, -} +}; ``` {{"demo": "CustomMultiValueOperator.js", "bg": "inline", "defaultCodeOpen": false}} @@ -179,38 +181,6 @@ The demo below shows how to anchor the filter panel to the toolbar button instea {{"demo": "CustomFilterPanelPosition.js", "bg": "inline", "defaultCodeOpen": false}} -### Optimize performance - -There is a new set of APIs with a more efficient interface that are going to be used by default at the next major release, v7. - -You can use them right now to make your custom filters faster. Instead of receiving a `GridCellParams` argument, they receive the parameters listed below. - -```ts -const noop = () => {}; -const operator: GridFilterOperator = { - /* ...other operator properties */ - getApplyFilterFn: noop /* It is required to pass a noop function until v7 */, - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - /* This example is our default string filter function for v7 */ - - if (!filterItem.value) { - return null; - } - const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); - const filterRegex = new RegExp(escapeRegExp(filterItemValue), 'i'); - - return ( - value: any, - row: GridValidRowModel, - column: GridColDef, - apiRef: React.MutableRefObject, - ): boolean => { - return value != null ? filterRegex.test(String(value)) : false; - }; - }, -}; -``` - ## API - [GridFilterOperator](/x/api/data-grid/grid-filter-operator/) diff --git a/docs/data/data-grid/filtering/header-filters.md b/docs/data/data-grid/filtering/header-filters.md index e4a1c174e4231..80f1f85ddb81c 100644 --- a/docs/data/data-grid/filtering/header-filters.md +++ b/docs/data/data-grid/filtering/header-filters.md @@ -72,6 +72,10 @@ Additionally, `slots.headerFilterMenu` could also be used to customize the menu {{"demo": "CustomHeaderFilterDataGridPro.js", "bg": "inline", "defaultCodeOpen": false}} +## Ignore diacritics (accents) + +You can ignore diacritics (accents) when filtering the rows. See [Quick filter - Ignore diacritics (accents)](/x/react-data-grid/filtering/quick-filter/#ignore-diacritics-accents). + ## API - [DataGrid](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/filtering/index.md b/docs/data/data-grid/filtering/index.md index 93c1fe8fe06ba..3d2597c607698 100644 --- a/docs/data/data-grid/filtering/index.md +++ b/docs/data/data-grid/filtering/index.md @@ -127,6 +127,10 @@ In the example below, the _rating_ column can not be filtered. {{"demo": "DisableFilteringGridSomeColumns.js", "bg": "inline", "defaultCodeOpen": false}} +## Ignore diacritics (accents) + +You can ignore diacritics (accents) when filtering the rows. See [Quick filter - Ignore diacritics (accents)](/x/react-data-grid/filtering/quick-filter/#ignore-diacritics-accents). + ## apiRef The grid exposes a set of methods that enables all of these features using the imperative `apiRef`. To know more about how to use it, check the [API Object](/x/react-data-grid/api-object/) section. diff --git a/docs/data/data-grid/filtering/quick-filter.md b/docs/data/data-grid/filtering/quick-filter.md index 8d184f697c0fb..bcc54abbec665 100644 --- a/docs/data/data-grid/filtering/quick-filter.md +++ b/docs/data/data-grid/filtering/quick-filter.md @@ -79,13 +79,16 @@ This function takes as an input a value of the quick filter and returns another In the example below, a custom filter is created for the `date` column to check if it contains the correct year. ```ts -getApplyQuickFilterFn: (value: string) => { +const getApplyQuickFilterFn: GetApplyQuickFilterFn = (value) => { if (!value || value.length !== 4 || !/\d{4}/.test(value)) { // If the value is not a 4 digit string, it can not be a year so applying this filter is useless return null; } - return (params: GridCellParams): boolean => { - return params.value.getFullYear() === Number(value); + return (cellValue) => { + if (cellValue instanceof Date) { + return cellValue.getFullYear() === Number(value); + } + return false; }; }; ``` @@ -122,6 +125,23 @@ In the following demo, the quick filter value `"Saint Martin, Saint Lucia"` will {{"demo": "QuickFilteringCustomizedGrid.js", "bg": "inline", "defaultCodeOpen": false}} +## Ignore diacritics (accents) + +In some languages, the letters can have diacritics (accents) - for instance, the letter `é` in French. +By default, these letters are considered different from their non-accented versions when filtering. + +To ignore diacritics, set the `ignoreDiacritics` prop to `true`: + +```tsx + +``` + +{{"demo": "QuickFilteringDiacritics.js", "bg": "inline", "defaultCodeOpen": false}} + +:::warning +Note that the `ignoreDiacritics` prop affects all columns and all filter types: [normal filters](/x/react-data-grid/filtering/), [quick filter](/x/react-data-grid/filtering/quick-filter/) and [header filters](/x/react-data-grid/filtering/header-filters/). +::: + ## API - [GridToolbarQuickFilter](/x/api/data-grid/grid-toolbar-quick-filter/) diff --git a/docs/data/data-grid/getting-started/getting-started.md b/docs/data/data-grid/getting-started/getting-started.md index 09b092da8d60f..9b1bc24a3ddb6 100644 --- a/docs/data/data-grid/getting-started/getting-started.md +++ b/docs/data/data-grid/getting-started/getting-started.md @@ -6,6 +6,10 @@ Using your favorite package manager, install `@mui/x-data-grid-pro` or `@mui/x-data-grid-premium` for the commercial version, or `@mui/x-data-grid` for the free community version. +:::warning +The `next` tag is used to download the latest v7 **pre-release** version. +::: + {{"component": "modules/components/DataGridInstallationInstructions.js"}} The Data Grid package has a peer dependency on `@mui/material`. @@ -178,6 +182,7 @@ The enterprise components come in two plans: Pro and Premium. | [Column groups](/x/react-data-grid/column-groups/) | ✅ | ✅ | ✅ | | [Column spanning](/x/react-data-grid/column-spanning/) | ✅ | ✅ | ✅ | | [Column resizing](/x/react-data-grid/column-dimensions/#resizing) | ❌ | ✅ | ✅ | +| [Column autosizing](/x/react-data-grid/column-dimensions/#autosizing) | ❌ | ✅ | ✅ | | [Column reorder](/x/react-data-grid/column-ordering/) | ❌ | ✅ | ✅ | | [Column pinning](/x/react-data-grid/column-pinning/) | ❌ | ✅ | ✅ | | **Row** | | | | diff --git a/docs/data/data-grid/localization/DataGridRTL.js b/docs/data/data-grid/localization/DataGridRTL.js new file mode 100644 index 0000000000000..a6b9d26a122ae --- /dev/null +++ b/docs/data/data-grid/localization/DataGridRTL.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { prefixer } from 'stylis'; +import rtlPlugin from 'stylis-plugin-rtl'; +import { DataGrid, arSD } from '@mui/x-data-grid'; +import createCache from '@emotion/cache'; +import { CacheProvider } from '@emotion/react'; +import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles'; + +// Create rtl cache +const cacheRtl = createCache({ + key: 'data-grid-rtl-demo', + stylisPlugins: [prefixer, rtlPlugin], +}); + +const columns = [ + { + field: 'id', + headerName: 'تعريف', + width: 150, + }, + { + field: 'name', + headerName: 'اسم', + width: 150, + }, + { + field: 'age', + headerName: 'عمر', + valueGetter: (params) => `${params.value} سنوات`, + width: 150, + }, + { + field: 'occupation', + headerName: 'المهنة', + width: 150, + }, + { + field: 'gender', + headerName: 'جنس', + width: 150, + }, +]; + +const rows = [ + { id: 1, name: 'سارہ', age: 35, occupation: 'معلم', gender: 'أنثى' }, + { id: 2, name: 'زید', age: 42, occupation: 'مهندس', gender: 'ذكر' }, + { id: 3, name: 'علی', age: 33, occupation: 'محاسب', gender: 'ذكر' }, + { id: 4, name: 'فاطمہ', age: 25, occupation: 'معلم', gender: 'أنثى' }, + { id: 5, name: 'ایندریو', age: 65, occupation: 'مهندس', gender: 'ذكر' }, +]; + +export default function DataGridRTL() { + // Inherit the theme from the docs site (dark/light mode) + const existingTheme = useTheme(); + + const theme = React.useMemo( + () => + createTheme({}, arSD, existingTheme, { + direction: 'rtl', + }), + [existingTheme], + ); + return ( + + +
+ +
+
+
+ ); +} diff --git a/docs/data/data-grid/localization/DataGridRTL.tsx b/docs/data/data-grid/localization/DataGridRTL.tsx new file mode 100644 index 0000000000000..a822bdc0792d9 --- /dev/null +++ b/docs/data/data-grid/localization/DataGridRTL.tsx @@ -0,0 +1,72 @@ +import * as React from 'react'; +import { prefixer } from 'stylis'; +import rtlPlugin from 'stylis-plugin-rtl'; +import { DataGrid, GridColDef, arSD } from '@mui/x-data-grid'; +import createCache from '@emotion/cache'; +import { CacheProvider } from '@emotion/react'; +import { createTheme, ThemeProvider, useTheme } from '@mui/material/styles'; + +// Create rtl cache +const cacheRtl = createCache({ + key: 'data-grid-rtl-demo', + stylisPlugins: [prefixer, rtlPlugin], +}); + +const columns: GridColDef[] = [ + { + field: 'id', + headerName: 'تعريف', + width: 150, + }, + { + field: 'name', + headerName: 'اسم', + width: 150, + }, + { + field: 'age', + headerName: 'عمر', + valueGetter: (params) => `${params.value} سنوات`, + width: 150, + }, + { + field: 'occupation', + headerName: 'المهنة', + width: 150, + }, + { + field: 'gender', + headerName: 'جنس', + width: 150, + }, +]; + +const rows = [ + { id: 1, name: 'سارہ', age: 35, occupation: 'معلم', gender: 'أنثى' }, + { id: 2, name: 'زید', age: 42, occupation: 'مهندس', gender: 'ذكر' }, + { id: 3, name: 'علی', age: 33, occupation: 'محاسب', gender: 'ذكر' }, + { id: 4, name: 'فاطمہ', age: 25, occupation: 'معلم', gender: 'أنثى' }, + { id: 5, name: 'ایندریو', age: 65, occupation: 'مهندس', gender: 'ذكر' }, +]; + +export default function DataGridRTL() { + // Inherit the theme from the docs site (dark/light mode) + const existingTheme = useTheme(); + + const theme = React.useMemo( + () => + createTheme({}, arSD, existingTheme, { + direction: 'rtl', + }), + [existingTheme], + ); + return ( + + +
+ +
+
+
+ ); +} diff --git a/docs/data/data-grid/localization/DataGridRTL.tsx.preview b/docs/data/data-grid/localization/DataGridRTL.tsx.preview new file mode 100644 index 0000000000000..9ed1cd7c02ac0 --- /dev/null +++ b/docs/data/data-grid/localization/DataGridRTL.tsx.preview @@ -0,0 +1,7 @@ + + +
+ +
+
+
\ No newline at end of file diff --git a/docs/data/data-grid/localization/data.json b/docs/data/data-grid/localization/data.json index 5e6f16f54e328..0a377047bfe4a 100644 --- a/docs/data/data-grid/localization/data.json +++ b/docs/data/data-grid/localization/data.json @@ -3,7 +3,7 @@ "languageTag": "ar-SD", "importName": "arSD", "localeName": "Arabic (Sudan)", - "missingKeysCount": 27, + "missingKeysCount": 1, "totalKeysCount": 119, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/arSD.ts" }, @@ -163,7 +163,7 @@ "languageTag": "pt-BR", "importName": "ptBR", "localeName": "Portuguese (Brazil)", - "missingKeysCount": 13, + "missingKeysCount": 0, "totalKeysCount": 119, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/ptBR.ts" }, @@ -179,7 +179,7 @@ "languageTag": "ru-RU", "importName": "ruRU", "localeName": "Russian", - "missingKeysCount": 26, + "missingKeysCount": 0, "totalKeysCount": 119, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/grid/x-data-grid/src/locales/ruRU.ts" }, diff --git a/docs/data/data-grid/localization/localization.md b/docs/data/data-grid/localization/localization.md index 8aafb5463db3d..905c39065e32a 100644 --- a/docs/data/data-grid/localization/localization.md +++ b/docs/data/data-grid/localization/localization.md @@ -101,13 +101,14 @@ You can [find the source](https://github.com/mui/mui-x/tree/HEAD/packages/grid/x To create your own translation or to customize the English text, copy this file to your project, make any changes needed and import the locale from there. Note that these translations of the Data Grid component depend on the [Localization strategy](/material-ui/guides/localization/) of the whole library. -## RTL Support 🚧 +## RTL Support -:::warning -RTL is not fully supported in the Data Grid. +Right-to-left languages such as Arabic, Persian, or Hebrew are supported. +Follow [this guide](/material-ui/guides/right-to-left/) to use them. -👍 Upvote [issue #230](https://github.com/mui/mui-x/issues/230) if that's a requirement in your project. -::: +The example below demonstrates how to use an RTL language (Arabic) with the data grid. + +{{"demo": "DataGridRTL.js", "bg": "inline"}} ## API diff --git a/docs/data/data-grid/performance/performance.md b/docs/data/data-grid/performance/performance.md index e1588e22c1f61..725f8ce25386d 100644 --- a/docs/data/data-grid/performance/performance.md +++ b/docs/data/data-grid/performance/performance.md @@ -50,10 +50,6 @@ shows you which cells re-render in reaction to your interaction with the grid. {{"demo": "GridVisualization.js", "bg": "inline", "defaultCodeOpen": false}} -## Filtering - -For filtering performance, see [the filter customization section](/x/react-data-grid/filtering/customization/#optimize-performance). - ## API - [DataGrid](/x/api/data-grid/data-grid/) diff --git a/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.js b/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.js new file mode 100644 index 0000000000000..57d79335d3269 --- /dev/null +++ b/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.js @@ -0,0 +1,196 @@ +/* eslint-disable no-underscore-dangle */ +import * as React from 'react'; +import { + DataGridPremium, + useGridApiRef, + GridActionsCellItem, +} from '@mui/x-data-grid-premium'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button from '@mui/material/Button'; +import DeleteIcon from '@mui/icons-material/Delete'; +import RestoreIcon from '@mui/icons-material/Restore'; +import LoadingButton from '@mui/lab/LoadingButton'; +import SaveIcon from '@mui/icons-material/Save'; +import { darken } from '@mui/material/styles'; + +export default function BulkEditingNoSnap() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 7, + editable: true, + visibleFields: [ + 'id', + 'commodity', + 'traderName', + 'traderEmail', + 'quantity', + 'filledQuantity', + ], + }); + + const apiRef = useGridApiRef(); + + const [hasUnsavedRows, setHasUnsavedRows] = React.useState(false); + const unsavedChangesRef = React.useRef({ + unsavedRows: {}, + rowsBeforeChange: {}, + }); + const [isSaving, setIsSaving] = React.useState(false); + + const columns = React.useMemo(() => { + return [ + { + field: 'actions', + type: 'actions', + getActions: ({ id, row }) => { + return [ + } + label="Discard changes" + disabled={unsavedChangesRef.current.unsavedRows[id] === undefined} + onClick={() => { + apiRef.current.updateRows([ + unsavedChangesRef.current.rowsBeforeChange[id], + ]); + delete unsavedChangesRef.current.rowsBeforeChange[id]; + delete unsavedChangesRef.current.unsavedRows[id]; + setHasUnsavedRows( + Object.keys(unsavedChangesRef.current.unsavedRows).length > 0, + ); + }} + />, + } + label="Delete" + onClick={() => { + unsavedChangesRef.current.unsavedRows[id] = { + ...row, + _action: 'delete', + }; + if (!unsavedChangesRef.current.rowsBeforeChange[id]) { + unsavedChangesRef.current.rowsBeforeChange[id] = row; + } + setHasUnsavedRows(true); + apiRef.current.updateRows([row]); // to trigger row render + }} + />, + ]; + }, + }, + ...data.columns, + ]; + }, [data.columns, unsavedChangesRef, apiRef]); + + const processRowUpdate = (newRow, oldRow) => { + const rowId = newRow.id; + + unsavedChangesRef.current.unsavedRows[rowId] = newRow; + if (!unsavedChangesRef.current.rowsBeforeChange[rowId]) { + unsavedChangesRef.current.rowsBeforeChange[rowId] = oldRow; + } + setHasUnsavedRows(true); + return newRow; + }; + + const discardChanges = () => { + setHasUnsavedRows(false); + apiRef.current.updateRows( + Object.values(unsavedChangesRef.current.rowsBeforeChange), + ); + unsavedChangesRef.current = { + unsavedRows: {}, + rowsBeforeChange: {}, + }; + }; + + const saveChanges = async () => { + try { + // Persist updates in the database + setIsSaving(true); + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + + setIsSaving(false); + const rowsToDelete = Object.values( + unsavedChangesRef.current.unsavedRows, + ).filter((row) => row._action === 'delete'); + if (rowsToDelete.length > 0) { + apiRef.current.updateRows(rowsToDelete); + } + + setHasUnsavedRows(false); + unsavedChangesRef.current = { + unsavedRows: {}, + rowsBeforeChange: {}, + }; + } catch (error) { + setIsSaving(false); + } + }; + + return ( +
+
+ } + loadingPosition="start" + > + Save + + +
+
+ { + if (theme.palette.mode === 'light') { + return 'rgba(255, 170, 170, 0.3)'; + } + return darken('rgba(255, 170, 170, 1)', 0.7); + }, + }, + '& .MuiDataGrid-row.row--edited': { + backgroundColor: (theme) => { + if (theme.palette.mode === 'light') { + return 'rgba(255, 254, 176, 0.3)'; + } + return darken('rgba(255, 254, 176, 1)', 0.6); + }, + }, + }} + loading={isSaving} + getRowClassName={({ id }) => { + const unsavedRow = unsavedChangesRef.current.unsavedRows[id]; + if (unsavedRow) { + if (unsavedRow._action === 'delete') { + return 'row--removed'; + } + return 'row--edited'; + } + return ''; + }} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.tsx b/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.tsx new file mode 100644 index 0000000000000..aeb9621bf044d --- /dev/null +++ b/docs/data/data-grid/recipes-editing/BulkEditingNoSnap.tsx @@ -0,0 +1,206 @@ +/* eslint-disable no-underscore-dangle */ +import * as React from 'react'; +import { + DataGridPremium, + GridRowId, + GridValidRowModel, + DataGridPremiumProps, + useGridApiRef, + GridActionsCellItem, + GridColDef, +} from '@mui/x-data-grid-premium'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import Button from '@mui/material/Button'; +import DeleteIcon from '@mui/icons-material/Delete'; +import RestoreIcon from '@mui/icons-material/Restore'; +import LoadingButton from '@mui/lab/LoadingButton'; +import SaveIcon from '@mui/icons-material/Save'; +import { darken } from '@mui/material/styles'; + +export default function BulkEditingNoSnap() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 7, + editable: true, + visibleFields: [ + 'id', + 'commodity', + 'traderName', + 'traderEmail', + 'quantity', + 'filledQuantity', + ], + }); + + const apiRef = useGridApiRef(); + + const [hasUnsavedRows, setHasUnsavedRows] = React.useState(false); + const unsavedChangesRef = React.useRef<{ + unsavedRows: Record; + rowsBeforeChange: Record; + }>({ + unsavedRows: {}, + rowsBeforeChange: {}, + }); + const [isSaving, setIsSaving] = React.useState(false); + + const columns = React.useMemo(() => { + return [ + { + field: 'actions', + type: 'actions', + getActions: ({ id, row }) => { + return [ + } + label="Discard changes" + disabled={unsavedChangesRef.current.unsavedRows[id] === undefined} + onClick={() => { + apiRef.current.updateRows([ + unsavedChangesRef.current.rowsBeforeChange[id], + ]); + delete unsavedChangesRef.current.rowsBeforeChange[id]; + delete unsavedChangesRef.current.unsavedRows[id]; + setHasUnsavedRows( + Object.keys(unsavedChangesRef.current.unsavedRows).length > 0, + ); + }} + />, + } + label="Delete" + onClick={() => { + unsavedChangesRef.current.unsavedRows[id] = { + ...row, + _action: 'delete', + }; + if (!unsavedChangesRef.current.rowsBeforeChange[id]) { + unsavedChangesRef.current.rowsBeforeChange[id] = row; + } + setHasUnsavedRows(true); + apiRef.current.updateRows([row]); // to trigger row render + }} + />, + ]; + }, + }, + ...data.columns, + ]; + }, [data.columns, unsavedChangesRef, apiRef]); + + const processRowUpdate: NonNullable = ( + newRow, + oldRow, + ) => { + const rowId = newRow.id; + + unsavedChangesRef.current.unsavedRows[rowId] = newRow; + if (!unsavedChangesRef.current.rowsBeforeChange[rowId]) { + unsavedChangesRef.current.rowsBeforeChange[rowId] = oldRow; + } + setHasUnsavedRows(true); + return newRow; + }; + + const discardChanges = () => { + setHasUnsavedRows(false); + apiRef.current.updateRows( + Object.values(unsavedChangesRef.current.rowsBeforeChange), + ); + unsavedChangesRef.current = { + unsavedRows: {}, + rowsBeforeChange: {}, + }; + }; + + const saveChanges = async () => { + try { + // Persist updates in the database + setIsSaving(true); + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + + setIsSaving(false); + const rowsToDelete = Object.values( + unsavedChangesRef.current.unsavedRows, + ).filter((row) => row._action === 'delete'); + if (rowsToDelete.length > 0) { + apiRef.current.updateRows(rowsToDelete); + } + + setHasUnsavedRows(false); + unsavedChangesRef.current = { + unsavedRows: {}, + rowsBeforeChange: {}, + }; + } catch (error) { + setIsSaving(false); + } + }; + + return ( +
+
+ } + loadingPosition="start" + > + Save + + +
+
+ { + if (theme.palette.mode === 'light') { + return 'rgba(255, 170, 170, 0.3)'; + } + return darken('rgba(255, 170, 170, 1)', 0.7); + }, + }, + '& .MuiDataGrid-row.row--edited': { + backgroundColor: (theme) => { + if (theme.palette.mode === 'light') { + return 'rgba(255, 254, 176, 0.3)'; + } + return darken('rgba(255, 254, 176, 1)', 0.6); + }, + }, + }} + loading={isSaving} + getRowClassName={({ id }) => { + const unsavedRow = unsavedChangesRef.current.unsavedRows[id]; + if (unsavedRow) { + if (unsavedRow._action === 'delete') { + return 'row--removed'; + } + return 'row--edited'; + } + return ''; + }} + /> +
+
+ ); +} diff --git a/docs/data/data-grid/recipes-editing/recipes-editing.md b/docs/data/data-grid/recipes-editing/recipes-editing.md index 30e93b5b796cd..8b053fe23e7ce 100644 --- a/docs/data/data-grid/recipes-editing/recipes-editing.md +++ b/docs/data/data-grid/recipes-editing/recipes-editing.md @@ -106,17 +106,13 @@ The following demo implements this behavior. {{"demo": "SingleClickEditing.js", "bg": "inline", "defaultCodeOpen": false}} -## Usage with `@mui/x-date-pickers` +## Bulk editing -By default, the data grid uses native browser inputs for editing `date` and `dateTime` columns. +The data grid [Editing](/x/react-data-grid/editing/) API exposes [the `processRowUpdate` callback](/x/react-data-grid/editing/#the-processrowupdate-callback) which is commonly used to persist edits on per-row basis. +You can utilize this callback to batch edits locally and then choose to either persist or discard them in bulk. -While [MUI X Date / Time Pickers](/x/react-date-pickers/getting-started/) are not supported by the data grid out of the box yet, it is easy to integrate them by creating [custom edit components](/x/react-data-grid/editing/#create-your-own-edit-component) and [custom filter operators](/x/react-data-grid/filtering/customization/#create-a-custom-operator). +The demo below stores edited and deleted rows in the `unsavedChangesRef`. +These changes are saved or discarded when the user clicks the **Save** or **Discard** buttons respectively. +Row updates from [Clipboard paste](/x/react-data-grid/clipboard/#clipboard-paste) are also batched, as [Clipboard paste uses Editing API for persistence](/x/react-data-grid/clipboard/#persisting-pasted-data). -The example below uses `@mui/x-date-pickers` for both `date` and `dateTime` column types: - -{{"demo": "EditingWithDatePickers.js", "bg": "inline", "defaultCodeOpen": false }} - -:::warning -You can change date format by importing different locale (`en-US` locale is used in the example above). -See [Localization](/x/react-date-pickers/localization/) for more information. -::: +{{"demo": "BulkEditingNoSnap.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/docs/data/data-grid/row-updates/UpdateRowsProp.js b/docs/data/data-grid/row-updates/UpdateRowsProp.js index 2b656b3b6a32e..5e467ce6a8029 100644 --- a/docs/data/data-grid/row-updates/UpdateRowsProp.js +++ b/docs/data/data-grid/row-updates/UpdateRowsProp.js @@ -26,6 +26,9 @@ export default function UpdateRowsProp() { ]); const handleUpdateRow = () => { + if (rows.length === 0) { + return; + } setRows((prevRows) => { const rowToUpdateIndex = randomInt(0, rows.length - 1); @@ -40,6 +43,9 @@ export default function UpdateRowsProp() { }; const handleDeleteRow = () => { + if (rows.length === 0) { + return; + } setRows((prevRows) => { const rowToDeleteIndex = randomInt(0, prevRows.length - 1); return [ diff --git a/docs/data/data-grid/row-updates/UpdateRowsProp.tsx b/docs/data/data-grid/row-updates/UpdateRowsProp.tsx index 2b656b3b6a32e..5e467ce6a8029 100644 --- a/docs/data/data-grid/row-updates/UpdateRowsProp.tsx +++ b/docs/data/data-grid/row-updates/UpdateRowsProp.tsx @@ -26,6 +26,9 @@ export default function UpdateRowsProp() { ]); const handleUpdateRow = () => { + if (rows.length === 0) { + return; + } setRows((prevRows) => { const rowToUpdateIndex = randomInt(0, rows.length - 1); @@ -40,6 +43,9 @@ export default function UpdateRowsProp() { }; const handleDeleteRow = () => { + if (rows.length === 0) { + return; + } setRows((prevRows) => { const rowToDeleteIndex = randomInt(0, prevRows.length - 1); return [ diff --git a/docs/data/data-grid/server-side-data/aggregation.md b/docs/data/data-grid/server-side-data/aggregation.md new file mode 100644 index 0000000000000..3836a25e9ed5a --- /dev/null +++ b/docs/data/data-grid/server-side-data/aggregation.md @@ -0,0 +1,15 @@ +--- +title: React Server-side row grouping +--- + +# Data Grid - Server-side aggregation 🚧 + +

Aggregation with server side data source.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #10860](https://github.com/mui/mui-x/issues/10860) if you want to see it land faster. + +Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with your current solution. +::: diff --git a/docs/data/data-grid/server-side-data/index.md b/docs/data/data-grid/server-side-data/index.md new file mode 100644 index 0000000000000..f235a05581833 --- /dev/null +++ b/docs/data/data-grid/server-side-data/index.md @@ -0,0 +1,291 @@ +--- +title: React Data Grid - Server-side data +--- + +# Data Grid - Server-side data + +

The data grid server-side data

+ +## Overview + +Managing server-side data efficiently in a React application can become complex as the dataset grows. + +Without a dedicated module that abstracts its complexities, developers often face challenges related to manual data fetching, pagination, sorting, and filtering, and it often gets trickier to tackle performance issues, which can lead to a poor user experience. + +Have a look at an example: + +### Example scenario + +Imagine having a data grid that displays a list of users. The data grid has pagination enabled and the user can sort the data by clicking on the column headers and also apply filters. + +The data grid is configured to fetch data from the server whenever the user changes the page or updates filtering or sorting. + +```tsx +const [rows, setRows] = React.useState([]); +const [paginationModel, setPaginationModel] = React.useState({ + page: 0, + pageSize: 10, +}); +const [filterModel, setFilterModel] = React.useState({ + items: [], +}); +const [sortModel, setSortModel] = React.useState([]); + +React.useEffect(() => { + const fetcher = async () => { + // fetch data from server + const data = await fetch('https://my-api.com/data', { + method: 'GET', + body: JSON.stringify({ + page: paginationModel.page, + pageSize: paginationModel.pageSize, + sortModel, + filterModel, + }), + }); + setRows(data.rows); + }; + fetcher(); +}, [paginationModel, sortModel, filterModel]); + +; +``` + +This example only scratches the surface with a lot of problems still unsolved like: + +- Performance optimization +- Caching data/deduping requests +- More complex use-cases on the server like grouping, tree data, etc. +- Server side row editing +- Lazy loading of data +- Handling updates to the data like row editing, row deletion, etc. +- Refetching data on-demand + +Trying to solve these problems one after the other can make the code complex and hard to maintain. + +## Data source + +A very common pattern to solve these problems is to use a centralized data source. A data source is an abstraction layer that sits between the data grid and the server. It provides a simple interface to the data grid to fetch data and update it. It handles a lot of the complexities related to server-side data fetching. Let's delve a bit deeper into how it will look like. + +:::warning + +This feature is still under development and the information shared on this page is subject to change. Feel free to subscribe or comment on the official GitHub [issue](https://github.com/mui/mui-x/issues/8179). + +::: + +### Overview + +The Data Grid already supports manual server-side data fetching for features like sorting, filtering, etc. In order to make it more powerful and simple to use, the grid will support a data source interface that you can implement with your existing data fetching logic. + +The datasource will work with all the major data grid features which require server-side data fetching such as sorting, filtering, pagination, grouping, etc. + +### Usage + +The data grid server-side data source has an initial set of required methods that you need to implement. The data grid will call these methods internally when the data is required for a specific page. + +```tsx +interface DataSource { + /** + Fetcher Functions: + - `getRows` is required + - `updateRow` is optional + + `getRows` will be used by the grid to fetch data for the current page or children for the current parent group + It may return a `rowCount` to update the total count of rows in the grid + */ + getRows(params: GetRowsParams): Promise; + updateRow?(updatedRow: GridRowModel): Promise; +} +``` + +Here's how the code will look like for the above example when implemented with data source: + +```tsx +const customDataSource: DataSource = { + getRows: async (params: GetRowsParams): GetRowsResponse => { + // fetch data from server + const response = await fetch('https://my-api.com/data', { + method: 'GET', + body: JSON.stringify(params), + }); + const data = await response.json(); + // return the data and the total number of rows + return { + rows: data.rows, + rowCount: data.totalCount, + }; + }, +} + + +``` + +Not only the code has been reduced significantly, it has removed the hassle of managing controlled states and data fetching logic too. + +On top of that, the data source will also handle a lot of other aspects like caching and deduping of requests. + +#### Loading data + +The method `dataSource.getRows` will be called with the `GetRowsParams` object whenever some data from the server is needed. This object contains all the information that you need to fetch the data from the server. + +Since previously, the data grid did not support internal data fetching, the `rows` prop was the way to pass the data to the grid. However, with server-side data, the `rows` prop is no longer needed. Instead, the data grid will call the `getRows` method whenever it needs to fetch data. + +Here's the `GetRowsParams` object for reference: + +```tsx +interface GetRowsParams { + sortModel: GridSortModel; + filterModel: GridFilterModel; + /** + * Alternate to `start` and `end`, maps to `GridPaginationModel` interface + */ + paginationModel: GridPaginationModel; + /** + * First row index to fetch (number) or cursor information (number | string) + */ + start: number | string; // first row index to fetch or cursor information + /** + * Last row index to fetch + */ + end: number; // last row index to fetch + /** + * Array of keys returned by `getGroupKey` of all the parent rows until the row for which the data is requested + * `getGroupKey` prop must be implemented to use this + * Useful for `treeData` and `rowGrouping` only + */ + groupKeys: string[]; + /** + * List of grouped columns (only applicable with `rowGrouping`) + */ + groupFields: GridColDef['field'][]; // list of grouped columns (`rowGrouping`) +} +``` + +And here's the `GetRowsResponse` object for reference: + +```tsx +interface GetRowsResponse { + /** + * Subset of the rows as per the passed `GetRowsParams` + */ + rows: GridRowModel[]; + /** + * To reflect updates in total `rowCount` (optional) + * Useful when the `rowCount` is inaccurate (e.g. when filtering) or not available upfront + */ + rowCount?: number; + /** + * Additional `pageInfo` to help the grid determine if there are more rows to fetch (corner-cases) + * `hasNextPage`: When row count is unknown/inaccurate, if `truncated` is set or rowCount is not known, data will keep loading until `hasNextPage` is `false` + * `truncated`: To reflect `rowCount` is inaccurate (will trigger `x-y of many` in pagination after the count of rows fetched is greater than provided `rowCount`) + * It could be useful with: + * 1. Cursor based pagination: + * When rowCount is not known, grid will check for `hasNextPage` to determine + * if there are more rows to fetch. + * 2. Inaccurate `rowCount`: + * `truncated: true` will let the grid know that `rowCount` is estimated/truncated. + * Thus `hasNextPage` will come into play to check more rows are available to fetch after the number becomes >= provided `rowCount` + */ + pageInfo?: { + hasNextPage?: boolean; + truncated?: number; + }; +} +``` + +#### Updating data + +If provided, the method `dataSource.updateRow` will be called with the `GridRowModel` object whenever the user edits a row. This method is optional and you can skip it if you don't need to update the data on the server. It will work in a similar way as the `processRowUpdate` prop. + +#### Data Grid props + +These data grid props will work with the server-side data source: + +- `dataSource: DataSource`: the data source object that you need to implement +- `rows`: will be ignored, could be skipped when `dataSource` is provided +- `rowCount`: will be used to identify the total number of rows in the grid, if not provided, the grid will check for the _GetRowsResponse.rowCount_ value, unless the feature being used is infinite loading where no `rowCount` is available at all. + +Props related to grouped data (`treeData` and `rowGrouping`): + +- `getGroupKey(row: GridRowModel): string` + + will be used by the grid to group rows by their parent group + This effectively replaces `getTreeDataPath`. + Consider this structure: + + ```js + - (1) Sarah // groupKey 'Sarah' + - (2) Thomas // groupKey 'Thomas' + ``` + + When (2) is expanded, the `getRows` function will be called with group keys `['Sarah', 'Thomas']`. + +- `hasChildren?(row: GridRowModel): boolean` + + Will be used by the grid to determine if a row has children on server + +- `getChildrenCount?: (row: GridRowModel) => number` + + Will be used by the grid to determine the number of children of a row on server + +#### Existing server-side features + +The server-side data source will change a bit the way existing server-side features like `filtering`, `sorting`, and `pagination` work. + +**Without data source**: +When there's no data source, the features `filtering`, `sorting`, `pagination` will work on `client` by default. In order for them to work with server-side data, you need to set them to `server` explicitly and listen to the [`onFilterModelChange`](https://mui.com/x/react-data-grid/filtering/server-side/), [`onSortModelChange`](https://mui.com/x/react-data-grid/sorting/#server-side-sorting), [`onPaginationModelChange`](https://mui.com/x/react-data-grid/pagination/#server-side-pagination) events to fetch the data from the server based on the updated variables. + +```tsx + { + // fetch data from server + }} + onSortModelChange={(newSortModel) => { + // fetch data from server + }} + onFilterModelChange={(newFilterModel) => { + // fetch data from server + }} +/> +``` + +**With data source**: +However, with a valid data source passed the features `filtering`, `sorting`, `pagination` will automatically be set to `server`. + +You just need to implement the `getRows` method and the data grid will call the `getRows` method with the proper params whenever it needs data. + +```tsx + +``` + +#### Caching + +The data grid will cache the data it receives from the server. This means that if the user navigates to a page that has already been fetched, the grid will not call the `getRows` function again. This is to avoid unnecessary calls to the server. + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) +- [DataGridPremium](/x/api/data-grid/data-grid-premium/) diff --git a/docs/data/data-grid/server-side-data/infinite-loading.md b/docs/data/data-grid/server-side-data/infinite-loading.md new file mode 100644 index 0000000000000..6c2533f51fad1 --- /dev/null +++ b/docs/data/data-grid/server-side-data/infinite-loading.md @@ -0,0 +1,15 @@ +--- +title: React Server-side infinite loading +--- + +# Data Grid - Server-side infinite loading 🚧 + +

Row infinite loading with server side data source.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #10858](https://github.com/mui/mui-x/issues/10858) if you want to see it land faster. + +Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with the [current solution](https://mui.com/x/react-data-grid/row-updates/#infinite-loading). +::: diff --git a/docs/data/data-grid/server-side-data/lazy-loading.md b/docs/data/data-grid/server-side-data/lazy-loading.md new file mode 100644 index 0000000000000..a8430737d67d9 --- /dev/null +++ b/docs/data/data-grid/server-side-data/lazy-loading.md @@ -0,0 +1,15 @@ +--- +title: React Server-side lazy loading +--- + +# Data Grid - Server-side lazy loading 🚧 + +

Row lazy-loading with server side data source.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #10857](https://github.com/mui/mui-x/issues/10857) if you want to see it land faster. + +Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with the [current solution](https://mui.com/x/react-data-grid/row-updates/#lazy-loading). +::: diff --git a/docs/data/data-grid/server-side-data/row-grouping.md b/docs/data/data-grid/server-side-data/row-grouping.md new file mode 100644 index 0000000000000..72fd21756bc29 --- /dev/null +++ b/docs/data/data-grid/server-side-data/row-grouping.md @@ -0,0 +1,15 @@ +--- +title: React Server-side row grouping +--- + +# Data Grid - Server-side row grouping 🚧 + +

Lazy-loaded row grouping with server side data source.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #10859](https://github.com/mui/mui-x/issues/10859) if you want to see it land faster. + +Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with your current solution. +::: diff --git a/docs/data/data-grid/server-side-data/tree-data.md b/docs/data/data-grid/server-side-data/tree-data.md new file mode 100644 index 0000000000000..ed69e9ac25d30 --- /dev/null +++ b/docs/data/data-grid/server-side-data/tree-data.md @@ -0,0 +1,15 @@ +--- +title: React Server-side tree data +--- + +# Data Grid - Server-side tree data 🚧 + +

Tree data lazy-loading with server side data source.

+ +:::warning +This feature isn't implemented yet. It's coming. + +👍 Upvote [issue #3377](https://github.com/mui/mui-x/issues/3377) if you want to see it land faster. + +Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with the [currently proposed workaround](https://mui.com/x/react-data-grid/tree-data/#children-lazy-loading). +::: diff --git a/docs/data/data-grid/state/SaveAndRestoreStateInitialState.js b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.js new file mode 100644 index 0000000000000..2418f501a73c5 --- /dev/null +++ b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.js @@ -0,0 +1,55 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import CircularProgress from '@mui/material/CircularProgress'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGridPremium, useGridApiRef } from '@mui/x-data-grid-premium'; + +export default function SaveAndRestoreStateInitialState() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 10, + }); + + const [initialState, setInitialState] = React.useState(); + + const saveSnapshot = React.useCallback(() => { + if (apiRef?.current?.exportState && localStorage) { + const currentState = apiRef.current.exportState(); + localStorage.setItem('dataGridState', JSON.stringify(currentState)); + } + }, [apiRef]); + + React.useLayoutEffect(() => { + const stateFromLocalStorage = localStorage?.getItem('dataGridState'); + setInitialState(stateFromLocalStorage ? JSON.parse(stateFromLocalStorage) : {}); + + // handle refresh and navigating away/refreshing + window.addEventListener('beforeunload', saveSnapshot); + + return () => { + // in case of an SPA remove the event-listener + window.removeEventListener('beforeunload', saveSnapshot); + saveSnapshot(); + }; + }, [saveSnapshot]); + + if (!initialState) { + return ; + } + + return ( + + + + ); +} diff --git a/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx new file mode 100644 index 0000000000000..7be0ad68d7393 --- /dev/null +++ b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import CircularProgress from '@mui/material/CircularProgress'; +import { useDemoData } from '@mui/x-data-grid-generator'; +import { DataGridPremium, useGridApiRef } from '@mui/x-data-grid-premium'; +import { GridInitialStatePremium } from '@mui/x-data-grid-premium/models/gridStatePremium'; + +export default function SaveAndRestoreStateInitialState() { + const apiRef = useGridApiRef(); + const { data, loading } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100, + maxColumns: 10, + }); + + const [initialState, setInitialState] = React.useState(); + + const saveSnapshot = React.useCallback(() => { + if (apiRef?.current?.exportState && localStorage) { + const currentState = apiRef.current.exportState(); + localStorage.setItem('dataGridState', JSON.stringify(currentState)); + } + }, [apiRef]); + + React.useLayoutEffect(() => { + const stateFromLocalStorage = localStorage?.getItem('dataGridState'); + setInitialState(stateFromLocalStorage ? JSON.parse(stateFromLocalStorage) : {}); + + // handle refresh and navigating away/refreshing + window.addEventListener('beforeunload', saveSnapshot); + + return () => { + // in case of an SPA remove the event-listener + window.removeEventListener('beforeunload', saveSnapshot); + saveSnapshot(); + }; + }, [saveSnapshot]); + + if (!initialState) { + return ; + } + + return ( + + + + ); +} diff --git a/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx.preview b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx.preview new file mode 100644 index 0000000000000..db90d55cdda06 --- /dev/null +++ b/docs/data/data-grid/state/SaveAndRestoreStateInitialState.tsx.preview @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/state/state.md b/docs/data/data-grid/state/state.md index fc6d84fb87cd1..5102914ad0d8f 100644 --- a/docs/data/data-grid/state/state.md +++ b/docs/data/data-grid/state/state.md @@ -93,6 +93,14 @@ If you restore the page using `initialState` before the data is fetched, the Dat {{"demo": "RestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}} +### 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 `localStorage` or `redux`). +This way the state can be persisted on refresh or navigating to another page. This is done by listening on the `beforeunload` event. +When the component is unmounted, the `useLayoutEffect` cleanup function is being used instead. + +{{"demo": "SaveAndRestoreStateInitialState.js", "bg": "inline", "defaultCodeOpen": false}} + ### Restore the state with apiRef You can pass the state returned by `apiRef.current.exportState()` to the `apiRef.current.restoreState` method. diff --git a/docs/data/data-grid/style-recipes/CellFocusNoOutline.js b/docs/data/data-grid/style-recipes/CellFocusNoOutline.js new file mode 100644 index 0000000000000..1936ceec9db3b --- /dev/null +++ b/docs/data/data-grid/style-recipes/CellFocusNoOutline.js @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DataGridPro, gridClasses } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CellFocusNoOutline() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx b/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx new file mode 100644 index 0000000000000..1936ceec9db3b --- /dev/null +++ b/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { DataGridPro, gridClasses } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +const VISIBLE_FIELDS = ['name', 'rating', 'country', 'dateCreated', 'isAdmin']; + +export default function CellFocusNoOutline() { + const { data } = useDemoData({ + dataSet: 'Employee', + visibleFields: VISIBLE_FIELDS, + rowLength: 100, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx.preview b/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx.preview new file mode 100644 index 0000000000000..3b8c85b269785 --- /dev/null +++ b/docs/data/data-grid/style-recipes/CellFocusNoOutline.tsx.preview @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.js b/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.js new file mode 100644 index 0000000000000..4ed59c8b462b4 --- /dev/null +++ b/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGridPremium, gridClasses } from '@mui/x-data-grid-premium'; + +// eliminate rounding errors in aggregation row +const valueFormatter = ({ value }) => `${Math.floor(value * 1000) / 1000} °C`; + +const columns = [ + { field: 'city' }, + { + field: 'oct', + type: 'number', + valueFormatter, + }, + { + field: 'nov', + type: 'number', + valueFormatter, + }, + { + field: 'dec', + type: 'number', + valueFormatter, + }, +]; + +const rows = [ + { id: 1, city: 'Amsterdam', oct: 7.1, nov: 4, dec: 10.2 }, + { id: 2, city: 'Barcelona', oct: 14.9, nov: 12.3, dec: 18.2 }, + { id: 3, city: 'Paris', oct: 8.1, nov: 5.4, dec: 12.3 }, + { id: 4, city: 'São Paulo', oct: 20.2, nov: 21.1, dec: 19.2 }, +]; + +export default function StylingAllCellsButAggregation() { + return ( + + { + if ( + params.field === 'city' || + params.value == null || + params.id.toString().startsWith('auto-generated') + ) { + return ''; + } + return params.value >= 15 ? 'hot' : 'cold'; + }} + initialState={{ + aggregation: { + model: { + oct: 'avg', + nov: 'avg', + dec: 'avg', + }, + }, + }} + /> + + ); +} diff --git a/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.tsx b/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.tsx new file mode 100644 index 0000000000000..5d0d5c0200c73 --- /dev/null +++ b/docs/data/data-grid/style-recipes/StylingAllCellsButAggregation.tsx @@ -0,0 +1,81 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { + GridColDef, + DataGridPremium, + GridCellParams, + gridClasses, +} from '@mui/x-data-grid-premium'; + +// eliminate rounding errors in aggregation row +const valueFormatter: GridColDef['valueFormatter'] = ({ value }) => + `${Math.floor(value * 1000) / 1000} °C`; + +const columns: GridColDef[] = [ + { field: 'city' }, + { + field: 'oct', + type: 'number', + valueFormatter, + }, + { + field: 'nov', + type: 'number', + valueFormatter, + }, + { + field: 'dec', + type: 'number', + valueFormatter, + }, +]; + +const rows = [ + { id: 1, city: 'Amsterdam', oct: 7.1, nov: 4, dec: 10.2 }, + { id: 2, city: 'Barcelona', oct: 14.9, nov: 12.3, dec: 18.2 }, + { id: 3, city: 'Paris', oct: 8.1, nov: 5.4, dec: 12.3 }, + { id: 4, city: 'São Paulo', oct: 20.2, nov: 21.1, dec: 19.2 }, +]; + +export default function StylingAllCellsButAggregation() { + return ( + + ) => { + if ( + params.field === 'city' || + params.value == null || + params.id.toString().startsWith('auto-generated') + ) { + return ''; + } + return params.value >= 15 ? 'hot' : 'cold'; + }} + initialState={{ + aggregation: { + model: { + oct: 'avg', + nov: 'avg', + dec: 'avg', + }, + }, + }} + /> + + ); +} diff --git a/docs/data/data-grid/style-recipes/style-recipes.md b/docs/data/data-grid/style-recipes/style-recipes.md new file mode 100644 index 0000000000000..4aa1485c560ca --- /dev/null +++ b/docs/data/data-grid/style-recipes/style-recipes.md @@ -0,0 +1,20 @@ +# Data Grid - Styling recipes + +

Advanced grid styling recipes.

+ +## Remove cell focus outline + +The data grid cells are actionable elements and visually indicate the `focus` state by default. +You can remove the focus outline by overriding the `:focus` and `:focus-within` styles for the cells and header cells. + +:::warning +Removing the visible `focus` state hurts the accessibility of the grid. +::: + +{{"demo": "CellFocusNoOutline.js", "bg": "inline", "defaultCodeOpen": false}} + +## Styling Cells without impacting aggregation cells [](/x/introduction/licensing/#premium-plan 'Premium plan') + +Aggregation cells do not receive a special class, so styling cells without impacting them needs a small workaround in the `getClassName` function. + +{{"demo": "StylingAllCellsButAggregation.js", "bg": "inline", "defaultCodeOpen": false}} diff --git a/docs/data/data-grid/style/StylingAllCells.js b/docs/data/data-grid/style/StylingAllCells.js index e8cd535272333..ebd925f7e3b8e 100644 --- a/docs/data/data-grid/style/StylingAllCells.js +++ b/docs/data/data-grid/style/StylingAllCells.js @@ -1,6 +1,6 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { DataGrid } from '@mui/x-data-grid'; +import { DataGrid, gridClasses } from '@mui/x-data-grid'; const columns = [ { field: 'city' }, @@ -22,11 +22,11 @@ export default function StylingAllCells() { sx={{ height: 300, width: '100%', - '& .cold': { + [`.${gridClasses.cell}.cold`]: { backgroundColor: '#b9d5ff91', color: '#1a3e72', }, - '& .hot': { + [`.${gridClasses.cell}.hot`]: { backgroundColor: '#ff943975', color: '#1a3e72', }, diff --git a/docs/data/data-grid/style/StylingAllCells.tsx b/docs/data/data-grid/style/StylingAllCells.tsx index c6d841f698604..ef089f4c9a6b6 100644 --- a/docs/data/data-grid/style/StylingAllCells.tsx +++ b/docs/data/data-grid/style/StylingAllCells.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import Box from '@mui/material/Box'; -import { GridColDef, DataGrid, GridCellParams } from '@mui/x-data-grid'; +import { GridColDef, DataGrid, GridCellParams, gridClasses } from '@mui/x-data-grid'; const columns: GridColDef[] = [ { field: 'city' }, @@ -22,11 +22,11 @@ export default function StylingAllCells() { sx={{ height: 300, width: '100%', - '& .cold': { + [`.${gridClasses.cell}.cold`]: { backgroundColor: '#b9d5ff91', color: '#1a3e72', }, - '& .hot': { + [`.${gridClasses.cell}.hot`]: { backgroundColor: '#ff943975', color: '#1a3e72', }, diff --git a/docs/data/date-pickers-component-api-pages.ts b/docs/data/date-pickers-component-api-pages.ts index 91448100bee33..16ed21dd10637 100644 --- a/docs/data/date-pickers-component-api-pages.ts +++ b/docs/data/date-pickers-component-api-pages.ts @@ -1,4 +1,4 @@ -import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import type { MuiPage } from 'docs/src/MuiPage'; export default [ { pathname: '/x/api/date-pickers/date-calendar', title: 'DateCalendar' }, diff --git a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.js b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.js index ef506eab13e78..1c157a8db2f78 100644 --- a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.js +++ b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.js @@ -12,7 +12,7 @@ export default function CustomDayOfWeekFormat() { setValue(newValue)} - dayOfWeekFormatter={(day) => `${day}.`} + dayOfWeekFormatter={(_day, weekday) => `${weekday.format('dd')}.`} /> ); diff --git a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx index 495cc7d811d9e..98e419ab94a2a 100644 --- a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx +++ b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx @@ -12,7 +12,7 @@ export default function CustomDayOfWeekFormat() { setValue(newValue)} - dayOfWeekFormatter={(day) => `${day}.`} + dayOfWeekFormatter={(_day, weekday) => `${weekday.format('dd')}.`} /> ); diff --git a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx.preview b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx.preview index 27d3be5d7b855..1feef34671ae2 100644 --- a/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx.preview +++ b/docs/data/date-pickers/adapters-locale/CustomDayOfWeekFormat.tsx.preview @@ -1,5 +1,5 @@ setValue(newValue)} - dayOfWeekFormatter={(day) => `${day}.`} + dayOfWeekFormatter={(_day, weekday) => `${weekday.format('dd')}.`} /> \ No newline at end of file diff --git a/docs/data/date-pickers/adapters-locale/adapters-locale.md b/docs/data/date-pickers/adapters-locale/adapters-locale.md index ceb45de7156ab..2395bcef41a36 100644 --- a/docs/data/date-pickers/adapters-locale/adapters-locale.md +++ b/docs/data/date-pickers/adapters-locale/adapters-locale.md @@ -230,8 +230,12 @@ This prop is available on all pickers. ### Custom day of week format Use `dayOfWeekFormatter` to customize day names in the calendar header. -This prop takes the short name of the day provided by the date library as an input, and returns it's formatted version. -The default formatter only keeps the first letter and capitalises it. +This prop takes two parameters, `day` (a string with the name of the day) and `date` ( the day in the format of your date library) and returns the formatted string to display. +The default formatter only keeps the first letter of the name and capitalises it. + +:::warning +The first parameter `day` will be removed in v7 in favor of the second parameter `date` for more flexibility. +::: :::info This prop is available on all components that render a day calendar, including the Date Calendar as well as all Date Pickers, Date Time Pickers, and Date Range Pickers. diff --git a/docs/data/date-pickers/base-concepts/ComponentFamilies.js b/docs/data/date-pickers/base-concepts/ComponentFamilies.js index ac81f4a141f89..de11cabb3c2e3 100644 --- a/docs/data/date-pickers/base-concepts/ComponentFamilies.js +++ b/docs/data/date-pickers/base-concepts/ComponentFamilies.js @@ -1,5 +1,6 @@ import * as React from 'react'; import dayjs from 'dayjs'; +import { styled } from '@mui/material/styles'; import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -12,12 +13,24 @@ import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInput import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; +const ProSpan = styled('span')({ + display: 'inline-block', + height: '1em', + width: '1em', + verticalAlign: 'middle', + marginLeft: '0.3em', + marginBottom: '0.08em', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundImage: 'url(https://mui.com/static/x/pro.svg)', +}); + function ProLabel({ children }) { return ( - - + + {children} diff --git a/docs/data/date-pickers/base-concepts/ComponentFamilies.tsx b/docs/data/date-pickers/base-concepts/ComponentFamilies.tsx index 0d381520068e0..a5d4ce28c561c 100644 --- a/docs/data/date-pickers/base-concepts/ComponentFamilies.tsx +++ b/docs/data/date-pickers/base-concepts/ComponentFamilies.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import dayjs from 'dayjs'; +import { styled } from '@mui/material/styles'; import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -12,12 +13,24 @@ import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInput import Stack from '@mui/material/Stack'; import Tooltip from '@mui/material/Tooltip'; +const ProSpan = styled('span')({ + display: 'inline-block', + height: '1em', + width: '1em', + verticalAlign: 'middle', + marginLeft: '0.3em', + marginBottom: '0.08em', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundImage: 'url(https://mui.com/static/x/pro.svg)', +}); + function ProLabel({ children }: { children: React.ReactNode }) { return ( - - + + {children} diff --git a/docs/data/date-pickers/base-concepts/CustomSlots.js b/docs/data/date-pickers/base-concepts/CustomSlots.js new file mode 100644 index 0000000000000..5a6d1197147ad --- /dev/null +++ b/docs/data/date-pickers/base-concepts/CustomSlots.js @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { PickersDay } from '@mui/x-date-pickers/PickersDay'; +import EditCalendarRoundedIcon from '@mui/icons-material/EditCalendarRounded'; +import { styled } from '@mui/material/styles'; +import IconButton from '@mui/material/IconButton'; + +const StyledButton = styled(IconButton)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, +})); +const StyledDay = styled(PickersDay)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + color: + theme.palette.mode === 'light' + ? theme.palette.secondary.dark + : theme.palette.secondary.light, +})); + +export default function CustomSlots() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/base-concepts/CustomSlots.tsx b/docs/data/date-pickers/base-concepts/CustomSlots.tsx new file mode 100644 index 0000000000000..5a6d1197147ad --- /dev/null +++ b/docs/data/date-pickers/base-concepts/CustomSlots.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { PickersDay } from '@mui/x-date-pickers/PickersDay'; +import EditCalendarRoundedIcon from '@mui/icons-material/EditCalendarRounded'; +import { styled } from '@mui/material/styles'; +import IconButton from '@mui/material/IconButton'; + +const StyledButton = styled(IconButton)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, +})); +const StyledDay = styled(PickersDay)(({ theme }) => ({ + borderRadius: theme.shape.borderRadius, + color: + theme.palette.mode === 'light' + ? theme.palette.secondary.dark + : theme.palette.secondary.light, +})); + +export default function CustomSlots() { + return ( + + + + + + ); +} diff --git a/docs/data/date-pickers/base-concepts/base-concepts.md b/docs/data/date-pickers/base-concepts/base-concepts.md index 65716d7e1c040..6bbb6df20f2d8 100644 --- a/docs/data/date-pickers/base-concepts/base-concepts.md +++ b/docs/data/date-pickers/base-concepts/base-concepts.md @@ -174,3 +174,20 @@ const cleanText = (string) => // Example of a test using the helper expect(cleanText(input.value)).to.equal('04-17-2022'); ``` + +## Overriding slots and slot props + +Date and Time Pickers are complex components built using many subcomponents known as **slots**. +Slots are commonly filled by React components that you can override using the `slots` prop. +You can also pass additional props to the available slots using the `slotProps` prop. +Learn more about the mental model of slots in the Base UI documentation: [Overriding component structure](/base-ui/guides/overriding-component-structure/). + +You can find the list of available slots for each component in its respective [API reference](/x/api/date-pickers/date-picker/#slots) doc. + +Some parts of the Pickers' UI are built on several nested slots. For instance, the adornment of the `TextField` on `DatePicker` contains three slots (`inputAdornment`, `openPickerButton`, and `openPickerIcon`) that you can use depending on what you are trying to customize. + +{{"demo": "CustomSlots.js"}} + +:::info +Learn more about overriding slots in the doc page about [Custom slots and subcomponents](/x/react-date-pickers/custom-components/). +::: diff --git a/docs/data/date-pickers/custom-components/PopperComponent.js b/docs/data/date-pickers/custom-components/PopperComponent.js deleted file mode 100644 index 70f7953d408c9..0000000000000 --- a/docs/data/date-pickers/custom-components/PopperComponent.js +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react'; -import dayjs from 'dayjs'; -import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { DatePicker } from '@mui/x-date-pickers/DatePicker'; - -export default function PopperComponent() { - return ( - - { - state.styles.popper.height = '320px'; - if (state.placement.includes('top-start')) { - state.styles.popper = { - ...state.styles.popper, - display: 'flex', - alignItems: 'flex-end', - }; - } - if (state.placement.includes('bottom')) { - state.styles.popper = { - ...state.styles.popper, - display: 'block', - }; - } - }, - }, - ], - }, - }} - /> - - ); -} diff --git a/docs/data/date-pickers/custom-components/PopperComponent.tsx b/docs/data/date-pickers/custom-components/PopperComponent.tsx deleted file mode 100644 index a43dcdae9a9a5..0000000000000 --- a/docs/data/date-pickers/custom-components/PopperComponent.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from 'react'; -import dayjs from 'dayjs'; -import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; -import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { DatePicker } from '@mui/x-date-pickers/DatePicker'; - -export default function PopperComponent() { - return ( - - }) => { - state.styles.popper.height = '320px'; - if (state.placement.includes('top-start')) { - state.styles.popper = { - ...state.styles.popper, - display: 'flex', - alignItems: 'flex-end', - }; - } - if (state.placement.includes('bottom')) { - state.styles.popper = { - ...state.styles.popper, - display: 'block', - }; - } - }, - }, - ], - }, - }} - /> - - ); -} diff --git a/docs/data/date-pickers/custom-components/custom-components.md b/docs/data/date-pickers/custom-components/custom-components.md index c1f8d245df02c..edf7e96405d6f 100644 --- a/docs/data/date-pickers/custom-components/custom-components.md +++ b/docs/data/date-pickers/custom-components/custom-components.md @@ -1,19 +1,19 @@ --- productId: x-date-pickers -title: Date and Time Pickers - Custom subcomponents -components: DateTimePickerTabs +title: Date and Time Pickers - Custom slots and subcomponents +components: DateTimePickerTabs, PickersActionBar, DatePickerToolbar, TimePickerToolbar, DateTimePickerToolbar, PickersCalendarHeader, PickersShortcuts --- -# Custom subcomponents +# Custom slots and subcomponents -

The date picker lets you customize subcomponents.

+

Learn how to override the default DOM structure of the Date and Time Pickers.

:::info The components that can be customized are listed under `slots` section in Date and Time Pickers [API Reference](/x/api/date-pickers/). For example, available Date Picker slots can be found [here](/x/api/date-pickers/date-picker/#slots). ::: -## Overriding components +## Overriding slot components You can override the internal elements of the component (known as "slots") using the `slots` prop. @@ -236,18 +236,6 @@ You can pass custom components—to replace the icons, for example—as shown be {{"demo": "ArrowSwitcherComponent.js", "defaultCodeOpen": false}} -## Popper - -You can customize the popper that wraps the desktop picker views the same way you would customize the [Material UI Popper](/material-ui/react-popper/). - -:::info -When the picker views have different heights, there might be a layout shift if there is not enough space in the viewport for one of the views **below** the input field. This is particularly noticeable if the selection of allowed years is very limited and there is a significant height difference between the views. You can refer to issues [#5490](https://github.com/mui/mui-x/issues/5490) and [#9288](https://github.com/mui/mui-x/issues/9288) for more examples. - -You can avoid this by customizing the popper height. This will not produce any visual changes, as the popper that wraps the pickers is transparent. -::: - -{{"demo": "PopperComponent.js", "defaultCodeOpen": true}} - ## Shortcuts You can add shortcuts to every pickers. diff --git a/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.js b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.js new file mode 100644 index 0000000000000..7180636a94dd7 --- /dev/null +++ b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.js @@ -0,0 +1,72 @@ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import useForkRef from '@mui/utils/useForkRef'; + +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; + +const DateRangeButtonField = React.forwardRef((props, ref) => { + const { + setOpen, + label, + id, + disabled, + InputProps: { ref: containerRef } = {}, + inputProps: { 'aria-label': ariaLabel } = {}, + } = props; + + const handleRef = useForkRef(ref, containerRef); + + return ( + + ); +}); + +DateRangeButtonField.fieldType = 'single-input'; + +const ButtonDateRangePicker = React.forwardRef((props, ref) => { + const [open, setOpen] = React.useState(false); + + return ( + setOpen(false)} + onOpen={() => setOpen(true)} + /> + ); +}); + +export default function DateRangePickerWithButtonField() { + const [value, setValue] = React.useState([null, null]); + + return ( + + (date ? date.format('MM/DD/YYYY') : 'null')) + .join(' - ') + } + value={value} + onChange={(newValue) => setValue(newValue)} + /> + + ); +} diff --git a/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx new file mode 100644 index 0000000000000..8b597fac8c61a --- /dev/null +++ b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx @@ -0,0 +1,91 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import Button from '@mui/material/Button'; +import useForkRef from '@mui/utils/useForkRef'; +import { DateRange } from '@mui/x-date-pickers-pro'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { + DateRangePicker, + DateRangePickerProps, +} from '@mui/x-date-pickers-pro/DateRangePicker'; +import { SingleInputDateRangeFieldProps } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; + +interface DateRangeButtonFieldProps extends SingleInputDateRangeFieldProps { + setOpen?: React.Dispatch>; +} + +type DateRangeButtonFieldComponent = (( + props: DateRangeButtonFieldProps & React.RefAttributes, +) => React.JSX.Element) & { fieldType?: string }; + +const DateRangeButtonField = React.forwardRef( + (props: DateRangeButtonFieldProps, ref: React.Ref) => { + const { + setOpen, + label, + id, + disabled, + InputProps: { ref: containerRef } = {}, + inputProps: { 'aria-label': ariaLabel } = {}, + } = props; + + const handleRef = useForkRef(ref, containerRef); + + return ( + + ); + }, +) as DateRangeButtonFieldComponent; + +DateRangeButtonField.fieldType = 'single-input'; + +const ButtonDateRangePicker = React.forwardRef( + ( + props: Omit, 'open' | 'onOpen' | 'onClose'>, + ref: React.Ref, + ) => { + const [open, setOpen] = React.useState(false); + + return ( + setOpen(false)} + onOpen={() => setOpen(true)} + /> + ); + }, +); + +export default function DateRangePickerWithButtonField() { + const [value, setValue] = React.useState>([null, null]); + + return ( + + (date ? date.format('MM/DD/YYYY') : 'null')) + .join(' - ') + } + value={value} + onChange={(newValue) => setValue(newValue)} + /> + + ); +} diff --git a/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx.preview b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx.preview new file mode 100644 index 0000000000000..3a8d6daacd70e --- /dev/null +++ b/docs/data/date-pickers/custom-field/DateRangePickerWithButtonField.tsx.preview @@ -0,0 +1,11 @@ + (date ? date.format('MM/DD/YYYY') : 'null')) + .join(' - ') + } + value={value} + onChange={(newValue) => setValue(newValue)} +/> \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/PickerWithBrowserField.js b/docs/data/date-pickers/custom-field/PickerWithBrowserField.js index 64b20beedc61b..a350cec681811 100644 --- a/docs/data/date-pickers/custom-field/PickerWithBrowserField.js +++ b/docs/data/date-pickers/custom-field/PickerWithBrowserField.js @@ -1,20 +1,14 @@ import * as React from 'react'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; -import { useSlotProps } from '@mui/base/utils'; import Box from '@mui/material/Box'; -import Stack from '@mui/material/Stack'; -import IconButton from '@mui/material/IconButton'; -import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; -import { DateRangeIcon } from '@mui/x-date-pickers/icons'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; -import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; + const BrowserField = React.forwardRef((props, ref) => { const { disabled, @@ -26,6 +20,7 @@ const BrowserField = React.forwardRef((props, ref) => { error, focused, ownerState, + sx, ...other } = props; @@ -33,7 +28,7 @@ const BrowserField = React.forwardRef((props, ref) => { return ( @@ -44,161 +39,45 @@ const BrowserField = React.forwardRef((props, ref) => { ); }); -const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => { - const { slots, slotProps, onAdornmentClick, ...other } = props; - - const { inputRef: externalInputRef, ...textFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - externalForwardedProps: other, - ownerState: props, - }); +const BrowserDateField = React.forwardRef((props, ref) => { + const { inputRef: externalInputRef, slots, slotProps, ...textFieldProps } = props; - const { ref: inputRef, ...response } = useSingleInputDateRangeField({ + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useDateField({ props: textFieldProps, inputRef: externalInputRef, }); + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); return ( - - - ), - }} - /> - ); -}); - -BrowserSingleInputDateRangeField.fieldType = 'single-input'; - -const BrowserSingleInputDateRangePicker = React.forwardRef((props, ref) => { - const [isOpen, setIsOpen] = React.useState(false); - - const toggleOpen = () => setIsOpen((currentOpen) => !currentOpen); - - const handleOpen = () => setIsOpen(true); - - const handleClose = () => setIsOpen(false); - - return ( - ); }); -const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => { - const { - slotProps, - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - className, - } = props; - - const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'start' }, - }); - - const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'end' }, - }); - - const { - startDate: { ref: startRef, ...startDateProps }, - endDate: { ref: endRef, ...endDateProps }, - } = useMultiInputDateRangeField({ - sharedProps: { - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - }, - startTextFieldProps, - endTextFieldProps, - startInputRef, - endInputRef, - }); - - return ( - - - - - - ); -}); - -const BrowserDateRangePicker = React.forwardRef((props, ref) => { - return ( - - ); -}); - -const BrowserDateField = React.forwardRef((props, ref) => { - const { inputRef: externalInputRef, slots, slotProps, ...textFieldProps } = props; - - const { ref: inputRef, ...other } = useDateField({ - props: textFieldProps, - inputRef: externalInputRef, - }); - - return ; -}); - const BrowserDatePicker = React.forwardRef((props, ref) => { return ( ); }); @@ -206,13 +85,11 @@ const BrowserDatePicker = React.forwardRef((props, ref) => { export default function PickerWithBrowserField() { return ( - - - - - + ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx b/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx index 263c232bf8b4f..700beb54cedb6 100644 --- a/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx +++ b/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx @@ -1,37 +1,22 @@ import * as React from 'react'; import { Dayjs } from 'dayjs'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; -import { useSlotProps } from '@mui/base/utils'; import Box from '@mui/material/Box'; -import Stack from '@mui/material/Stack'; -import IconButton from '@mui/material/IconButton'; -import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; -import { DateRangeIcon } from '@mui/x-date-pickers/icons'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { - DateRangePicker, - DateRangePickerProps, -} from '@mui/x-date-pickers-pro/DateRangePicker'; -import { - unstable_useSingleInputDateRangeField as useSingleInputDateRangeField, - SingleInputDateRangeFieldProps, -} from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker'; import { unstable_useDateField as useDateField, UseDateFieldProps, } from '@mui/x-date-pickers/DateField'; import { - BaseMultiInputFieldProps, - DateRange, - DateRangeValidationError, - UseDateRangeFieldProps, - MultiInputFieldSlotTextFieldProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps, +} from '@mui/x-date-pickers/DateField/DateField.types'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; +import { BaseSingleInputFieldProps, DateValidationError, - RangeFieldSection, FieldSection, } from '@mui/x-date-pickers-pro'; @@ -47,6 +32,7 @@ interface BrowserFieldProps error?: boolean; focused?: boolean; ownerState?: any; + sx?: any; } type BrowserFieldComponent = (( @@ -65,6 +51,7 @@ const BrowserField = React.forwardRef( error, focused, ownerState, + sx, ...other } = props; @@ -72,7 +59,7 @@ const BrowserField = React.forwardRef( return ( @@ -84,183 +71,6 @@ const BrowserField = React.forwardRef( }, ) as BrowserFieldComponent; -interface BrowserSingleInputDateRangeFieldProps - extends SingleInputDateRangeFieldProps< - Dayjs, - Omit, 'size'> - > { - onAdornmentClick?: () => void; -} - -type BrowserSingleInputDateRangeFieldComponent = (( - props: BrowserSingleInputDateRangeFieldProps & React.RefAttributes, -) => React.JSX.Element) & { fieldType?: string }; - -const BrowserSingleInputDateRangeField = React.forwardRef( - (props: BrowserSingleInputDateRangeFieldProps, ref: React.Ref) => { - const { slots, slotProps, onAdornmentClick, ...other } = props; - - const { - inputRef: externalInputRef, - ...textFieldProps - }: SingleInputDateRangeFieldProps = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - externalForwardedProps: other, - ownerState: props as any, - }); - - const { ref: inputRef, ...response } = useSingleInputDateRangeField< - Dayjs, - typeof textFieldProps - >({ - props: textFieldProps, - inputRef: externalInputRef, - }); - - return ( - - - - ), - }} - /> - ); - }, -) as BrowserSingleInputDateRangeFieldComponent; - -BrowserSingleInputDateRangeField.fieldType = 'single-input'; - -const BrowserSingleInputDateRangePicker = React.forwardRef( - (props: DateRangePickerProps, ref: React.Ref) => { - const [isOpen, setIsOpen] = React.useState(false); - - const toggleOpen = () => setIsOpen((currentOpen) => !currentOpen); - - const handleOpen = () => setIsOpen(true); - - const handleClose = () => setIsOpen(false); - - return ( - - ); - }, -); - -interface BrowserMultiInputDateRangeFieldProps - extends UseDateRangeFieldProps, - BaseMultiInputFieldProps< - DateRange, - Dayjs, - RangeFieldSection, - DateRangeValidationError - > {} - -type BrowserMultiInputDateRangeFieldComponent = (( - props: BrowserMultiInputDateRangeFieldProps & React.RefAttributes, -) => React.JSX.Element) & { propTypes?: any }; - -const BrowserMultiInputDateRangeField = React.forwardRef( - (props: BrowserMultiInputDateRangeFieldProps, ref: React.Ref) => { - const { - slotProps, - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - className, - } = props; - - const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'start' }, - }) as MultiInputFieldSlotTextFieldProps; - - const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'end' }, - }) as MultiInputFieldSlotTextFieldProps; - - const { - startDate: { ref: startRef, ...startDateProps }, - endDate: { ref: endRef, ...endDateProps }, - } = useMultiInputDateRangeField({ - sharedProps: { - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - }, - startTextFieldProps, - endTextFieldProps, - startInputRef, - endInputRef, - }); - - return ( - - - - - - ); - }, -) as BrowserMultiInputDateRangeFieldComponent; - -const BrowserDateRangePicker = React.forwardRef( - (props: DateRangePickerProps, ref: React.Ref) => { - return ( - - ); - }, -); - interface BrowserDateFieldProps extends UseDateFieldProps, BaseSingleInputFieldProps< @@ -279,12 +89,39 @@ const BrowserDateField = React.forwardRef( ...textFieldProps } = props; - const { ref: inputRef, ...other } = useDateField({ + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useDateField({ props: textFieldProps, inputRef: externalInputRef, }); - return ; + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField< + {}, + typeof textFieldProps.InputProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( + + ); }, ); @@ -293,8 +130,8 @@ const BrowserDatePicker = React.forwardRef( return ( ); }, @@ -303,13 +140,11 @@ const BrowserDatePicker = React.forwardRef( export default function PickerWithBrowserField() { return ( - - - - - + ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx.preview b/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx.preview index 4e96e10bc78ad..0bb9e399d3cb4 100644 --- a/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx.preview +++ b/docs/data/date-pickers/custom-field/PickerWithBrowserField.tsx.preview @@ -1,3 +1,5 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/PickerWithButtonField.js b/docs/data/date-pickers/custom-field/PickerWithButtonField.js index fe6502169e812..255784386528b 100644 --- a/docs/data/date-pickers/custom-field/PickerWithButtonField.js +++ b/docs/data/date-pickers/custom-field/PickerWithButtonField.js @@ -1,7 +1,6 @@ import * as React from 'react'; import Button from '@mui/material/Button'; -import Stack from '@mui/material/Stack'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; @@ -25,7 +24,7 @@ function ButtonField(props) { aria-label={ariaLabel} onClick={() => setOpen?.((prev) => !prev)} > - {label ?? 'Pick a date'} + {label ? `Current date: ${label}` : 'Pick a date'} ); } @@ -50,15 +49,11 @@ export default function PickerWithButtonField() { return ( - - setValue(newValue)} - /> - + setValue(newValue)} + /> ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx b/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx index e7137238b01cb..c8cfc01a1964e 100644 --- a/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx +++ b/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { Dayjs } from 'dayjs'; import Button from '@mui/material/Button'; -import Stack from '@mui/material/Stack'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker'; @@ -42,7 +41,7 @@ function ButtonField(props: ButtonFieldProps) { aria-label={ariaLabel} onClick={() => setOpen?.((prev) => !prev)} > - {label ?? 'Pick a date'} + {label ? `Current date: ${label}` : 'Pick a date'} ); } @@ -69,15 +68,11 @@ export default function PickerWithButtonField() { return ( - - setValue(newValue)} - /> - + setValue(newValue)} + /> ); } diff --git a/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx.preview b/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx.preview index 77227d9491559..173a0ba169624 100644 --- a/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx.preview +++ b/docs/data/date-pickers/custom-field/PickerWithButtonField.tsx.preview @@ -1,7 +1,5 @@ setValue(newValue)} /> \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.js b/docs/data/date-pickers/custom-field/PickerWithJoyField.js index 3289e3e068c24..b195500b8b7d3 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.js +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.js @@ -8,27 +8,19 @@ import { import { extendTheme as extendJoyTheme, useColorScheme, - styled, CssVarsProvider, THEME_ID, } from '@mui/joy/styles'; -import { useSlotProps } from '@mui/base/utils'; import Input from '@mui/joy/Input'; -import Stack from '@mui/joy/Stack'; import FormControl from '@mui/joy/FormControl'; import FormLabel from '@mui/joy/FormLabel'; -import Typography from '@mui/joy/Typography'; -import IconButton from '@mui/joy/IconButton'; -import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; -import { DateRangeIcon } from '@mui/x-date-pickers/icons'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; -import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import { unstable_useDateField as useDateField } from '@mui/x-date-pickers/DateField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; + const joyTheme = extendJoyTheme(); const JoyField = React.forwardRef((props, ref) => { @@ -38,6 +30,8 @@ const JoyField = React.forwardRef((props, ref) => { label, InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, formControlSx, + endDecorator, + startDecorator, slotProps, ...other } = props; @@ -46,217 +40,59 @@ const JoyField = React.forwardRef((props, ref) => { {label} + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } slotProps={{ ...slotProps, root: { ...slotProps?.root, ref: containerRef }, }} - startDecorator={startAdornment} - endDecorator={endAdornment} {...other} /> ); }); -const JoySingleInputDateRangeField = React.forwardRef((props, ref) => { - const { slots, slotProps, onAdornmentClick, ...other } = props; - - const { inputRef: externalInputRef, ...textFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - externalForwardedProps: other, - ownerState: props, - }); - - const { ref: inputRef, ...response } = useSingleInputDateRangeField({ - props: textFieldProps, - inputRef: externalInputRef, - }); - - return ( - - - - } - /> - ); -}); - -JoySingleInputDateRangeField.fieldType = 'single-input'; - -const JoySingleInputDateRangePicker = React.forwardRef((props, ref) => { - const [isOpen, setIsOpen] = React.useState(false); - - const toggleOpen = (event) => { - // allows toggle behavior - event.stopPropagation(); - setIsOpen((currentOpen) => !currentOpen); - }; - - const handleOpen = () => setIsOpen(true); - - const handleClose = () => setIsOpen(false); - - return ( - - ); -}); - -const MultiInputJoyDateRangeFieldRoot = styled( - React.forwardRef((props, ref) => ( - - )), - { - name: 'MuiMultiInputDateRangeField', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, - }, -)({}); - -const MultiInputJoyDateRangeFieldSeparator = styled( - (props) => ( - - {/* Ensure that the separator is correctly aligned */} - - {props.children ?? ' — '} - - ), - { - name: 'MuiMultiInputDateRangeField', - slot: 'Separator', - overridesResolver: (props, styles) => styles.separator, - }, -)({ marginTop: '25px' }); - -const JoyMultiInputDateRangeField = React.forwardRef((props, ref) => { - const { - slotProps, - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - className, - } = props; - - const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'start' }, - }); - - const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ - elementType: null, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'end' }, - }); - - const { - startDate: { ref: startRef, ...startDateProps }, - endDate: { ref: endRef, ...endDateProps }, - } = useMultiInputDateRangeField({ - sharedProps: { - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - }, - startTextFieldProps, - endTextFieldProps, - startInputRef, - endInputRef, - }); - - return ( - - - - - - ); -}); - -const JoyDateRangePicker = React.forwardRef((props, ref) => { - return ( - - ); -}); - const JoyDateField = React.forwardRef((props, ref) => { const { inputRef: externalInputRef, slots, slotProps, ...textFieldProps } = props; - const { ref: inputRef, ...other } = useDateField({ + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useDateField({ props: textFieldProps, inputRef: externalInputRef, }); + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( { ref: inputRef, }, }} - {...other} + {...processedFieldProps} + InputProps={ProcessedInputProps} /> ); }); @@ -277,7 +114,9 @@ const JoyDatePicker = React.forwardRef((props, ref) => { {...props} slots={{ field: JoyDateField, ...props.slots }} slotProps={{ + ...props.slotProps, field: { + ...props.slotProps?.field, formControlSx: { flexDirection: 'row', }, @@ -288,7 +127,7 @@ const JoyDatePicker = React.forwardRef((props, ref) => { }); /** - * This component is for syncing the MUI docs's mode with this demo. + * This component is for syncing the theme mode of this demo with the MUI docs mode. * You might not need this component in your project. */ function SyncThemeMode({ mode }) { @@ -308,13 +147,11 @@ export default function PickerWithJoyField() { - - - - - + diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx index 845d6e6aa7b72..b68b28d688940 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx @@ -8,46 +8,29 @@ import { import { extendTheme as extendJoyTheme, useColorScheme, - styled, CssVarsProvider, THEME_ID, } from '@mui/joy/styles'; -import { useSlotProps } from '@mui/base/utils'; import Input, { InputProps } from '@mui/joy/Input'; -import Stack, { StackProps } from '@mui/joy/Stack'; import FormControl from '@mui/joy/FormControl'; import FormLabel from '@mui/joy/FormLabel'; -import Typography, { TypographyProps } from '@mui/joy/Typography'; -import IconButton from '@mui/joy/IconButton'; -import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; -import { DateRangeIcon } from '@mui/x-date-pickers/icons'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; -import { - DateRangePicker, - DateRangePickerProps, -} from '@mui/x-date-pickers-pro/DateRangePicker'; -import { - unstable_useSingleInputDateRangeField as useSingleInputDateRangeField, - SingleInputDateRangeFieldProps, -} from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker'; import { unstable_useDateField as useDateField, UseDateFieldProps, } from '@mui/x-date-pickers/DateField'; import { - BaseMultiInputFieldProps, - DateRange, - DateRangeValidationError, - UseDateRangeFieldProps, - MultiInputFieldSlotTextFieldProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps, +} from '@mui/x-date-pickers/DateField/DateField.types'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; +import { BaseSingleInputFieldProps, DateValidationError, - RangeFieldSection, FieldSection, -} from '@mui/x-date-pickers-pro'; +} from '@mui/x-date-pickers/models'; const joyTheme = extendJoyTheme(); @@ -73,6 +56,8 @@ const JoyField = React.forwardRef( label, InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, formControlSx, + endDecorator, + startDecorator, slotProps, ...other } = props; @@ -81,23 +66,29 @@ const JoyField = React.forwardRef( {label} + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } slotProps={{ ...slotProps, root: { ...slotProps?.root, ref: containerRef }, }} - startDecorator={startAdornment} - endDecorator={endAdornment} {...other} /> @@ -105,225 +96,6 @@ const JoyField = React.forwardRef( }, ) as JoyFieldComponent; -interface JoySingleInputDateRangeFieldProps - extends SingleInputDateRangeFieldProps { - onAdornmentClick?: () => void; -} - -type JoySingleInputDateRangeFieldComponent = (( - props: JoySingleInputDateRangeFieldProps & React.RefAttributes, -) => React.JSX.Element) & { fieldType?: string }; - -const JoySingleInputDateRangeField = React.forwardRef( - (props: JoySingleInputDateRangeFieldProps, ref: React.Ref) => { - const { slots, slotProps, onAdornmentClick, ...other } = props; - - const { - inputRef: externalInputRef, - ...textFieldProps - }: SingleInputDateRangeFieldProps< - Dayjs, - JoyFieldProps & { inputRef: React.Ref } - > = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - externalForwardedProps: other, - ownerState: props as any, - }); - - const { ref: inputRef, ...response } = useSingleInputDateRangeField< - Dayjs, - JoyFieldProps - >({ - props: textFieldProps, - inputRef: externalInputRef, - }); - - return ( - - - - } - /> - ); - }, -) as JoySingleInputDateRangeFieldComponent; - -JoySingleInputDateRangeField.fieldType = 'single-input'; - -const JoySingleInputDateRangePicker = React.forwardRef( - (props: DateRangePickerProps, ref: React.Ref) => { - const [isOpen, setIsOpen] = React.useState(false); - - const toggleOpen = (event: React.PointerEvent) => { - // allows toggle behavior - event.stopPropagation(); - setIsOpen((currentOpen) => !currentOpen); - }; - - const handleOpen = () => setIsOpen(true); - - const handleClose = () => setIsOpen(false); - - return ( - - ); - }, -); - -const MultiInputJoyDateRangeFieldRoot = styled( - React.forwardRef((props: StackProps, ref: React.Ref) => ( - - )), - { - name: 'MuiMultiInputDateRangeField', - slot: 'Root', - overridesResolver: (props, styles) => styles.root, - }, -)({}); - -const MultiInputJoyDateRangeFieldSeparator = styled( - (props: TypographyProps) => ( - - {/* Ensure that the separator is correctly aligned */} - - {props.children ?? ' — '} - - ), - { - name: 'MuiMultiInputDateRangeField', - slot: 'Separator', - overridesResolver: (props, styles) => styles.separator, - }, -)({ marginTop: '25px' }); - -interface JoyMultiInputDateRangeFieldProps - extends UseDateRangeFieldProps, - BaseMultiInputFieldProps< - DateRange, - Dayjs, - RangeFieldSection, - DateRangeValidationError - > {} - -type JoyMultiInputDateRangeFieldComponent = (( - props: JoyMultiInputDateRangeFieldProps & React.RefAttributes, -) => React.JSX.Element) & { propTypes?: any }; - -const JoyMultiInputDateRangeField = React.forwardRef( - (props: JoyMultiInputDateRangeFieldProps, ref: React.Ref) => { - const { - slotProps, - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - className, - } = props; - - const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'start' }, - }) as MultiInputFieldSlotTextFieldProps; - - const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ - elementType: null as any, - externalSlotProps: slotProps?.textField, - ownerState: { ...props, position: 'end' }, - }) as MultiInputFieldSlotTextFieldProps; - - const { - startDate: { ref: startRef, ...startDateProps }, - endDate: { ref: endRef, ...endDateProps }, - } = useMultiInputDateRangeField({ - sharedProps: { - value, - defaultValue, - format, - onChange, - readOnly, - disabled, - onError, - shouldDisableDate, - minDate, - maxDate, - disableFuture, - disablePast, - selectedSections, - onSelectedSectionsChange, - }, - startTextFieldProps, - endTextFieldProps, - startInputRef, - endInputRef, - }); - - return ( - - - - - - ); - }, -) as JoyMultiInputDateRangeFieldComponent; - -const JoyDateRangePicker = React.forwardRef( - (props: DateRangePickerProps, ref: React.Ref) => { - return ( - - ); - }, -); - interface JoyDateFieldProps extends UseDateFieldProps, BaseSingleInputFieldProps< @@ -342,11 +114,32 @@ const JoyDateField = React.forwardRef( ...textFieldProps } = props; - const { ref: inputRef, ...other } = useDateField({ + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useDateField({ props: textFieldProps, inputRef: externalInputRef, }); + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField< + {}, + typeof textFieldProps.InputProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); }, @@ -369,7 +163,9 @@ const JoyDatePicker = React.forwardRef( {...props} slots={{ field: JoyDateField, ...props.slots }} slotProps={{ + ...props.slotProps, field: { + ...props.slotProps?.field, formControlSx: { flexDirection: 'row', }, @@ -381,7 +177,7 @@ const JoyDatePicker = React.forwardRef( ); /** - * This component is for syncing the MUI docs's mode with this demo. + * This component is for syncing the theme mode of this demo with the MUI docs mode. * You might not need this component in your project. */ function SyncThemeMode({ mode }: { mode: 'light' | 'dark' }) { @@ -401,13 +197,11 @@ export default function PickerWithJoyField() { - - - - - + diff --git a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview index 1549f760fbeaf..cff735f8a7af0 100644 --- a/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview +++ b/docs/data/date-pickers/custom-field/PickerWithJoyField.tsx.preview @@ -2,13 +2,11 @@ - - - - - + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.js b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.js new file mode 100644 index 0000000000000..346191d5faa58 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.js @@ -0,0 +1,131 @@ +import * as React from 'react'; + +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import { useSlotProps } from '@mui/base/utils'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; + +const BrowserField = React.forwardRef((props, ref) => { + const { + disabled, + id, + label, + inputRef, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + // extracting `error`, 'focused', and `ownerState` as `input` does not support those props + error, + focused, + ownerState, + sx, + ...other + } = props; + + const handleRef = useForkRef(containerRef, ref); + + return ( + + {startAdornment} + + {endAdornment} + + ); +}); + +const BrowserMultiInputDateRangeField = React.forwardRef((props, ref) => { + const { + slotProps, + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + className, + } = props; + + const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'start' }, + }); + + const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'end' }, + }); + + const { + startDate: { ref: startRef, ...startDateProps }, + endDate: { ref: endRef, ...endDateProps }, + } = useMultiInputDateRangeField({ + sharedProps: { + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + }, + startTextFieldProps, + endTextFieldProps, + startInputRef, + endInputRef, + }); + + return ( + + + + + + ); +}); + +const BrowserDateRangePicker = React.forwardRef((props, ref) => { + return ( + + ); +}); + +export default function RangePickerWithBrowserField() { + return ( + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx new file mode 100644 index 0000000000000..b5cf357624ba2 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx @@ -0,0 +1,180 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import { useSlotProps } from '@mui/base/utils'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { + DateRangePicker, + DateRangePickerProps, +} from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; +import { + BaseMultiInputFieldProps, + DateRange, + DateRangeValidationError, + UseDateRangeFieldProps, + MultiInputFieldSlotTextFieldProps, + RangeFieldSection, +} from '@mui/x-date-pickers-pro'; + +interface BrowserFieldProps + extends Omit, 'size'> { + label?: React.ReactNode; + inputRef?: React.Ref; + InputProps?: { + ref?: React.Ref; + endAdornment?: React.ReactNode; + startAdornment?: React.ReactNode; + }; + error?: boolean; + focused?: boolean; + ownerState?: any; + sx?: any; +} + +type BrowserFieldComponent = (( + props: BrowserFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const BrowserField = React.forwardRef( + (props: BrowserFieldProps, ref: React.Ref) => { + const { + disabled, + id, + label, + inputRef, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + // extracting `error`, 'focused', and `ownerState` as `input` does not support those props + error, + focused, + ownerState, + sx, + ...other + } = props; + + const handleRef = useForkRef(containerRef, ref); + + return ( + + {startAdornment} + + {endAdornment} + + ); + }, +) as BrowserFieldComponent; + +interface BrowserMultiInputDateRangeFieldProps + extends UseDateRangeFieldProps, + BaseMultiInputFieldProps< + DateRange, + Dayjs, + RangeFieldSection, + DateRangeValidationError + > {} + +type BrowserMultiInputDateRangeFieldComponent = (( + props: BrowserMultiInputDateRangeFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const BrowserMultiInputDateRangeField = React.forwardRef( + (props: BrowserMultiInputDateRangeFieldProps, ref: React.Ref) => { + const { + slotProps, + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + className, + } = props; + + const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'start' }, + }) as MultiInputFieldSlotTextFieldProps; + + const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'end' }, + }) as MultiInputFieldSlotTextFieldProps; + + const { + startDate: { ref: startRef, ...startDateProps }, + endDate: { ref: endRef, ...endDateProps }, + } = useMultiInputDateRangeField({ + sharedProps: { + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + }, + startTextFieldProps, + endTextFieldProps, + startInputRef, + endInputRef, + }); + + return ( + + + + + + ); + }, +) as BrowserMultiInputDateRangeFieldComponent; + +const BrowserDateRangePicker = React.forwardRef( + (props: DateRangePickerProps, ref: React.Ref) => { + return ( + + ); + }, +); + +export default function RangePickerWithBrowserField() { + return ( + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx.preview b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx.preview new file mode 100644 index 0000000000000..d797406fa9997 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithBrowserField.tsx.preview @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/RangePickerWithJoyField.js b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.js new file mode 100644 index 0000000000000..26cb65d1b0351 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.js @@ -0,0 +1,218 @@ +import * as React from 'react'; + +import { + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, +} from '@mui/material/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + styled, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; +import { useSlotProps } from '@mui/base/utils'; +import Input from '@mui/joy/Input'; +import Stack from '@mui/joy/Stack'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import Typography from '@mui/joy/Typography'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; + +const joyTheme = extendJoyTheme(); + +const JoyField = React.forwardRef((props, ref) => { + const { + disabled, + id, + label, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + endDecorator, + startDecorator, + slotProps, + ...other + } = props; + + return ( + + {label} + + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } + slotProps={{ + ...slotProps, + root: { ...slotProps?.root, ref: containerRef }, + }} + {...other} + /> + + ); +}); + +const MultiInputJoyDateRangeFieldRoot = styled( + React.forwardRef((props, ref) => ( + + )), + { + name: 'MuiMultiInputDateRangeField', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, + }, +)({}); + +const MultiInputJoyDateRangeFieldSeparator = styled( + (props) => ( + + {/* Ensure that the separator is correctly aligned */} + + {props.children ?? ' — '} + + ), + { + name: 'MuiMultiInputDateRangeField', + slot: 'Separator', + overridesResolver: (props, styles) => styles.separator, + }, +)({ marginTop: '25px' }); + +const JoyMultiInputDateRangeField = React.forwardRef((props, ref) => { + const { + slotProps, + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + className, + } = props; + + const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'start' }, + }); + + const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'end' }, + }); + + const { + startDate: { ref: startRef, ...startDateProps }, + endDate: { ref: endRef, ...endDateProps }, + } = useMultiInputDateRangeField({ + sharedProps: { + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + }, + startTextFieldProps, + endTextFieldProps, + startInputRef, + endInputRef, + }); + + return ( + + + + + + ); +}); + +const JoyDateRangePicker = React.forwardRef((props, ref) => { + return ( + + ); +}); + +/** + * This component is for syncing the theme mode of this demo with the MUI docs mode. + * You might not need this component in your project. + */ +function SyncThemeMode({ mode }) { + const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); + React.useEffect(() => { + setMode(mode); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); + return null; +} + +export default function RangePickerWithJoyField() { + const materialTheme = useMaterialTheme(); + return ( + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx new file mode 100644 index 0000000000000..a5506efdaff72 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx @@ -0,0 +1,261 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, +} from '@mui/material/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + styled, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; +import { useSlotProps } from '@mui/base/utils'; +import Input, { InputProps } from '@mui/joy/Input'; +import Stack, { StackProps } from '@mui/joy/Stack'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import Typography, { TypographyProps } from '@mui/joy/Typography'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { + DateRangePicker, + DateRangePickerProps, +} from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useMultiInputDateRangeField as useMultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; +import { + BaseMultiInputFieldProps, + DateRange, + DateRangeValidationError, + UseDateRangeFieldProps, + MultiInputFieldSlotTextFieldProps, + RangeFieldSection, +} from '@mui/x-date-pickers-pro'; + +const joyTheme = extendJoyTheme(); + +interface JoyFieldProps extends InputProps { + label?: React.ReactNode; + InputProps?: { + ref?: React.Ref; + endAdornment?: React.ReactNode; + startAdornment?: React.ReactNode; + }; +} + +type JoyFieldComponent = (( + props: JoyFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const JoyField = React.forwardRef( + (props: JoyFieldProps, ref: React.Ref) => { + const { + disabled, + id, + label, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + endDecorator, + startDecorator, + slotProps, + ...other + } = props; + + return ( + + {label} + + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } + slotProps={{ + ...slotProps, + root: { ...slotProps?.root, ref: containerRef }, + }} + {...other} + /> + + ); + }, +) as JoyFieldComponent; + +const MultiInputJoyDateRangeFieldRoot = styled( + React.forwardRef((props: StackProps, ref: React.Ref) => ( + + )), + { + name: 'MuiMultiInputDateRangeField', + slot: 'Root', + overridesResolver: (props, styles) => styles.root, + }, +)({}); + +const MultiInputJoyDateRangeFieldSeparator = styled( + (props: TypographyProps) => ( + + {/* Ensure that the separator is correctly aligned */} + + {props.children ?? ' — '} + + ), + { + name: 'MuiMultiInputDateRangeField', + slot: 'Separator', + overridesResolver: (props, styles) => styles.separator, + }, +)({ marginTop: '25px' }); + +interface JoyMultiInputDateRangeFieldProps + extends UseDateRangeFieldProps, + BaseMultiInputFieldProps< + DateRange, + Dayjs, + RangeFieldSection, + DateRangeValidationError + > {} + +type JoyMultiInputDateRangeFieldComponent = (( + props: JoyMultiInputDateRangeFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const JoyMultiInputDateRangeField = React.forwardRef( + (props: JoyMultiInputDateRangeFieldProps, ref: React.Ref) => { + const { + slotProps, + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + className, + } = props; + + const { inputRef: startInputRef, ...startTextFieldProps } = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'start' }, + }) as MultiInputFieldSlotTextFieldProps; + + const { inputRef: endInputRef, ...endTextFieldProps } = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + ownerState: { ...props, position: 'end' }, + }) as MultiInputFieldSlotTextFieldProps; + + const { + startDate: { ref: startRef, ...startDateProps }, + endDate: { ref: endRef, ...endDateProps }, + } = useMultiInputDateRangeField({ + sharedProps: { + value, + defaultValue, + format, + onChange, + readOnly, + disabled, + onError, + shouldDisableDate, + minDate, + maxDate, + disableFuture, + disablePast, + selectedSections, + onSelectedSectionsChange, + }, + startTextFieldProps, + endTextFieldProps, + startInputRef, + endInputRef, + }); + + return ( + + + + + + ); + }, +) as JoyMultiInputDateRangeFieldComponent; + +const JoyDateRangePicker = React.forwardRef( + (props: DateRangePickerProps, ref: React.Ref) => { + return ( + + ); + }, +); + +/** + * This component is for syncing the theme mode of this demo with the MUI docs mode. + * You might not need this component in your project. + */ +function SyncThemeMode({ mode }: { mode: 'light' | 'dark' }) { + const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); + React.useEffect(() => { + setMode(mode); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); + return null; +} + +export default function RangePickerWithJoyField() { + const materialTheme = useMaterialTheme(); + return ( + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx.preview b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx.preview new file mode 100644 index 0000000000000..c1fcecdde3b53 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithJoyField.tsx.preview @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.js b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.js new file mode 100644 index 0000000000000..b7971dde7bbb5 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.js @@ -0,0 +1,138 @@ +import * as React from 'react'; + +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import { useSlotProps } from '@mui/base/utils'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import { DateRangeIcon } from '@mui/x-date-pickers/icons'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; + +const BrowserField = React.forwardRef((props, ref) => { + const { + disabled, + id, + label, + inputRef, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + // extracting `error`, 'focused', and `ownerState` as `input` does not support those props + error, + focused, + ownerState, + sx, + ...other + } = props; + + const handleRef = useForkRef(containerRef, ref); + + return ( + + {startAdornment} + + {endAdornment} + + ); +}); + +const BrowserSingleInputDateRangeField = React.forwardRef((props, ref) => { + const { slots, slotProps, onAdornmentClick, ...other } = props; + + const { inputRef: externalInputRef, ...textFieldProps } = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + externalForwardedProps: other, + ownerState: props, + }); + + const { + ref: inputRef, + onClear, + clearable, + ...fieldProps + } = useSingleInputDateRangeField({ + props: textFieldProps, + inputRef: externalInputRef, + }); + + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField({ + onClear, + clearable, + fieldProps, + InputProps: { + ...fieldProps.InputProps, + endAdornment: ( + + + + + + ), + }, + slots, + slotProps, + }); + + return ( + + ); +}); + +BrowserSingleInputDateRangeField.fieldType = 'single-input'; + +const BrowserSingleInputDateRangePicker = React.forwardRef((props, ref) => { + const [isOpen, setIsOpen] = React.useState(false); + + const toggleOpen = () => setIsOpen((currentOpen) => !currentOpen); + + const handleOpen = () => setIsOpen(true); + + const handleClose = () => setIsOpen(false); + + return ( + + ); +}); + +export default function RangePickerWithSingleInputBrowserField() { + return ( + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx new file mode 100644 index 0000000000000..68c03dfa4d3f1 --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx @@ -0,0 +1,193 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { unstable_useForkRef as useForkRef } from '@mui/utils'; +import { useSlotProps } from '@mui/base/utils'; +import Box from '@mui/material/Box'; +import IconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import { DateRangeIcon } from '@mui/x-date-pickers/icons'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { + DateRangePicker, + DateRangePickerProps, +} from '@mui/x-date-pickers-pro/DateRangePicker'; +import { + unstable_useSingleInputDateRangeField as useSingleInputDateRangeField, + SingleInputDateRangeFieldProps, +} from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; +import type { + SingleInputDateRangeFieldSlotsComponent, + SingleInputDateRangeFieldSlotsComponentsProps, +} from '@mui/x-date-pickers-pro/SingleInputDateRangeField/SingleInputDateRangeField.types'; + +interface BrowserFieldProps + extends Omit, 'size'> { + label?: React.ReactNode; + inputRef?: React.Ref; + InputProps?: { + ref?: React.Ref; + endAdornment?: React.ReactNode; + startAdornment?: React.ReactNode; + }; + error?: boolean; + focused?: boolean; + ownerState?: any; + sx?: any; +} + +type BrowserFieldComponent = (( + props: BrowserFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const BrowserField = React.forwardRef( + (props: BrowserFieldProps, ref: React.Ref) => { + const { + disabled, + id, + label, + inputRef, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + // extracting `error`, 'focused', and `ownerState` as `input` does not support those props + error, + focused, + ownerState, + sx, + ...other + } = props; + + const handleRef = useForkRef(containerRef, ref); + + return ( + + {startAdornment} + + {endAdornment} + + ); + }, +) as BrowserFieldComponent; + +interface BrowserSingleInputDateRangeFieldProps + extends SingleInputDateRangeFieldProps< + Dayjs, + Omit, 'size'> + > { + onAdornmentClick?: () => void; +} + +type BrowserSingleInputDateRangeFieldComponent = (( + props: BrowserSingleInputDateRangeFieldProps & React.RefAttributes, +) => React.JSX.Element) & { fieldType?: string }; + +const BrowserSingleInputDateRangeField = React.forwardRef( + (props: BrowserSingleInputDateRangeFieldProps, ref: React.Ref) => { + const { slots, slotProps, onAdornmentClick, ...other } = props; + + const { + inputRef: externalInputRef, + ...textFieldProps + }: SingleInputDateRangeFieldProps = useSlotProps({ + elementType: 'input', + externalSlotProps: slotProps?.textField, + externalForwardedProps: other, + ownerState: props as any, + }); + + const { + ref: inputRef, + onClear, + clearable, + ...fieldProps + } = useSingleInputDateRangeField({ + props: textFieldProps, + inputRef: externalInputRef, + }); + + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField< + {}, + typeof textFieldProps.InputProps, + SingleInputDateRangeFieldSlotsComponent, + SingleInputDateRangeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: { + ...fieldProps.InputProps, + endAdornment: ( + + + + + + ), + }, + slots, + slotProps, + }); + + return ( + + ); + }, +) as BrowserSingleInputDateRangeFieldComponent; + +BrowserSingleInputDateRangeField.fieldType = 'single-input'; + +const BrowserSingleInputDateRangePicker = React.forwardRef( + (props: DateRangePickerProps, ref: React.Ref) => { + const [isOpen, setIsOpen] = React.useState(false); + + const toggleOpen = () => setIsOpen((currentOpen) => !currentOpen); + + const handleOpen = () => setIsOpen(true); + + const handleClose = () => setIsOpen(false); + + return ( + + ); + }, +); + +export default function RangePickerWithSingleInputBrowserField() { + return ( + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx.preview b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx.preview new file mode 100644 index 0000000000000..bcaf8043948fb --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputBrowserField.tsx.preview @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.js b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.js new file mode 100644 index 0000000000000..02299b65247ec --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.js @@ -0,0 +1,187 @@ +import * as React from 'react'; + +import { + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, +} from '@mui/material/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; +import { useSlotProps } from '@mui/base/utils'; +import Input from '@mui/joy/Input'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import IconButton from '@mui/joy/IconButton'; +import { DateRangeIcon } from '@mui/x-date-pickers/icons'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { unstable_useSingleInputDateRangeField as useSingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; + +const joyTheme = extendJoyTheme(); + +const JoyField = React.forwardRef((props, ref) => { + const { + disabled, + id, + label, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + endDecorator, + startDecorator, + slotProps, + ...other + } = props; + + return ( + + {label} + + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } + slotProps={{ + ...slotProps, + root: { ...slotProps?.root, ref: containerRef }, + }} + {...other} + /> + + ); +}); + +const JoySingleInputDateRangeField = React.forwardRef((props, ref) => { + const { slots, slotProps, onAdornmentClick, ...other } = props; + + const { inputRef: externalInputRef, ...textFieldProps } = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + externalForwardedProps: other, + ownerState: props, + }); + + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useSingleInputDateRangeField({ + props: textFieldProps, + inputRef: externalInputRef, + }); + + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots: { ...slots, clearButton: IconButton }, + slotProps: { ...slotProps, clearIcon: { color: 'action' } }, + }); + + return ( + + + + } + InputProps={{ ...ProcessedInputProps }} + /> + ); +}); + +JoySingleInputDateRangeField.fieldType = 'single-input'; + +const JoySingleInputDateRangePicker = React.forwardRef((props, ref) => { + const [isOpen, setIsOpen] = React.useState(false); + + const toggleOpen = (event) => { + // allows toggle behavior + event.stopPropagation(); + setIsOpen((currentOpen) => !currentOpen); + }; + + const handleOpen = () => setIsOpen(true); + + const handleClose = () => setIsOpen(false); + + return ( + + ); +}); + +/** + * This component is for syncing the theme mode of this demo with the MUI docs mode. + * You might not need this component in your project. + */ +function SyncThemeMode({ mode }) { + const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); + React.useEffect(() => { + setMode(mode); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); + return null; +} + +export default function RangePickerWithSingleInputJoyField() { + const materialTheme = useMaterialTheme(); + return ( + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx new file mode 100644 index 0000000000000..8f7f9554f589f --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx @@ -0,0 +1,236 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { + useTheme as useMaterialTheme, + useColorScheme as useMaterialColorScheme, + Experimental_CssVarsProvider as MaterialCssVarsProvider, +} from '@mui/material/styles'; +import { + extendTheme as extendJoyTheme, + useColorScheme, + CssVarsProvider, + THEME_ID, +} from '@mui/joy/styles'; +import { useSlotProps } from '@mui/base/utils'; +import Input, { InputProps } from '@mui/joy/Input'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import IconButton from '@mui/joy/IconButton'; +import { DateRangeIcon } from '@mui/x-date-pickers/icons'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { + DateRangePicker, + DateRangePickerProps, +} from '@mui/x-date-pickers-pro/DateRangePicker'; +import { + unstable_useSingleInputDateRangeField as useSingleInputDateRangeField, + SingleInputDateRangeFieldProps, +} from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; +import type { + SingleInputDateRangeFieldSlotsComponent, + SingleInputDateRangeFieldSlotsComponentsProps, +} from '@mui/x-date-pickers-pro/SingleInputDateRangeField/SingleInputDateRangeField.types'; + +const joyTheme = extendJoyTheme(); + +interface JoyFieldProps extends InputProps { + label?: React.ReactNode; + InputProps?: { + ref?: React.Ref; + endAdornment?: React.ReactNode; + startAdornment?: React.ReactNode; + }; +} + +type JoyFieldComponent = (( + props: JoyFieldProps & React.RefAttributes, +) => React.JSX.Element) & { propTypes?: any }; + +const JoyField = React.forwardRef( + (props: JoyFieldProps, ref: React.Ref) => { + const { + disabled, + id, + label, + InputProps: { ref: containerRef, startAdornment, endAdornment } = {}, + endDecorator, + startDecorator, + slotProps, + ...other + } = props; + + return ( + + {label} + + {startAdornment} + {startDecorator} + + } + endDecorator={ + + {endAdornment} + {endDecorator} + + } + slotProps={{ + ...slotProps, + root: { ...slotProps?.root, ref: containerRef }, + }} + {...other} + /> + + ); + }, +) as JoyFieldComponent; + +interface JoySingleInputDateRangeFieldProps + extends SingleInputDateRangeFieldProps { + onAdornmentClick?: () => void; +} + +type JoySingleInputDateRangeFieldComponent = (( + props: JoySingleInputDateRangeFieldProps & React.RefAttributes, +) => React.JSX.Element) & { fieldType?: string }; + +const JoySingleInputDateRangeField = React.forwardRef( + (props: JoySingleInputDateRangeFieldProps, ref: React.Ref) => { + const { slots, slotProps, onAdornmentClick, ...other } = props; + + const { + inputRef: externalInputRef, + ...textFieldProps + }: SingleInputDateRangeFieldProps< + Dayjs, + JoyFieldProps & { inputRef: React.Ref } + > = useSlotProps({ + elementType: FormControl, + externalSlotProps: slotProps?.textField, + externalForwardedProps: other, + ownerState: props as any, + }); + + const { + onClear, + clearable, + ref: inputRef, + ...fieldProps + } = useSingleInputDateRangeField({ + props: textFieldProps, + inputRef: externalInputRef, + }); + + /* If you don't need a clear button, you can skip the use of this hook */ + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = + useClearableField< + {}, + typeof textFieldProps.InputProps, + SingleInputDateRangeFieldSlotsComponent, + SingleInputDateRangeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots: { ...slots, clearButton: IconButton }, + slotProps: { ...slotProps, clearIcon: { color: 'action' } }, + }); + + return ( + + + + } + InputProps={{ ...ProcessedInputProps }} + /> + ); + }, +) as JoySingleInputDateRangeFieldComponent; + +JoySingleInputDateRangeField.fieldType = 'single-input'; + +const JoySingleInputDateRangePicker = React.forwardRef( + (props: DateRangePickerProps, ref: React.Ref) => { + const [isOpen, setIsOpen] = React.useState(false); + + const toggleOpen = (event: React.PointerEvent) => { + // allows toggle behavior + event.stopPropagation(); + setIsOpen((currentOpen) => !currentOpen); + }; + + const handleOpen = () => setIsOpen(true); + + const handleClose = () => setIsOpen(false); + + return ( + + ); + }, +); + +/** + * This component is for syncing the theme mode of this demo with the MUI docs mode. + * You might not need this component in your project. + */ +function SyncThemeMode({ mode }: { mode: 'light' | 'dark' }) { + const { setMode } = useColorScheme(); + const { setMode: setMaterialMode } = useMaterialColorScheme(); + React.useEffect(() => { + setMode(mode); + setMaterialMode(mode); + }, [mode, setMode, setMaterialMode]); + return null; +} + +export default function RangePickerWithSingleInputJoyField() { + const materialTheme = useMaterialTheme(); + return ( + + + + + + + + + ); +} diff --git a/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx.preview b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx.preview new file mode 100644 index 0000000000000..90b0c5ff3c08f --- /dev/null +++ b/docs/data/date-pickers/custom-field/RangePickerWithSingleInputJoyField.tsx.preview @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/docs/data/date-pickers/custom-field/custom-field.md b/docs/data/date-pickers/custom-field/custom-field.md index f16a6f3ec7ae8..8dfa870b53ddb 100644 --- a/docs/data/date-pickers/custom-field/custom-field.md +++ b/docs/data/date-pickers/custom-field/custom-field.md @@ -69,12 +69,24 @@ A higher-level solution for _Joy UI_ will be provided in the near future for eve {{"demo": "PickerWithJoyField.js", "defaultCodeOpen": false}} +{{"demo": "RangePickerWithSingleInputJoyField.js", "defaultCodeOpen": false}} + +{{"demo": "RangePickerWithJoyField.js", "defaultCodeOpen": false}} + #### With the browser input You can also use any other input: {{"demo": "PickerWithBrowserField.js", "defaultCodeOpen": false}} +{{"demo": "RangePickerWithSingleInputBrowserField.js", "defaultCodeOpen": false}} + +{{"demo": "RangePickerWithBrowserField.js", "defaultCodeOpen": false}} + +:::warning +You will need to use a component that supports the `sx` prop as a wrapper for your input, in order to be able to benefit from the **hover** and **focus** behavior of the clear button. You will have access to the `clearable` and `onClear` props using native HTML elements, but the on **focus** and **hover** behavior depends on styles applied via the `sx` prop. +::: + ### Using an `Autocomplete` If your user can only select a value in a small list of available dates, @@ -89,6 +101,10 @@ you can replace the field with a `Button`: {{"demo": "PickerWithButtonField.js", "defaultCodeOpen": false}} +The same can be applied to the `DateRangePicker`: + +{{"demo": "DateRangePickerWithButtonField.js", "defaultCodeOpen": false}} + ## How to build a custom field The main challenge when building a custom field, is to make sure that all the relevant props passed by the pickers are correctly handled. diff --git a/docs/data/date-pickers/date-picker/ClearableProp.js b/docs/data/date-pickers/date-picker/ClearableProp.js new file mode 100644 index 0000000000000..c5ecfc1beaf8d --- /dev/null +++ b/docs/data/date-pickers/date-picker/ClearableProp.js @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import Box from '@mui/material/Box'; +import Alert from '@mui/material/Alert'; + +export default function ClearableProp() { + const [cleared, setCleared] = React.useState(false); + + React.useEffect(() => { + if (cleared) { + const timeout = setTimeout(() => { + setCleared(false); + }, 1500); + + return () => clearTimeout(timeout); + } + return () => {}; + }, [cleared]); + + return ( + + + + setCleared(true) }, + }} + /> + + + {cleared && ( + + Field cleared! + + )} + + + ); +} diff --git a/docs/data/date-pickers/date-picker/ClearableProp.tsx b/docs/data/date-pickers/date-picker/ClearableProp.tsx new file mode 100644 index 0000000000000..d2d509261589b --- /dev/null +++ b/docs/data/date-pickers/date-picker/ClearableProp.tsx @@ -0,0 +1,54 @@ +import * as React from 'react'; +import { DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import Box from '@mui/material/Box'; +import Alert from '@mui/material/Alert'; + +export default function ClearableProp() { + const [cleared, setCleared] = React.useState(false); + + React.useEffect(() => { + if (cleared) { + const timeout = setTimeout(() => { + setCleared(false); + }, 1500); + + return () => clearTimeout(timeout); + } + return () => {}; + }, [cleared]); + + return ( + + + + setCleared(true) }, + }} + /> + + + {cleared && ( + + Field cleared! + + )} + + + ); +} diff --git a/docs/data/date-pickers/date-picker/CustomizationExamplesNoSnap.js b/docs/data/date-pickers/date-picker/CustomizationExamplesNoSnap.js new file mode 100644 index 0000000000000..fe78364ad7445 --- /dev/null +++ b/docs/data/date-pickers/date-picker/CustomizationExamplesNoSnap.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import Stack from '@mui/material/Stack'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import BrandingProvider from 'docs/src/BrandingProvider'; +import CustomizationPlayground from 'docsx/src/modules/components/CustomizationPlayground'; +import CircularProgress from '@mui/material/CircularProgress'; +import { pickerExamples } from './examplesConfig'; + +export default function CustomizationExamplesNoSnap() { + const [selectedPicker, setSelectedPicker] = React.useState(0); + + const handleSelectedPickerChange = (_e, newValue) => { + if (newValue !== null) { + setSelectedPicker(newValue); + } + }; + + if (!pickerExamples[selectedPicker]?.examples) { + return ( + + + + ); + } + + return ( + + + + + {pickerExamples.map(({ name }, index) => ( + + {name} + + ))} + + + + {pickerExamples.map( + (example, index) => + String(index) === String(selectedPicker) && ( + + ), + )} + + + + ); +} diff --git a/docs/data/date-pickers/date-picker/date-picker.md b/docs/data/date-pickers/date-picker/date-picker.md index c3aac1f06bc58..85018f5efc0bb 100644 --- a/docs/data/date-pickers/date-picker/date-picker.md +++ b/docs/data/date-pickers/date-picker/date-picker.md @@ -1,7 +1,7 @@ --- productId: x-date-pickers title: React Date Picker component -components: DatePicker, DesktopDatePicker, MobileDatePicker, StaticDatePicker +components: DatePicker, DesktopDatePicker, MobileDatePicker, StaticDatePicker, DateCalendar githubLabel: 'component: DatePicker' packageName: '@mui/x-date-pickers' materialDesign: https://m2.material.io/components/date-pickers @@ -106,6 +106,12 @@ You can show a helper text with the date format accepted: {{"demo": "HelperText.js"}} +## Clearing the value + +You can enable the clearable behavior: + +{{"demo": "ClearableProp.js"}} + ## Validation You can find the documentation in the [Validation page](/x/react-date-pickers/validation/). @@ -113,3 +119,9 @@ You can find the documentation in the [Validation page](/x/react-date-pickers/va ## Localization You can find the documentation about localization in the [Date format and localization](/x/react-date-pickers/adapters-locale/) and [Translated components](/x/react-date-pickers/localization/). + +## Customization + +You can check out multiple examples of how to customize the date pickers and their subcomponents. + +{{"demo": "CustomizationExamplesNoSnap.js", "hideToolbar": true, "bg": "inline"}} diff --git a/docs/data/date-pickers/date-picker/examplesConfig.ts b/docs/data/date-pickers/date-picker/examplesConfig.ts new file mode 100644 index 0000000000000..059a94ff0f8b3 --- /dev/null +++ b/docs/data/date-pickers/date-picker/examplesConfig.ts @@ -0,0 +1,278 @@ +import { StaticDatePicker, StaticDatePickerProps } from '@mui/x-date-pickers/StaticDatePicker'; +import { DesktopDatePicker, DesktopDatePickerProps } from '@mui/x-date-pickers/DesktopDatePicker'; +import { DatePickerProps } from '@mui/x-date-pickers/DatePicker'; +import dayjs, { Dayjs } from 'dayjs'; +import { PickersSubcomponentType } from 'docsx/src/modules/utils/useCustomizationPlayground'; + +type PickerExamplesType = { + name: string; + component: TComponent; + componentProps: TComponentProps; + examples: PickersSubcomponentType; +}; + +export const staticDatePickerExamples: PickersSubcomponentType = { + PickersToolbar: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'success', + }, + styledComponents: { + type: 'success', + }, + }, + slots: ['root', 'content'], + }, + DateCalendar: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'success', + }, + styledComponents: { + type: 'success', + }, + }, + slots: ['root'], + }, + PickersCalendarHeader: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'success', + }, + styledComponents: { + type: 'success', + }, + }, + slots: ['root', 'label', 'labelContainer', 'switchViewButton', 'switchViewIcon'], + }, + DayCalendar: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'success', + }, + + styledComponents: { + type: 'success', + }, + }, + slots: ['root', 'weekDayLabel', 'weekContainer', 'weekNumberLabel', 'weekNumber'], + }, + PickersDay: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'success', + }, + + styledComponents: { + type: 'success', + }, + }, + slots: ['root', 'today'], + }, + MonthCalendar: { + examples: { + customTheme: { + type: 'success', + componentProps: { views: ['month'] }, + }, + sxProp: { + type: 'success', + componentProps: { views: ['month'] }, + }, + + styledComponents: { + type: 'success', + componentProps: { views: ['month'] }, + }, + }, + slots: ['root'], + }, + PickersMonth: { + examples: { + customTheme: { + type: 'success', + componentProps: { views: ['month'] }, + }, + sxProp: { + type: 'success', + componentProps: { views: ['month'] }, + }, + + styledComponents: { + type: 'success', + componentProps: { views: ['month'] }, + }, + }, + slots: ['root', 'monthButton'], + }, +}; + +export const datePickerExamples: PickersSubcomponentType = { + DateCalendar: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'warning', + parentSlot: 'layout', + comments: + 'Because of the structure of the DesktopDatePicker, the `sx` prop needs to be applied to the `layout` slot', + }, + styledComponents: { + type: 'warning', + parentSlot: 'layout', + parentComponent: 'PickersLayout', + comments: + 'Because of the structure of the DesktopDatePicker and the way the popper renders, the `layout` slot will need to be replaced with a wtyled component', + }, + }, + slots: ['root'], + }, + PickersCalendarHeader: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'warning', + parentSlot: 'layout', + comments: + 'Because of the structure of the DesktopDatePicker, the `sx` prop needs to be applied to the `layout` slot', + }, + styledComponents: { + type: 'warning', + parentSlot: 'layout', + parentComponent: 'PickersLayout', + comments: + 'Because of the structure of the DesktopDatePicker and the way the popper renders, the `layout` slot will need to be replaced with a wtyled component', + }, + }, + slots: ['root', 'label', 'labelContainer', 'switchViewButton', 'switchViewIcon'], + }, + DayCalendar: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'warning', + parentSlot: 'layout', + comments: + 'Because of the structure of the DesktopDatePicker, the `sx` prop needs to be applied to the `layout` slot', + }, + styledComponents: { + type: 'warning', + parentSlot: 'layout', + parentComponent: 'PickersLayout', + comments: + 'Because of the structure of the DesktopDatePicker and the way the popper renders, the `layout` slot will need to be replaced with a wtyled component', + }, + }, + slots: ['root', 'weekDayLabel', 'weekContainer', 'weekNumberLabel', 'weekNumber'], + }, + PickersDay: { + examples: { + customTheme: { + type: 'success', + }, + sxProp: { + type: 'warning', + parentSlot: 'layout', + comments: + 'Because of the structure of the DesktopDatePicker, the `sx` prop needs to be applied to the `layout` slot', + }, + styledComponents: { + type: 'warning', + parentSlot: 'layout', + parentComponent: 'PickersLayout', + comments: + 'Because of the structure of the DesktopDatePicker and the way the popper renders, the `layout` slot will need to be replaced with a wtyled component', + }, + }, + slots: ['root', 'today'], + }, + PickersMonth: { + examples: { + customTheme: { + type: 'success', + componentProps: { views: ['month'] }, + }, + sxProp: { + type: 'warning', + parentSlot: 'layout', + comments: + 'Because of the structure of the DesktopDatePicker, the `sx` prop needs to be applied to the `layout` slot', + componentProps: { views: ['month'] }, + }, + + styledComponents: { + type: 'warning', + parentSlot: 'layout', + parentComponent: 'PickersLayout', + comments: + 'Because of the structure of the DesktopDatePicker and the way the popper renders, the `layout` slot will need to be replaced with a wtyled component', + componentProps: { views: ['month'] }, + }, + }, + slots: ['root', 'monthButton'], + }, +}; + +const pickerProps: DatePickerProps = { + views: ['day', 'month', 'year'], + monthsPerRow: 3, + yearsPerRow: 3, + minDate: dayjs(new Date(2020, 0, 1)), + maxDate: dayjs(new Date(2028, 12, 31)), + displayWeekNumber: true, +}; + +export const pickerExamples = [ + { + name: 'StaticDatePicker', + component: StaticDatePicker, + componentProps: { ...pickerProps, orientation: 'portrait' }, + examples: staticDatePickerExamples, + } as PickerExamplesType>, + { + name: 'DesktopDatePicker', + component: DesktopDatePicker, + componentProps: { + open: true, + reduceAnimations: true, + slotProps: { + popper: { + sx: { zIndex: 1 }, + disablePortal: true, + placement: 'bottom-start', + modifiers: [ + { + name: 'flip', + options: { + fallbackPlacements: ['bottom-start'], + }, + }, + ], + }, + }, + ...pickerProps, + }, + examples: datePickerExamples, + } as PickerExamplesType>, +]; 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 41bc514f3a2bc..15f6c45a5a151 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 @@ -1,7 +1,7 @@ --- productId: x-date-pickers title: React Date Time Picker component -components: DateTimePicker, DesktopDateTimePicker, MobileDateTimePicker, StaticDateTimePicker +components: DateTimePicker, DesktopDateTimePicker, MobileDateTimePicker, StaticDateTimePicker, DigitalClock, MultiSectionDigitalClock, TimeClock githubLabel: 'component: DateTimePicker' packageName: '@mui/x-date-pickers' materialDesign: https://m2.material.io/components/date-pickers diff --git a/docs/data/date-pickers/fields/ClearableBehavior.js b/docs/data/date-pickers/fields/ClearableBehavior.js new file mode 100644 index 0000000000000..c66c5e1538ba8 --- /dev/null +++ b/docs/data/date-pickers/fields/ClearableBehavior.js @@ -0,0 +1,56 @@ +import * as React from 'react'; + +import { DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateField } from '@mui/x-date-pickers/DateField'; +import Box from '@mui/material/Box'; +import Alert from '@mui/material/Alert'; + +export default function ClearableBehavior() { + const [value, setValue] = React.useState(null); + const [cleared, setCleared] = React.useState(false); + + React.useEffect(() => { + if (cleared) { + const timeout = setTimeout(() => { + setCleared(false); + }, 1500); + + return () => clearTimeout(timeout); + } + return () => {}; + }, [cleared]); + + return ( + + + + setValue(newValue)} + onClear={() => setCleared(true)} + clearable + /> + + {cleared && !value && ( + + Field cleared! + + )} + + + ); +} diff --git a/docs/data/date-pickers/fields/ClearableBehavior.tsx b/docs/data/date-pickers/fields/ClearableBehavior.tsx new file mode 100644 index 0000000000000..0aeceeaf2950e --- /dev/null +++ b/docs/data/date-pickers/fields/ClearableBehavior.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import { Dayjs } from 'dayjs'; +import { DemoItem } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateField } from '@mui/x-date-pickers/DateField'; +import Box from '@mui/material/Box'; +import Alert from '@mui/material/Alert'; + +export default function ClearableBehavior() { + const [value, setValue] = React.useState(null); + const [cleared, setCleared] = React.useState(false); + + React.useEffect(() => { + if (cleared) { + const timeout = setTimeout(() => { + setCleared(false); + }, 1500); + + return () => clearTimeout(timeout); + } + return () => {}; + }, [cleared]); + + return ( + + + + setValue(newValue)} + onClear={() => setCleared(true)} + clearable + /> + + {cleared && !value && ( + + Field cleared! + + )} + + + ); +} diff --git a/docs/data/date-pickers/fields/CustomizeClearIcon.js b/docs/data/date-pickers/fields/CustomizeClearIcon.js new file mode 100644 index 0000000000000..416541f175a8d --- /dev/null +++ b/docs/data/date-pickers/fields/CustomizeClearIcon.js @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateField } from '@mui/x-date-pickers/DateField'; +import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import BackspaceIcon from '@mui/icons-material/Backspace'; +import HighlightOffIcon from '@mui/icons-material/HighlightOff'; + +export default function CustomizeClearIcon() { + return ( + + + + + + + ); +} diff --git a/docs/data/date-pickers/fields/CustomizeClearIcon.tsx b/docs/data/date-pickers/fields/CustomizeClearIcon.tsx new file mode 100644 index 0000000000000..416541f175a8d --- /dev/null +++ b/docs/data/date-pickers/fields/CustomizeClearIcon.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateField } from '@mui/x-date-pickers/DateField'; +import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; +import BackspaceIcon from '@mui/icons-material/Backspace'; +import HighlightOffIcon from '@mui/icons-material/HighlightOff'; + +export default function CustomizeClearIcon() { + return ( + + + + + + + ); +} diff --git a/docs/data/date-pickers/fields/CustomizeClearIcon.tsx.preview b/docs/data/date-pickers/fields/CustomizeClearIcon.tsx.preview new file mode 100644 index 0000000000000..bb0651a2a82b3 --- /dev/null +++ b/docs/data/date-pickers/fields/CustomizeClearIcon.tsx.preview @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/docs/data/date-pickers/fields/fields.md b/docs/data/date-pickers/fields/fields.md index f0821ea234f69..9bd7260fff468 100644 --- a/docs/data/date-pickers/fields/fields.md +++ b/docs/data/date-pickers/fields/fields.md @@ -74,3 +74,17 @@ Please only use it if needed. ::: {{"demo": "ControlledSelectedSectionsSingleInputRangeField.js", "defaultCodeOpen": false }} + +### Clearable behavior + +You can use the `clearable` prop to enable the clearing behavior on a field. You can also add an event handler using the `onClear` callback prop. + +:::info +For **multi-input** range fields the clearable behavior is not supported yet. +::: + +{{"demo": "ClearableBehavior.js"}} + +You can also customize the icon you want to be displayed inside the clear `IconButton`. + +{{"demo": "CustomizeClearIcon.js"}} diff --git a/docs/data/date-pickers/getting-started/getting-started.md b/docs/data/date-pickers/getting-started/getting-started.md index a8d568899bb52..da81844bab88a 100644 --- a/docs/data/date-pickers/getting-started/getting-started.md +++ b/docs/data/date-pickers/getting-started/getting-started.md @@ -18,6 +18,10 @@ Using your favorite package manager, install: - `@mui/x-date-pickers` for the free community version or `@mui/x-date-pickers-pro` for the commercial version. - The date library to manipulate the date. +:::warning +The `next` tag is used to download the latest v7 **pre-release** version. +::: + {{"component": "modules/components/PickersInstallationInstructions.js"}} :::info diff --git a/docs/data/date-pickers/localization/data.json b/docs/data/date-pickers/localization/data.json index 0c8369ecba5e5..2adcf451219a4 100644 --- a/docs/data/date-pickers/localization/data.json +++ b/docs/data/date-pickers/localization/data.json @@ -1,98 +1,106 @@ [ + { + "languageTag": "eu", + "importName": "eu", + "localeName": "Basque", + "missingKeysCount": 0, + "totalKeysCount": 37, + "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/eu.ts" + }, { "languageTag": "be-BY", "importName": "beBY", "localeName": "Belarusian", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/beBY.ts" }, { "languageTag": "ca-ES", "importName": "caES", "localeName": "Catalan", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/caES.ts" }, { "languageTag": "zh-HK", "importName": "zhHK", "localeName": "Chinese (Hong Kong)", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/zhHK.ts" }, { "languageTag": "zh-CN", "importName": "zhCN", "localeName": "Chinese (Simplified)", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/zhCN.ts" }, { "languageTag": "cs-CZ", "importName": "csCZ", "localeName": "Czech", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/csCZ.ts" }, { "languageTag": "da-DK", "importName": "daDK", "localeName": "Danish", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/daDK.ts" }, { "languageTag": "nl-NL", "importName": "nlNL", "localeName": "Dutch", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/nlNL.ts" }, { "languageTag": "fi-FI", "importName": "fiFI", "localeName": "Finnish", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/fiFI.ts" }, { "languageTag": "fr-FR", "importName": "frFR", "localeName": "French", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/frFR.ts" }, { "languageTag": "de-DE", "importName": "deDE", "localeName": "German", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/deDE.ts" }, { "languageTag": "el-GR", "importName": "elGR", "localeName": "Greek", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/elGR.ts" }, { "languageTag": "he-IL", "importName": "heIL", "localeName": "Hebrew", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/heIL.ts" }, { @@ -100,79 +108,79 @@ "importName": "huHU", "localeName": "Hungarian", "missingKeysCount": 1, - "totalKeysCount": 36, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/huHU.ts" }, { "languageTag": "is-IS", "importName": "isIS", "localeName": "Icelandic", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/isIS.ts" }, { "languageTag": "it-IT", "importName": "itIT", "localeName": "Italian", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/itIT.ts" }, { "languageTag": "ja-JP", "importName": "jaJP", "localeName": "Japanese", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/jaJP.ts" }, { "languageTag": "kz-KZ", "importName": "kzKZ", "localeName": "Kazakh", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/kzKZ.ts" }, { "languageTag": "ko-KR", "importName": "koKR", "localeName": "Korean", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/koKR.ts" }, { "languageTag": "nb-NO", "importName": "nbNO", "localeName": "Norwegian (Bokmål)", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/nbNO.ts" }, { "languageTag": "fa-IR", "importName": "faIR", "localeName": "Persian", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/faIR.ts" }, { "languageTag": "pl-PL", "importName": "plPL", "localeName": "Polish", - "missingKeysCount": 8, - "totalKeysCount": 36, + "missingKeysCount": 9, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/plPL.ts" }, { "languageTag": "pt-BR", "importName": "ptBR", "localeName": "Portuguese (Brazil)", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ptBR.ts" }, { @@ -180,23 +188,23 @@ "importName": "roRO", "localeName": "Romanian", "missingKeysCount": 1, - "totalKeysCount": 36, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/roRO.ts" }, { "languageTag": "ru-RU", "importName": "ruRU", "localeName": "Russian", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ruRU.ts" }, { "languageTag": "sk-SK", "importName": "skSK", "localeName": "Slovak", - "missingKeysCount": 1, - "totalKeysCount": 36, + "missingKeysCount": 2, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/skSK.ts" }, { @@ -204,47 +212,47 @@ "importName": "esES", "localeName": "Spanish", "missingKeysCount": 0, - "totalKeysCount": 36, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/esES.ts" }, { "languageTag": "sv-SE", "importName": "svSE", "localeName": "Swedish", - "missingKeysCount": 8, - "totalKeysCount": 36, + "missingKeysCount": 9, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/svSE.ts" }, { "languageTag": "tr-TR", "importName": "trTR", "localeName": "Turkish", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/trTR.ts" }, { "languageTag": "uk-UA", "importName": "ukUA", "localeName": "Ukrainian", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/ukUA.ts" }, { "languageTag": "ur-PK", "importName": "urPK", "localeName": "Urdu (Pakistan)", - "missingKeysCount": 8, - "totalKeysCount": 36, + "missingKeysCount": 9, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/urPK.ts" }, { "languageTag": "vi-VN", "importName": "viVN", "localeName": "Vietnamese", - "missingKeysCount": 0, - "totalKeysCount": 36, + "missingKeysCount": 1, + "totalKeysCount": 37, "githubLink": "https://github.com/mui/mui-x/blob/master/packages/x-date-pickers/src/locales/viVN.ts" } ] diff --git a/docs/data/date-pickers/overview/CommonlyUsedComponents.js b/docs/data/date-pickers/overview/CommonlyUsedComponents.js index 3bf2d5d176ca7..71e181a247898 100644 --- a/docs/data/date-pickers/overview/CommonlyUsedComponents.js +++ b/docs/data/date-pickers/overview/CommonlyUsedComponents.js @@ -1,4 +1,5 @@ import * as React from 'react'; +import { styled } from '@mui/material/styles'; import Tooltip from '@mui/material/Tooltip'; import Stack from '@mui/material/Stack'; import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; @@ -9,6 +10,18 @@ import { TimePicker } from '@mui/x-date-pickers/TimePicker'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +const ProSpan = styled('span')({ + display: 'inline-block', + height: '1em', + width: '1em', + verticalAlign: 'middle', + marginLeft: '0.3em', + marginBottom: '0.08em', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundImage: 'url(https://mui.com/static/x/pro.svg)', +}); + function Label({ componentName, valueType, isProOnly }) { const content = ( @@ -20,8 +33,8 @@ function Label({ componentName, valueType, isProOnly }) { return ( - - + + {content} diff --git a/docs/data/date-pickers/overview/CommonlyUsedComponents.tsx b/docs/data/date-pickers/overview/CommonlyUsedComponents.tsx index 41459182b6f25..1b301e5b83817 100644 --- a/docs/data/date-pickers/overview/CommonlyUsedComponents.tsx +++ b/docs/data/date-pickers/overview/CommonlyUsedComponents.tsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import { styled } from '@mui/material/styles'; import Tooltip from '@mui/material/Tooltip'; import Stack from '@mui/material/Stack'; import { DemoContainer, DemoItem } from '@mui/x-date-pickers/internals/demo'; @@ -9,6 +10,18 @@ import { TimePicker } from '@mui/x-date-pickers/TimePicker'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +const ProSpan = styled('span')({ + display: 'inline-block', + height: '1em', + width: '1em', + verticalAlign: 'middle', + marginLeft: '0.3em', + marginBottom: '0.08em', + backgroundSize: 'contain', + backgroundRepeat: 'no-repeat', + backgroundImage: 'url(https://mui.com/static/x/pro.svg)', +}); + function Label({ componentName, valueType, @@ -28,8 +41,8 @@ function Label({ return ( - - + + {content} diff --git a/docs/data/date-pickers/time-picker/time-picker.md b/docs/data/date-pickers/time-picker/time-picker.md index 41faeb501ecae..ed5d6492e8e2b 100644 --- a/docs/data/date-pickers/time-picker/time-picker.md +++ b/docs/data/date-pickers/time-picker/time-picker.md @@ -1,7 +1,7 @@ --- productId: x-date-pickers title: React Time Picker component -components: TimePicker, DesktopTimePicker, MobileTimePicker, StaticTimePicker +components: TimePicker, DesktopTimePicker, MobileTimePicker, StaticTimePicker, DigitalClock, MultiSectionDigitalClock, TimeClock githubLabel: 'component: TimePicker' packageName: '@mui/x-date-pickers' materialDesign: https://m2.material.io/components/time-pickers diff --git a/docs/data/date-pickers/validation/validation.md b/docs/data/date-pickers/validation/validation.md index 4116308df95db..1d16bec6a620f 100644 --- a/docs/data/date-pickers/validation/validation.md +++ b/docs/data/date-pickers/validation/validation.md @@ -1,6 +1,6 @@ --- productId: x-date-pickers -components: DatePicker, DesktopDatePicker, MobileDatePicker, StaticDatePicker, TimePicker, DesktopTimePicker, MobileTimePicker, StaticTimePicker, DateTimePicker, DesktopDateTimePicker, MobileDateTimePicker, StaticDateTimePicker, DateRangePicker, DesktopDateRangePicker, MobileDateRangePicker, StaticDateRangePicker +components: DatePicker, DesktopDatePicker, MobileDatePicker, StaticDatePicker, TimePicker, DesktopTimePicker, MobileTimePicker, StaticTimePicker, DateTimePicker, DesktopDateTimePicker, MobileDateTimePicker, StaticDateTimePicker, DateRangePicker, DesktopDateRangePicker, MobileDateRangePicker, StaticDateRangePicker, DateCalendar githubLabel: 'component: pickers' packageName: '@mui/x-date-pickers' --- @@ -108,6 +108,10 @@ If you know that all days of some months are disabled—you can provide the [`sh Same with the [`shouldDisableYear`](#disable-specific-years) prop for the `year` view. ::: +:::success +Please note that `shouldDisableDate` will execute on every date rendered in the `day` view. Expensive computations in this validation function can impact performance. +::: + #### Disable specific dates in range components [](/x/introduction/licensing/#pro-plan) For components supporting date range edition (`DateRangePicker`, `DateTimeRangePicker` 🚧)—the `shouldDisableDate` prop receives a second argument to differentiate the start and the end date. diff --git a/docs/data/introduction/overview/overview.md b/docs/data/introduction/overview/overview.md index 6fe4380551add..97084bfe954bf 100644 --- a/docs/data/introduction/overview/overview.md +++ b/docs/data/introduction/overview/overview.md @@ -4,7 +4,7 @@ title: Overview # MUI X - Overview -

MUI X is a collection of advanced React UI components for complex use cases.

+

MUI X is a collection of advanced React UI components for complex use cases. Use the native integration with Material UI or extend your design system.

## Introduction diff --git a/docs/data/introduction/support/support.md b/docs/data/introduction/support/support.md index 2c8f9225195a2..55681910a65cb 100644 --- a/docs/data/introduction/support/support.md +++ b/docs/data/introduction/support/support.md @@ -32,4 +32,4 @@ Including your Order ID (or Support key) in the issue helps us prioritize the is 1. MUI X Pro: maintainers give these issues more attention than the ones from the community. 2. MUI X Premium: The same as MUI X Pro, but with higher priority. -3. MUI X Priority support add-on (coming soon): Provides a 24h SLA for the first answer. +3. MUI X Priority Support add-on (coming soon): Provides a 24h SLA for the first answer. diff --git a/docs/data/migration/migration-charts-v6/migration-charts-v6.md b/docs/data/migration/migration-charts-v6/migration-charts-v6.md new file mode 100644 index 0000000000000..96e02fd5f2822 --- /dev/null +++ b/docs/data/migration/migration-charts-v6/migration-charts-v6.md @@ -0,0 +1,36 @@ +--- +productId: x-charts +--- + +# Migration from v6 to v7 + +

This guide describes the changes needed to migrate Charts from v6 to v7.

+ +## Introduction + +This is a reference guide for upgrading `@mui/x-charts` from v6 to v7. +The change between v6 and v7 is mostly here to match the version with other MUI X packages. +Not big breaking changes are expected. + +## Start using the new release + +In `package.json`, change the version of the charts package to `next`. + +```diff +-"@mui/x-charts": "6.x.x", ++"@mui/x-charts": "next", +``` + +## Breaking changes + +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. + +### Renaming + +Some types got renamed for coherence + +| v6 | v7 | +| :------------------------------ | :--------------------- | +| ChartsTooltipSlotComponentProps | ChartsTooltipSlotProps | +| ChartsTooltipSlotsComponent | ChartsTooltipSlots | 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 new file mode 100644 index 0000000000000..1ebfbeed7f218 --- /dev/null +++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md @@ -0,0 +1,173 @@ +--- +productId: x-data-grid +--- + +# Migration from v6 to v7 + + + +

This guide describes the changes needed to migrate the Data Grid from v6 to v7.

+ + + +## Start using the new release + +In `package.json`, change the version of the data grid package to `next`. + +```diff +-"@mui/x-data-grid": "^6.0.0", ++"@mui/x-data-grid": "next", +``` + +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. +Described below are the steps needed to migrate from v6 to v7. + + + +## Breaking changes + +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. + + + +### Removed props + +- The deprecated props `components` and `componentsProps` have been removed. Use `slots` and `slotProps` instead. See [components section](/x/react-data-grid/components/) for more details. + + + + + + + + + + + +### Print export + +- The print export will now only print the selected rows if there are any. + If there are no selected rows, it will print all rows. This makes the print export consistent with the other exports. + You can [customize the rows to export by using the `getRowsToExport` function](/x/react-data-grid/export/#customizing-the-rows-to-export). + +### Filtering + +- The `getApplyFilterFnV7` in `GridFilterOperator` was renamed to `getApplyFilterFn`. + If you use `getApplyFilterFnV7` directly - rename it to `getApplyFilterFn`. + +- The signature of the function returned by `getApplyFilterFn` has changed for performance reasons: + +```diff + const getApplyFilterFn: GetApplyFilterFn = (filterItem) => { + if (!filterItem.value) { + return null; + } + + const filterRegex = new RegExp(escapeRegExp(filterItem.value), 'i'); +- return (cellParams) => { +- const { value } = cellParams; ++ return (value, row, colDef, apiRef) => { + return value != null ? filterRegex.test(String(value)) : false; + }; + } +``` + +- The `getApplyQuickFilterFnV7` in `GridColDef` was renamed to `getApplyQuickFilterFn`. + If you use `getApplyQuickFilterFnV7` directly - rename it to `getApplyQuickFilterFn`. + +- The signature of the function returned by `getApplyQuickFilterFn` has changed for performance reasons: + +```diff + const getGridStringQuickFilterFn: GetApplyQuickFilterFn = (value) => { + if (!value) { + return null; + } + const filterRegex = new RegExp(escapeRegExp(value), 'i'); +- return (cellParams) => { +- const { formattedValue } = cellParams; ++ return (value, row, column, apiRef) => { ++ let formattedValue = apiRef.current.getRowFormattedValue(row, column); + return formattedValue != null ? filterRegex.test(formattedValue.toString()) : false; + }; + }; +``` + + + + + + + + + + diff --git a/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md new file mode 100644 index 0000000000000..0645f478a89f3 --- /dev/null +++ b/docs/data/migration/migration-pickers-v6/migration-pickers-v6.md @@ -0,0 +1,65 @@ +--- +productId: x-date-pickers +--- + +# Migration from v6 to v7 + + + +

This guide describes the changes needed to migrate the Date and Time Pickers from v6 to v7.

+ +## Introduction + +TBD + +## Start using the new release + +In `package.json`, change the version of the date pickers package to `next`. + +```diff +-"@mui/x-date-pickers": "6.x.x", ++"@mui/x-date-pickers": "next", +``` + +## Breaking changes + +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. + +### Rename `components` to `slots` + +The `components` and `componentsProps` props are renamed to `slots` and `slotProps` props respectively. +This is a slow and ongoing effort between the different MUI libraries. +To smooth the transition, they were deprecated during the [v6](/x/migration/migration-pickers-v5/#rename-components-to-slots-optional). +And are removed from the v7. + +If not already done, this modification can be handled by the codemod + +```bash +npx @mui/x-codemod v7.0.0/pickers/ +``` + +Take a look at [the RFC](https://github.com/mui/material-ui/issues/33416) for more information. + +:::warning +If this codemod is applied on a component with both a `slots` and a `components` prop, the output will contain two `slots` props. +You are then responsible for merging those two props manually. + +For example: + +```tsx +// Before running the codemod + + +// After running the codemod + +``` + +The same applies to `slotProps` and `componentsProps`. +::: 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 new file mode 100644 index 0000000000000..d19064564e0fc --- /dev/null +++ b/docs/data/migration/migration-tree-view-v6/migration-tree-view-v6.md @@ -0,0 +1,27 @@ +--- +productId: x-tree-view +--- + +# Migration from v6 to v7 + + + +

This guide describes the changes needed to migrate the Tree View from v6 to v7.

+ +## Introduction + +TBD + +## Start using the alpha release + +In `package.json`, change the version of the tree view package to `next`. + +```diff +-"@mui/x-tree-view": "6.x.x", ++"@mui/x-tree-view": "next", +``` + +## Breaking changes + +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. diff --git a/docs/data/pages.ts b/docs/data/pages.ts index 7ade010f41314..b882dc772dfcd 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -1,4 +1,4 @@ -import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import type { MuiPage } from 'docs/src/MuiPage'; import dataGridComponentApi from './data-grid-component-api-pages'; import pickersComponentApi from './date-pickers-component-api-pages'; import chartsComponentApi from './charts-component-api-pages'; @@ -6,8 +6,8 @@ import treeViewComponentApi from './tree-view-component-api-pages'; const pages: MuiPage[] = [ { - pathname: '/blog/mui-x-v6/', - title: "✨ What's new in v6? ✨", + pathname: '/x/whats-new', + title: "What's new in MUI X", }, { pathname: '/x/introduction-group', @@ -36,6 +36,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/column-definition' }, { pathname: '/x/react-data-grid/column-dimensions' }, { pathname: '/x/react-data-grid/column-visibility' }, + { pathname: '/x/react-data-grid/custom-columns' }, { pathname: '/x/react-data-grid/column-header' }, { pathname: '/x/react-data-grid/column-menu' }, { pathname: '/x/react-data-grid/column-spanning' }, @@ -87,7 +88,14 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/export' }, { pathname: '/x/react-data-grid/clipboard', title: 'Copy and paste', newFeature: true }, { pathname: '/x/react-data-grid/components', title: 'Custom subcomponents' }, - { pathname: '/x/react-data-grid/style' }, + { + pathname: '/x/react-data-grid/style-group', + title: 'Style', + children: [ + { pathname: '/x/react-data-grid/style', title: 'Overview' }, + { pathname: '/x/react-data-grid/style-recipes', title: 'Recipes' }, + ], + }, { pathname: '/x/react-data-grid/localization' }, { pathname: '/x/react-data-grid/scrolling' }, { pathname: '/x/react-data-grid/virtualization' }, @@ -104,6 +112,39 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/pivoting', title: 'Pivoting 🚧', plan: 'premium' }, ], }, + { + pathname: '/x/react-data-grid/server-side-data-group', + title: 'Server-side data 🚧', + plan: 'pro', + children: [ + { pathname: '/x/react-data-grid/server-side-data', title: 'Overview' }, + { + pathname: '/x/react-data-grid/server-side-data/lazy-loading', + title: 'Lazy loading 🚧', + plan: 'pro', + }, + { + pathname: '/x/react-data-grid/server-side-data/infinite-loading', + title: 'Infinite loading 🚧', + plan: 'pro', + }, + { + pathname: '/x/react-data-grid/server-side-data/tree-data', + title: 'Tree data 🚧', + plan: 'pro', + }, + { + pathname: '/x/react-data-grid/server-side-data/row-grouping', + title: 'Row grouping 🚧', + plan: 'pro', + }, + { + pathname: '/x/react-data-grid/server-side-data/aggregation', + title: 'Aggregation 🚧', + plan: 'premium', + }, + ], + }, { title: 'Advanced', pathname: '/x/react-data-grid/advanced', @@ -327,7 +368,7 @@ const pages: MuiPage[] = [ children: [ { pathname: '/x/react-date-pickers/custom-components', - title: 'Custom subcomponents', + title: 'Custom slots and subcomponents', }, { pathname: '/x/react-date-pickers/custom-layout' }, { pathname: '/x/react-date-pickers/custom-field' }, @@ -344,7 +385,7 @@ const pages: MuiPage[] = [ }, { pathname: '/x/react-charts-group', - title: 'Charts 🧪', + title: 'Charts', newFeature: true, children: [ { pathname: '/x/react-charts', title: 'Overview' }, @@ -396,13 +437,13 @@ const pages: MuiPage[] = [ title: 'API Reference', children: [...chartsComponentApi], }, - { pathname: '/x/react-charts/funnel', title: 'Funnel 🚧' }, - { pathname: '/x/react-charts/gantt', title: 'Gantt 🚧' }, { pathname: '/x/react-charts/gauge', title: 'Gauge 🚧' }, + { pathname: '/x/react-charts/tree-map', title: 'Tree map 🚧' }, { pathname: '/x/react-charts/heat-map', title: 'Heat map 🚧' }, { pathname: '/x/react-charts/radar', title: 'Radar 🚧' }, - { pathname: '/x/react-charts/sankey', title: 'Sankey 🚧' }, - { pathname: '/x/react-charts/tree-map', title: 'Tree map 🚧' }, + { pathname: '/x/react-charts/funnel', title: 'Funnel 🚧', plan: 'pro' }, + { pathname: '/x/react-charts/sankey', title: 'Sankey 🚧', plan: 'pro' }, + { pathname: '/x/react-charts/gantt', title: 'Gantt 🚧', plan: 'pro' }, ], }, { @@ -423,6 +464,25 @@ const pages: MuiPage[] = [ pathname: '/x/migration-group', title: 'Migration', children: [ + { + pathname: '/x/migration-v7', + subheader: 'Upgrade to v7', + children: [ + { pathname: '/x/migration/migration-data-grid-v6', title: 'Breaking changes: Data Grid' }, + { + pathname: '/x/migration/migration-pickers-v6', + title: 'Breaking changes: Date and Time Pickers', + }, + { + pathname: '/x/migration/migration-tree-view-v6', + title: 'Breaking changes: Tree View', + }, + { + pathname: '/x/migration/migration-charts-v6', + title: 'Breaking changes: Charts', + }, + ], + }, { pathname: '/x/migration-v6', subheader: 'Upgrade to v6', diff --git a/docs/data/tree-view-component-api-pages.ts b/docs/data/tree-view-component-api-pages.ts index 6a91c1202fb10..d721136445373 100644 --- a/docs/data/tree-view-component-api-pages.ts +++ b/docs/data/tree-view-component-api-pages.ts @@ -1,4 +1,4 @@ -import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; +import type { MuiPage } from 'docs/src/MuiPage'; export default [ { pathname: '/x/api/tree-view/tree-item', title: 'TreeItem' }, diff --git a/docs/data/tree-view/getting-started/getting-started.md b/docs/data/tree-view/getting-started/getting-started.md index 4d921b1ce5ce7..52ba88a1bfc8c 100644 --- a/docs/data/tree-view/getting-started/getting-started.md +++ b/docs/data/tree-view/getting-started/getting-started.md @@ -14,17 +14,21 @@ waiAria: https://www.w3.org/WAI/ARIA/apg/patterns/treeview/ Using your favorite package manager, install `@mui/x-tree-view`: +:::warning +The `next` tag is used to download the latest v7 **pre-release** version. +::: + ```bash npm -npm install @mui/x-tree-view +npm install @mui/x-tree-view@next ``` ```bash yarn -yarn add @mui/x-tree-view +yarn add @mui/x-tree-view@next ``` ```bash pnpm -pnpm add @mui/x-tree-view +pnpm add @mui/x-tree-view@next ``` diff --git a/docs/data/tree-view/overview/overview.md b/docs/data/tree-view/overview/overview.md index 1438db4ab7381..dabd994c8d325 100644 --- a/docs/data/tree-view/overview/overview.md +++ b/docs/data/tree-view/overview/overview.md @@ -1,5 +1,5 @@ --- -productId: material-ui +productId: x-tree-view title: Tree View React component components: TreeView, TreeItem githubLabel: 'component: tree view' diff --git a/docs/data/whats-new/WhatsNewLayout.js b/docs/data/whats-new/WhatsNewLayout.js new file mode 100644 index 0000000000000..907a33d27f8dd --- /dev/null +++ b/docs/data/whats-new/WhatsNewLayout.js @@ -0,0 +1,256 @@ +import * as React from 'react'; +import Link from '@mui/material/Link'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import CardContent from '@mui/material/CardContent'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import { alpha } from '@mui/material/styles'; + +const blogs = [ + { + title: 'MUI X v6.11.0', + description: 'A roundup of all new features since v6.0.0.', + announcementDate: 'Monday, Aug 14, 2023', + url: 'https://mui.com/blog/mui-x-mid-v6-features/', + highlightList: [ + { + title: 'Support for timezone on Date and Time Pickers', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#support-for-time-zones', + }, + { + title: 'Digital Clock', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#digital-clock', + }, + { + title: 'Filters on Data Grid column headers', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#filter-on-column-headers', + }, + { + title: 'Copy and Paste on Data Grid', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#copy-and-paste', + }, + { + title: 'Charts Alpha 🧪', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#charts-alpha-version', + }, + { + title: 'TreeView migration from lab', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#tree-view-is-moving-to-mui-x', + }, + ], + }, + { + title: 'MUI X v6.0.0', + description: + 'A new major is available, with many new features and improvements.', + announcementDate: 'Monday, Mar 06, 2023', + url: 'https://mui.com/blog/mui-x-v6/', + highlightList: [ + { + title: 'Date and time fields', + url: 'https://mui.com/blog/mui-x-v6/#fields-the-new-default-input-gt-for-pickers', + }, + { + title: 'Date Range shortcuts', + url: 'https://mui.com/blog/mui-x-v6/#shortcuts-for-picking-specific-dates-in-a-calendar', + }, + { + title: 'Improved layout customization', + url: 'https://mui.com/blog/mui-x-v6/#improved-layout-customization', + }, + { + title: 'Edit ranges with drag and drop', + url: 'https://mui.com/blog/mui-x-v6/#edit-date-ranges-with-drag-and-drop', + }, + { + title: 'New Column menu', + url: 'https://mui.com/blog/mui-x-v6/#improved-column-menu', + }, + { + title: 'ApiRef in community', + url: 'https://mui.com/blog/mui-x-v6/#apiref-moved-to-the-mit-community-version', + }, + { + title: 'Cell selection', + url: 'https://mui.com/blog/mui-x-v6/#cell-selection', + }, + ], + }, + { + title: 'Date Pickers v5.0.0', + description: + 'After some months of polishing in pre-releases, the Date Pickers finally get a stable.', + announcementDate: 'Monday, Sep 22, 2022', + url: 'https://mui.com/blog/date-pickers-stable-v5/', + highlightList: [ + { + title: 'Better APIs', + url: 'https://mui.com/blog/date-pickers-stable-v5/#better-apis-and-improved-customization', + }, + { + title: 'Easier customization', + url: 'https://mui.com/blog/date-pickers-stable-v5/#better-apis-and-improved-customization', + }, + { + title: 'Integrated localization', + url: 'https://mui.com/blog/date-pickers-stable-v5/#integrated-localization', + }, + ], + }, + { + title: 'Data Grid v5.15', + description: + 'This version brings an amazing set of new supported use cases with the Data Grid Premium.', + announcementDate: 'Monday, Aug 12, 2022', + url: 'https://mui.com/blog/aggregation-functions/', + highlightList: [ + { + title: 'Aggregation in summary rows and row groups', + url: 'https://mui.com/blog/aggregation-functions/#wait-what-is-an-aggregation-function', + }, + { + title: 'Row pinning', + url: 'https://mui.com/blog/aggregation-functions/#row-pinning', + }, + ], + }, + { + title: 'New Premium plan', + description: + 'Premium plan announcement, including the most advanced features for data analysis and management.', + announcementDate: 'Thursday, May 12, 2022', + url: 'https://mui.com/blog/premium-plan-release/', + highlightList: [ + { title: 'Row Grouping', url: '/x/react-data-grid/row-grouping/' }, + { title: 'Excel export', url: '/x/react-data-grid/export/#exported-rows' }, + ], + }, + { + title: 'MUI X v5.0.0', + description: 'A new virtualization engine, and improvements in several APIs.', + announcementDate: 'Monday, Nov 22, 2021', + url: 'https://mui.com/blog/mui-x-v5/', + highlightList: [ + { + title: 'New virtualization engine', + url: 'https://mui.com/blog/mui-x-v5/#a-new-virtualization-engine', + }, + { + title: 'Improved state management', + url: 'https://mui.com/blog/mui-x-v5/#improved-state-management', + }, + { + title: 'Simplified style customization', + url: 'https://mui.com/blog/mui-x-v5/#simplified-style-customization', + }, + ], + }, +]; + +function BlogCard(props) { + return ( + + theme.palette.mode === 'dark' + ? 'rgba(0, 27, 55, 0.2)' + : `${alpha(theme.palette.grey[50], 0.4)}`, + borderColor: 'divider', + [`& .MuiTypography-root`]: { + fontFamily: 'IBM Plex Sans', + }, + }} + component="article" + variant="outlined" + > + + + + {props.blog.announcementDate} + + + {props.blog.title} + + + + + + {props.blog.description} + + + + {props.blog.highlightList.map((item) => ( + + + {item.title} + + + ))} + + + + + + + + ); +} + +export default function WhatsNewLayout() { + return ( + + {blogs.map((blog) => ( + + + + ))} + + ); +} diff --git a/docs/data/whats-new/WhatsNewLayout.tsx b/docs/data/whats-new/WhatsNewLayout.tsx new file mode 100644 index 0000000000000..ddf6a0b801198 --- /dev/null +++ b/docs/data/whats-new/WhatsNewLayout.tsx @@ -0,0 +1,264 @@ +import * as React from 'react'; +import Link from '@mui/material/Link'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import Box from '@mui/material/Box'; +import Grid from '@mui/material/Grid'; +import Card from '@mui/material/Card'; +import CardActions from '@mui/material/CardActions'; +import CardContent from '@mui/material/CardContent'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import { alpha } from '@mui/material/styles'; + +type Blog = { + title: string; + announcementDate: string; + description: string; + url: string; + highlightList: { title: string; url: string }[]; +}; + +const blogs: Blog[] = [ + { + title: 'MUI X v6.11.0', + description: 'A roundup of all new features since v6.0.0.', + announcementDate: 'Monday, Aug 14, 2023', + url: 'https://mui.com/blog/mui-x-mid-v6-features/', + highlightList: [ + { + title: 'Support for timezone on Date and Time Pickers', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#support-for-time-zones', + }, + { + title: 'Digital Clock', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#digital-clock', + }, + { + title: 'Filters on Data Grid column headers', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#filter-on-column-headers', + }, + { + title: 'Copy and Paste on Data Grid', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#copy-and-paste', + }, + { + title: 'Charts Alpha 🧪', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#charts-alpha-version', + }, + { + title: 'TreeView migration from lab', + url: 'https://mui.com/blog/mui-x-mid-v6-features/#tree-view-is-moving-to-mui-x', + }, + ], + }, + { + title: 'MUI X v6.0.0', + description: + 'A new major is available, with many new features and improvements.', + announcementDate: 'Monday, Mar 06, 2023', + url: 'https://mui.com/blog/mui-x-v6/', + highlightList: [ + { + title: 'Date and time fields', + url: 'https://mui.com/blog/mui-x-v6/#fields-the-new-default-input-gt-for-pickers', + }, + { + title: 'Date Range shortcuts', + url: 'https://mui.com/blog/mui-x-v6/#shortcuts-for-picking-specific-dates-in-a-calendar', + }, + { + title: 'Improved layout customization', + url: 'https://mui.com/blog/mui-x-v6/#improved-layout-customization', + }, + { + title: 'Edit ranges with drag and drop', + url: 'https://mui.com/blog/mui-x-v6/#edit-date-ranges-with-drag-and-drop', + }, + { + title: 'New Column menu', + url: 'https://mui.com/blog/mui-x-v6/#improved-column-menu', + }, + { + title: 'ApiRef in community', + url: 'https://mui.com/blog/mui-x-v6/#apiref-moved-to-the-mit-community-version', + }, + { + title: 'Cell selection', + url: 'https://mui.com/blog/mui-x-v6/#cell-selection', + }, + ], + }, + { + title: 'Date Pickers v5.0.0', + description: + 'After some months of polishing in pre-releases, the Date Pickers finally get a stable.', + announcementDate: 'Monday, Sep 22, 2022', + url: 'https://mui.com/blog/date-pickers-stable-v5/', + highlightList: [ + { + title: 'Better APIs', + url: 'https://mui.com/blog/date-pickers-stable-v5/#better-apis-and-improved-customization', + }, + { + title: 'Easier customization', + url: 'https://mui.com/blog/date-pickers-stable-v5/#better-apis-and-improved-customization', + }, + { + title: 'Integrated localization', + url: 'https://mui.com/blog/date-pickers-stable-v5/#integrated-localization', + }, + ], + }, + { + title: 'Data Grid v5.15', + description: + 'This version brings an amazing set of new supported use cases with the Data Grid Premium.', + announcementDate: 'Monday, Aug 12, 2022', + url: 'https://mui.com/blog/aggregation-functions/', + highlightList: [ + { + title: 'Aggregation in summary rows and row groups', + url: 'https://mui.com/blog/aggregation-functions/#wait-what-is-an-aggregation-function', + }, + { + title: 'Row pinning', + url: 'https://mui.com/blog/aggregation-functions/#row-pinning', + }, + ], + }, + { + title: 'New Premium plan', + description: + 'Premium plan announcement, including the most advanced features for data analysis and management.', + announcementDate: 'Thursday, May 12, 2022', + url: 'https://mui.com/blog/premium-plan-release/', + highlightList: [ + { title: 'Row Grouping', url: '/x/react-data-grid/row-grouping/' }, + { title: 'Excel export', url: '/x/react-data-grid/export/#exported-rows' }, + ], + }, + { + title: 'MUI X v5.0.0', + description: 'A new virtualization engine, and improvements in several APIs.', + announcementDate: 'Monday, Nov 22, 2021', + url: 'https://mui.com/blog/mui-x-v5/', + highlightList: [ + { + title: 'New virtualization engine', + url: 'https://mui.com/blog/mui-x-v5/#a-new-virtualization-engine', + }, + { + title: 'Improved state management', + url: 'https://mui.com/blog/mui-x-v5/#improved-state-management', + }, + { + title: 'Simplified style customization', + url: 'https://mui.com/blog/mui-x-v5/#simplified-style-customization', + }, + ], + }, +]; + +function BlogCard(props: { blog: Blog }) { + return ( + + theme.palette.mode === 'dark' + ? 'rgba(0, 27, 55, 0.2)' + : `${alpha(theme.palette.grey[50], 0.4)}`, + borderColor: 'divider', + [`& .MuiTypography-root`]: { + fontFamily: 'IBM Plex Sans', + }, + }} + component="article" + variant="outlined" + > + + + + {props.blog.announcementDate} + + + {props.blog.title} + + + + + + {props.blog.description} + + + + {props.blog.highlightList.map((item) => ( + + + {item.title} + + + ))} + + + + + + + + ); +} + +export default function WhatsNewLayout() { + return ( + + {blogs.map((blog) => ( + + + + ))} + + ); +} diff --git a/docs/data/whats-new/WhatsNewLayout.tsx.preview b/docs/data/whats-new/WhatsNewLayout.tsx.preview new file mode 100644 index 0000000000000..b701ffab9b3b5 --- /dev/null +++ b/docs/data/whats-new/WhatsNewLayout.tsx.preview @@ -0,0 +1,7 @@ + + {blogs.map((blog) => ( + + + + ))} + \ No newline at end of file diff --git a/docs/data/whats-new/whats-new.md b/docs/data/whats-new/whats-new.md new file mode 100644 index 0000000000000..3bde5a552f383 --- /dev/null +++ b/docs/data/whats-new/whats-new.md @@ -0,0 +1,5 @@ +# What's new in MUI X + +

Discover what's new in the latest versions.

+ +{{"demo": "WhatsNewLayout.js", "hideToolbar": true, "bg": "inline"}} diff --git a/docs/next.config.js b/docs/next.config.js index 2320ae81e6073..c7c8ef6f2fc43 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -1,7 +1,7 @@ const path = require('path'); const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); // const withTM = require('next-transpile-modules')(['@mui/monorepo']); -const withDocsInfra = require('@mui/monorepo/docs/nextConfigDocsInfra'); +const withDocsInfra = require('docs/nextConfigDocsInfra'); const pkg = require('../package.json'); const dataGridPkg = require('../packages/grid/x-data-grid/package.json'); const datePickersPkg = require('../packages/x-date-pickers/package.json'); diff --git a/docs/package.json b/docs/package.json index 6fed129a4d10c..f7ab046191b68 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,27 +20,28 @@ "populate:demos": "cross-env BABEL_ENV=development babel-node --extensions \".tsx,.ts,.js\" scripts/populatePickersDemos" }, "dependencies": { - "@babel/core": "^7.22.15", + "@babel/core": "^7.23.2", "@babel/plugin-transform-object-assign": "^7.22.5", - "@babel/runtime-corejs2": "^7.22.15", + "@babel/runtime-corejs2": "^7.23.2", "@docsearch/react": "^3.5.2", "@emotion/cache": "^11.11.0", "@emotion/react": "^11.11.1", "@emotion/server": "^11.11.0", "@emotion/styled": "^11.11.0", - "@mui/icons-material": "^5.14.8", - "@mui/joy": "^5.0.0-beta.5", - "@mui/material": "^5.14.8", - "@mui/styles": "^5.14.7", - "@mui/utils": "^5.14.8", + "@mui/icons-material": "^5.14.16", + "@mui/joy": "^5.0.0-beta.13", + "@mui/lab": "^5.0.0-alpha.151", + "@mui/material": "^5.14.16", + "@mui/styles": "^5.14.16", + "@mui/utils": "^5.14.16", "@react-spring/web": "^9.7.3", "@trendmicro/react-interpolate": "^0.5.5", - "@types/lodash": "^4.14.198", - "@types/moment-hijri": "^2.1.0", - "@types/react-dom": "^18.2.7", + "@types/lodash": "^4.14.200", + "@types/moment-hijri": "^2.1.2", + "@types/react-dom": "^18.2.14", "@types/react-router-dom": "^5.3.3", "ast-types": "^0.14.2", - "autoprefixer": "^10.4.15", + "autoprefixer": "^10.4.16", "babel-plugin-module-resolver": "^4.1.0", "babel-plugin-optimize-clsx": "^2.6.2", "babel-plugin-preval": "^5.1.0", @@ -53,7 +54,7 @@ "cross-env": "^7.0.3", "date-fns": "^2.30.0", "date-fns-jalali": "^2.21.3-1", - "dayjs": "^1.11.9", + "dayjs": "^1.11.10", "doctrine": "^3.0.0", "exceljs": "^4.3.0", "express": "^4.18.2", @@ -69,14 +70,14 @@ "moment-timezone": "^0.5.43", "next": "^13.4.19", "nprogress": "^0.2.0", - "postcss": "^8.4.29", + "postcss": "^8.4.31", "prismjs": "^1.29.0", "prop-types": "^15.8.1", "raw-loader": "^1.0.0", "react": "^18.2.0", "react-docgen": "^5.4.3", "react-dom": "^18.2.0", - "react-hook-form": "^7.46.1", + "react-hook-form": "^7.47.0", "react-is": "^18.2.0", "react-router": "^6.11.2", "react-router-dom": "^6.11.2", @@ -84,16 +85,15 @@ "react-simple-code-editor": "^0.13.1", "recast": "^0.23.4", "rimraf": "^5.0.1", - "styled-components": "^5.3.11", - "stylis": "^4.3.0", + "styled-components": "^6.1.0", "stylis-plugin-rtl": "^2.1.1", - "stylis-plugin-rtl-sc": "npm:stylis-plugin-rtl@^2.1.1", "webpack-bundle-analyzer": "^4.9.1" }, "devDependencies": { "@babel/plugin-transform-react-constant-elements": "^7.22.5", - "@babel/preset-typescript": "^7.22.15", - "@types/doctrine": "^0.0.6", + "@babel/preset-typescript": "^7.23.2", + "@types/doctrine": "^0.0.8", + "@types/stylis": "^4.2.2", "cpy-cli": "^5.0.0", "gm": "^1.25.0" } diff --git a/docs/pages/_app.js b/docs/pages/_app.js index fb7f90d7d62ec..dc094e5d3edc9 100644 --- a/docs/pages/_app.js +++ b/docs/pages/_app.js @@ -213,7 +213,11 @@ function AppWrapper(props) { metadata: '', name: 'MUI X', versions: [ - { text: `v${process.env.LIB_VERSION}`, current: true }, + { + text: `v${process.env.LIB_VERSION}`, + current: true, + }, + { text: 'v6', href: `https://mui.com${languagePrefix}/x/introduction/` }, { text: 'v5', href: `https://v5.mui.com${languagePrefix}/x/introduction/` }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/components/data-grid/` }, ], @@ -224,7 +228,11 @@ function AppWrapper(props) { metadata: 'MUI X', name: 'Data Grid', versions: [ - { text: `v${process.env.DATA_GRID_VERSION}`, current: true }, + { + text: `v${process.env.DATA_GRID_VERSION}`, + current: true, + }, + { text: 'v6', href: `https://mui.com${languagePrefix}/components/data-grid/` }, { text: 'v5', href: `https://v5.mui.com${languagePrefix}/components/data-grid/` }, { text: 'v4', href: `https://v4.mui.com${languagePrefix}/components/data-grid/` }, ], @@ -234,7 +242,14 @@ function AppWrapper(props) { metadata: 'MUI X', name: 'Date Pickers', versions: [ - { text: `v${process.env.DATE_PICKERS_VERSION}`, current: true }, + { + text: `v${process.env.DATE_PICKERS_VERSION}`, + current: true, + }, + { + text: 'v6', + href: `https://mui.com${languagePrefix}/x/react-date-pickers/getting-started/`, + }, { text: 'v5', href: `https://v5.mui.com${languagePrefix}/x/react-date-pickers/getting-started/`, @@ -245,13 +260,28 @@ function AppWrapper(props) { productIdentifier = { metadata: 'MUI X', name: 'Charts', - versions: [{ text: `v${process.env.CHARTS_VERSION}`, current: true }], + versions: [ + { + text: `v${process.env.CHARTS_VERSION}`, + current: true, + }, + { text: 'v6', href: `https://mui.com${languagePrefix}/x/react-charts/getting-started` }, + ], }; } else if (productId === 'x-tree-view') { productIdentifier = { metadata: 'MUI X', name: 'Tree View', - versions: [{ text: `v${process.env.TREE_VIEW_VERSION}`, current: true }], + versions: [ + { + text: `v${process.env.TREE_VIEW_VERSION}`, + current: true, + }, + { + text: 'v6', + href: `https://mui.com${languagePrefix}/x/react-tree-view/getting-started`, + }, + ], }; } diff --git a/docs/pages/_document.js b/docs/pages/_document.js index f9dfc9a7f73a6..d3958152942a4 100644 --- a/docs/pages/_document.js +++ b/docs/pages/_document.js @@ -1,3 +1,3 @@ -import MyDocument from '@mui/monorepo/docs/pages/_document'; +import MyDocument from 'docs/pages/_document'; export default MyDocument; diff --git a/docs/pages/x/api/charts/area-element.json b/docs/pages/x/api/charts/area-element.json index 2d28a9e84535e..88a03febfead4 100644 --- a/docs/pages/x/api/charts/area-element.json +++ b/docs/pages/x/api/charts/area-element.json @@ -3,7 +3,14 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "area", + "description": "The component that renders the root.", + "default": "AreaElementPath" + } + ], "name": "AreaElement", "imports": [ "import { AreaElement } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/area-plot.json b/docs/pages/x/api/charts/area-plot.json index 8314d42eea7bf..8f1a7a0793cfe 100644 --- a/docs/pages/x/api/charts/area-plot.json +++ b/docs/pages/x/api/charts/area-plot.json @@ -3,7 +3,14 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "area", + "description": "The component that renders the root.", + "default": "AreaElementPath" + } + ], "name": "AreaPlot", "imports": [ "import { AreaPlot } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/bar-chart.json b/docs/pages/x/api/charts/bar-chart.json index 90f88f7b340a5..c2aa08e4aedb1 100644 --- a/docs/pages/x/api/charts/bar-chart.json +++ b/docs/pages/x/api/charts/bar-chart.json @@ -1,40 +1,80 @@ { "props": { + "axisHighlight": { + "type": { + "name": "shape", + "description": "{ x?: 'band'
| 'line'
| 'none', y?: 'band'
| 'line'
| 'none' }" + } + }, "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: 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" }, "default": "xAxisIds[0] The id of the first provided axis" }, "colors": { "type": { "name": "union", "description": "Array<string>
| func" } }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "disableAxisListener": { "type": { "name": "bool" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: 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" }, "default": "yAxisIds[0] The id of the first provided axis" }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
| 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: 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" }, "default": "null" }, + "skipAnimation": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
| 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
| string" + "description": "{ axisId?: 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" }, "default": "null" + }, + "width": { "type": { "name": "number" }, "default": "undefined" }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', 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 }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', 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 }>" + } } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisContent", "description": "" }, + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" }, + { "class": null, "name": "bar", "description": "" }, + { "class": null, "name": "itemContent", "description": "" }, + { "class": null, "name": "legend", "description": "" }, + { "class": null, "name": "popper", "description": "" } + ], "name": "BarChart", "imports": [ "import { BarChart } from '@mui/x-charts/BarChart';", diff --git a/docs/pages/x/api/charts/bar-plot.json b/docs/pages/x/api/charts/bar-plot.json index 36df19d48773a..1c3a017e0f3e1 100644 --- a/docs/pages/x/api/charts/bar-plot.json +++ b/docs/pages/x/api/charts/bar-plot.json @@ -1,9 +1,17 @@ { "props": { + "skipAnimation": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "bar", + "description": "The component that renders the root.", + "default": "BarElementPath" + } + ], "name": "BarPlot", "imports": [ "import { BarPlot } from '@mui/x-charts/BarChart';", diff --git a/docs/pages/x/api/charts/cartesian-context-provider.json b/docs/pages/x/api/charts/cartesian-context-provider.json index bf6b224c7ec15..5a654ee74bfdb 100644 --- a/docs/pages/x/api/charts/cartesian-context-provider.json +++ b/docs/pages/x/api/charts/cartesian-context-provider.json @@ -1,5 +1,19 @@ { - "props": {}, + "props": { + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', 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 }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
| number, min?: Date
| number, position?: 'bottom'
| 'left'
| 'right'
| 'top', 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 }>" + } + } + }, "slots": [], "name": "CartesianContextProvider", "imports": [ diff --git a/docs/pages/x/api/charts/charts-axis-highlight.json b/docs/pages/x/api/charts/charts-axis-highlight.json index b0fa1a543d571..363412b12f924 100644 --- a/docs/pages/x/api/charts/charts-axis-highlight.json +++ b/docs/pages/x/api/charts/charts-axis-highlight.json @@ -6,7 +6,7 @@ "import { ChartsAxisHighlight } from '@mui/x-charts/ChartsAxisHighlight';", "import { ChartsAxisHighlight } from '@mui/x-charts';" ], - "styles": { "classes": [], "globalClasses": {}, "name": "MuiChartsAxisHighlight" }, + "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiChartsAxisHighlight" }, "filename": "/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx", "demos": "
    " } diff --git a/docs/pages/x/api/charts/charts-axis.json b/docs/pages/x/api/charts/charts-axis.json index 284533555f66c..9845f37eb5803 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: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
    | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
    | string" + "description": "{ axisId?: 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" }, "default": "xAxisIds[0] The id of the first provided axis" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
    | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
    | string" + "description": "{ axisId?: 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" }, "default": "yAxisIds[0] The id of the first provided axis" }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
    | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
    | string" + "description": "{ axisId?: 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" }, "default": "null" }, @@ -26,12 +26,17 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
    | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
    | string" + "description": "{ axisId?: 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" }, "default": "null" } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" } + ], "name": "ChartsAxis", "imports": [ "import { ChartsAxis } from '@mui/x-charts/ChartsAxis';", diff --git a/docs/pages/x/api/charts/charts-legend.js b/docs/pages/x/api/charts/charts-legend.js new file mode 100644 index 0000000000000..db51e358d1e77 --- /dev/null +++ b/docs/pages/x/api/charts/charts-legend.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 './charts-legend.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/charts', + false, + /\.\/charts-legend(-[a-z]{2})?\.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/charts/charts-legend.json b/docs/pages/x/api/charts/charts-legend.json new file mode 100644 index 0000000000000..d76d6df02ac00 --- /dev/null +++ b/docs/pages/x/api/charts/charts-legend.json @@ -0,0 +1,22 @@ +{ + "props": { + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "direction": { "type": { "name": "enum", "description": "'column'
    | 'row'" } }, + "hidden": { "type": { "name": "bool" } }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { "type": { "name": "object" }, "default": "{}" } + }, + "slots": [{ "class": null, "name": "legend", "description": "" }], + "name": "ChartsLegend", + "imports": [ + "import { ChartsLegend } from '@mui/x-charts/ChartsLegend';", + "import { ChartsLegend } from '@mui/x-charts';" + ], + "styles": { + "classes": ["root", "series", "mark", "label", "column", "row"], + "globalClasses": {}, + "name": "MuiChartsLegend" + }, + "filename": "/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx", + "demos": "
      " +} diff --git a/docs/pages/x/api/charts/charts-reference-line.js b/docs/pages/x/api/charts/charts-reference-line.js new file mode 100644 index 0000000000000..d4424ba41e0cb --- /dev/null +++ b/docs/pages/x/api/charts/charts-reference-line.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 './charts-reference-line.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/charts', + false, + /\.\/charts-reference-line(-[a-z]{2})?\.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/charts/charts-reference-line.json b/docs/pages/x/api/charts/charts-reference-line.json new file mode 100644 index 0000000000000..36045f6f118ce --- /dev/null +++ b/docs/pages/x/api/charts/charts-reference-line.json @@ -0,0 +1,42 @@ +{ + "props": { + "axisId": { "type": { "name": "string" }, "default": "The `id` of the first defined axis." }, + "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, + "label": { "type": { "name": "string" } }, + "labelAlign": { + "type": { + "name": "enum", + "description": "'end'
      | 'middle'
      | 'start'" + }, + "default": "'middle'" + }, + "labelStyle": { "type": { "name": "object" } }, + "lineStyle": { "type": { "name": "object" } }, + "spacing": { + "type": { + "name": "union", + "description": "number
      | { x?: number, y?: number }" + }, + "default": "5" + }, + "x": { + "type": { "name": "union", "description": "Date
      | number
      | string" } + }, + "y": { + "type": { "name": "union", "description": "Date
      | number
      | string" } + } + }, + "slots": [], + "name": "ChartsReferenceLine", + "imports": [ + "import { ChartsReferenceLine } from '@mui/x-charts/ChartsReferenceLine';", + "import { ChartsReferenceLine } from '@mui/x-charts';" + ], + "styles": { + "classes": ["root", "vertical", "horizontal", "line", "label"], + "globalClasses": {}, + "name": "MuiChartsReferenceLine" + }, + "filename": "/packages/x-charts/src/ChartsReferenceLine/ChartsReferenceLine.tsx", + "demos": "
        " +} diff --git a/docs/pages/x/api/charts/charts-tooltip.json b/docs/pages/x/api/charts/charts-tooltip.json index 776aeb3834e56..3a35f708fbeaa 100644 --- a/docs/pages/x/api/charts/charts-tooltip.json +++ b/docs/pages/x/api/charts/charts-tooltip.json @@ -1,8 +1,18 @@ { "props": { - "axisContent": { "type": { "name": "elementType" } }, + "axisContent": { + "type": { "name": "elementType" }, + "deprecated": true, + "deprecationInfo": "Use slots.axisContent instead" + }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "itemContent": { "type": { "name": "elementType" } }, + "itemContent": { + "type": { "name": "elementType" }, + "deprecated": true, + "deprecationInfo": "Use slots.itemContent instead" + }, + "slotProps": { "type": { "name": "object" }, "default": "{}" }, + "slots": { "type": { "name": "object" }, "default": "{}" }, "trigger": { "type": { "name": "enum", @@ -11,7 +21,11 @@ "default": "'item'" } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisContent", "description": "" }, + { "class": null, "name": "itemContent", "description": "" }, + { "class": null, "name": "popper", "description": "" } + ], "name": "ChartsTooltip", "imports": [ "import { ChartsTooltip } from '@mui/x-charts/ChartsTooltip';", diff --git a/docs/pages/x/api/charts/charts-x-axis.json b/docs/pages/x/api/charts/charts-x-axis.json index e1aa65e211405..3464148ac316f 100644 --- a/docs/pages/x/api/charts/charts-x-axis.json +++ b/docs/pages/x/api/charts/charts-x-axis.json @@ -1,23 +1,48 @@ { "props": { - "axisId": { "type": { "name": "string" }, "required": true }, + "axisId": { "type": { "name": "string" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, "disableLine": { "type": { "name": "bool" } }, "disableTicks": { "type": { "name": "bool" } }, "fill": { "type": { "name": "string" }, "default": "'currentColor'" }, "label": { "type": { "name": "string" } }, - "labelFontSize": { "type": { "name": "number" }, "default": "14" }, + "labelFontSize": { + "type": { "name": "number" }, + "default": "14", + "deprecated": true, + "deprecationInfo": "Consider using labelStyle.fontSize instead." + }, + "labelStyle": { "type": { "name": "object" } }, "position": { "type": { "name": "enum", "description": "'bottom'
        | 'top'" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "stroke": { "type": { "name": "string" }, "default": "'currentColor'" }, - "tickFontSize": { "type": { "name": "number" }, "default": "12" }, + "tickFontSize": { + "type": { "name": "number" }, + "default": "12", + "deprecated": true, + "deprecationInfo": "Consider using tickLabelStyle.fontSize instead." + }, + "tickInterval": { + "type": { "name": "union", "description": "'auto'
        | array
        | func" }, + "default": "'auto'" + }, + "tickLabelInterval": { + "type": { "name": "union", "description": "'auto'
        | func" }, + "default": "'auto'" + }, + "tickLabelStyle": { "type": { "name": "object" } }, "tickMaxStep": { "type": { "name": "number" } }, "tickMinStep": { "type": { "name": "number" } }, "tickNumber": { "type": { "name": "number" } }, "tickSize": { "type": { "name": "number" }, "default": "6" } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" } + ], "name": "ChartsXAxis", "imports": [ "import { ChartsXAxis } from '@mui/x-charts/ChartsXAxis';", diff --git a/docs/pages/x/api/charts/charts-y-axis.json b/docs/pages/x/api/charts/charts-y-axis.json index e49510b318500..f0e7874c67c86 100644 --- a/docs/pages/x/api/charts/charts-y-axis.json +++ b/docs/pages/x/api/charts/charts-y-axis.json @@ -1,23 +1,48 @@ { "props": { - "axisId": { "type": { "name": "string" }, "required": true }, + "axisId": { "type": { "name": "string" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, "disableLine": { "type": { "name": "bool" } }, "disableTicks": { "type": { "name": "bool" } }, "fill": { "type": { "name": "string" }, "default": "'currentColor'" }, "label": { "type": { "name": "string" } }, - "labelFontSize": { "type": { "name": "number" }, "default": "14" }, + "labelFontSize": { + "type": { "name": "number" }, + "default": "14", + "deprecated": true, + "deprecationInfo": "Consider using labelStyle.fontSize instead." + }, + "labelStyle": { "type": { "name": "object" } }, "position": { "type": { "name": "enum", "description": "'left'
        | 'right'" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" }, "stroke": { "type": { "name": "string" }, "default": "'currentColor'" }, - "tickFontSize": { "type": { "name": "number" }, "default": "12" }, + "tickFontSize": { + "type": { "name": "number" }, + "default": "12", + "deprecated": true, + "deprecationInfo": "Consider using tickLabelStyle.fontSize instead." + }, + "tickInterval": { + "type": { "name": "union", "description": "'auto'
        | array
        | func" }, + "default": "'auto'" + }, + "tickLabelInterval": { + "type": { "name": "union", "description": "'auto'
        | func" }, + "default": "'auto'" + }, + "tickLabelStyle": { "type": { "name": "object" } }, "tickMaxStep": { "type": { "name": "number" } }, "tickMinStep": { "type": { "name": "number" } }, "tickNumber": { "type": { "name": "number" } }, "tickSize": { "type": { "name": "number" }, "default": "6" } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" } + ], "name": "ChartsYAxis", "imports": [ "import { ChartsYAxis } from '@mui/x-charts/ChartsYAxis';", diff --git a/docs/pages/x/api/charts/drawing-provider.json b/docs/pages/x/api/charts/drawing-provider.json index 04a2e652ed38a..47946baf9dda5 100644 --- a/docs/pages/x/api/charts/drawing-provider.json +++ b/docs/pages/x/api/charts/drawing-provider.json @@ -1,5 +1,13 @@ { - "props": {}, + "props": { + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + } + }, "slots": [], "name": "DrawingProvider", "imports": [ diff --git a/docs/pages/x/api/charts/line-chart.json b/docs/pages/x/api/charts/line-chart.json index 6f29efdbda6c4..9ac45f784a759 100644 --- a/docs/pages/x/api/charts/line-chart.json +++ b/docs/pages/x/api/charts/line-chart.json @@ -1,27 +1,43 @@ { "props": { + "axisHighlight": { + "type": { + "name": "shape", + "description": "{ x?: 'band'
        | 'line'
        | 'none', y?: 'band'
        | 'line'
        | 'none' }" + } + }, "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "xAxisIds[0] The id of the first provided axis" }, "colors": { "type": { "name": "union", "description": "Array<string>
        | func" } }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "disableAxisListener": { "type": { "name": "bool" } }, "disableLineItemHighlight": { "type": { "name": "bool" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "yAxisIds[0] The id of the first provided axis" }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" }, @@ -30,12 +46,38 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" + }, + "width": { "type": { "name": "number" }, "default": "undefined" }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } } }, - "slots": [], + "slots": [ + { "class": null, "name": "area", "description": "" }, + { "class": null, "name": "axisContent", "description": "" }, + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" }, + { "class": null, "name": "itemContent", "description": "" }, + { "class": null, "name": "legend", "description": "" }, + { "class": null, "name": "line", "description": "" }, + { "class": null, "name": "lineHighlight", "description": "" }, + { "class": null, "name": "mark", "description": "" }, + { "class": null, "name": "popper", "description": "" } + ], "name": "LineChart", "imports": [ "import { LineChart } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/line-element.json b/docs/pages/x/api/charts/line-element.json index 4b97cc5ba9301..3a088bc7acbee 100644 --- a/docs/pages/x/api/charts/line-element.json +++ b/docs/pages/x/api/charts/line-element.json @@ -3,7 +3,14 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "line", + "description": "The component that renders the root.", + "default": "LineElementPath" + } + ], "name": "LineElement", "imports": [ "import { LineElement } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/line-highlight-plot.json b/docs/pages/x/api/charts/line-highlight-plot.json index 7843dba61f120..c939c5af6d818 100644 --- a/docs/pages/x/api/charts/line-highlight-plot.json +++ b/docs/pages/x/api/charts/line-highlight-plot.json @@ -3,7 +3,7 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [{ "class": null, "name": "lineHighlight", "description": "" }], "name": "LineHighlightPlot", "imports": [ "import { LineHighlightPlot } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/line-plot.json b/docs/pages/x/api/charts/line-plot.json index f1823692c9227..7a88452cd6142 100644 --- a/docs/pages/x/api/charts/line-plot.json +++ b/docs/pages/x/api/charts/line-plot.json @@ -3,7 +3,14 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "line", + "description": "The component that renders the root.", + "default": "LineElementPath" + } + ], "name": "LinePlot", "imports": [ "import { LinePlot } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/mark-plot.json b/docs/pages/x/api/charts/mark-plot.json index b35f000735a26..25c608bdbdaf0 100644 --- a/docs/pages/x/api/charts/mark-plot.json +++ b/docs/pages/x/api/charts/mark-plot.json @@ -3,7 +3,7 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [{ "class": null, "name": "mark", "description": "" }], "name": "MarkPlot", "imports": [ "import { MarkPlot } from '@mui/x-charts/LineChart';", diff --git a/docs/pages/x/api/charts/pie-chart.json b/docs/pages/x/api/charts/pie-chart.json index 5089fd20032e3..80256df705d63 100644 --- a/docs/pages/x/api/charts/pie-chart.json +++ b/docs/pages/x/api/charts/pie-chart.json @@ -3,34 +3,58 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "xAxisIds[0] The id of the first provided axis" }, "colors": { "type": { "name": "union", "description": "Array<string>
        | func" } }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "disableAxisListener": { "type": { "name": "bool" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "yAxisIds[0] The id of the first provided axis" }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" }, + "skipAnimation": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" + }, + "width": { "type": { "name": "number" }, "default": "undefined" }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } } }, "slots": [], diff --git a/docs/pages/x/api/charts/pie-plot.json b/docs/pages/x/api/charts/pie-plot.json index d2cdc016d3e7b..d6c6ff13d881c 100644 --- a/docs/pages/x/api/charts/pie-plot.json +++ b/docs/pages/x/api/charts/pie-plot.json @@ -1,9 +1,20 @@ { "props": { + "onClick": { + "type": { "name": "func" }, + "signature": { + "type": "function(event: React.MouseEvent, pieItemIdentifier: PieItemIdentifier, item: DefaultizedPieValueType) => void", + "describedArgs": ["event", "pieItemIdentifier", "item"] + } + }, + "skipAnimation": { "type": { "name": "bool" } }, "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [ + { "class": null, "name": "pieArc", "description": "" }, + { "class": null, "name": "pieArcLabel", "description": "" } + ], "name": "PiePlot", "imports": [ "import { PiePlot } from '@mui/x-charts/PieChart';", diff --git a/docs/pages/x/api/charts/scatter-chart.json b/docs/pages/x/api/charts/scatter-chart.json index c0c77195ea168..9f6991e3d775f 100644 --- a/docs/pages/x/api/charts/scatter-chart.json +++ b/docs/pages/x/api/charts/scatter-chart.json @@ -3,24 +3,34 @@ "bottomAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "xAxisIds[0] The id of the first provided axis" }, "colors": { "type": { "name": "union", "description": "Array<string>
        | func" } }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "disableAxisListener": { "type": { "name": "bool" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, "leftAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "yAxisIds[0] The id of the first provided axis" }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, "rightAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'left'
        | 'right', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" }, @@ -29,12 +39,35 @@ "topAxis": { "type": { "name": "union", - "description": "{ axisId: string, classes?: object, disableLine?: bool, disableTicks?: bool, fill?: string, label?: string, labelFontSize?: number, position?: 'bottom'
        | 'top', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number }
        | string" + "description": "{ axisId?: 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" }, "default": "null" + }, + "width": { "type": { "name": "number" }, "default": "undefined" }, + "xAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } + }, + "yAxis": { + "type": { + "name": "arrayOf", + "description": "Array<{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }>" + } } }, - "slots": [], + "slots": [ + { "class": null, "name": "axisContent", "description": "" }, + { "class": null, "name": "axisLabel", "description": "" }, + { "class": null, "name": "axisLine", "description": "" }, + { "class": null, "name": "axisTick", "description": "" }, + { "class": null, "name": "axisTickLabel", "description": "" }, + { "class": null, "name": "itemContent", "description": "" }, + { "class": null, "name": "legend", "description": "" }, + { "class": null, "name": "popper", "description": "" }, + { "class": null, "name": "scatter", "description": "" } + ], "name": "ScatterChart", "imports": [ "import { ScatterChart } from '@mui/x-charts/ScatterChart';", diff --git a/docs/pages/x/api/charts/scatter-plot.json b/docs/pages/x/api/charts/scatter-plot.json index 7e44a83dd7ded..a3a337d14e88c 100644 --- a/docs/pages/x/api/charts/scatter-plot.json +++ b/docs/pages/x/api/charts/scatter-plot.json @@ -3,7 +3,7 @@ "slotProps": { "type": { "name": "object" }, "default": "{}" }, "slots": { "type": { "name": "object" }, "default": "{}" } }, - "slots": [], + "slots": [{ "class": null, "name": "scatter", "description": "" }], "name": "ScatterPlot", "imports": [ "import { ScatterPlot } from '@mui/x-charts/ScatterChart';", diff --git a/docs/pages/x/api/charts/spark-line-chart.json b/docs/pages/x/api/charts/spark-line-chart.json index 324c72d58cc76..225500cb1c45a 100644 --- a/docs/pages/x/api/charts/spark-line-chart.json +++ b/docs/pages/x/api/charts/spark-line-chart.json @@ -8,6 +8,16 @@ "colors": { "type": { "name": "union", "description": "Array<string>
        | func" } }, + "dataset": { "type": { "name": "arrayOf", "description": "Array<object>" } }, + "disableAxisListener": { "type": { "name": "bool" } }, + "height": { "type": { "name": "number" }, "default": "undefined" }, + "margin": { + "type": { + "name": "shape", + "description": "{ bottom?: number, left?: number, right?: number, top?: number }" + }, + "default": "object Depends on the charts type." + }, "plotType": { "type": { "name": "enum", "description": "'bar'
        | 'line'" }, "default": "'line'" @@ -24,14 +34,24 @@ "returned": "string" } }, + "width": { "type": { "name": "number" }, "default": "undefined" }, "xAxis": { "type": { "name": "shape", - "description": "{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', scaleType?: 'band'
        | 'linear'
        | 'log'
        | 'point'
        | 'pow'
        | 'sqrt'
        | 'time'
        | 'utc', slotProps?: object, slots?: object, stroke?: string, tickFontSize?: number, tickMaxStep?: number, tickMinStep?: number, tickNumber?: number, tickSize?: number, valueFormatter?: func }" + "description": "{ axisId?: string, classes?: object, data?: array, dataKey?: string, disableLine?: bool, disableTicks?: bool, fill?: string, hideTooltip?: bool, id?: string, label?: string, labelFontSize?: number, labelStyle?: object, max?: Date
        | number, min?: Date
        | number, position?: 'bottom'
        | 'left'
        | 'right'
        | 'top', 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 }" } } }, - "slots": [], + "slots": [ + { "class": null, "name": "area", "description": "" }, + { "class": null, "name": "axisContent", "description": "" }, + { "class": null, "name": "bar", "description": "" }, + { "class": null, "name": "itemContent", "description": "" }, + { "class": null, "name": "line", "description": "" }, + { "class": null, "name": "lineHighlight", "description": "" }, + { "class": null, "name": "mark", "description": "" }, + { "class": null, "name": "popper", "description": "" } + ], "name": "SparkLineChart", "imports": [ "import { SparkLineChart } from '@mui/x-charts/SparkLineChart';", 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 bdc26c3d9d287..98b8f8862234b 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -22,6 +22,13 @@ "aria-labelledby": { "type": { "name": "string" } }, "autoHeight": { "type": { "name": "bool" } }, "autoPageSize": { "type": { "name": "bool" } }, + "autosizeOnMount": { "type": { "name": "bool" } }, + "autosizeOptions": { + "type": { + "name": "shape", + "description": "{ columns?: Array<string>, expand?: bool, includeHeaders?: bool, includeOutliers?: bool, outliersFactor?: number }" + } + }, "cellModesModel": { "type": { "name": "object" } }, "checkboxSelection": { "type": { "name": "bool" } }, "checkboxSelectionVisibleOnly": { @@ -34,16 +41,6 @@ "columnHeaderHeight": { "type": { "name": "number" }, "default": "56" }, "columnThreshold": { "type": { "name": "number" }, "default": "3" }, "columnVisibilityModel": { "type": { "name": "object" } }, - "components": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use the slots prop instead." - }, - "componentsProps": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use the slotProps prop instead." - }, "defaultGroupingExpansionDepth": { "type": { "name": "number" }, "default": "0" }, "density": { "type": { @@ -56,6 +53,7 @@ "type": { "name": "arrayOf", "description": "Array<number
        | string>" } }, "disableAggregation": { "type": { "name": "bool" } }, + "disableAutosize": { "type": { "name": "bool" } }, "disableChildrenFiltering": { "type": { "name": "bool" } }, "disableChildrenSorting": { "type": { "name": "bool" } }, "disableClipboardPaste": { "type": { "name": "bool" } }, @@ -176,6 +174,7 @@ "default": "false" }, "hideFooterSelectedRowCount": { "type": { "name": "bool" } }, + "ignoreDiacritics": { "type": { "name": "bool" } }, "initialState": { "type": { "name": "object" } }, "isCellEditable": { "type": { "name": "func" }, @@ -547,6 +546,7 @@ "rowGroupingModel": { "type": { "name": "arrayOf", "description": "Array<string>" } }, "rowHeight": { "type": { "name": "number" }, "default": "52" }, "rowModesModel": { "type": { "name": "object" } }, + "rowPositionsDebounceMs": { "type": { "name": "number" }, "default": "166" }, "rowReordering": { "type": { "name": "bool" } }, "rowSelection": { "type": { "name": "bool" }, "default": "true" }, "rowSelectionModel": { @@ -600,7 +600,7 @@ "name": "union", "description": "{ clipboardExport?: bool, csvExport?: bool }
        | bool" }, - "default": ": false" + "default": "false" }, "unstable_onCellSelectionModelChange": { "type": { "name": "func" }, @@ -1062,6 +1062,7 @@ "aggregationColumnHeader--alignRight", "aggregationColumnHeaderLabel", "autoHeight", + "autosizing", "booleanCell", "cell--editable", "cell--editing", @@ -1073,6 +1074,7 @@ "cell--rangeBottom", "cell--rangeLeft", "cell--rangeRight", + "cell--selectionMode", "cell", "cellContent", "cellCheckbox", 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 726b11be83007..70038eceda232 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -13,6 +13,13 @@ "aria-labelledby": { "type": { "name": "string" } }, "autoHeight": { "type": { "name": "bool" } }, "autoPageSize": { "type": { "name": "bool" } }, + "autosizeOnMount": { "type": { "name": "bool" } }, + "autosizeOptions": { + "type": { + "name": "shape", + "description": "{ columns?: Array<string>, expand?: bool, includeHeaders?: bool, includeOutliers?: bool, outliersFactor?: number }" + } + }, "cellModesModel": { "type": { "name": "object" } }, "checkboxSelection": { "type": { "name": "bool" } }, "checkboxSelectionVisibleOnly": { @@ -25,16 +32,6 @@ "columnHeaderHeight": { "type": { "name": "number" }, "default": "56" }, "columnThreshold": { "type": { "name": "number" }, "default": "3" }, "columnVisibilityModel": { "type": { "name": "object" } }, - "components": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use the slots prop instead." - }, - "componentsProps": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use the slotProps prop instead." - }, "defaultGroupingExpansionDepth": { "type": { "name": "number" }, "default": "0" }, "density": { "type": { @@ -46,6 +43,7 @@ "detailPanelExpandedRowIds": { "type": { "name": "arrayOf", "description": "Array<number
        | string>" } }, + "disableAutosize": { "type": { "name": "bool" } }, "disableChildrenFiltering": { "type": { "name": "bool" } }, "disableChildrenSorting": { "type": { "name": "bool" } }, "disableColumnFilter": { "type": { "name": "bool" } }, @@ -155,6 +153,7 @@ "default": "false" }, "hideFooterSelectedRowCount": { "type": { "name": "bool" } }, + "ignoreDiacritics": { "type": { "name": "bool" } }, "initialState": { "type": { "name": "object" } }, "isCellEditable": { "type": { "name": "func" }, @@ -498,6 +497,7 @@ "rowCount": { "type": { "name": "number" } }, "rowHeight": { "type": { "name": "number" }, "default": "52" }, "rowModesModel": { "type": { "name": "object" } }, + "rowPositionsDebounceMs": { "type": { "name": "number" }, "default": "166" }, "rowReordering": { "type": { "name": "bool" } }, "rowSelection": { "type": { "name": "bool" }, "default": "true" }, "rowSelectionModel": { @@ -549,7 +549,7 @@ "name": "union", "description": "{ clipboardExport?: bool, csvExport?: bool }
        | bool" }, - "default": ": false" + "default": "false" } }, "slots": [ @@ -983,6 +983,7 @@ "aggregationColumnHeader--alignRight", "aggregationColumnHeaderLabel", "autoHeight", + "autosizing", "booleanCell", "cell--editable", "cell--editing", @@ -994,6 +995,7 @@ "cell--rangeBottom", "cell--rangeLeft", "cell--rangeRight", + "cell--selectionMode", "cell", "cellContent", "cellCheckbox", diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index ce4283f5740f5..bef51450f8dc8 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -18,16 +18,6 @@ "columnHeaderHeight": { "type": { "name": "number" }, "default": "56" }, "columnThreshold": { "type": { "name": "number" }, "default": "3" }, "columnVisibilityModel": { "type": { "name": "object" } }, - "components": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use slots instead." - }, - "componentsProps": { - "type": { "name": "object" }, - "deprecated": true, - "deprecationInfo": "Use the slotProps prop instead." - }, "density": { "type": { "name": "enum", @@ -114,6 +104,7 @@ "hideFooter": { "type": { "name": "bool" } }, "hideFooterPagination": { "type": { "name": "bool" } }, "hideFooterSelectedRowCount": { "type": { "name": "bool" } }, + "ignoreDiacritics": { "type": { "name": "bool" } }, "initialState": { "type": { "name": "object" } }, "isCellEditable": { "type": { "name": "func" }, @@ -386,6 +377,7 @@ "rowCount": { "type": { "name": "number" } }, "rowHeight": { "type": { "name": "number" }, "default": "52" }, "rowModesModel": { "type": { "name": "object" } }, + "rowPositionsDebounceMs": { "type": { "name": "number" }, "default": "166" }, "rowSelection": { "type": { "name": "bool" }, "default": "true" }, "rowSelectionModel": { "type": { @@ -429,7 +421,7 @@ "name": "union", "description": "{ clipboardExport?: bool, csvExport?: bool }
        | bool" }, - "default": ": false" + "default": "false" } }, "slots": [ @@ -840,6 +832,7 @@ "aggregationColumnHeader--alignRight", "aggregationColumnHeaderLabel", "autoHeight", + "autosizing", "booleanCell", "cell--editable", "cell--editing", @@ -851,6 +844,7 @@ "cell--rangeBottom", "cell--rangeLeft", "cell--rangeRight", + "cell--selectionMode", "cell", "cellContent", "cellCheckbox", 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 982145c83efd7..af3663b0805c6 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 @@ -40,8 +40,7 @@ import { GridActionsColDef } from '@mui/x-data-grid'; | filterOperators? | GridFilterOperator<R, V, F>[] | | Allows setting the filter operators for this column. | | flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | | getActions | (params: GridRowParams<R>) => React.ReactElement<GridActionsCellItemProps>[] | | Function that returns the actions to be shown. | -| getApplyQuickFilterFn? | GetApplyQuickFilterFnLegacy<R, V, F> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | -| getApplyQuickFilterFnV7? | GetApplyQuickFilterFnV7<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | +| getApplyQuickFilterFn? | GetApplyQuickFilterFn<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | | groupable? | boolean | true | If `true`, the rows can be grouped based on this column values (pro-plan only).
        Only available in DataGridPremium. | | groupingValueGetter? [](/x/introduction/licensing/#premium-plan) | (params: GridGroupingValueGetterParams<R, V>) => GridKeyValue \| null \| undefined | | Function that transforms a complex cell value into a key that be used for grouping the rows. | | headerAlign? | GridAlignment | | Header cell element alignment. | diff --git a/docs/pages/x/api/data-grid/grid-api.md b/docs/pages/x/api/data-grid/grid-api.md index fd398b891abd1..255aa9b096bfc 100644 --- a/docs/pages/x/api/data-grid/grid-api.md +++ b/docs/pages/x/api/data-grid/grid-api.md @@ -27,6 +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. | | 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. | @@ -73,6 +74,7 @@ import { GridApi } from '@mui/x-data-grid'; | hideFilterPanel | () => void | Hides the filter panel. | | hideHeaderFilterMenu | () => void | Hides the header filter menu. | | hidePreferences | () => void | Hides the preferences panel. | +| ignoreDiacritics | DataGridProcessedProps['ignoreDiacritics'] | Returns the value of the `ignoreDiacritics` prop. | | isCellEditable | (params: GridCellParams) => boolean | Controls if a cell is editable. | | isColumnPinned [](/x/introduction/licensing/#pro-plan) | (field: string) => GridPinnedPosition \| false | Returns which side a column is pinned to. | | isRowSelectable | (id: GridRowId) => boolean | Determines if a row can be selected or not. | @@ -105,7 +107,7 @@ import { GridApi } from '@mui/x-data-grid'; | setPageSize | (pageSize: number) => void | Sets the number of displayed rows to the value given by `pageSize`. | | setPaginationModel | (model: GridPaginationModel) => void | Sets the `paginationModel` to a new value. | | setPinnedColumns [](/x/introduction/licensing/#pro-plan) | (pinnedColumns: GridPinnedColumns) => void | Changes the pinned columns. | -| setQuickFilterValues | (values: any[]) => void | Set the quick filter values ot the one given by `values` | +| 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. | | 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. | @@ -122,9 +124,9 @@ import { GridApi } from '@mui/x-data-grid'; | startHeaderFilterEditMode | (field: GridColDef['field']) => void | Puts the cell corresponding to the given row id and field into edit mode. | | startRowEditMode | (params: GridStartRowEditModeParams) => void | Puts the row corresponding to the given id into edit mode. | | state | State | Property that contains the whole state of the grid. | -| stopCellEditMode | (params: GridStopCellEditModeParams) => void | Puts the cell corresponding to the given row id and field into view mode and updates the original row with the new value stored.
        If `params.ignoreModifications` is `false` it will discard the modifications made. | +| stopCellEditMode | (params: GridStopCellEditModeParams) => void | Puts the cell corresponding to the given row id and field into view mode and updates the original row with the new value stored.
        If `params.ignoreModifications` is `true` it will discard the modifications made. | | stopHeaderFilterEditMode | () => void | Stops the edit mode for the current field. | -| stopRowEditMode | (params: GridStopRowEditModeParams) => void | Puts the row corresponding to the given id and into view mode and updates the original row with the new values stored.
        If `params.ignoreModifications` is `false` it will discard the modifications made. | +| stopRowEditMode | (params: GridStopRowEditModeParams) => void | Puts the row corresponding to the given id and into view mode and updates the original row with the new values stored.
        If `params.ignoreModifications` is `true` it will discard the modifications made. | | subscribeEvent | <E extends GridEvents>(event: E, handler: GridEventListener<E>, options?: EventListenerOptions) => () => void | Registers a handler for an event. | | toggleColumnMenu | (field: string) => void | Toggles the column menu under the `field` column. | | toggleDetailPanel [](/x/introduction/licensing/#pro-plan) | (id: GridRowId) => void | Expands or collapses the detail panel of a row. | @@ -137,7 +139,9 @@ import { GridApi } from '@mui/x-data-grid'; | unstable_replaceRows | (firstRowToReplace: number, newRows: GridRowModel[]) => void | Replace a set of rows with new rows. | | unstable_selectCellRange [](/x/introduction/licensing/#premium-plan) | (start: GridCellCoordinates, end: GridCellCoordinates, keepOtherSelected?: boolean) => void | Selects all cells that are inside the range given by `start` and `end` coordinates. | | unstable_setCellSelectionModel [](/x/introduction/licensing/#premium-plan) | (newModel: GridCellSelectionModel) => void | Updates the selected cells to be those passed to the `newModel` argument.
        Any cell already selected will be unselected. | +| unstable_setColumnVirtualization | (enabled: boolean) => void | Enable/disable column virtualization. | | unstable_setPinnedRows [](/x/introduction/licensing/#pro-plan) | (pinnedRows?: GridPinnedRowsProp) => void | Changes the pinned rows. | +| unstable_setVirtualization | (enabled: boolean) => void | Enable/disable virtualization. | | updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. | | updateRows | (updates: GridRowModelUpdate[]) => void | Allows to update, insert and delete rows. | | upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a [GridFilterItem](/x/api/data-grid/grid-filter-item/). | 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 a63fffa486f50..c05e09badc9cf 100644 --- a/docs/pages/x/api/data-grid/grid-col-def.md +++ b/docs/pages/x/api/data-grid/grid-col-def.md @@ -39,8 +39,7 @@ import { GridColDef } from '@mui/x-data-grid'; | filterable? | boolean | true | If `true`, the column is filterable. | | filterOperators? | GridFilterOperator<R, V, F>[] | | Allows setting the filter operators for this column. | | flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| getApplyQuickFilterFn? | GetApplyQuickFilterFnLegacy<R, V, F> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | -| getApplyQuickFilterFnV7? | GetApplyQuickFilterFnV7<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | +| getApplyQuickFilterFn? | GetApplyQuickFilterFn<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | | groupable? | boolean | true | If `true`, the rows can be grouped based on this column values (pro-plan only).
        Only available in DataGridPremium. | | groupingValueGetter? [](/x/introduction/licensing/#premium-plan) | (params: GridGroupingValueGetterParams<R, V>) => GridKeyValue \| null \| undefined | | Function that transforms a complex cell value into a key that be used for grouping the rows. | | headerAlign? | GridAlignment | | Header cell element alignment. | diff --git a/docs/pages/x/api/data-grid/grid-column-resize-api.json b/docs/pages/x/api/data-grid/grid-column-resize-api.json new file mode 100644 index 0000000000000..50378d4b2d1a4 --- /dev/null +++ b/docs/pages/x/api/data-grid/grid-column-resize-api.json @@ -0,0 +1,11 @@ +{ + "name": "GridColumnResizeApi", + "description": "The Resize API interface that is available in the grid `apiRef`.", + "properties": [ + { + "name": "autosizeColumns", + "description": "Auto-size the columns of the grid based on the cells' content and the space available.", + "type": "(options?: GridAutosizeOptions) => Promise" + } + ] +} diff --git a/docs/pages/x/api/data-grid/grid-editing-api.json b/docs/pages/x/api/data-grid/grid-editing-api.json index 9c2e5fdea9db5..4545356fbfd5e 100644 --- a/docs/pages/x/api/data-grid/grid-editing-api.json +++ b/docs/pages/x/api/data-grid/grid-editing-api.json @@ -39,12 +39,12 @@ }, { "name": "stopCellEditMode", - "description": "Puts the cell corresponding to the given row id and field into view mode and updates the original row with the new value stored. If params.ignoreModifications is false it will discard the modifications made.", + "description": "Puts the cell corresponding to the given row id and field into view mode and updates the original row with the new value stored. If params.ignoreModifications is true it will discard the modifications made.", "type": "(params: GridStopCellEditModeParams) => void" }, { "name": "stopRowEditMode", - "description": "Puts the row corresponding to the given id and into view mode and updates the original row with the new values stored. If params.ignoreModifications is false it will discard the modifications made.", + "description": "Puts the row corresponding to the given id and into view mode and updates the original row with the new values stored. If params.ignoreModifications is true it will discard the modifications made.", "type": "(params: GridStopRowEditModeParams) => void" } ] diff --git a/docs/pages/x/api/data-grid/grid-filter-api.json b/docs/pages/x/api/data-grid/grid-filter-api.json index 08c8d1f9593d7..626ae4fcb29ec 100644 --- a/docs/pages/x/api/data-grid/grid-filter-api.json +++ b/docs/pages/x/api/data-grid/grid-filter-api.json @@ -8,6 +8,11 @@ "type": "(item: GridFilterItem) => void" }, { "name": "hideFilterPanel", "description": "Hides the filter panel.", "type": "() => void" }, + { + "name": "ignoreDiacritics", + "description": "Returns the value of the ignoreDiacritics prop.", + "type": "DataGridProcessedProps['ignoreDiacritics']" + }, { "name": "setFilterLogicOperator", "description": "Changes the GridLogicOperator used to connect the filters.", @@ -20,7 +25,7 @@ }, { "name": "setQuickFilterValues", - "description": "Set the quick filter values ot the one given by values", + "description": "Set the quick filter values to the one given by values", "type": "(values: any[]) => void" }, { diff --git a/docs/pages/x/api/data-grid/grid-filter-operator.md b/docs/pages/x/api/data-grid/grid-filter-operator.md index c90f8462dfe4e..9cdcdb86d29cf 100644 --- a/docs/pages/x/api/data-grid/grid-filter-operator.md +++ b/docs/pages/x/api/data-grid/grid-filter-operator.md @@ -23,14 +23,13 @@ import { GridFilterOperator } from '@mui/x-data-grid'; ## Properties -| Name | Type | Default | Description | -| :---------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| getApplyFilterFn | GetApplyFilterFnLegacy<R, V, F> | | The callback that generates a filtering function for a given filter item and column.
        This function can return `null` to skip filtering for this item and column. | -| getApplyFilterFnV7? | GetApplyFilterFnV7<R, V, F> | | The callback that generates a filtering function for a given filter item and column.
        This function can return `null` to skip filtering for this item and column.
        This function uses the more performant v7 API. | -| getValueAsString? | (value: GridFilterItem['value']) => string | | Converts the value of a filter item to a human-readable form. | -| headerLabel? | string | | The label of the filter shown in header filter row. | -| InputComponent? | React.JSXElementConstructor<any> | | The input component to render in the filter panel for this filter operator. | -| InputComponentProps? | Record<string, any> | | The props to pass to the input component in the filter panel for this filter operator. | -| label? | string | | The label of the filter operator. | -| requiresFilterValue? | boolean | true | If `false`, filter operator doesn't require user-entered value to work.
        Usually should be set to `false` for filter operators that don't have `InputComponent` (for example `isEmpty`) | -| value | string | | The name of the filter operator.
        It will be matched with the `operator` property of the filter items. | +| Name | Type | Default | Description | +| :---------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------- | :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| getApplyFilterFn | GetApplyFilterFn<R, V, F> | | The callback that generates a filtering function for a given filter item and column.
        This function can return `null` to skip filtering for this item and column. | +| getValueAsString? | (value: GridFilterItem['value']) => string | | Converts the value of a filter item to a human-readable form. | +| headerLabel? | string | | The label of the filter shown in header filter row. | +| InputComponent? | React.JSXElementConstructor<any> | | The input component to render in the filter panel for this filter operator. | +| InputComponentProps? | Record<string, any> | | The props to pass to the input component in the filter panel for this filter operator. | +| label? | string | | The label of the filter operator. | +| requiresFilterValue? | boolean | true | If `false`, filter operator doesn't require user-entered value to work.
        Usually should be set to `false` for filter operators that don't have `InputComponent` (for example `isEmpty`) | +| value | string | | The name of the filter operator.
        It will be matched with the `operator` property of the filter items. | 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 5919c1df9977a..4b3ce7f21236d 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 @@ -39,8 +39,7 @@ import { GridSingleSelectColDef } from '@mui/x-data-grid'; | filterable? | boolean | true | If `true`, the column is filterable. | | filterOperators? | GridFilterOperator<R, V, F>[] | | Allows setting the filter operators for this column. | | flex? | number | | If set, it indicates that a column has fluid width. Range [0, ∞). | -| getApplyQuickFilterFn? | GetApplyQuickFilterFnLegacy<R, V, F> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | -| getApplyQuickFilterFnV7? | GetApplyQuickFilterFnV7<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | +| getApplyQuickFilterFn? | GetApplyQuickFilterFn<R, V> | | The callback that generates a filtering function for a given quick filter value.
        This function can return `null` to skip filtering for this value and column. | | getOptionLabel? | (value: ValueOptions) => string | | Used to determine the label displayed for a given value option. | | getOptionValue? | (value: ValueOptions) => any | | Used to determine the value used for a value option. | | groupable? | boolean | true | If `true`, the rows can be grouped based on this column values (pro-plan only).
        Only available in DataGridPremium. | diff --git a/docs/pages/x/api/data-grid/grid-virtualization-api.json b/docs/pages/x/api/data-grid/grid-virtualization-api.json new file mode 100644 index 0000000000000..d295357e4397a --- /dev/null +++ b/docs/pages/x/api/data-grid/grid-virtualization-api.json @@ -0,0 +1,16 @@ +{ + "name": "GridVirtualizationApi", + "description": "", + "properties": [ + { + "name": "unstable_setColumnVirtualization", + "description": "Enable/disable column virtualization.", + "type": "(enabled: boolean) => void" + }, + { + "name": "unstable_setVirtualization", + "description": "Enable/disable virtualization.", + "type": "(enabled: boolean) => void" + } + ] +} diff --git a/docs/pages/x/api/data-grid/index.md b/docs/pages/x/api/data-grid/index.md index c7ca384217c1d..ee1e07476e8c5 100644 --- a/docs/pages/x/api/data-grid/index.md +++ b/docs/pages/x/api/data-grid/index.md @@ -16,7 +16,7 @@ - [GridCellParams](/x/api/data-grid/grid-cell-params/) - [GridColDef](/x/api/data-grid/grid-col-def/) - [GridSingleSelectColDef](/x/api/data-grid/grid-single-select-col-def/) -- [GriActionsColDef](/x/api/data-grid/grid-actions-col-def/) +- [GridActionsColDef](/x/api/data-grid/grid-actions-col-def/) - [GridExportStateParams](/x/api/data-grid/grid-export-state-params/) - [GridFilterItem](/x/api/data-grid/grid-filter-item/) - [GridFilterModel](/x/api/data-grid/grid-filter-model/) diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index 418ad2366f6bc..ed03b0af5172a 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -424,6 +424,27 @@ "description": "", "supportsApiRef": true }, + { + "name": "gridVirtualizationColumnEnabledSelector", + "returnType": "boolean", + "category": "Virtualization", + "description": "Get the enabled state for virtualization", + "supportsApiRef": true + }, + { + "name": "gridVirtualizationEnabledSelector", + "returnType": "boolean", + "category": "Virtualization", + "description": "Get the enabled state for virtualization", + "supportsApiRef": true + }, + { + "name": "gridVirtualizationSelector", + "returnType": "GridVirtualizationState", + "category": "Virtualization", + "description": "Get the columns state", + "supportsApiRef": false + }, { "name": "gridVisibleColumnDefinitionsSelector", "returnType": "GridStateColDef[]", diff --git a/docs/pages/x/api/date-pickers/date-calendar.json b/docs/pages/x/api/date-pickers/date-calendar.json index 4508121b930c8..ce0c3851261ac 100644 --- a/docs/pages/x/api/date-pickers/date-calendar.json +++ b/docs/pages/x/api/date-pickers/date-calendar.json @@ -1,24 +1,12 @@ { "props": { "autoFocus": { "type": { "name": "bool" } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -214,5 +202,5 @@ "forwardsRefTo": "HTMLDivElement", "filename": "/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx", "inheritance": null, - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/date-field.json b/docs/pages/x/api/date-pickers/date-field.json index 56d51b4e59765..fc8d18137f4b3 100644 --- a/docs/pages/x/api/date-pickers/date-field.json +++ b/docs/pages/x/api/date-pickers/date-field.json @@ -1,6 +1,7 @@ { "props": { "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -8,18 +9,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -57,6 +46,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -135,6 +125,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/date-picker-toolbar.json b/docs/pages/x/api/date-pickers/date-picker-toolbar.json index f66e825caeae9..15463e76d9e75 100644 --- a/docs/pages/x/api/date-pickers/date-picker-toolbar.json +++ b/docs/pages/x/api/date-pickers/date-picker-toolbar.json @@ -26,5 +26,5 @@ ], "styles": { "classes": ["root", "title"], "globalClasses": {}, "name": "MuiDatePickerToolbar" }, "filename": "/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx", - "demos": "
          " + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/date-picker.json b/docs/pages/x/api/date-pickers/date-picker.json index dfd25d49ff8d7..4ca2eb7fafc22 100644 --- a/docs/pages/x/api/date-pickers/date-picker.json +++ b/docs/pages/x/api/date-pickers/date-picker.json @@ -6,24 +6,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -199,6 +187,18 @@ "description": "Custom component for calendar header. Check the PickersCalendarHeader component.", "default": "PickersCalendarHeader" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", diff --git a/docs/pages/x/api/date-pickers/date-range-calendar.json b/docs/pages/x/api/date-pickers/date-range-calendar.json index e6df5a984469a..6c1c36aa8e3de 100644 --- a/docs/pages/x/api/date-pickers/date-range-calendar.json +++ b/docs/pages/x/api/date-pickers/date-range-calendar.json @@ -5,28 +5,16 @@ "type": { "name": "enum", "description": "1
          | 2
          | 3" }, "default": "2" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "currentMonthCalendarPosition": { "type": { "name": "enum", "description": "1
          | 2
          | 3" }, "default": "1" }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/date-range-picker.json b/docs/pages/x/api/date-pickers/date-range-picker.json index 57bfacbb15f30..f0770e77c4ade 100644 --- a/docs/pages/x/api/date-pickers/date-range-picker.json +++ b/docs/pages/x/api/date-pickers/date-range-picker.json @@ -10,28 +10,16 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "currentMonthCalendarPosition": { "type": { "name": "enum", "description": "1
          | 2
          | 3" }, "default": "1" }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -160,6 +148,18 @@ "description": "Custom component for the action bar, it is placed below the picker views.", "default": "PickersActionBar" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", diff --git a/docs/pages/x/api/date-pickers/date-time-field.json b/docs/pages/x/api/date-pickers/date-time-field.json index 5572cc24adaf2..45de7f45e5a64 100644 --- a/docs/pages/x/api/date-pickers/date-time-field.json +++ b/docs/pages/x/api/date-pickers/date-time-field.json @@ -2,6 +2,7 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -9,18 +10,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -64,6 +53,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -160,6 +150,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/date-time-picker-tabs.json b/docs/pages/x/api/date-pickers/date-time-picker-tabs.json index 6f2207afbc7b6..9d8583d058438 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker-tabs.json +++ b/docs/pages/x/api/date-pickers/date-time-picker-tabs.json @@ -29,5 +29,5 @@ ], "styles": { "classes": ["root"], "globalClasses": {}, "name": "MuiDateTimePickerTabs" }, "filename": "/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx", - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json b/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json index 3641c56d2b63e..8b917a3e60035 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json +++ b/docs/pages/x/api/date-pickers/date-time-picker-toolbar.json @@ -41,5 +41,5 @@ "name": "MuiDateTimePickerToolbar" }, "filename": "/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx", - "demos": "
            " + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/date-time-picker.json b/docs/pages/x/api/date-pickers/date-time-picker.json index 9678e58a782f0..4d51de00dfe7b 100644 --- a/docs/pages/x/api/date-pickers/date-time-picker.json +++ b/docs/pages/x/api/date-pickers/date-time-picker.json @@ -8,24 +8,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -188,6 +176,7 @@ }, "additionalInfo": { "sx": true } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, "timeSteps": { "type": { "name": "shape", @@ -236,6 +225,18 @@ "description": "Custom component for calendar header. Check the PickersCalendarHeader component.", "default": "PickersCalendarHeader" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", @@ -266,6 +267,12 @@ "description": "Custom component for the dialog inside which the views are rendered on mobile.", "default": "PickersModalDialogRoot" }, + { + "class": null, + "name": "digitalClockItem", + "description": "Component responsible for rendering a single digital clock item.", + "default": "MenuItem from '@mui/material'" + }, { "class": null, "name": "digitalClockSectionItem", diff --git a/docs/pages/x/api/date-pickers/desktop-date-picker.json b/docs/pages/x/api/date-pickers/desktop-date-picker.json index 7c13e56265ccd..d3090c3862bd9 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-picker.json @@ -6,24 +6,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -195,6 +183,18 @@ "description": "Custom component for calendar header. Check the PickersCalendarHeader component.", "default": "PickersCalendarHeader" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", diff --git a/docs/pages/x/api/date-pickers/desktop-date-range-picker.json b/docs/pages/x/api/date-pickers/desktop-date-range-picker.json index 98d873b0b18f7..abe69b08636e8 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-range-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-range-picker.json @@ -10,28 +10,16 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "currentMonthCalendarPosition": { "type": { "name": "enum", "description": "1
            | 2
            | 3" }, "default": "1" }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -156,6 +144,18 @@ "description": "Custom component for the action bar, it is placed below the picker views.", "default": "PickersActionBar" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", diff --git a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json index e58f80d0bb7f2..34033bc429627 100644 --- a/docs/pages/x/api/date-pickers/desktop-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-date-time-picker.json @@ -8,24 +8,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -184,6 +172,7 @@ }, "additionalInfo": { "sx": true } }, + "thresholdToRenderTimeInASingleColumn": { "type": { "name": "number" }, "default": "24" }, "timeSteps": { "type": { "name": "shape", @@ -232,6 +221,18 @@ "description": "Custom component for calendar header. Check the PickersCalendarHeader component.", "default": "PickersCalendarHeader" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", @@ -256,6 +257,12 @@ "description": "Custom component for trapping the focus inside the views on desktop.", "default": "FocusTrap from '@mui/base'." }, + { + "class": null, + "name": "digitalClockItem", + "description": "Component responsible for rendering a single digital clock item.", + "default": "MenuItem from '@mui/material'" + }, { "class": null, "name": "digitalClockSectionItem", diff --git a/docs/pages/x/api/date-pickers/desktop-time-picker.json b/docs/pages/x/api/date-pickers/desktop-time-picker.json index c14f69525db20..c1a7f0be0f9a2 100644 --- a/docs/pages/x/api/date-pickers/desktop-time-picker.json +++ b/docs/pages/x/api/date-pickers/desktop-time-picker.json @@ -8,18 +8,6 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -159,6 +147,18 @@ "description": "Custom component for the action bar, it is placed below the picker views.", "default": "PickersActionBar" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "desktopPaper", diff --git a/docs/pages/x/api/date-pickers/digital-clock.json b/docs/pages/x/api/date-pickers/digital-clock.json index 4d197b8342318..1c4d1f7136c3e 100644 --- a/docs/pages/x/api/date-pickers/digital-clock.json +++ b/docs/pages/x/api/date-pickers/digital-clock.json @@ -3,18 +3,6 @@ "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -104,5 +92,5 @@ "forwardsRefTo": "HTMLDivElement", "filename": "/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx", "inheritance": null, - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/index.md b/docs/pages/x/api/date-pickers/index.md index 5ac65d65a4038..c6c4bfb031609 100644 --- a/docs/pages/x/api/date-pickers/index.md +++ b/docs/pages/x/api/date-pickers/index.md @@ -42,7 +42,9 @@ - [MultiInputDateRangeField](/x/api/date-pickers/multi-input-date-range-field/) - [SingleInputDateRangeField](/x/api/date-pickers/single-input-date-range-field/) - [MultiInputTimeRangeField](/x/api/date-pickers/multi-input-time-range-field/) +- [SingleInputTimeRangeField](/x/api/date-pickers/single-input-time-range-field/) - [MultiInputDateTimeRangeField](/x/api/date-pickers/multi-input-date-time-range-field/) +- [SingleInputDateTimeRangeField](/x/api/date-pickers/single-input-date-time-range-field/) ### Calendars diff --git a/docs/pages/x/api/date-pickers/mobile-date-picker.json b/docs/pages/x/api/date-pickers/mobile-date-picker.json index 8bc29dee16515..9e81fe59468c2 100644 --- a/docs/pages/x/api/date-pickers/mobile-date-picker.json +++ b/docs/pages/x/api/date-pickers/mobile-date-picker.json @@ -6,24 +6,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/mobile-date-range-picker.json b/docs/pages/x/api/date-pickers/mobile-date-range-picker.json index 31acd8d9789ff..3cb09164007cc 100644 --- a/docs/pages/x/api/date-pickers/mobile-date-range-picker.json +++ b/docs/pages/x/api/date-pickers/mobile-date-range-picker.json @@ -10,28 +10,16 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "currentMonthCalendarPosition": { "type": { "name": "enum", "description": "1
            | 2
            | 3" }, "default": "1" }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, @@ -156,6 +144,18 @@ "description": "Custom component for the action bar, it is placed below the picker views.", "default": "PickersActionBar" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "day", diff --git a/docs/pages/x/api/date-pickers/mobile-date-time-picker.json b/docs/pages/x/api/date-pickers/mobile-date-time-picker.json index 30a78d922d349..770df156198e2 100644 --- a/docs/pages/x/api/date-pickers/mobile-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/mobile-date-time-picker.json @@ -8,24 +8,12 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/mobile-time-picker.json b/docs/pages/x/api/date-pickers/mobile-time-picker.json index cab1eaf180258..f45db734ff6a5 100644 --- a/docs/pages/x/api/date-pickers/mobile-time-picker.json +++ b/docs/pages/x/api/date-pickers/mobile-time-picker.json @@ -8,18 +8,6 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, diff --git a/docs/pages/x/api/date-pickers/multi-input-date-range-field.json b/docs/pages/x/api/date-pickers/multi-input-date-range-field.json index 95026cefd8c4c..70cc83e045942 100644 --- a/docs/pages/x/api/date-pickers/multi-input-date-range-field.json +++ b/docs/pages/x/api/date-pickers/multi-input-date-range-field.json @@ -1,18 +1,6 @@ { "props": { "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "direction": { "type": { diff --git a/docs/pages/x/api/date-pickers/multi-input-date-time-range-field.json b/docs/pages/x/api/date-pickers/multi-input-date-time-range-field.json index 97dd146374a0e..000779bda61bc 100644 --- a/docs/pages/x/api/date-pickers/multi-input-date-time-range-field.json +++ b/docs/pages/x/api/date-pickers/multi-input-date-time-range-field.json @@ -2,18 +2,6 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "direction": { "type": { diff --git a/docs/pages/x/api/date-pickers/multi-input-time-range-field.json b/docs/pages/x/api/date-pickers/multi-input-time-range-field.json index 52441f66220d2..53a912ae4d73c 100644 --- a/docs/pages/x/api/date-pickers/multi-input-time-range-field.json +++ b/docs/pages/x/api/date-pickers/multi-input-time-range-field.json @@ -2,18 +2,6 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "direction": { "type": { diff --git a/docs/pages/x/api/date-pickers/multi-section-digital-clock.json b/docs/pages/x/api/date-pickers/multi-section-digital-clock.json index 256d1589acc80..f7851168da5cf 100644 --- a/docs/pages/x/api/date-pickers/multi-section-digital-clock.json +++ b/docs/pages/x/api/date-pickers/multi-section-digital-clock.json @@ -3,18 +3,6 @@ "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -130,5 +118,5 @@ "forwardsRefTo": "HTMLDivElement", "filename": "/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx", "inheritance": null, - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/pickers-action-bar.json b/docs/pages/x/api/date-pickers/pickers-action-bar.json index c02d0ed573f2a..4dc6d8b14b2e2 100644 --- a/docs/pages/x/api/date-pickers/pickers-action-bar.json +++ b/docs/pages/x/api/date-pickers/pickers-action-bar.json @@ -26,5 +26,5 @@ "styles": { "classes": ["root", "spacing"], "globalClasses": {}, "name": "MuiPickersActionBar" }, "filename": "/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.tsx", "inheritance": null, - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/pickers-calendar-header.json b/docs/pages/x/api/date-pickers/pickers-calendar-header.json index f206564a80788..72b7782222543 100644 --- a/docs/pages/x/api/date-pickers/pickers-calendar-header.json +++ b/docs/pages/x/api/date-pickers/pickers-calendar-header.json @@ -12,7 +12,44 @@ "additionalInfo": { "sx": true } } }, - "slots": [], + "slots": [ + { + "class": null, + "name": "leftArrowIcon", + "description": "Icon displayed in the left view switch button.", + "default": "ArrowLeft" + }, + { + "class": null, + "name": "nextIconButton", + "description": "Button allowing to switch to the right view.", + "default": "IconButton" + }, + { + "class": null, + "name": "previousIconButton", + "description": "Button allowing to switch to the left view.", + "default": "IconButton" + }, + { + "class": null, + "name": "rightArrowIcon", + "description": "Icon displayed in the right view switch button.", + "default": "ArrowRight" + }, + { + "class": null, + "name": "switchViewButton", + "description": "Button displayed to switch between different calendar views.", + "default": "IconButton" + }, + { + "class": null, + "name": "switchViewIcon", + "description": "Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is 'year'.", + "default": "ArrowDropDown" + } + ], "name": "PickersCalendarHeader", "imports": [ "import { PickersCalendarHeader } from '@mui/x-date-pickers/PickersCalendarHeader';", @@ -25,5 +62,5 @@ "name": "MuiPickersCalendarHeader" }, "filename": "/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx", - "demos": "
              " + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/pickers-layout.json b/docs/pages/x/api/date-pickers/pickers-layout.json index 86d237f30ae5f..0ded142344a53 100644 --- a/docs/pages/x/api/date-pickers/pickers-layout.json +++ b/docs/pages/x/api/date-pickers/pickers-layout.json @@ -1,17 +1,5 @@ { "props": { - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "orientation": { "type": { "name": "enum", "description": "'landscape'
              | 'portrait'" } }, diff --git a/docs/pages/x/api/date-pickers/pickers-shortcuts.json b/docs/pages/x/api/date-pickers/pickers-shortcuts.json index 9444bc132107a..169630c019633 100644 --- a/docs/pages/x/api/date-pickers/pickers-shortcuts.json +++ b/docs/pages/x/api/date-pickers/pickers-shortcuts.json @@ -35,5 +35,5 @@ "name": "MuiPickersShortcuts" }, "filename": "/packages/x-date-pickers/src/PickersShortcuts/PickersShortcuts.tsx", - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/single-input-date-range-field.json b/docs/pages/x/api/date-pickers/single-input-date-range-field.json index 8bda7a1f2bcf2..ff21ea8b08373 100644 --- a/docs/pages/x/api/date-pickers/single-input-date-range-field.json +++ b/docs/pages/x/api/date-pickers/single-input-date-range-field.json @@ -1,6 +1,7 @@ { "props": { "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -8,18 +9,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -57,6 +46,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -119,6 +109,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json b/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json index 3fa7b79e206a0..72f065f4391ff 100644 --- a/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json +++ b/docs/pages/x/api/date-pickers/single-input-date-time-range-field.json @@ -2,6 +2,7 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -9,18 +10,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -64,6 +53,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -144,6 +134,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/single-input-time-range-field.json b/docs/pages/x/api/date-pickers/single-input-time-range-field.json index f40874c7ca8c9..92a3650234182 100644 --- a/docs/pages/x/api/date-pickers/single-input-time-range-field.json +++ b/docs/pages/x/api/date-pickers/single-input-time-range-field.json @@ -2,6 +2,7 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -9,18 +10,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "arrayOf", "description": "Array<any>" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -60,6 +49,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -132,6 +122,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/static-date-picker.json b/docs/pages/x/api/date-pickers/static-date-picker.json index 16149bcb58eb1..4a774e712581f 100644 --- a/docs/pages/x/api/date-pickers/static-date-picker.json +++ b/docs/pages/x/api/date-pickers/static-date-picker.json @@ -2,24 +2,12 @@ "props": { "autoFocus": { "type": { "name": "bool" } }, "className": { "type": { "name": "string" } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/static-date-range-picker.json b/docs/pages/x/api/date-pickers/static-date-range-picker.json index 73f1726a1af4f..0bbcee14a4247 100644 --- a/docs/pages/x/api/date-pickers/static-date-range-picker.json +++ b/docs/pages/x/api/date-pickers/static-date-range-picker.json @@ -6,28 +6,16 @@ "default": "2" }, "className": { "type": { "name": "string" } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "currentMonthCalendarPosition": { "type": { "name": "enum", "description": "1
              | 2
              | 3" }, "default": "1" }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/static-date-time-picker.json b/docs/pages/x/api/date-pickers/static-date-time-picker.json index de304a3c3adc4..0b79a299cc7b0 100644 --- a/docs/pages/x/api/date-pickers/static-date-time-picker.json +++ b/docs/pages/x/api/date-pickers/static-date-time-picker.json @@ -4,24 +4,12 @@ "ampmInClock": { "type": { "name": "bool" }, "default": "true on desktop, false on mobile" }, "autoFocus": { "type": { "name": "bool" } }, "className": { "type": { "name": "string" } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "dayOfWeekFormatter": { "type": { "name": "func" }, - "default": "(day) => day.charAt(0).toUpperCase()", + "default": "(_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase()", "signature": { - "type": "function(day: string) => string", - "describedArgs": ["day"], + "type": "function(day: string, date: TDate) => string", + "describedArgs": ["day", "date"], "returned": "string" } }, diff --git a/docs/pages/x/api/date-pickers/static-time-picker.json b/docs/pages/x/api/date-pickers/static-time-picker.json index 1bac66ea138b1..a6f98b36d330e 100644 --- a/docs/pages/x/api/date-pickers/static-time-picker.json +++ b/docs/pages/x/api/date-pickers/static-time-picker.json @@ -4,18 +4,6 @@ "ampmInClock": { "type": { "name": "bool" }, "default": "true on desktop, false on mobile" }, "autoFocus": { "type": { "name": "bool" } }, "className": { "type": { "name": "string" } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, diff --git a/docs/pages/x/api/date-pickers/time-clock.json b/docs/pages/x/api/date-pickers/time-clock.json index b452da8e52307..4ebcbdd0edb85 100644 --- a/docs/pages/x/api/date-pickers/time-clock.json +++ b/docs/pages/x/api/date-pickers/time-clock.json @@ -4,18 +4,6 @@ "ampmInClock": { "type": { "name": "bool" } }, "autoFocus": { "type": { "name": "bool" } }, "classes": { "type": { "name": "object" }, "additionalInfo": { "cssApi": true } }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -141,5 +129,5 @@ "forwardsRefTo": "HTMLDivElement", "filename": "/packages/x-date-pickers/src/TimeClock/TimeClock.tsx", "inheritance": null, - "demos": "" + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/time-field.json b/docs/pages/x/api/date-pickers/time-field.json index 694e207ef527f..516e5b176d0c1 100644 --- a/docs/pages/x/api/date-pickers/time-field.json +++ b/docs/pages/x/api/date-pickers/time-field.json @@ -2,6 +2,7 @@ "props": { "ampm": { "type": { "name": "bool" }, "default": "`utils.is12HourCycleInCurrentLocale()`" }, "autoFocus": { "type": { "name": "bool" } }, + "clearable": { "type": { "name": "bool" } }, "color": { "type": { "name": "enum", @@ -9,18 +10,6 @@ }, "default": "'primary'" }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "disabled": { "type": { "name": "bool" } }, "disableFuture": { "type": { "name": "bool" } }, @@ -60,6 +49,7 @@ "describedArgs": ["value", "context"] } }, + "onClear": { "type": { "name": "func" } }, "onError": { "type": { "name": "func" }, "signature": { @@ -132,6 +122,18 @@ } }, "slots": [ + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "textField", diff --git a/docs/pages/x/api/date-pickers/time-picker-toolbar.json b/docs/pages/x/api/date-pickers/time-picker-toolbar.json index ec3663d62dfca..8c1a39182317c 100644 --- a/docs/pages/x/api/date-pickers/time-picker-toolbar.json +++ b/docs/pages/x/api/date-pickers/time-picker-toolbar.json @@ -39,5 +39,5 @@ "name": "MuiTimePickerToolbar" }, "filename": "/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx", - "demos": "
                " + "demos": "" } diff --git a/docs/pages/x/api/date-pickers/time-picker.json b/docs/pages/x/api/date-pickers/time-picker.json index 8f7a3476cf2d9..7488b5966ea29 100644 --- a/docs/pages/x/api/date-pickers/time-picker.json +++ b/docs/pages/x/api/date-pickers/time-picker.json @@ -8,18 +8,6 @@ "type": { "name": "bool" }, "default": "`true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop)." }, - "components": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slots." - }, - "componentsProps": { - "type": { "name": "object" }, - "default": "{}", - "deprecated": true, - "deprecationInfo": "Please use slotProps." - }, "defaultValue": { "type": { "name": "any" } }, "desktopModeMediaQuery": { "type": { "name": "string" }, @@ -163,6 +151,18 @@ "description": "Custom component for the action bar, it is placed below the picker views.", "default": "PickersActionBar" }, + { + "class": null, + "name": "clearButton", + "description": "Button to clear the value.", + "default": "IconButton" + }, + { + "class": null, + "name": "clearIcon", + "description": "Icon to display inside the clear button.", + "default": "ClearIcon" + }, { "class": null, "name": "desktopPaper", diff --git a/docs/pages/x/migration/migration-charts-v6.js b/docs/pages/x/migration/migration-charts-v6.js new file mode 100644 index 0000000000000..4ba678d249864 --- /dev/null +++ b/docs/pages/x/migration/migration-charts-v6.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/migration/migration-charts-v6/migration-charts-v6.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/migration/migration-data-grid-v6.js b/docs/pages/x/migration/migration-data-grid-v6.js new file mode 100644 index 0000000000000..8ce5141b987e5 --- /dev/null +++ b/docs/pages/x/migration/migration-data-grid-v6.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/migration/migration-data-grid-v6/migration-data-grid-v6.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/migration/migration-pickers-v6.js b/docs/pages/x/migration/migration-pickers-v6.js new file mode 100644 index 0000000000000..8f64f9f109e48 --- /dev/null +++ b/docs/pages/x/migration/migration-pickers-v6.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/migration/migration-pickers-v6/migration-pickers-v6.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/migration/migration-tree-view-v6.js b/docs/pages/x/migration/migration-tree-view-v6.js new file mode 100644 index 0000000000000..1ebe531b3f6b2 --- /dev/null +++ b/docs/pages/x/migration/migration-tree-view-v6.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/migration/migration-tree-view-v6/migration-tree-view-v6.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-charts/funnel.js b/docs/pages/x/react-charts/funnel.js index 60fbdf25f9638..3500b4c849ac0 100644 --- a/docs/pages/x/react-charts/funnel.js +++ b/docs/pages/x/react-charts/funnel.js @@ -3,5 +3,5 @@ import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; import * as pageProps from 'docsx/data/charts/funnel/funnel.md?@mui/markdown'; export default function Page() { - return ; + return ; } diff --git a/docs/pages/x/react-charts/gantt.js b/docs/pages/x/react-charts/gantt.js index 14b3b86973e76..e897b8385bb32 100644 --- a/docs/pages/x/react-charts/gantt.js +++ b/docs/pages/x/react-charts/gantt.js @@ -3,5 +3,5 @@ import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; import * as pageProps from 'docsx/data/charts/gantt/gantt.md?@mui/markdown'; export default function Page() { - return ; + return ; } diff --git a/docs/pages/x/react-charts/sankey.js b/docs/pages/x/react-charts/sankey.js index eb43ab647b950..98525d5604850 100644 --- a/docs/pages/x/react-charts/sankey.js +++ b/docs/pages/x/react-charts/sankey.js @@ -3,5 +3,5 @@ import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; import * as pageProps from 'docsx/data/charts/sankey/sankey.md?@mui/markdown'; export default function Page() { - return ; + return ; } diff --git a/docs/pages/x/react-data-grid/custom-columns.js b/docs/pages/x/react-data-grid/custom-columns.js new file mode 100644 index 0000000000000..4af41a45ef3d2 --- /dev/null +++ b/docs/pages/x/react-data-grid/custom-columns.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/custom-columns/custom-columns.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/aggregation.js b/docs/pages/x/react-data-grid/server-side-data/aggregation.js new file mode 100644 index 0000000000000..35594f6fa6413 --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/aggregation.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/aggregation.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/index.js b/docs/pages/x/react-data-grid/server-side-data/index.js new file mode 100644 index 0000000000000..8d183d6d499c0 --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/index.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/index.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/infinite-loading.js b/docs/pages/x/react-data-grid/server-side-data/infinite-loading.js new file mode 100644 index 0000000000000..9b9c08c05e85f --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/infinite-loading.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/infinite-loading.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/lazy-loading.js b/docs/pages/x/react-data-grid/server-side-data/lazy-loading.js new file mode 100644 index 0000000000000..f0b78beb6cac3 --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/lazy-loading.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/lazy-loading.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/row-grouping.js b/docs/pages/x/react-data-grid/server-side-data/row-grouping.js new file mode 100644 index 0000000000000..06017357e71e6 --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/row-grouping.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/row-grouping.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/server-side-data/tree-data.js b/docs/pages/x/react-data-grid/server-side-data/tree-data.js new file mode 100644 index 0000000000000..6d122e9e139f9 --- /dev/null +++ b/docs/pages/x/react-data-grid/server-side-data/tree-data.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/server-side-data/tree-data.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/react-data-grid/style-recipes.js b/docs/pages/x/react-data-grid/style-recipes.js new file mode 100644 index 0000000000000..bdfb64e5b91bf --- /dev/null +++ b/docs/pages/x/react-data-grid/style-recipes.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/data-grid/style-recipes/style-recipes.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/pages/x/whats-new.js b/docs/pages/x/whats-new.js new file mode 100644 index 0000000000000..d30bf447441f7 --- /dev/null +++ b/docs/pages/x/whats-new.js @@ -0,0 +1,7 @@ +import * as React from 'react'; +import MarkdownDocs from 'docs/src/modules/components/MarkdownDocs'; +import * as pageProps from 'docsx/data/whats-new/whats-new.md?@mui/markdown'; + +export default function Page() { + return ; +} diff --git a/docs/public/_redirects b/docs/public/_redirects index 50aa676917f48..5852a23a91e3b 100644 --- a/docs/public/_redirects +++ b/docs/public/_redirects @@ -48,7 +48,6 @@ /:lang/x/api/date-pickers/clock-picker/ /:lang/x/api/date-pickers/time-clock/ 301 /:lang/x/* /x/:splat 301 # 2023 -/x/whats-new/ https://mui.com/blog/mui-x-v6/ 301 /x/api/date-pickers/calendar-picker/ /x/api/date-pickers/date-calendar/ 301 /x/api/date-pickers/calendar-picker-skeleton/ /x/api/date-pickers/day-calendar-skeleton/ 301 /x/api/date-pickers/month-picker/ /x/api/date-pickers/month-calendar/ 301 diff --git a/docs/public/static/styles/prism-okaidia.css b/docs/public/static/styles/prism-okaidia.css index 8a3a1ac149a55..37e28ee86b482 100644 --- a/docs/public/static/styles/prism-okaidia.css +++ b/docs/public/static/styles/prism-okaidia.css @@ -16,7 +16,7 @@ pre[class*='language-'] { word-spacing: normal; word-break: normal; word-wrap: normal; - line-height: 1.5; + /* line-height: 1.5; */ /* -moz-tab-size: 4; */ /* -o-tab-size: 4; */ @@ -147,7 +147,6 @@ pre[class*='language-'] { .token.property, .token.tag, -.token.constant, .token.symbol, .token.deleted { color: #fc929e; diff --git a/docs/scripts/api/buildComponentsDocumentation.ts b/docs/scripts/api/buildComponentsDocumentation.ts index 97cd3bc99b47a..7a759a65dae78 100644 --- a/docs/scripts/api/buildComponentsDocumentation.ts +++ b/docs/scripts/api/buildComponentsDocumentation.ts @@ -15,7 +15,6 @@ import generatePropTypeDescription, { } from '@mui-internal/api-docs-builder/utils/generatePropTypeDescription'; import parseTest from '@mui-internal/api-docs-builder/utils/parseTest'; import kebabCase from 'lodash/kebabCase'; -import camelCase from 'lodash/camelCase'; import { LANGUAGES } from 'docs/config'; import findPagesMarkdownNew from '@mui-internal/api-docs-builder/utils/findPagesMarkdown'; import { defaultHandlers, parse as docgenParse } from 'react-docgen'; @@ -80,11 +79,13 @@ function extractSlots(options: { project, checkDeclarations: true, shouldResolveObject: ({ name }) => { - return name === 'components'; + // TODO v7: Remove the `components` fallback once `slots` is used everywhere + return name === 'slots' || name === 'components'; }, shouldInclude: ({ name, depth }) => { // The keys allowed in the `components` prop have depth=2 - return name === 'components' || depth === 2; + // TODO v7: Remove the `components` fallback once `slots` is used everywhere + return name === 'slots' || name === 'components' || depth === 2; }, }); @@ -93,7 +94,10 @@ function extractSlots(options: { throw new Error(`No proptypes found for \`${displayName}\``); } - const componentsProps = props.types.find((type) => type.name === 'components')!; + const componentsProps = props.types.find( + // TODO v7: Remove the `components` fallback once `slots` is used everywhere + (type) => type.name === 'slots' || type.name === 'components', + )!; if (!componentsProps) { return slots; } @@ -126,12 +130,7 @@ function extractSlots(options: { return; } - // Workaround to generate correct (camelCase) keys for slots in v6 `API Reference` documentation - // TODO v7: Remove camelCase once `Grid(Pro|Premium)SlotsComponent` type is refactored to have `camelCase` names - // Shifting to `slots` prop instead of `components` prop strips off the `default` property due to deduced type `UncapitalizedGridSlotsComponent` - const slotName = camelCase(name); - - slots[slotName] = { + slots[name] = { type, description, default: defaultValue, @@ -478,7 +477,8 @@ const buildComponentDocumentation = async (options: { /** * Slot descriptions. */ - if (componentApi.propDescriptions.components) { + // TODO v7: Remove the `components` fallback once `slots` is used everywhere + if (componentApi.propDescriptions.slots || componentApi.propDescriptions.components) { const slots = extractSlots({ filename, name: reactApi.name, // e.g. DataGrid diff --git a/docs/scripts/api/buildInterfacesDocumentation.ts b/docs/scripts/api/buildInterfacesDocumentation.ts index 44b17ac4f9842..0efd19ec7401c 100644 --- a/docs/scripts/api/buildInterfacesDocumentation.ts +++ b/docs/scripts/api/buildInterfacesDocumentation.ts @@ -40,21 +40,22 @@ interface ParsedProperty { } const GRID_API_INTERFACES_WITH_DEDICATED_PAGES = [ - 'GridRowSelectionApi', - 'GridRowMultiSelectionApi', 'GridCellSelectionApi', - 'GridFilterApi', - 'GridSortApi', - 'GridPaginationApi', - 'GridCsvExportApi', - 'GridScrollApi', - 'GridEditingApi', - 'GridRowGroupingApi', 'GridColumnPinningApi', + 'GridColumnResizeApi', + 'GridCsvExportApi', 'GridDetailPanelApi', - 'GridPrintExportApi', - 'GridDisableVirtualizationApi', + 'GridEditingApi', 'GridExcelExportApi', + 'GridFilterApi', + 'GridPaginationApi', + 'GridPrintExportApi', + 'GridRowGroupingApi', + 'GridRowMultiSelectionApi', + 'GridRowSelectionApi', + 'GridScrollApi', + 'GridSortApi', + 'GridVirtualizationApi', ]; const OTHER_GRID_INTERFACES_WITH_DEDICATED_PAGES = [ diff --git a/docs/scripts/api/saveApiDocPages.ts b/docs/scripts/api/saveApiDocPages.ts index 1f6d1c1d1cf04..8310ecca33305 100644 --- a/docs/scripts/api/saveApiDocPages.ts +++ b/docs/scripts/api/saveApiDocPages.ts @@ -59,7 +59,7 @@ export default async function saveApiDocPages( writePrettifiedFile( path.resolve(dataFolder, `${folderName}-${identifier}-pages.ts`), - `import type { MuiPage } from '@mui/monorepo/docs/src/MuiPage'; + `import type { MuiPage } from 'docs/src/MuiPage'; export default ${JSON.stringify(pagesPerFolder[folderName])} as MuiPage[]`, project, diff --git a/docs/scripts/formattedTSDemos.js b/docs/scripts/formattedTSDemos.js index 61f790481aed7..5e5406d565453 100644 --- a/docs/scripts/formattedTSDemos.js +++ b/docs/scripts/formattedTSDemos.js @@ -9,7 +9,7 @@ * List of demos to ignore when transpiling * Example: "app-bar/BottomAppBar.tsx" */ -const ignoreList = ['/pages.ts']; +const ignoreList = ['/pages.ts', 'styling.ts', 'styling.tsx']; const fs = require('fs'); const fse = require('fs-extra'); diff --git a/docs/scripts/pages/playground/index.tsx b/docs/scripts/pages/playground/index.tsx new file mode 100644 index 0000000000000..8892bf1b46a8d --- /dev/null +++ b/docs/scripts/pages/playground/index.tsx @@ -0,0 +1,7 @@ +import * as React from 'react'; + +export default function Playground() { + return ( +
                A playground for a fast iteration development cycle in isolation outside of git.
                + ); +} diff --git a/docs/scripts/reportBrokenLinks.js b/docs/scripts/reportBrokenLinks.js index b63814708e958..47d79bf250f1f 100644 --- a/docs/scripts/reportBrokenLinks.js +++ b/docs/scripts/reportBrokenLinks.js @@ -1,7 +1,7 @@ /* eslint-disable no-console */ const path = require('path'); const fse = require('fs-extra'); -const { parseDocFolder, getAnchor } = require('@mui/monorepo/docs/scripts/reportBrokenLinks'); +const { parseDocFolder, getAnchor } = require('docs/scripts/reportBrokenLinks'); const docsSpaceRoot = path.join(__dirname, '../'); diff --git a/docs/src/modules/components/CustomizationPlayground.tsx b/docs/src/modules/components/CustomizationPlayground.tsx new file mode 100644 index 0000000000000..e211ecc4a9ac6 --- /dev/null +++ b/docs/src/modules/components/CustomizationPlayground.tsx @@ -0,0 +1,396 @@ +import * as React from 'react'; +// @ts-ignore +import HighlightedCode from 'docs/src/modules/components/HighlightedCode'; +// @ts-ignore +import BrandingProvider from 'docs/src/BrandingProvider'; +import { styled, Theme, alpha, useTheme } from '@mui/material/styles'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import CheckIcon from '@mui/icons-material/Check'; +import Chip from '@mui/material/Chip'; +import ToggleButton, { ToggleButtonProps } from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import Tooltip from '@mui/material/Tooltip'; +import Slider from '@mui/material/Slider'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; +import Alert from '@mui/material/Alert'; +import { grey } from '@mui/material/colors'; +import pick from 'lodash/pick'; +import { + useCustomizationPlayground, + UseCustomizationPlaygroundProps, + DEFAULT_COLORS, + withStyles, + ColorKey, + StyleTokensType, + CustomizationLabelType, +} from '../utils/useCustomizationPlayground'; + +const PlaygroundWrapper = styled(Box)(({ theme }) => ({ + display: 'flex', + borderRadius: theme.shape.borderRadius, + border: `1px solid ${grey[200]}`, + padding: theme.spacing(2), + gap: theme.spacing(2), + justifyContent: 'space-between', + + [theme.breakpoints.down('lg')]: { flexWrap: 'wrap-reverse' }, +})); + +const PlaygroundDemoArea = styled(Box)(() => ({ + minWidth: 320, +})); + +const PlaygroundConfigArea = styled(Box)(({ theme }) => ({ + padding: theme.spacing(2), + backgroundColor: alpha(theme.palette.primary.light, 0.05), + border: `1px solid ${grey[200]}`, + borderRadius: '4px', + [theme.breakpoints.down('lg')]: { + display: 'flex', + flexWrap: 'wrap', + flexGrow: 1, + gap: theme.spacing(3), + }, +})); + +const ComponentsSelect = styled(Select)(({ theme }) => ({ + ...theme.typography.caption, +})); + +const ConfigSectionWrapper = styled(Box)(({ theme }) => ({ + gap: theme.spacing(0.5), + width: 250, +})); + +const ConfigLabel = styled(Typography)(({ theme }) => ({ + marginTop: theme.spacing(2), + marginBottom: theme.spacing(1), + fontWeight: theme.typography.fontWeightBold, + fontSize: theme.typography.pxToRem(12), + textTransform: 'uppercase', + letterSpacing: '.08rem', +})); + +const ConfigItemLabel = styled(Typography)(({ theme }) => ({ + ...theme.typography.caption, + letterSpacing: '.08rem', + textTransform: 'uppercase', + color: theme.palette.text.secondary, + fontSize: theme.typography.pxToRem(11), + fontweight: 600, +})); + +const SlotItemsWrapper = styled(Box)(({ theme }) => ({ + display: 'flex', + gap: theme.spacing(0.5), + flexWrap: 'wrap', +})); + +const SlotItem = styled(Button)(({ theme }) => ({ + borderWidth: 1, + borderRadius: theme.spacing(2), + textTransform: 'none', + padding: theme.spacing(0.1, 1), +})); + +const TabsWrapper = styled(Box)(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + marginBottom: theme.spacing(1), + gap: theme.spacing(1), + [theme.breakpoints.down('sm')]: { + flexDirection: 'column', + alignItems: 'flex-start', + }, +})); + +type TabsProps = { + value: string; + onChange: (e: React.SyntheticEvent, value: any) => void; + options: Partial; +}; + +function StylingApproachTabs({ value, onChange, options }: TabsProps) { + return ( + + + {(Object.keys(options) as Array)?.map((option) => ( + + ))} + + + ); +} + +const RECOMMENDATION_MESSAGES: { [k in 'warning' | 'success']: string } = { + warning: + 'This might not be the best styling approach for the selected component. You can check out the other options for better results.', + success: 'This is the recommended styling approach for the selected component.', +}; + +function StylingRecommendation({ type = 'success' }: { type?: 'warning' | 'success' }) { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); + + if (isMobile) { + return ( + + {RECOMMENDATION_MESSAGES[type]} + + ); + } + + return ( + + {type === 'warning' ? ( + + ) : ( + + )} + + ); +} + +const StyledToggleButtonGroup = styled(ToggleButtonGroup)(({ theme }) => ({ + backgroundColor: 'transparent', + '& .MuiToggleButtonGroup-grouped': { + margin: theme.spacing(0.5), + border: 0, + '&.Mui-disabled': { + border: 0, + }, + '&:not(:first-of-type)': { + borderRadius: theme.shape.borderRadius, + }, + '&:first-of-type': { + borderRadius: theme.shape.borderRadius, + }, + }, +})); + +interface Props extends ToggleButtonProps { + selectedColor: ColorKey; +} + +const StyledToggleButton = styled(ToggleButton, { + shouldForwardProp: (prop) => prop !== 'selectedColor', +})(({ theme, selectedColor = 'blue' }: Props & { theme: Theme }) => ({ + height: theme.spacing(3), + minWidth: theme.spacing(3), + color: theme.palette.primary.contrastText, + background: DEFAULT_COLORS[selectedColor][500], + '&:hover': { + background: DEFAULT_COLORS[selectedColor][600], + }, + '&.Mui-selected': { + borderColor: 'transparent!important', + background: DEFAULT_COLORS[selectedColor][500], + color: theme.palette.primary.contrastText, + '&:hover': { + borderColor: 'transparent!important', + background: DEFAULT_COLORS[selectedColor][600], + }, + }, +})); + +function ColorSwitcher({ + handleTokenChange, + selectedColor, +}: { + handleTokenChange: (token: 'color', value: keyof typeof DEFAULT_COLORS) => void; + selectedColor: ColorKey; +}) { + return ( + + Color + { + if (value !== null) { + handleTokenChange('color', value); + } + }} + aria-label="color palette" + > + {(Object.keys(DEFAULT_COLORS) as Array).map((color) => ( + + {color === selectedColor ? : ''} + + ))} + + + ); +} + +function NumericTokensSlider({ + handleTokenChange, + tokens, +}: { + handleTokenChange: (token: keyof StyleTokensType, value: number) => void; + tokens: Record, number>; +}) { + return ( + + {(Object.keys(tokens) as Array).map((token) => ( + + {token} + handleTokenChange(token, value as number)} + aria-label={`${token} slider`} + key={token} + min={0} + max={20} + marks + step={1} + /> + + ))} + + ); +} + +const CustomizationPlayground = function CustomizationPlayground({ + children, + examples, + componentName, +}: UseCustomizationPlaygroundProps) { + const { + selectedDemo, + customizationOptions, + selectedCustomizationOption, + selectDemo, + setSelectedCustomizationOption, + selectedSlot, + setSelectedSlot, + codeExample, + availableSlots, + handleTokenChange, + selectedTokens, + selectedExample, + } = useCustomizationPlayground({ examples, componentName }); + + const StyledChild = withStyles( + Box, + selectedTokens, + selectedCustomizationOption, + selectedDemo, + selectedSlot, + ); + + const shouldBeInteractive = selectedDemo && selectedCustomizationOption && codeExample; + + return ( + + {selectedDemo && customizationOptions && selectedCustomizationOption && ( + + + { + setSelectedCustomizationOption(newValue); + }} + value={selectedCustomizationOption} + options={customizationOptions} + /> + {selectedExample && } + {' '} + + )} + + + + {React.Children.map( + children, + (child) => + React.isValidElement(child) && + React.cloneElement( + child, + selectedDemo && selectedCustomizationOption + ? { + ...examples[selectedDemo]?.examples[selectedCustomizationOption] + ?.componentProps, + } + : {}, + ), + )} + + + {shouldBeInteractive && ( + + + + Components + + selectDemo(e.target.value as string)} + > + {Object.keys(examples || {}).map((item) => ( + + {item} + + ))} + + + {availableSlots && ( + + Slots + + {(availableSlots as string[]).map((slot: string) => ( + setSelectedSlot(slot)} + key={slot} + variant={selectedSlot === slot ? 'contained' : 'outlined'} + > + {slot} + + ))} + + + )} + + + Playground + + + + + + + + )} + + + {shouldBeInteractive && ( + + )} + + ); +}; + +export default CustomizationPlayground; diff --git a/docs/src/modules/components/DataGridInstallationInstructions.js b/docs/src/modules/components/DataGridInstallationInstructions.js index ac7d0d07d9322..3deba052a6b9c 100644 --- a/docs/src/modules/components/DataGridInstallationInstructions.js +++ b/docs/src/modules/components/DataGridInstallationInstructions.js @@ -2,9 +2,9 @@ import * as React from 'react'; import InstallationInstructions from './InstallationInstructions'; const packages = { - Community: '@mui/x-data-grid', - Pro: '@mui/x-data-grid-pro', - Premium: '@mui/x-data-grid-premium', + Community: '@mui/x-data-grid@next', + Pro: '@mui/x-data-grid-pro@next', + Premium: '@mui/x-data-grid-premium@next', }; export default function DataGridInstallationInstructions() { diff --git a/docs/src/modules/components/PickersInstallationInstructions.js b/docs/src/modules/components/PickersInstallationInstructions.js index 123fa23345f82..02dc44eea2296 100644 --- a/docs/src/modules/components/PickersInstallationInstructions.js +++ b/docs/src/modules/components/PickersInstallationInstructions.js @@ -2,8 +2,8 @@ import * as React from 'react'; import InstallationInstructions from './InstallationInstructions'; const packages = { - Community: '@mui/x-date-pickers', - Pro: '@mui/x-date-pickers-pro', + Community: '@mui/x-date-pickers@next', + Pro: '@mui/x-date-pickers-pro@next', }; const peerDependency = { diff --git a/docs/src/modules/components/PickersPlayground.tsx b/docs/src/modules/components/PickersPlayground.tsx index ae6d73d25c696..5d47348d87b70 100644 --- a/docs/src/modules/components/PickersPlayground.tsx +++ b/docs/src/modules/components/PickersPlayground.tsx @@ -10,10 +10,11 @@ import FormControlLabel, { formControlLabelClasses } from '@mui/material/FormCon import Radio from '@mui/material/Radio'; import FormHelperText from '@mui/material/FormHelperText'; import FormGroup from '@mui/material/FormGroup'; -import Checkbox from '@mui/material/Checkbox'; +import Checkbox, { CheckboxProps } from '@mui/material/Checkbox'; import Select from '@mui/material/Select'; import MenuItem from '@mui/material/MenuItem'; import Divider from '@mui/material/Divider'; +import Tooltip from '@mui/material/Tooltip'; 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'; @@ -168,6 +169,31 @@ const shortcutsItems: PickersShortcutsItem>[] = [ { label: 'Reset', getValue: () => [null, null] }, ]; +function DisabledCheckboxTooltip({ children }: { children: React.ReactElement }) { + return ( + {children} + ); +} + +function ViewCheckbox({ + onlyOneView, + name, + ...other +}: Pick & { + onlyOneView: boolean; +}) { + const isDisabled = other.checked && onlyOneView; + const Wrapper = isDisabled ? DisabledCheckboxTooltip : React.Fragment; + return ( + + } + label={name} + /> + + ); +} + function ViewSwitcher({ showDateViews, showTimeViews, @@ -179,6 +205,20 @@ function ViewSwitcher({ views: ViewsMap; handleViewsChange: (event: React.ChangeEvent, checked: boolean) => void; }) { + const relevantViews = React.useMemo( + () => + Object.entries(views).filter( + ([view]) => + (showDateViews && isDatePickerView(view as DateOrTimeView)) || + (showTimeViews && isTimeView(view as DateOrTimeView)), + ), + [showDateViews, showTimeViews, views], + ); + const onlyOneView = React.useMemo( + () => relevantViews.filter(([, enabled]) => Boolean(enabled)).length === 1, + [relevantViews], + ); + return ( @@ -196,37 +236,45 @@ function ViewSwitcher({ > {showDateViews && ( - } - label="year" + - } - label="month" + - } - label="day" + )} {showTimeViews && ( - } - label="hours" + - - } - label="minutes" + - - } - label="seconds" + )} diff --git a/docs/src/modules/utils/useCustomizationPlayground.tsx b/docs/src/modules/utils/useCustomizationPlayground.tsx new file mode 100644 index 0000000000000..ea8a49945b6db --- /dev/null +++ b/docs/src/modules/utils/useCustomizationPlayground.tsx @@ -0,0 +1,431 @@ +import * as React from 'react'; +import pick from 'lodash/pick'; +import { blue, pink } from '@mui/material/colors'; +import { BoxProps } from '@mui/material/Box'; +import { createTheme, ThemeProvider, styled, useTheme, Theme } from '@mui/material/styles'; + +export type CustomizationLabelType = { + [k in 'customTheme' | 'styledComponents' | 'sxProp']: string; +}; + +type CustomizationItemType = { + type: 'warning' | 'success'; + comments?: string; + componentProps?: any; + parentSlot?: string; + parentComponent?: string; +}; +export type CustomizationItemsType = Partial<{ + [k in keyof CustomizationLabelType]: CustomizationItemType; +}>; + +export type PickersSubcomponentType = { + [k: string]: { examples: CustomizationItemsType; slots: string[] }; +}; + +export interface UseCustomizationPlaygroundProps { + examples: PickersSubcomponentType; + children?: React.ReactElement | React.ReactElement[]; + componentName: string; +} +export const customizationLabels: CustomizationLabelType = { + customTheme: 'Custom theme', + styledComponents: 'Styled components', + sxProp: 'sx prop', +}; + +export const DEFAULT_COLORS = { pink, blue }; +export type ColorKey = keyof typeof DEFAULT_COLORS; + +export type StyleTokensType = { + color: ColorKey; + borderRadius: number; + borderWidth: number; +}; +const styleTokens: StyleTokensType = { + color: 'blue', + borderRadius: 2, + borderWidth: 1, +}; +export type HandleTokenChangeType = ( + token: keyof Partial, + value: string | keyof typeof DEFAULT_COLORS | number, +) => void; + +export type UseCustomizationPlaygroundReturnType = { + selectedDemo: string | null; + customizationOptions: Partial | null; + selectedCustomizationOption: keyof CustomizationLabelType | null; + selectDemo: (interactionTarget: string | null) => void; + setSelectedCustomizationOption: ( + customizationOption: keyof CustomizationLabelType | null, + ) => void; + selectedSlot: string | null; + setSelectedSlot: (slot: string | null) => void; + codeExample: string | null; + availableSlots: string[] | null; + handleTokenChange: HandleTokenChangeType; + selectedTokens: StyleTokensType; + selectedExample?: CustomizationItemType | null; +}; + +export function withStyles( + Component: React.ElementType, + selectedTokens: StyleTokensType, + selectedCustomizationOption: string | null, + selectedDemo: string | null, + selectedSlot: string | null, +) { + return function StyledChild(props: T) { + const defaultTheme = useTheme(); + + const tokens = { + borderRadius: `${selectedTokens.borderRadius}px`, + borderColor: DEFAULT_COLORS[selectedTokens.color][500], + border: `${selectedTokens.borderWidth}px solid`, + backgroundColor: + defaultTheme.palette.mode === 'light' + ? DEFAULT_COLORS[selectedTokens.color][100] + : DEFAULT_COLORS[selectedTokens.color][900], + color: + defaultTheme.palette.mode === 'light' + ? DEFAULT_COLORS[selectedTokens.color][800] + : DEFAULT_COLORS[selectedTokens.color][100], + }; + + if (selectedCustomizationOption === 'sxProp') { + const sxProp = { + [`.Mui${selectedDemo}-${selectedSlot}`]: { ...tokens }, + }; + + return ; + } + + if (selectedCustomizationOption === 'customTheme') { + const newTheme = (theme: Theme) => + createTheme({ + ...theme, + components: { + [`Mui${selectedDemo}`]: { styleOverrides: { [selectedSlot as string]: { ...tokens } } }, + }, + }); + return ( + + + + ); + } + + if (selectedCustomizationOption === 'styledComponents') { + const StyledComponent = styled(Component as React.JSXElementConstructor)(() => ({ + [`.Mui${selectedDemo}-${selectedSlot}`]: { ...tokens }, + })); + return ; + } + return ; + }; +} + +interface Props + extends Pick< + UseCustomizationPlaygroundReturnType, + 'selectedDemo' | 'selectedSlot' | 'selectedCustomizationOption' | 'selectedTokens' + >, + Pick { + theme: Theme; + examples: CustomizationItemType; + selectedExample: CustomizationItemType | null; +} + +/* I use this method to parse whatever component props are passed in and format them for the code example, +so the code example includes the same props as the rendered component. e.g. the views={['month']} */ +function formatComponentProps(componentProps?: Object, spacing: number = 1) { + function formatObject(obj: Object, indentLevel = 0, separator = ': '): string { + const indent = ' '.repeat(indentLevel * 2); + + return (Object.keys(obj) as Array) + .map((key) => { + const getValue = (val: any) => { + if (typeof val === 'string' && !val.includes('Styled')) { + return `'${val}'`; + } + if (separator === '=' && typeof val !== 'object') { + return `{${val}}`; + } + return val; + }; + + const value = obj[key]; + if (typeof value === 'object' && !Array.isArray(value)) { + return `${indent}${key}${separator}${separator === '=' ? '{' : ''}{\n${formatObject( + getValue(value), + indentLevel + 1, + ': ', + )}\n${indent}${separator === '=' ? '}' : ''}}`; + } + + if (Array.isArray(value)) { + return `${indent}${key}${separator}${separator === '=' ? '{' : ''}[${value.map((val) => + getValue(val), + )}]${separator === '=' ? '}' : ''}`; + } + + return `${indent}${key}${separator}${getValue(value)},`; + }) + .join('\n'); + } + + if (!componentProps) { + return ''; + } + + return `\n${formatObject(componentProps, spacing, '=')} `; +} + +const getCodeExample = ({ + selectedDemo, + selectedSlot, + selectedCustomizationOption, + selectedTokens, + componentName, + examples, + selectedExample, + theme, +}: Props) => { + const tokens = { + ...selectedTokens, + borderColor: DEFAULT_COLORS[selectedTokens.color][500], + border: `${selectedTokens.borderWidth}px solid`, + backgroundColor: + theme.palette.mode === 'light' + ? DEFAULT_COLORS[selectedTokens.color][100] + : DEFAULT_COLORS[selectedTokens.color][900], + color: + theme.palette.mode === 'light' + ? DEFAULT_COLORS[selectedTokens.color][800] + : DEFAULT_COLORS[selectedTokens.color][100], + }; + + const getTokensString = (indent: number = 0): string => { + const spaces = ' '.repeat(indent * 2); + return (Object.keys(tokens) as Array).reduce((acc, key) => { + return `${acc}\n${spaces}${key}: ${ + typeof tokens[key] === 'string' ? `'${tokens[key]}'` : tokens[key] + },`; + }, ''); + }; + + const splitStringWithoutWordBreak = (inputString: string, maxLineLength: number): string[] => { + let lines: string[] = []; + let startIndex = 0; + + while (startIndex < inputString.length) { + let endIndex = startIndex + maxLineLength; + + if (endIndex < inputString.length) { + while (endIndex > startIndex && inputString[endIndex] !== ' ') { + endIndex -= 1; + } + + if (endIndex === startIndex) { + endIndex = startIndex + maxLineLength; + } + } + + lines = [...lines, inputString.substring(startIndex, endIndex).trim()]; + + startIndex = endIndex; + } + + return lines; + }; + + let code; + if (examples?.comments) { + const lines = splitStringWithoutWordBreak(examples.comments, 90); + code = `\n/* ${lines.join('\n')} */`; + } else { + code = ''; + } + + if (selectedCustomizationOption === 'sxProp') { + if (selectedExample?.parentSlot) { + const componentProps = { + ...examples.componentProps, + slotProps: { + ...examples.componentProps?.slotProps, + [selectedExample?.parentSlot]: { + sx: { [`'.Mui${selectedDemo}-${selectedSlot}'`]: tokens }, + }, + }, + }; + code = `${code}\n<${componentName}${formatComponentProps(componentProps, 1)} +/>`; + } else { + code = `${code}\n<${componentName}${formatComponentProps(examples.componentProps, 1)} + sx={{ + '.Mui${selectedDemo}-${selectedSlot}': {${getTokensString(3)} + }, + }} +/>`; + } + } else if (selectedCustomizationOption === 'customTheme') { + code = `import { createTheme } from '@mui/material/styles'\n${code} +const newTheme = (theme) => createTheme({ + ...theme, + components: { + Mui${selectedDemo}: { + styleOverrides: { + ${selectedSlot}: {${getTokensString(5)} + } + } + } + } +}) + + <${componentName}${formatComponentProps(examples.componentProps, 2)} /> +`; + } else if (selectedCustomizationOption === 'styledComponents') { + if (selectedExample?.parentSlot && selectedExample?.parentComponent) { + const componentProps = { + ...examples.componentProps, + slots: { + ...examples.componentProps?.slots, + [selectedExample?.parentSlot]: `Styled${selectedExample?.parentComponent}`, + }, + }; + return `import { styled } from '@mui/material/styles'\n${code} +const Styled${selectedExample?.parentComponent} = styled(${selectedExample?.parentComponent})({ + '.Mui${selectedDemo}-${selectedSlot}': {${getTokensString(2)} + } +}) + +export default function StyledPickerContainer() { + return ( + <${componentName} ${formatComponentProps(componentProps, 3)} + /> + ); +}`; + } + return `import { styled } from '@mui/material/styles'\n${code} +const Styled${componentName} = styled(${componentName})({ + '.Mui${selectedDemo}-${selectedSlot}': {${getTokensString(2)} + } +}) + +export default function StyledPickerContainer() { + return ( + + ); +}`; + } + + return code; +}; + +export function useCustomizationPlayground({ + examples, + componentName, +}: UseCustomizationPlaygroundProps): UseCustomizationPlaygroundReturnType { + const theme = useTheme(); + + const [selectedDemo, setSelectedDemo] = React.useState(null); + const [selectedSlot, setSelectedSlot] = React.useState(null); + const [customizationOptions, setCustomizationOptions] = + React.useState | null>(null); + const [selectedCustomizationOption, setSelectedCustomizationOption] = React.useState< + keyof CustomizationLabelType | null + >(null); + + const [codeExample, setCodeExample] = React.useState(null); + + const [selectedTokens, setSelectedTokens] = React.useState(styleTokens); + + React.useEffect(() => { + setSelectedDemo(Object.keys(examples)[0]); + }, [examples]); + + const setOptions = React.useCallback( + (demo: string) => { + const slot = examples[demo].slots.length ? examples[demo].slots[0] : 'root'; + const customizationExamples = Object.keys(examples[demo].examples) as Array< + keyof CustomizationLabelType + >; + // set the array of customization options to the available options for the selected subcomponent + setCustomizationOptions(pick(customizationLabels, customizationExamples)); + // set the selected customization option to the first available option for the selected subcomponent + setSelectedCustomizationOption(customizationExamples[0]); + setSelectedSlot(slot); + }, + [examples, setSelectedCustomizationOption, setSelectedSlot], + ); + + React.useEffect(() => { + if (selectedDemo && examples && examples[selectedDemo]) { + setOptions(selectedDemo); + } + }, [selectedDemo, setOptions, examples]); + + React.useEffect(() => { + if (selectedDemo && selectedCustomizationOption && examples[selectedDemo]) { + const code = getCodeExample({ + selectedDemo, + selectedSlot, + selectedCustomizationOption, + selectedTokens, + componentName, + examples: examples[selectedDemo].examples[ + selectedCustomizationOption + ] as CustomizationItemType, + selectedExample: + selectedDemo && selectedCustomizationOption + ? (examples[selectedDemo]?.examples[ + selectedCustomizationOption + ] as CustomizationItemType) + : null, + theme, + }); + if (code) { + setCodeExample(code); + } + } + }, [ + selectedDemo, + examples, + selectedTokens, + theme, + selectedSlot, + selectedCustomizationOption, + componentName, + ]); + + const selectDemo = (interactionTarget: string | null) => { + setSelectedDemo(interactionTarget); + }; + + const handleTokenChange = ( + token: keyof typeof styleTokens, + value: string | keyof typeof DEFAULT_COLORS | number, + ) => { + setSelectedTokens((prev: StyleTokensType) => ({ ...prev, [token]: value })); + }; + + return { + selectedDemo, + customizationOptions, + selectedCustomizationOption, + selectDemo, + setSelectedCustomizationOption, + selectedSlot, + setSelectedSlot, + codeExample, + availableSlots: + selectedDemo && examples[selectedDemo] ? examples[selectedDemo].slots : ['root'], + handleTokenChange, + selectedTokens, + selectedExample: + selectedDemo && selectedCustomizationOption + ? examples[selectedDemo]?.examples[selectedCustomizationOption] + : null, + }; +} diff --git a/docs/translations/api-docs/charts/area-element.json b/docs/translations/api-docs/charts/area-element.json index fec8833da4eeb..5d077363ea787 100644 --- a/docs/translations/api-docs/charts/area-element.json +++ b/docs/translations/api-docs/charts/area-element.json @@ -25,5 +25,5 @@ "conditions": "faded" } }, - "slotDescriptions": {} + "slotDescriptions": { "area": "The component that renders the root." } } diff --git a/docs/translations/api-docs/charts/area-plot.json b/docs/translations/api-docs/charts/area-plot.json index 193abb7bf3a9f..c045ac6d34fd8 100644 --- a/docs/translations/api-docs/charts/area-plot.json +++ b/docs/translations/api-docs/charts/area-plot.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "area": "The component that renders the root." } } diff --git a/docs/translations/api-docs/charts/bar-chart.json b/docs/translations/api-docs/charts/bar-chart.json index e785806eeef7c..6c0d6d7be4f9d 100644 --- a/docs/translations/api-docs/charts/bar-chart.json +++ b/docs/translations/api-docs/charts/bar-chart.json @@ -1,6 +1,11 @@ { "componentDescription": "", "propDescriptions": { + "axisHighlight": { + "description": "Object { x, y } that defines how the charts highlight the mouse position along the x- and y-axes. The two properties accept the following values: - 'none': display nothing. - 'line': display a line at the current mouse position. - 'band': display a band at the current mouse position. Only available with band scale.", + "deprecated": "", + "typeDescriptions": {} + }, "bottomAxis": { "description": "Indicate which axis to display the bottom of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", @@ -11,16 +16,41 @@ "deprecated": "", "typeDescriptions": {} }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "disableAxisListener": { + "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance.", + "deprecated": "", + "typeDescriptions": {} + }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, "leftAxis": { "description": "Indicate which axis to display the left of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + }, "rightAxis": { "description": "Indicate which axis to display the right of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "skipAnimation": { + "description": "If true, animations are skiped.", + "deprecated": "", + "typeDescriptions": {} + }, "slotProps": { "description": "The props used for each component slot.", "deprecated": "", @@ -35,8 +65,33 @@ "description": "Indicate which axis to display the top of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", "typeDescriptions": {} + }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used with id set to DEFAULT_X_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used with id set to DEFAULT_Y_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { + "axisContent": "", + "axisLabel": "", + "axisLine": "", + "axisTick": "", + "axisTickLabel": "", + "bar": "", + "itemContent": "", + "legend": "", + "popper": "" + } } diff --git a/docs/translations/api-docs/charts/bar-plot.json b/docs/translations/api-docs/charts/bar-plot.json index 193abb7bf3a9f..0df4fa1114fe6 100644 --- a/docs/translations/api-docs/charts/bar-plot.json +++ b/docs/translations/api-docs/charts/bar-plot.json @@ -1,6 +1,11 @@ { "componentDescription": "", "propDescriptions": { + "skipAnimation": { + "description": "If true, animations are skiped.", + "deprecated": "", + "typeDescriptions": {} + }, "slotProps": { "description": "The props used for each component slot.", "deprecated": "", @@ -13,5 +18,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "bar": "The component that renders the root." } } diff --git a/docs/translations/api-docs/charts/cartesian-context-provider.json b/docs/translations/api-docs/charts/cartesian-context-provider.json index be9cfbbe310cd..7efe6db29fd49 100644 --- a/docs/translations/api-docs/charts/cartesian-context-provider.json +++ b/docs/translations/api-docs/charts/cartesian-context-provider.json @@ -1,6 +1,22 @@ { "componentDescription": "", - "propDescriptions": {}, + "propDescriptions": { + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used with id set to DEFAULT_X_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used with id set to DEFAULT_Y_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + } + }, "classDescriptions": {}, "slotDescriptions": {} } diff --git a/docs/translations/api-docs/charts/charts-axis-highlight.json b/docs/translations/api-docs/charts/charts-axis-highlight.json index be9cfbbe310cd..2a71bfb090893 100644 --- a/docs/translations/api-docs/charts/charts-axis-highlight.json +++ b/docs/translations/api-docs/charts/charts-axis-highlight.json @@ -1,6 +1,6 @@ { "componentDescription": "", "propDescriptions": {}, - "classDescriptions": {}, + "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": {} } diff --git a/docs/translations/api-docs/charts/charts-axis.json b/docs/translations/api-docs/charts/charts-axis.json index 6dba8cffb4407..ba8da4b4492b5 100644 --- a/docs/translations/api-docs/charts/charts-axis.json +++ b/docs/translations/api-docs/charts/charts-axis.json @@ -44,13 +44,16 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, - "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, - "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, + "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x-axes" }, + "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y-axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, "bottom": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the bottom axis" }, "left": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the left axis" }, "right": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the right axis" } }, - "slotDescriptions": {} + "slotDescriptions": { "axisLabel": "", "axisLine": "", "axisTick": "", "axisTickLabel": "" } } diff --git a/docs/translations/api-docs/charts/charts-legend.json b/docs/translations/api-docs/charts/charts-legend.json new file mode 100644 index 0000000000000..1a628c7ae7d50 --- /dev/null +++ b/docs/translations/api-docs/charts/charts-legend.json @@ -0,0 +1,45 @@ +{ + "componentDescription": "", + "propDescriptions": { + "classes": { + "description": "Override or extend the styles applied to the component.", + "deprecated": "", + "typeDescriptions": {} + }, + "direction": { + "description": "The direction of the legend layout. The default depends on the chart.", + "deprecated": "", + "typeDescriptions": {} + }, + "hidden": { + "description": "Set to true to hide the legend.", + "deprecated": "", + "typeDescriptions": {} + }, + "slotProps": { + "description": "The props used for each component slot.", + "deprecated": "", + "typeDescriptions": {} + }, + "slots": { + "description": "Overridable component slots.", + "deprecated": "", + "typeDescriptions": {} + } + }, + "classDescriptions": { + "root": { "description": "Styles applied to the root element." }, + "series": { "description": "Styles applied to {{nodeName}}.", "nodeName": "a series element" }, + "mark": { "description": "Styles applied to {{nodeName}}.", "nodeName": "series mark element" }, + "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the series label" }, + "column": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the legend with column layout" + }, + "row": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the legend with row layout" + } + }, + "slotDescriptions": { "legend": "" } +} diff --git a/docs/translations/api-docs/charts/charts-reference-line.json b/docs/translations/api-docs/charts/charts-reference-line.json new file mode 100644 index 0000000000000..dc186914dfa34 --- /dev/null +++ b/docs/translations/api-docs/charts/charts-reference-line.json @@ -0,0 +1,66 @@ +{ + "componentDescription": "", + "propDescriptions": { + "axisId": { + "description": "The id of the axis used for the reference value.", + "deprecated": "", + "typeDescriptions": {} + }, + "classes": { + "description": "Override or extend the styles applied to the component.", + "deprecated": "", + "typeDescriptions": {} + }, + "label": { + "description": "The label to display along the reference line.", + "deprecated": "", + "typeDescriptions": {} + }, + "labelAlign": { + "description": "The alignment if the label is in the chart drawing area.", + "deprecated": "", + "typeDescriptions": {} + }, + "labelStyle": { + "description": "The style applied to the label.", + "deprecated": "", + "typeDescriptions": {} + }, + "lineStyle": { + "description": "The style applied to the line.", + "deprecated": "", + "typeDescriptions": {} + }, + "spacing": { + "description": "Additional space arround the label in px. Can be a number or an object { x, y } to distinguish space with the reference line and space with axes.", + "deprecated": "", + "typeDescriptions": {} + }, + "x": { + "description": "The x value associated with the reference line. If defined the reference line will be vertical.", + "deprecated": "", + "typeDescriptions": {} + }, + "y": { + "description": "The y value associated with the reference line. If defined the reference line will be horizontal.", + "deprecated": "", + "typeDescriptions": {} + } + }, + "classDescriptions": { + "root": { "description": "Styles applied to the root element." }, + "vertical": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "the reference line is vertical" + }, + "horizontal": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the root element", + "conditions": "the reference line is horizontal" + }, + "line": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the reference line" }, + "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the reference label" } + }, + "slotDescriptions": {} +} diff --git a/docs/translations/api-docs/charts/charts-tooltip.json b/docs/translations/api-docs/charts/charts-tooltip.json index e3d84230879f8..a35867dfa1356 100644 --- a/docs/translations/api-docs/charts/charts-tooltip.json +++ b/docs/translations/api-docs/charts/charts-tooltip.json @@ -16,6 +16,16 @@ "deprecated": "", "typeDescriptions": {} }, + "slotProps": { + "description": "The props used for each component slot.", + "deprecated": "", + "typeDescriptions": {} + }, + "slots": { + "description": "Overridable component slots.", + "deprecated": "", + "typeDescriptions": {} + }, "trigger": { "description": "Select the kind of tooltip to display - 'item': Shows data about the item below the mouse. - 'axis': Shows values associated with the hovered x value - 'none': Does not display tooltip", "deprecated": "", @@ -37,5 +47,5 @@ "nodeName": "the valueCell element" } }, - "slotDescriptions": {} + "slotDescriptions": { "axisContent": "", "itemContent": "", "popper": "" } } diff --git a/docs/translations/api-docs/charts/charts-x-axis.json b/docs/translations/api-docs/charts/charts-x-axis.json index 69d91b0720061..3e977dd3e48ea 100644 --- a/docs/translations/api-docs/charts/charts-x-axis.json +++ b/docs/translations/api-docs/charts/charts-x-axis.json @@ -2,7 +2,7 @@ "componentDescription": "", "propDescriptions": { "axisId": { - "description": "Id of the axis to render.", + "description": "The id of the axis to render. If undefined, it will be the first defined axis.", "deprecated": "", "typeDescriptions": {} }, @@ -32,6 +32,11 @@ "deprecated": "", "typeDescriptions": {} }, + "labelStyle": { + "description": "The style applied to the axis label.", + "deprecated": "", + "typeDescriptions": {} + }, "position": { "description": "Position of the axis.", "deprecated": "", @@ -57,6 +62,21 @@ "deprecated": "", "typeDescriptions": {} }, + "tickInterval": { + "description": "Defines which ticks are displayed. Its value can be: - 'auto' In such case the ticks are computed based on axis scale and other parameters. - a filtering function of the form (value, index) => boolean which is available only if the axis has a data property. - an array containing the values where ticks should be displayed.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelInterval": { + "description": "Defines which ticks get its label displayed. Its value can be: - 'auto' In such case, labels are displayed if they do not overlap with the previous one. - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelStyle": { + "description": "The style applied to ticks text.", + "deprecated": "", + "typeDescriptions": {} + }, "tickMaxStep": { "description": "Maximal step between two ticks. When using time data, the value is assumed to be in ms. Not supported by categorical axis (band, points).", "deprecated": "", @@ -90,13 +110,16 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, - "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, - "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, + "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x-axes" }, + "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y-axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, "bottom": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the bottom axis" }, "left": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the left axis" }, "right": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the right axis" } }, - "slotDescriptions": {} + "slotDescriptions": { "axisLabel": "", "axisLine": "", "axisTick": "", "axisTickLabel": "" } } diff --git a/docs/translations/api-docs/charts/charts-y-axis.json b/docs/translations/api-docs/charts/charts-y-axis.json index 69d91b0720061..3e977dd3e48ea 100644 --- a/docs/translations/api-docs/charts/charts-y-axis.json +++ b/docs/translations/api-docs/charts/charts-y-axis.json @@ -2,7 +2,7 @@ "componentDescription": "", "propDescriptions": { "axisId": { - "description": "Id of the axis to render.", + "description": "The id of the axis to render. If undefined, it will be the first defined axis.", "deprecated": "", "typeDescriptions": {} }, @@ -32,6 +32,11 @@ "deprecated": "", "typeDescriptions": {} }, + "labelStyle": { + "description": "The style applied to the axis label.", + "deprecated": "", + "typeDescriptions": {} + }, "position": { "description": "Position of the axis.", "deprecated": "", @@ -57,6 +62,21 @@ "deprecated": "", "typeDescriptions": {} }, + "tickInterval": { + "description": "Defines which ticks are displayed. Its value can be: - 'auto' In such case the ticks are computed based on axis scale and other parameters. - a filtering function of the form (value, index) => boolean which is available only if the axis has a data property. - an array containing the values where ticks should be displayed.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelInterval": { + "description": "Defines which ticks get its label displayed. Its value can be: - 'auto' In such case, labels are displayed if they do not overlap with the previous one. - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones.", + "deprecated": "", + "typeDescriptions": {} + }, + "tickLabelStyle": { + "description": "The style applied to ticks text.", + "deprecated": "", + "typeDescriptions": {} + }, "tickMaxStep": { "description": "Maximal step between two ticks. When using time data, the value is assumed to be in ms. Not supported by categorical axis (band, points).", "deprecated": "", @@ -90,13 +110,16 @@ }, "tick": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks" }, "tickLabel": { "description": "Styles applied to {{nodeName}}.", "nodeName": "ticks label" }, - "label": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the axis label" }, - "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x axes" }, - "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y axes" }, + "label": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the group containing the axis label" + }, + "directionX": { "description": "Styles applied to {{nodeName}}.", "nodeName": "x-axes" }, + "directionY": { "description": "Styles applied to {{nodeName}}.", "nodeName": "y-axes" }, "top": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the top axis" }, "bottom": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the bottom axis" }, "left": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the left axis" }, "right": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the right axis" } }, - "slotDescriptions": {} + "slotDescriptions": { "axisLabel": "", "axisLine": "", "axisTick": "", "axisTickLabel": "" } } diff --git a/docs/translations/api-docs/charts/drawing-provider.json b/docs/translations/api-docs/charts/drawing-provider.json index be9cfbbe310cd..a691b1e65b432 100644 --- a/docs/translations/api-docs/charts/drawing-provider.json +++ b/docs/translations/api-docs/charts/drawing-provider.json @@ -1,6 +1,12 @@ { "componentDescription": "", - "propDescriptions": {}, + "propDescriptions": { + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + } + }, "classDescriptions": {}, "slotDescriptions": {} } diff --git a/docs/translations/api-docs/charts/line-chart.json b/docs/translations/api-docs/charts/line-chart.json index 76819e02e47ed..08e5e23e02278 100644 --- a/docs/translations/api-docs/charts/line-chart.json +++ b/docs/translations/api-docs/charts/line-chart.json @@ -1,6 +1,11 @@ { "componentDescription": "", "propDescriptions": { + "axisHighlight": { + "description": "Object { x, y } that defines how the charts highlight the mouse position along the x- and y-axes. The two properties accept the following values: - 'none': display nothing. - 'line': display a line at the current mouse position. - 'band': display a band at the current mouse position. Only available with band scale.", + "deprecated": "", + "typeDescriptions": {} + }, "bottomAxis": { "description": "Indicate which axis to display the bottom of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", @@ -11,16 +16,36 @@ "deprecated": "", "typeDescriptions": {} }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "disableAxisListener": { + "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance.", + "deprecated": "", + "typeDescriptions": {} + }, "disableLineItemHighlight": { "description": "If true, render the line highlight item.", "deprecated": "", "typeDescriptions": {} }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, "leftAxis": { "description": "Indicate which axis to display the left of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + }, "rightAxis": { "description": "Indicate which axis to display the right of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", @@ -40,8 +65,36 @@ "description": "Indicate which axis to display the top of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", "typeDescriptions": {} + }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used with id set to DEFAULT_X_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used with id set to DEFAULT_Y_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { + "area": "", + "axisContent": "", + "axisLabel": "", + "axisLine": "", + "axisTick": "", + "axisTickLabel": "", + "itemContent": "", + "legend": "", + "line": "", + "lineHighlight": "", + "mark": "", + "popper": "" + } } diff --git a/docs/translations/api-docs/charts/line-element.json b/docs/translations/api-docs/charts/line-element.json index fec8833da4eeb..9ef25541e1e52 100644 --- a/docs/translations/api-docs/charts/line-element.json +++ b/docs/translations/api-docs/charts/line-element.json @@ -25,5 +25,5 @@ "conditions": "faded" } }, - "slotDescriptions": {} + "slotDescriptions": { "line": "The component that renders the root." } } diff --git a/docs/translations/api-docs/charts/line-highlight-plot.json b/docs/translations/api-docs/charts/line-highlight-plot.json index 193abb7bf3a9f..852747b8f3b12 100644 --- a/docs/translations/api-docs/charts/line-highlight-plot.json +++ b/docs/translations/api-docs/charts/line-highlight-plot.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "lineHighlight": "" } } diff --git a/docs/translations/api-docs/charts/line-plot.json b/docs/translations/api-docs/charts/line-plot.json index 193abb7bf3a9f..1d035d2105337 100644 --- a/docs/translations/api-docs/charts/line-plot.json +++ b/docs/translations/api-docs/charts/line-plot.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "line": "The component that renders the root." } } diff --git a/docs/translations/api-docs/charts/mark-plot.json b/docs/translations/api-docs/charts/mark-plot.json index 193abb7bf3a9f..04790a6a55758 100644 --- a/docs/translations/api-docs/charts/mark-plot.json +++ b/docs/translations/api-docs/charts/mark-plot.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "mark": "" } } diff --git a/docs/translations/api-docs/charts/pie-chart.json b/docs/translations/api-docs/charts/pie-chart.json index 80ba8d6563ace..36ab78abec453 100644 --- a/docs/translations/api-docs/charts/pie-chart.json +++ b/docs/translations/api-docs/charts/pie-chart.json @@ -11,16 +11,41 @@ "deprecated": "", "typeDescriptions": {} }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "disableAxisListener": { + "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance.", + "deprecated": "", + "typeDescriptions": {} + }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, "leftAxis": { "description": "Indicate which axis to display the left of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + }, "rightAxis": { "description": "Indicate which axis to display the right of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "skipAnimation": { + "description": "If true, animations are skiped.", + "deprecated": "", + "typeDescriptions": {} + }, "slotProps": { "description": "The props used for each component slot.", "deprecated": "", @@ -30,6 +55,21 @@ "description": "Indicate which axis to display the top of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", "typeDescriptions": {} + }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used with id set to DEFAULT_X_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used with id set to DEFAULT_Y_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} } }, "classDescriptions": {}, diff --git a/docs/translations/api-docs/charts/pie-plot.json b/docs/translations/api-docs/charts/pie-plot.json index 193abb7bf3a9f..326efd758310c 100644 --- a/docs/translations/api-docs/charts/pie-plot.json +++ b/docs/translations/api-docs/charts/pie-plot.json @@ -1,6 +1,20 @@ { "componentDescription": "", "propDescriptions": { + "onClick": { + "description": "Callback fired when a pie item is clicked.", + "deprecated": "", + "typeDescriptions": { + "event": "The event source of the callback.", + "pieItemIdentifier": "The pie item identifier.", + "item": "The pie item." + } + }, + "skipAnimation": { + "description": "If true, animations are skiped.", + "deprecated": "", + "typeDescriptions": {} + }, "slotProps": { "description": "The props used for each component slot.", "deprecated": "", @@ -13,5 +27,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "pieArc": "", "pieArcLabel": "" } } diff --git a/docs/translations/api-docs/charts/scatter-chart.json b/docs/translations/api-docs/charts/scatter-chart.json index e785806eeef7c..1971b9e72b037 100644 --- a/docs/translations/api-docs/charts/scatter-chart.json +++ b/docs/translations/api-docs/charts/scatter-chart.json @@ -11,11 +11,31 @@ "deprecated": "", "typeDescriptions": {} }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "disableAxisListener": { + "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance.", + "deprecated": "", + "typeDescriptions": {} + }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, "leftAxis": { "description": "Indicate which axis to display the left of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", "typeDescriptions": {} }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + }, "rightAxis": { "description": "Indicate which axis to display the right of the charts. Can be a string (the id of the axis) or an object ChartsYAxisProps.", "deprecated": "", @@ -35,8 +55,33 @@ "description": "Indicate which axis to display the top of the charts. Can be a string (the id of the axis) or an object ChartsXAxisProps.", "deprecated": "", "typeDescriptions": {} + }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, + "xAxis": { + "description": "The configuration of the x-axes. If not provided, a default axis config is used with id set to DEFAULT_X_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} + }, + "yAxis": { + "description": "The configuration of the y-axes. If not provided, a default axis config is used with id set to DEFAULT_Y_AXIS_KEY.", + "deprecated": "", + "typeDescriptions": {} } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { + "axisContent": "", + "axisLabel": "", + "axisLine": "", + "axisTick": "", + "axisTickLabel": "", + "itemContent": "", + "legend": "", + "popper": "", + "scatter": "" + } } diff --git a/docs/translations/api-docs/charts/scatter-plot.json b/docs/translations/api-docs/charts/scatter-plot.json index 193abb7bf3a9f..67c937d9f2817 100644 --- a/docs/translations/api-docs/charts/scatter-plot.json +++ b/docs/translations/api-docs/charts/scatter-plot.json @@ -13,5 +13,5 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { "scatter": "" } } diff --git a/docs/translations/api-docs/charts/spark-line-chart.json b/docs/translations/api-docs/charts/spark-line-chart.json index f07c45c29efb1..098cbc907bf3a 100644 --- a/docs/translations/api-docs/charts/spark-line-chart.json +++ b/docs/translations/api-docs/charts/spark-line-chart.json @@ -12,6 +12,26 @@ "typeDescriptions": {} }, "data": { "description": "Data to plot.", "deprecated": "", "typeDescriptions": {} }, + "dataset": { + "description": "An array of objects that can be used to populate series and axes data using their dataKey property.", + "deprecated": "", + "typeDescriptions": {} + }, + "disableAxisListener": { + "description": "If true, the charts will not listen to the mouse move event. It might break interactive features, but will improve performance.", + "deprecated": "", + "typeDescriptions": {} + }, + "height": { + "description": "The height of the chart in px. If not defined, it takes the height of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, + "margin": { + "description": "The margin between the SVG and the drawing area. It's used for leaving some space for extra information such as the x- and y-axis or legend. Accepts an object with the optional properties: top, bottom, left, and right.", + "deprecated": "", + "typeDescriptions": {} + }, "plotType": { "description": "Type of plot used.", "deprecated": "", "typeDescriptions": {} }, "showHighlight": { "description": "Set to true to highlight the value. With line, it shows a point. With bar, it shows a highlight band.", @@ -38,6 +58,11 @@ "deprecated": "", "typeDescriptions": { "value": "The value to format.", "string": "the formatted value." } }, + "width": { + "description": "The width of the chart in px. If not defined, it takes the width of the parent element.", + "deprecated": "", + "typeDescriptions": {} + }, "xAxis": { "description": "The xAxis configuration. Notice it is a single configuration object, not an array of configuration.", "deprecated": "", @@ -45,5 +70,14 @@ } }, "classDescriptions": {}, - "slotDescriptions": {} + "slotDescriptions": { + "area": "", + "axisContent": "", + "bar": "", + "itemContent": "", + "line": "", + "lineHighlight": "", + "mark": "", + "popper": "" + } } diff --git a/docs/translations/api-docs/data-grid/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium.json index 6777cb6c53e5a..99d1d6ca45421 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium.json @@ -41,6 +41,16 @@ "deprecated": "", "typeDescriptions": {} }, + "autosizeOnMount": { + "description": "If true, columns are autosized after the datagrid is mounted.", + "deprecated": "", + "typeDescriptions": {} + }, + "autosizeOptions": { + "description": "The options for autosize when user-initiated.", + "deprecated": "", + "typeDescriptions": {} + }, "cellModesModel": { "description": "Controls the modes of the cells.", "deprecated": "", @@ -91,16 +101,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "Overridable components props dynamically passed to the component at rendering.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultGroupingExpansionDepth": { "description": "If above 0, the row children will be expanded up to this depth. If equal to -1, all the row children will be expanded.", "deprecated": "", @@ -121,6 +121,11 @@ "deprecated": "", "typeDescriptions": {} }, + "disableAutosize": { + "description": "If true, column autosizing on header separator double-click is disabled.", + "deprecated": "", + "typeDescriptions": {} + }, "disableChildrenFiltering": { "description": "If true, the filtering will only be applied to the top level rows when grouping rows with the treeData prop.", "deprecated": "", @@ -328,6 +333,11 @@ "deprecated": "", "typeDescriptions": {} }, + "ignoreDiacritics": { + "description": "If true, the diacritics (accents) are ignored when filtering or quick filtering. E.g. when filter value is cafe, the rows with café will be visible.", + "deprecated": "", + "typeDescriptions": {} + }, "initialState": { "description": "The initial state of the DataGridPremium. The data in it is set in the state on initialization but isn't controlled. If one of the data in initialState is also being controlled, then the control state wins.", "deprecated": "", @@ -812,6 +822,11 @@ "deprecated": "", "typeDescriptions": {} }, + "rowPositionsDebounceMs": { + "description": "The milliseconds delay to wait after measuring the row height before recalculating row positions. Setting it to a lower value could be useful when using dynamic row height, but might reduce performance when displaying a large number of rows.", + "deprecated": "", + "typeDescriptions": {} + }, "rowReordering": { "description": "If true, the reordering of rows is enabled.", "deprecated": "", @@ -972,6 +987,10 @@ "nodeName": "the root element", "conditions": "autoHeight={true}" }, + "autosizing": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root element while it is being autosized" + }, "booleanCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the icon of the boolean cell" @@ -1026,6 +1045,11 @@ "nodeName": "the cell element", "conditions": "it is at the right edge of a cell selection range" }, + "cell--selectionMode": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the cell element", + "conditions": "it is in a cell selection range" + }, "cell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the cell element" }, "cellContent": { "description": "Styles applied to {{nodeName}}.", diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json index 141f12c38f150..0d06e5d3bbb4a 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro.json @@ -26,6 +26,16 @@ "deprecated": "", "typeDescriptions": {} }, + "autosizeOnMount": { + "description": "If true, columns are autosized after the datagrid is mounted.", + "deprecated": "", + "typeDescriptions": {} + }, + "autosizeOptions": { + "description": "The options for autosize when user-initiated.", + "deprecated": "", + "typeDescriptions": {} + }, "cellModesModel": { "description": "Controls the modes of the cells.", "deprecated": "", @@ -76,16 +86,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "Overridable components props dynamically passed to the component at rendering.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultGroupingExpansionDepth": { "description": "If above 0, the row children will be expanded up to this depth. If equal to -1, all the row children will be expanded.", "deprecated": "", @@ -101,6 +101,11 @@ "deprecated": "", "typeDescriptions": {} }, + "disableAutosize": { + "description": "If true, column autosizing on header separator double-click is disabled.", + "deprecated": "", + "typeDescriptions": {} + }, "disableChildrenFiltering": { "description": "If true, the filtering will only be applied to the top level rows when grouping rows with the treeData prop.", "deprecated": "", @@ -290,6 +295,11 @@ "deprecated": "", "typeDescriptions": {} }, + "ignoreDiacritics": { + "description": "If true, the diacritics (accents) are ignored when filtering or quick filtering. E.g. when filter value is cafe, the rows with café will be visible.", + "deprecated": "", + "typeDescriptions": {} + }, "initialState": { "description": "The initial state of the DataGridPro. The data in it will be set in the state on initialization but will not be controlled. If one of the data in initialState is also being controlled, then the control state wins.", "deprecated": "", @@ -733,6 +743,11 @@ "deprecated": "", "typeDescriptions": {} }, + "rowPositionsDebounceMs": { + "description": "The milliseconds delay to wait after measuring the row height before recalculating row positions. Setting it to a lower value could be useful when using dynamic row height, but might reduce performance when displaying a large number of rows.", + "deprecated": "", + "typeDescriptions": {} + }, "rowReordering": { "description": "If true, the reordering of rows is enabled.", "deprecated": "", @@ -870,6 +885,10 @@ "nodeName": "the root element", "conditions": "autoHeight={true}" }, + "autosizing": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root element while it is being autosized" + }, "booleanCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the icon of the boolean cell" @@ -924,6 +943,11 @@ "nodeName": "the cell element", "conditions": "it is at the right edge of a cell selection range" }, + "cell--selectionMode": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the cell element", + "conditions": "it is in a cell selection range" + }, "cell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the cell element" }, "cellContent": { "description": "Styles applied to {{nodeName}}.", diff --git a/docs/translations/api-docs/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid.json index b6a89769ecb34..2d3b9a8807a96 100644 --- a/docs/translations/api-docs/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid.json @@ -71,16 +71,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "Overridable components props dynamically passed to the component at rendering.", - "deprecated": "", - "typeDescriptions": {} - }, "density": { "description": "Set the density of the grid.", "deprecated": "", @@ -209,6 +199,11 @@ "deprecated": "", "typeDescriptions": {} }, + "ignoreDiacritics": { + "description": "If true, the diacritics (accents) are ignored when filtering or quick filtering. E.g. when filter value is cafe, the rows with café will be visible.", + "deprecated": "", + "typeDescriptions": {} + }, "initialState": { "description": "The initial state of the DataGrid. The data in it will be set in the state on initialization but will not be controlled. If one of the data in initialState is also being controlled, then the control state wins.", "deprecated": "", @@ -563,6 +558,11 @@ "deprecated": "", "typeDescriptions": {} }, + "rowPositionsDebounceMs": { + "description": "The milliseconds delay to wait after measuring the row height before recalculating row positions. Setting it to a lower value could be useful when using dynamic row height, but might reduce performance when displaying a large number of rows.", + "deprecated": "", + "typeDescriptions": {} + }, "rows": { "description": "Set of rows of type GridRowsProp.", "deprecated": "", @@ -670,6 +670,10 @@ "nodeName": "the root element", "conditions": "autoHeight={true}" }, + "autosizing": { + "description": "Styles applied to {{nodeName}}.", + "nodeName": "the root element while it is being autosized" + }, "booleanCell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the icon of the boolean cell" @@ -724,6 +728,11 @@ "nodeName": "the cell element", "conditions": "it is at the right edge of a cell selection range" }, + "cell--selectionMode": { + "description": "Styles applied to {{nodeName}} if {{conditions}}.", + "nodeName": "the cell element", + "conditions": "it is in a cell selection range" + }, "cell": { "description": "Styles applied to {{nodeName}}.", "nodeName": "the cell element" }, "cellContent": { "description": "Styles applied to {{nodeName}}.", diff --git a/docs/translations/api-docs/date-pickers/date-calendar.json b/docs/translations/api-docs/date-pickers/date-calendar.json index 3908de15a44ed..857c033ce240d 100644 --- a/docs/translations/api-docs/date-pickers/date-calendar.json +++ b/docs/translations/api-docs/date-pickers/date-calendar.json @@ -6,21 +6,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -146,7 +137,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/date-field.json b/docs/translations/api-docs/date-pickers/date-field.json index 378955d48647a..b12cd7be0bb83 100644 --- a/docs/translations/api-docs/date-pickers/date-field.json +++ b/docs/translations/api-docs/date-pickers/date-field.json @@ -6,18 +6,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -130,6 +125,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -164,7 +164,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -231,6 +231,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/date-picker.json b/docs/translations/api-docs/date-pickers/date-picker.json index fe129ddf55b1b..a280e01ed87ae 100644 --- a/docs/translations/api-docs/date-pickers/date-picker.json +++ b/docs/translations/api-docs/date-pickers/date-picker.json @@ -16,21 +16,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -212,7 +203,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -286,6 +277,8 @@ "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", "calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day. Check the PickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", diff --git a/docs/translations/api-docs/date-pickers/date-range-calendar.json b/docs/translations/api-docs/date-pickers/date-range-calendar.json index 8da2d3ac03815..fe23714b76ed3 100644 --- a/docs/translations/api-docs/date-pickers/date-range-calendar.json +++ b/docs/translations/api-docs/date-pickers/date-range-calendar.json @@ -11,16 +11,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "currentMonthCalendarPosition": { "description": "Position the current month is rendered in.", "deprecated": "", @@ -30,7 +20,8 @@ "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -148,7 +139,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/date-range-picker.json b/docs/translations/api-docs/date-pickers/date-range-picker.json index 3e87d04ff351f..070193a07ba13 100644 --- a/docs/translations/api-docs/date-pickers/date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/date-range-picker.json @@ -21,16 +21,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "currentMonthCalendarPosition": { "description": "Position the current month is rendered in.", "deprecated": "", @@ -40,7 +30,8 @@ "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -226,7 +217,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -273,6 +264,8 @@ "classDescriptions": {}, "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day in range pickers. Check the DateRangePickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", diff --git a/docs/translations/api-docs/date-pickers/date-time-field.json b/docs/translations/api-docs/date-pickers/date-time-field.json index 4242bafaa32c4..eed092b018dea 100644 --- a/docs/translations/api-docs/date-pickers/date-time-field.json +++ b/docs/translations/api-docs/date-pickers/date-time-field.json @@ -11,18 +11,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -165,6 +160,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -208,7 +208,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -284,6 +284,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/date-time-picker.json b/docs/translations/api-docs/date-pickers/date-time-picker.json index 8c00d1add2d06..056e1fcfacdd1 100644 --- a/docs/translations/api-docs/date-pickers/date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/date-time-picker.json @@ -26,21 +26,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -261,7 +252,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -318,6 +309,11 @@ "deprecated": "", "typeDescriptions": {} }, + "thresholdToRenderTimeInASingleColumn": { + "description": "Amount of time options below or at which the single column time renderer is used.", + "deprecated": "", + "typeDescriptions": {} + }, "timeSteps": { "description": "The time steps between two time unit options. For example, if timeStep.minutes = 8, then the available minute options will be [0, 8, 16, 24, 32, 40, 48, 56]. When single column time renderer is used, only timeStep.minutes will be used.", "deprecated": "", @@ -354,11 +350,14 @@ "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", "calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day. Check the PickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", "desktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", "dialog": "Custom component for the dialog inside which the views are rendered on mobile.", + "digitalClockItem": "Component responsible for rendering a single digital clock item.", "digitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.", "field": "Component used to enter the date with the keyboard.", "inputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-picker.json index 17576497cc0db..d9cd1114164cb 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-picker.json @@ -16,21 +16,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -207,7 +198,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -281,6 +272,8 @@ "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", "calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day. Check the PickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json index 12038cb013996..fc15a71344677 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-range-picker.json @@ -21,16 +21,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "currentMonthCalendarPosition": { "description": "Position the current month is rendered in.", "deprecated": "", @@ -40,7 +30,8 @@ "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -221,7 +212,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -268,6 +259,8 @@ "classDescriptions": {}, "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day in range pickers. Check the DateRangePickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", diff --git a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json index c803b39fc8370..0ce59ae323f02 100644 --- a/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-date-time-picker.json @@ -26,21 +26,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -256,7 +247,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -313,6 +304,11 @@ "deprecated": "", "typeDescriptions": {} }, + "thresholdToRenderTimeInASingleColumn": { + "description": "Amount of time options below or at which the single column time renderer is used.", + "deprecated": "", + "typeDescriptions": {} + }, "timeSteps": { "description": "The time steps between two time unit options. For example, if timeStep.minutes = 8, then the available minute options will be [0, 8, 16, 24, 32, 40, 48, 56]. When single column time renderer is used, only timeStep.minutes will be used.", "deprecated": "", @@ -349,10 +345,13 @@ "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", "calendarHeader": "Custom component for calendar header. Check the PickersCalendarHeader component.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day. Check the PickersDay component.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", "desktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", + "digitalClockItem": "Component responsible for rendering a single digital clock item.", "digitalClockSectionItem": "Component responsible for rendering a single multi section digital clock section item.", "field": "Component used to enter the date with the keyboard.", "inputAdornment": "Component displayed on the start or end input adornment used to open the picker on desktop.", diff --git a/docs/translations/api-docs/date-pickers/desktop-time-picker.json b/docs/translations/api-docs/date-pickers/desktop-time-picker.json index 06abd1fc1f5fc..121b72455cd8e 100644 --- a/docs/translations/api-docs/date-pickers/desktop-time-picker.json +++ b/docs/translations/api-docs/date-pickers/desktop-time-picker.json @@ -26,16 +26,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Used when the component is not controlled.", "deprecated": "", @@ -246,6 +236,8 @@ "classDescriptions": {}, "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", "desktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", diff --git a/docs/translations/api-docs/date-pickers/digital-clock.json b/docs/translations/api-docs/date-pickers/digital-clock.json index 84f118205f68a..c4589302dbcd7 100644 --- a/docs/translations/api-docs/date-pickers/digital-clock.json +++ b/docs/translations/api-docs/date-pickers/digital-clock.json @@ -16,16 +16,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overrideable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default selected value. Used when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-picker.json index 306ef8698be73..be0a71c9317e3 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-picker.json @@ -16,21 +16,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -207,7 +198,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json index 8bf59738d93c3..2151b51c04950 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-range-picker.json @@ -21,16 +21,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "currentMonthCalendarPosition": { "description": "Position the current month is rendered in.", "deprecated": "", @@ -40,7 +30,8 @@ "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -221,7 +212,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -268,6 +259,8 @@ "classDescriptions": {}, "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "day": "Custom component for day in range pickers. Check the DateRangePickersDay component.", "dialog": "Custom component for the dialog inside which the views are rendered on mobile.", "field": "", diff --git a/docs/translations/api-docs/date-pickers/mobile-date-time-picker.json b/docs/translations/api-docs/date-pickers/mobile-date-time-picker.json index 2042bdfba2434..16de54aaf4868 100644 --- a/docs/translations/api-docs/date-pickers/mobile-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-date-time-picker.json @@ -26,21 +26,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -256,7 +247,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/mobile-time-picker.json b/docs/translations/api-docs/date-pickers/mobile-time-picker.json index 27011c64ba91a..8245c3762d9b3 100644 --- a/docs/translations/api-docs/date-pickers/mobile-time-picker.json +++ b/docs/translations/api-docs/date-pickers/mobile-time-picker.json @@ -26,16 +26,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Used when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json index 28c138342d669..616108b1bce1d 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-range-field.json @@ -6,16 +6,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Use when the component is not controlled.", "deprecated": "", @@ -103,7 +93,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json index 360dd6261504a..4aeadc09b71b8 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-date-time-range-field.json @@ -11,16 +11,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Use when the component is not controlled.", "deprecated": "", @@ -147,7 +137,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json b/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json index 819c192e52b1d..fb146478cf5f4 100644 --- a/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/multi-input-time-range-field.json @@ -11,16 +11,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Use when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json b/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json index fa580e7ea58b5..18cbe3fb0d20d 100644 --- a/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json +++ b/docs/translations/api-docs/date-pickers/multi-section-digital-clock.json @@ -16,16 +16,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overrideable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default selected value. Used when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/pickers-calendar-header.json b/docs/translations/api-docs/date-pickers/pickers-calendar-header.json index fa256e342ed43..3a5f0afb726f6 100644 --- a/docs/translations/api-docs/date-pickers/pickers-calendar-header.json +++ b/docs/translations/api-docs/date-pickers/pickers-calendar-header.json @@ -43,5 +43,12 @@ "nodeName": "the switch view icon element" } }, - "slotDescriptions": {} + "slotDescriptions": { + "leftArrowIcon": "Icon displayed in the left view switch button.", + "nextIconButton": "Button allowing to switch to the right view.", + "previousIconButton": "Button allowing to switch to the left view.", + "rightArrowIcon": "Icon displayed in the right view switch button.", + "switchViewButton": "Button displayed to switch between different calendar views.", + "switchViewIcon": "Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is 'year'." + } } diff --git a/docs/translations/api-docs/date-pickers/pickers-layout.json b/docs/translations/api-docs/date-pickers/pickers-layout.json index 3222758beae18..717417e43e1cb 100644 --- a/docs/translations/api-docs/date-pickers/pickers-layout.json +++ b/docs/translations/api-docs/date-pickers/pickers-layout.json @@ -1,16 +1,6 @@ { "componentDescription": "", "propDescriptions": { - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "orientation": { "description": "Force rendering in particular orientation.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/single-input-date-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-range-field.json index e4c730bfee336..540fc9eedced9 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-range-field.json @@ -6,18 +6,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -130,6 +125,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -164,7 +164,7 @@ "typeDescriptions": {} }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -216,6 +216,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field.json b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field.json index a95d30e9fba2c..8ec3fb6233815 100644 --- a/docs/translations/api-docs/date-pickers/single-input-date-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-date-time-range-field.json @@ -11,18 +11,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -165,6 +160,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -208,7 +208,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", @@ -269,6 +269,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/single-input-time-range-field.json b/docs/translations/api-docs/date-pickers/single-input-time-range-field.json index 7feea9066577a..101840b437aea 100644 --- a/docs/translations/api-docs/date-pickers/single-input-time-range-field.json +++ b/docs/translations/api-docs/date-pickers/single-input-time-range-field.json @@ -11,18 +11,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -145,6 +140,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -240,6 +240,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/static-date-picker.json b/docs/translations/api-docs/date-pickers/static-date-picker.json index e766bdae4b858..20ee802b91b48 100644 --- a/docs/translations/api-docs/date-pickers/static-date-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-picker.json @@ -11,21 +11,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -166,7 +157,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/static-date-range-picker.json b/docs/translations/api-docs/date-pickers/static-date-range-picker.json index 73e2eba642f5c..43d254b56fd7b 100644 --- a/docs/translations/api-docs/date-pickers/static-date-range-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-range-picker.json @@ -16,16 +16,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "currentMonthCalendarPosition": { "description": "Position the current month is rendered in.", "deprecated": "", @@ -35,7 +25,8 @@ "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -176,7 +167,7 @@ "typeDescriptions": { "React.ReactNode": "The node to render when loading." } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/static-date-time-picker.json b/docs/translations/api-docs/date-pickers/static-date-time-picker.json index fb22d4cf4af0d..fc40f385eabff 100644 --- a/docs/translations/api-docs/date-pickers/static-date-time-picker.json +++ b/docs/translations/api-docs/date-pickers/static-date-time-picker.json @@ -21,21 +21,12 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "dayOfWeekFormatter": { "description": "Formats the day of week displayed in the calendar header.", "deprecated": "", "typeDescriptions": { - "day": "The day of week provided by the adapter's method getWeekdays.", + "day": "The day of week provided by the adapter. Deprecated, will be removed in v7: Use date instead.", + "date": "The date of the day of week provided by the adapter.", "string": "The name to display." } }, @@ -215,7 +206,7 @@ } }, "shouldDisableDate": { - "description": "Disable specific date.", + "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.", "deprecated": "", "typeDescriptions": { "day": "The date to test.", diff --git a/docs/translations/api-docs/date-pickers/static-time-picker.json b/docs/translations/api-docs/date-pickers/static-time-picker.json index 78332bdd81626..209d2760361de 100644 --- a/docs/translations/api-docs/date-pickers/static-time-picker.json +++ b/docs/translations/api-docs/date-pickers/static-time-picker.json @@ -21,16 +21,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Used when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/time-clock.json b/docs/translations/api-docs/date-pickers/time-clock.json index 4ba344f71a60f..b8215f1cd72a0 100644 --- a/docs/translations/api-docs/date-pickers/time-clock.json +++ b/docs/translations/api-docs/date-pickers/time-clock.json @@ -21,16 +21,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default selected value. Used when the component is not controlled.", "deprecated": "", diff --git a/docs/translations/api-docs/date-pickers/time-field.json b/docs/translations/api-docs/date-pickers/time-field.json index 7feea9066577a..101840b437aea 100644 --- a/docs/translations/api-docs/date-pickers/time-field.json +++ b/docs/translations/api-docs/date-pickers/time-field.json @@ -11,18 +11,13 @@ "deprecated": "", "typeDescriptions": {} }, - "color": { - "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", + "clearable": { + "description": "If true, a clear button will be shown in the field allowing value clearing.", "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", + "color": { + "description": "The color of the component. It supports both default and custom theme colors, which can be added as shown in the palette customization guide.", "deprecated": "", "typeDescriptions": {} }, @@ -145,6 +140,11 @@ "context": "The context containing the validation result of the current value." } }, + "onClear": { + "description": "Callback fired when the clear button is clicked.", + "deprecated": "", + "typeDescriptions": {} + }, "onError": { "description": "Callback fired when the error associated to the current value changes.", "deprecated": "", @@ -240,6 +240,8 @@ }, "classDescriptions": { "root": { "description": "Styles applied to the root element." } }, "slotDescriptions": { + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "textField": "Form control with an input to render the value. Receives the same props as @mui/material/TextField." } } diff --git a/docs/translations/api-docs/date-pickers/time-picker.json b/docs/translations/api-docs/date-pickers/time-picker.json index 823d14046c7e2..536223cc08acc 100644 --- a/docs/translations/api-docs/date-pickers/time-picker.json +++ b/docs/translations/api-docs/date-pickers/time-picker.json @@ -26,16 +26,6 @@ "deprecated": "", "typeDescriptions": {} }, - "components": { - "description": "Overridable components.", - "deprecated": "", - "typeDescriptions": {} - }, - "componentsProps": { - "description": "The props used for each component slot.", - "deprecated": "", - "typeDescriptions": {} - }, "defaultValue": { "description": "The default value. Used when the component is not controlled.", "deprecated": "", @@ -251,6 +241,8 @@ "classDescriptions": {}, "slotDescriptions": { "actionBar": "Custom component for the action bar, it is placed below the picker views.", + "clearButton": "Button to clear the value.", + "clearIcon": "Icon to display inside the clear button.", "desktopPaper": "Custom component for the paper rendered inside the desktop picker's Popper.", "desktopTransition": "Custom component for the desktop popper Transition.", "desktopTrapFocus": "Custom component for trapping the focus inside the views on desktop.", diff --git a/package.json b/package.json index 5c5683b37cdf3..2ed5b4650529b 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "6.14.0", + "version": "6.18.0", "private": true, "scripts": { "start": "yarn && yarn docs:dev", @@ -25,7 +25,7 @@ "eslint:ci": "eslint . --report-unused-disable-directives --ext .js,.ts,.tsx --max-warnings 0", "markdownlint": "markdownlint-cli2 \"**/*.md\"", "postinstall": "patch-package", - "prettier": "pretty-quick --branch master --ignore-path .eslintignore", + "prettier": "pretty-quick --branch next --ignore-path .eslintignore", "prettier:all": "prettier --write . --ignore-path .eslintignore", "proptypes": "cross-env BABEL_ENV=development babel-node -i \"/node_modules/(?!@mui)/\" -x .ts,.tsx,.js ./docs/scripts/generateProptypes.ts", "size:snapshot": "node --max-old-space-size=2048 ./scripts/sizeSnapshot/create", @@ -48,11 +48,6 @@ "test:regressions:dev": "concurrently \"yarn test:regressions:build --watch\" \"yarn test:regressions:server\"", "test:regressions:run": "mocha --config test/regressions/.mocharc.js --delay 'test/regressions/**/*.test.js'", "test:regressions:server": "serve test/regressions -p 5001", - "test:performance": "cross-env NODE_ENV=production yarn test:performance:build && concurrently --success first --kill-others \"yarn test:performance:run\" \"yarn test:performance:server\"", - "test:performance:dev": "concurrently \"yarn test:performance:build --watch\" \"yarn test:performance:server\"", - "test:performance:run": "node ./scripts/performance.mjs", - "test:performance:build": "webpack --config test/performance/webpack.config.js", - "test:performance:server": "serve test/performance -p 5001", "test:argos": "node ./scripts/pushArgos.mjs", "typescript": "lerna run --no-bail --parallel typescript", "typescript:ci": "lerna run --concurrency 3 --no-bail --no-sort typescript", @@ -67,51 +62,52 @@ "validate": "concurrently \"yarn prettier && yarn eslint\" \"yarn proptypes\" \"yarn docs:typescript:formatted\" \"yarn docs:api\"" }, "devDependencies": { - "@argos-ci/core": "^0.11.1", - "@babel/cli": "^7.22.15", - "@babel/core": "^7.22.15", - "@babel/node": "^7.22.15", + "@argos-ci/core": "^1.0.0", + "@babel/cli": "^7.23.0", + "@babel/core": "^7.23.2", + "@babel/node": "^7.22.19", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-object-rest-spread": "^7.20.7", "@babel/plugin-proposal-private-methods": "^7.18.6", "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-transform-object-assign": "^7.22.5", "@babel/plugin-transform-react-constant-elements": "^7.22.5", - "@babel/plugin-transform-runtime": "^7.22.15", - "@babel/preset-env": "^7.22.15", + "@babel/plugin-transform-runtime": "^7.23.2", + "@babel/preset-env": "^7.23.2", "@babel/preset-react": "^7.22.15", - "@babel/preset-typescript": "^7.22.15", + "@babel/preset-typescript": "^7.23.2", "@babel/register": "^7.22.15", - "@babel/traverse": "^7.22.15", - "@babel/types": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", "@emotion/cache": "^11.11.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@mnajdova/enzyme-adapter-react-18": "^0.2.0", - "@mui/icons-material": "^5.14.8", - "@mui/material": "^5.14.8", + "@mui/icons-material": "^5.14.16", + "@mui/material": "^5.14.16", "@mui/monorepo": "https://github.com/mui/material-ui.git#master", - "@mui/utils": "^5.14.8", - "@octokit/plugin-retry": "^6.0.0", - "@octokit/rest": "^20.0.1", - "@playwright/test": "1.37.1", + "@mui/utils": "^5.14.16", + "@next/eslint-plugin-next": "^13.5.6", + "@octokit/plugin-retry": "^6.0.1", + "@octokit/rest": "^20.0.2", + "@playwright/test": "1.39.0", "@testing-library/react": "^14.0.0", - "@types/babel__core": "^7.20.2", - "@types/chai": "^4.3.6", - "@types/chai-dom": "^1.11.1", + "@types/babel__core": "^7.20.3", + "@types/chai": "^4.3.9", + "@types/chai-dom": "^1.11.2", "@types/enzyme": "^3.10.12", - "@types/mocha": "^10.0.1", - "@types/node": "^18.17.18", + "@types/mocha": "^10.0.3", + "@types/node": "^18.18.6", "@types/prettier": "^2.7.3", - "@types/react": "^18.2.21", - "@types/react-dom": "^18.2.7", - "@types/react-test-renderer": "^18.0.1", - "@types/requestidlecallback": "^0.3.5", - "@types/sinon": "^10.0.16", - "@types/yargs": "^17.0.24", - "@typescript-eslint/eslint-plugin": "^6.7.0", - "@typescript-eslint/parser": "^6.7.0", - "axe-core": "4.8.1", + "@types/react": "^18.2.33", + "@types/react-dom": "^18.2.14", + "@types/react-test-renderer": "^18.0.5", + "@types/requestidlecallback": "^0.3.6", + "@types/sinon": "^10.0.20", + "@types/yargs": "^17.0.29", + "@typescript-eslint/eslint-plugin": "^6.9.1", + "@typescript-eslint/parser": "^6.9.1", + "axe-core": "4.8.2", "babel-loader": "^9.1.3", "babel-plugin-istanbul": "^6.1.1", "babel-plugin-module-resolver": "^4.1.0", @@ -120,24 +116,24 @@ "babel-plugin-search-and-replace": "^1.1.1", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "babel-plugin-transform-rename-import": "^2.3.0", - "chai": "^4.3.8", + "chai": "^4.3.10", "chai-dom": "^1.11.0", "compression-webpack-plugin": "^10.0.0", - "concurrently": "^8.2.1", + "concurrently": "^8.2.2", "cross-env": "^7.0.3", - "danger": "^11.2.8", + "danger": "^11.3.0", "enzyme": "^3.11.0", - "eslint": "^8.49.0", + "eslint": "^8.52.0", "eslint-config-airbnb": "^19.0.4", "eslint-config-airbnb-typescript": "^17.1.0", "eslint-config-prettier": "^9.0.0", - "eslint-import-resolver-webpack": "^0.13.7", + "eslint-import-resolver-webpack": "^0.13.8", "eslint-plugin-filenames": "^1.3.2", - "eslint-plugin-import": "^2.28.1", + "eslint-plugin-import": "^2.29.0", "eslint-plugin-jsdoc": "^46.8.2", "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-mocha": "^10.1.0", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-mocha": "^10.2.0", + "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "format-util": "^1.0.5", @@ -153,7 +149,7 @@ "karma-mocha": "^2.0.1", "karma-sourcemap-loader": "^0.4.0", "karma-webpack": "^5.0.0", - "lerna": "^7.2.0", + "lerna": "^7.4.1", "markdownlint-cli2": "^0.10.0", "mocha": "^10.2.0", "nyc": "^15.1.0", @@ -165,12 +161,12 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "serve": "^14.2.1", - "sinon": "^16.0.0", + "sinon": "^16.1.3", "stream-browserify": "^3.0.0", "string-replace-loader": "^3.1.0", "typescript": "^5.2.2", "util": "^0.12.5", - "webpack": "^5.88.2", + "webpack": "^5.89.0", "webpack-cli": "^5.1.4", "yargs": "^17.7.2", "yarn-deduplicate": "^6.0.2" @@ -192,6 +188,6 @@ "dependencies": {}, "resolutions": { "**/react-is": "^18.2.0", - "**/@types/node": "^18.17.18" + "**/@types/node": "^18.18.6" } } diff --git a/packages/eslint-plugin-material-ui/package.json b/packages/eslint-plugin-material-ui/package.json index f718d191c700b..3928e91567b31 100644 --- a/packages/eslint-plugin-material-ui/package.json +++ b/packages/eslint-plugin-material-ui/package.json @@ -5,9 +5,9 @@ "description": "Custom eslint rules for MUI X.", "main": "src/index.js", "devDependencies": { - "@types/eslint": "^8.44.2", + "@types/eslint": "^8.44.6", "@typescript-eslint/experimental-utils": "^5.62.0", - "@typescript-eslint/parser": "^6.7.0" + "@typescript-eslint/parser": "^6.9.1" }, "scripts": { "test": "cd ../../ && cross-env NODE_ENV=test mocha 'packages/eslint-plugin-material-ui/**/*.test.js' --timeout 3000" diff --git a/packages/eslint-plugin-material-ui/src/rules/no-direct-state-access.js b/packages/eslint-plugin-material-ui/src/rules/no-direct-state-access.js index 61b06c9fd6a5b..6b67202bee092 100644 --- a/packages/eslint-plugin-material-ui/src/rules/no-direct-state-access.js +++ b/packages/eslint-plugin-material-ui/src/rules/no-direct-state-access.js @@ -22,7 +22,7 @@ function reportIfDirectlyAccessingState(node, context, nodeToReport = node) { return; } - const parserServices = ESLintUtils.getParserServices(context); + const { parserServices } = context.sourceCode; const checker = parserServices.program.getTypeChecker(); const originalNode = parserServices.esTreeNodeToTSNodeMap.get(maybeApiRef); diff --git a/packages/grid/x-data-grid-generator/package.json b/packages/grid/x-data-grid-generator/package.json index 0f04f326f1e85..3d2635b260fa8 100644 --- a/packages/grid/x-data-grid-generator/package.json +++ b/packages/grid/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "6.14.0", + "version": "6.18.0", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", @@ -30,15 +30,15 @@ "directory": "packages/grid/x-data-grid-generator" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/base": "^5.0.0-beta.14", - "@mui/x-data-grid-premium": "6.14.0", + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/x-data-grid-premium": "6.18.0", "chance": "^1.1.11", "clsx": "^2.0.0", "lru-cache": "^7.18.3" }, "devDependencies": { - "@types/chance": "^1.1.4", + "@types/chance": "^1.1.5", "@types/lru-cache": "^7.10.9" }, "peerDependencies": { diff --git a/packages/grid/x-data-grid-generator/src/services/random-generator.ts b/packages/grid/x-data-grid-generator/src/services/random-generator.ts index a379ef2811d79..877e01634b81e 100644 --- a/packages/grid/x-data-grid-generator/src/services/random-generator.ts +++ b/packages/grid/x-data-grid-generator/src/services/random-generator.ts @@ -1,4 +1,4 @@ -import globalChance, { Chance } from 'chance'; +import { Chance } from 'chance'; import { COLORS, COMMODITY_OPTIONS, @@ -12,15 +12,17 @@ import { } from './static-data'; import { GridDataGeneratorContext } from './gridColDefGenerator'; -const chanceId = globalChance(); let chance: Chance.Chance; +let chanceGuid: Chance.Chance; declare const DISABLE_CHANCE_RANDOM: any; if (typeof DISABLE_CHANCE_RANDOM !== 'undefined' && DISABLE_CHANCE_RANDOM) { - chance = globalChance(() => 0.5); + chance = new Chance(() => 0.5); + chanceGuid = new Chance(42); } else { - chance = chanceId; + chance = new Chance(); + chanceGuid = chance; } type ColumnDataGenerator = (data: any, context: GridDataGeneratorContext) => Value; @@ -113,7 +115,7 @@ export const randomArrayItem = (arr: T[]) => arr[randomInt(0, arr.length - 1) export const randomBoolean = (): boolean => randomArrayItem([true, false]); export const randomColor = () => randomArrayItem(COLORS); -export const randomId = () => chanceId.guid(); +export const randomId = () => chanceGuid.guid(); export const randomDesk = () => `D-${chance.integer({ min: 0, max: 10000 })}`; export const randomCommodity = () => randomArrayItem(COMMODITY_OPTIONS); export const randomTraderName = () => chance.name(); diff --git a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts index 80e37aeb901ab..57ae9f0dfc27a 100644 --- a/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts +++ b/packages/grid/x-data-grid-generator/src/services/tree-data-generator.ts @@ -11,13 +11,12 @@ export interface AddPathToDemoDataOptions { /** * The depth of the tree - * @default: 1 + * @default 1 */ maxDepth?: number; - /** * The average amount of children in a node - * @default: 2 + * @default 2 */ averageChildren?: number; } diff --git a/packages/grid/x-data-grid-premium/package.json b/packages/grid/x-data-grid-premium/package.json index 603ac67a094d7..33ed5bb10dd07 100644 --- a/packages/grid/x-data-grid-premium/package.json +++ b/packages/grid/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "6.14.0", + "version": "6.18.0", "description": "The Premium plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -42,12 +42,12 @@ "directory": "packages/grid/x-data-grid-premium" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/utils": "^5.14.8", - "@mui/x-data-grid": "6.14.0", - "@mui/x-data-grid-pro": "6.14.0", + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16", + "@mui/x-data-grid": "6.18.0", + "@mui/x-data-grid-pro": "6.18.0", "@mui/x-license-pro": "6.10.2", - "@types/format-util": "^1.0.2", + "@types/format-util": "^1.0.3", "clsx": "^2.0.0", "exceljs": "^4.3.0", "prop-types": "^15.8.1", diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 0357715bb15ce..72a84ab08ee13 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -60,6 +60,13 @@ interface DataGridPremiumComponent { propTypes?: any; } +/** + * Demos: + * - [DataGridPremium](https://mui.com/x/react-data-grid/demo/) + * + * API: + * - [DataGridPremium API](https://mui.com/x/api/data-grid/data-grid-premium/) + */ export const DataGridPremium = React.memo(DataGridPremiumRaw) as DataGridPremiumComponent; DataGridPremiumRaw.propTypes = { @@ -107,6 +114,21 @@ DataGridPremiumRaw.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. */ @@ -163,16 +185,6 @@ DataGridPremiumRaw.propTypes = { * If defined, the grid will ignore the `hide` property in [[GridColDef]]. */ columnVisibilityModel: PropTypes.object, - /** - * Overridable components. - * @deprecated Use the `slots` prop instead. - */ - components: PropTypes.object, - /** - * Overridable components props dynamically passed to the component at rendering. - * @deprecated Use the `slotProps` prop instead. - */ - componentsProps: PropTypes.object, /** * If above 0, the row children will be expanded up to this depth. * If equal to -1, all the row children will be expanded. @@ -195,6 +207,11 @@ DataGridPremiumRaw.propTypes = { * @default false */ disableAggregation: PropTypes.bool, + /** + * If `true`, column autosizing on header separator double-click is disabled. + * @default false + */ + disableAutosize: PropTypes.bool, /** * If `true`, the filtering will only be applied to the top level rows when grouping rows with the `treeData` prop. * @default false @@ -434,6 +451,12 @@ DataGridPremiumRaw.propTypes = { * @default false */ hideFooterSelectedRowCount: PropTypes.bool, + /** + * If `true`, the diacritics (accents) are ignored when filtering or quick filtering. + * E.g. when filter value is `cafe`, the rows with `café` will be visible. + * @default false + */ + ignoreDiacritics: PropTypes.bool, /** * The initial state of the DataGridPremium. * The data in it is set in the state on initialization but isn't controlled. @@ -865,6 +888,13 @@ DataGridPremiumRaw.propTypes = { * Controls the modes of the rows. */ rowModesModel: PropTypes.object, + /** + * The milliseconds delay to wait after measuring the row height before recalculating row positions. + * Setting it to a lower value could be useful when using dynamic row height, + * but might reduce performance when displaying a large number of rows. + * @default 166 + */ + rowPositionsDebounceMs: PropTypes.number, /** * If `true`, the reordering of rows is enabled. * @default false @@ -988,7 +1018,7 @@ DataGridPremiumRaw.propTypes = { /** * If `true`, the grid will not use `valueFormatter` when exporting to CSV or copying to clipboard. * If an object is provided, you can choose to ignore the `valueFormatter` for CSV export or clipboard export. - * @default: false + * @default false */ unstable_ignoreValueFormatterDuringExport: PropTypes.oneOfType([ PropTypes.shape({ diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx index 4da70394a3142..aaca7bff91acb 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx @@ -62,6 +62,8 @@ import { useGridLazyLoaderPreProcessors, headerFilteringStateInitializer, useGridHeaderFiltering, + virtualizationStateInitializer, + useGridVirtualization, } from '@mui/x-data-grid-pro/internals'; import { GridApiPremium, GridPrivateApiPremium } from '../models/gridApiPremium'; import { DataGridPremiumProcessedProps } from '../models/dataGridPremiumProps'; @@ -87,91 +89,90 @@ export const useDataGridPremiumComponent = ( inputApiRef: React.MutableRefObject | undefined, props: DataGridPremiumProcessedProps, ) => { - const privateApiRef = useGridInitialization( - inputApiRef, - props, - ); + const apiRef = useGridInitialization(inputApiRef, props); /** * Register all pre-processors called during state initialization here. */ - useGridRowSelectionPreProcessors(privateApiRef, props); - useGridRowReorderPreProcessors(privateApiRef, props); - useGridRowGroupingPreProcessors(privateApiRef, props); - useGridTreeDataPreProcessors(privateApiRef, props); - useGridLazyLoaderPreProcessors(privateApiRef, props); - useGridRowPinningPreProcessors(privateApiRef); - useGridAggregationPreProcessors(privateApiRef, props); - useGridDetailPanelPreProcessors(privateApiRef, props); + useGridRowSelectionPreProcessors(apiRef, props); + useGridRowReorderPreProcessors(apiRef, props); + useGridRowGroupingPreProcessors(apiRef, props); + useGridTreeDataPreProcessors(apiRef, props); + useGridLazyLoaderPreProcessors(apiRef, props); + useGridRowPinningPreProcessors(apiRef); + useGridAggregationPreProcessors(apiRef, props); + useGridDetailPanelPreProcessors(apiRef, props); // The column pinning `hydrateColumns` pre-processor must be after every other `hydrateColumns` pre-processors // Because it changes the order of the columns. - useGridColumnPinningPreProcessors(privateApiRef, props); - useGridRowsPreProcessors(privateApiRef); + useGridColumnPinningPreProcessors(apiRef, props); + useGridRowsPreProcessors(apiRef); /** * Register all state initializers here. */ - useGridInitializeState(headerFilteringStateInitializer, privateApiRef, props); - useGridInitializeState(rowGroupingStateInitializer, privateApiRef, props); - useGridInitializeState(aggregationStateInitializer, privateApiRef, props); - useGridInitializeState(rowSelectionStateInitializer, privateApiRef, props); - useGridInitializeState(cellSelectionStateInitializer, privateApiRef, props); - useGridInitializeState(detailPanelStateInitializer, privateApiRef, props); - useGridInitializeState(columnPinningStateInitializer, privateApiRef, props); - useGridInitializeState(columnsStateInitializer, privateApiRef, props); - useGridInitializeState(rowPinningStateInitializer, privateApiRef, props); - useGridInitializeState(rowsStateInitializer, privateApiRef, props); - useGridInitializeState(editingStateInitializer, privateApiRef, props); - useGridInitializeState(focusStateInitializer, privateApiRef, props); - useGridInitializeState(sortingStateInitializer, privateApiRef, props); - useGridInitializeState(preferencePanelStateInitializer, privateApiRef, props); - useGridInitializeState(filterStateInitializer, privateApiRef, props); - useGridInitializeState(densityStateInitializer, privateApiRef, props); - useGridInitializeState(columnReorderStateInitializer, privateApiRef, props); - useGridInitializeState(columnResizeStateInitializer, privateApiRef, props); - useGridInitializeState(paginationStateInitializer, privateApiRef, props); - useGridInitializeState(rowsMetaStateInitializer, privateApiRef, props); - useGridInitializeState(columnMenuStateInitializer, privateApiRef, props); - useGridInitializeState(columnGroupsStateInitializer, privateApiRef, props); + useGridInitializeState(headerFilteringStateInitializer, apiRef, props); + useGridInitializeState(rowGroupingStateInitializer, apiRef, props); + useGridInitializeState(aggregationStateInitializer, apiRef, props); + useGridInitializeState(rowSelectionStateInitializer, apiRef, props); + useGridInitializeState(cellSelectionStateInitializer, apiRef, props); + useGridInitializeState(detailPanelStateInitializer, apiRef, props); + useGridInitializeState(columnPinningStateInitializer, apiRef, props); + useGridInitializeState(columnsStateInitializer, apiRef, props); + useGridInitializeState(rowPinningStateInitializer, apiRef, props); + useGridInitializeState(rowsStateInitializer, apiRef, props); + useGridInitializeState(editingStateInitializer, apiRef, props); + useGridInitializeState(focusStateInitializer, apiRef, props); + useGridInitializeState(sortingStateInitializer, apiRef, props); + useGridInitializeState(preferencePanelStateInitializer, apiRef, props); + useGridInitializeState(filterStateInitializer, apiRef, props); + useGridInitializeState(densityStateInitializer, apiRef, props); + useGridInitializeState(columnReorderStateInitializer, apiRef, props); + useGridInitializeState(columnResizeStateInitializer, apiRef, props); + useGridInitializeState(paginationStateInitializer, apiRef, props); + useGridInitializeState(rowsMetaStateInitializer, apiRef, props); + useGridInitializeState(columnMenuStateInitializer, apiRef, props); + useGridInitializeState(columnGroupsStateInitializer, apiRef, props); + useGridInitializeState(virtualizationStateInitializer, apiRef, props); - useGridRowGrouping(privateApiRef, props); - useGridHeaderFiltering(privateApiRef, props); - useGridTreeData(privateApiRef); - useGridAggregation(privateApiRef, props); - useGridKeyboardNavigation(privateApiRef, props); - useGridRowSelection(privateApiRef, props); - useGridCellSelection(privateApiRef, props); - useGridColumnPinning(privateApiRef, props); - useGridRowPinning(privateApiRef, props); - useGridColumns(privateApiRef, props); - useGridRows(privateApiRef, props); - useGridParamsApi(privateApiRef, props); - useGridDetailPanel(privateApiRef, props); - useGridColumnSpanning(privateApiRef); - useGridColumnGrouping(privateApiRef, props); - useGridClipboardImport(privateApiRef, props); - useGridEditing(privateApiRef, props); - useGridFocus(privateApiRef, props); - useGridPreferencesPanel(privateApiRef, props); - useGridFilter(privateApiRef, props); - useGridSorting(privateApiRef, props); - useGridDensity(privateApiRef, props); - useGridColumnReorder(privateApiRef, props); - useGridColumnResize(privateApiRef, props); - useGridPagination(privateApiRef, props); - useGridRowsMeta(privateApiRef, props); - useGridRowReorder(privateApiRef, props); - useGridScroll(privateApiRef, props); - useGridInfiniteLoader(privateApiRef, props); - useGridLazyLoader(privateApiRef, props); - useGridColumnMenu(privateApiRef); - useGridCsvExport(privateApiRef, props); - useGridPrintExport(privateApiRef, props); - useGridExcelExport(privateApiRef, props); - useGridClipboard(privateApiRef, props); - useGridDimensions(privateApiRef, props); - useGridEvents(privateApiRef, props); - useGridStatePersistence(privateApiRef); + useGridRowGrouping(apiRef, props); + useGridHeaderFiltering(apiRef, props); + useGridTreeData(apiRef); + useGridAggregation(apiRef, props); + useGridKeyboardNavigation(apiRef, props); + useGridRowSelection(apiRef, props); + useGridCellSelection(apiRef, props); + useGridColumnPinning(apiRef, props); + useGridRowPinning(apiRef, props); + useGridColumns(apiRef, props); + useGridRows(apiRef, props); + useGridParamsApi(apiRef, props); + useGridDetailPanel(apiRef, props); + useGridColumnSpanning(apiRef); + useGridColumnGrouping(apiRef, props); + useGridClipboardImport(apiRef, props); + useGridEditing(apiRef, props); + useGridFocus(apiRef, props); + useGridPreferencesPanel(apiRef, props); + useGridFilter(apiRef, props); + useGridSorting(apiRef, props); + useGridDensity(apiRef, props); + useGridColumnReorder(apiRef, props); + useGridColumnResize(apiRef, props); + useGridPagination(apiRef, props); + useGridRowsMeta(apiRef, props); + useGridRowReorder(apiRef, props); + useGridScroll(apiRef, props); + useGridInfiniteLoader(apiRef, props); + useGridLazyLoader(apiRef, props); + useGridColumnMenu(apiRef); + useGridCsvExport(apiRef, props); + useGridPrintExport(apiRef, props); + useGridExcelExport(apiRef, props); + useGridClipboard(apiRef, props); + useGridDimensions(apiRef, props); + useGridEvents(apiRef, props); + useGridStatePersistence(apiRef); + useGridVirtualization(apiRef, props); - return privateApiRef; + return apiRef; }; diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts index 8cbfd79b9d0f2..87a389c36c719 100644 --- a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts +++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts @@ -1,13 +1,13 @@ import * as React from 'react'; import { useThemeProps } from '@mui/material/styles'; import { DATA_GRID_PRO_PROPS_DEFAULT_VALUES, GRID_DEFAULT_LOCALE_TEXT } from '@mui/x-data-grid-pro'; -import { computeSlots, useProps, uncapitalizeObjectKeys } from '@mui/x-data-grid-pro/internals'; +import { computeSlots, useProps } from '@mui/x-data-grid-pro/internals'; import { DataGridPremiumProps, DataGridPremiumProcessedProps, DataGridPremiumPropsWithDefaultValue, } from '../models/dataGridPremiumProps'; -import { GridPremiumSlotsComponent, UncapitalizedGridPremiumSlotsComponent } from '../models'; +import { GridPremiumSlotsComponent } from '../models'; import { GRID_AGGREGATION_FUNCTIONS } from '../hooks/features/aggregation'; import { DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS } from '../constants/dataGridPremiumDefaultSlotsComponents'; @@ -32,10 +32,10 @@ export const DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES: DataGridPremiumPropsWithDef }, }; -const defaultSlots = uncapitalizeObjectKeys(DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS)!; +const defaultSlots = DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS; export const useDataGridPremiumProps = (inProps: DataGridPremiumProps) => { - const [components, componentsProps, themedProps] = useProps( + const themedProps = useProps( useThemeProps({ props: inProps, name: 'MuiDataGrid', @@ -47,25 +47,23 @@ export const useDataGridPremiumProps = (inProps: DataGridPremiumProps) => { [themedProps.localeText], ); - const slots = React.useMemo( + const slots = React.useMemo( () => computeSlots({ defaultSlots, - components, slots: themedProps.slots, }), - [components, themedProps.slots], + [themedProps.slots], ); return React.useMemo( () => ({ ...DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES, ...themedProps, - slotProps: themedProps.slotProps ?? componentsProps, localeText, slots, signature: 'DataGridPremium', }), - [themedProps, componentsProps, localeText, slots], + [themedProps, localeText, slots], ); }; diff --git a/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx b/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx index 59dff478d8afc..c2dd3b239ed5d 100644 --- a/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx +++ b/packages/grid/x-data-grid-premium/src/components/GridAggregationHeader.tsx @@ -9,6 +9,7 @@ import { GridColumnHeaderParams, GridColumnHeaderTitle, } from '@mui/x-data-grid'; +import type { GridBaseColDef } from '@mui/x-data-grid/internals'; import { getAggregationFunctionLabel } from '../hooks/features/aggregation/gridAggregationUtils'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; @@ -66,8 +67,13 @@ const useUtilityClasses = (ownerState: OwnerState) => { return composeClasses(slots, getDataGridUtilityClass, classes); }; -function GridAggregationHeader(props: GridColumnHeaderParams) { - const { colDef, aggregation } = props; +function GridAggregationHeader( + props: GridColumnHeaderParams & { + renderHeader: GridBaseColDef['renderHeader']; + }, +) { + const { renderHeader, ...params } = props; + const { colDef, aggregation } = params; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); @@ -86,11 +92,15 @@ function GridAggregationHeader(props: GridColumnHeaderParams) { return ( - + {renderHeader ? ( + renderHeader(params) + ) : ( + + )} {aggregationLabel} diff --git a/packages/grid/x-data-grid-premium/src/constants/dataGridPremiumDefaultSlotsComponents.ts b/packages/grid/x-data-grid-premium/src/constants/dataGridPremiumDefaultSlotsComponents.ts index 7e30dd8f341f3..9efc1a8f5606d 100644 --- a/packages/grid/x-data-grid-premium/src/constants/dataGridPremiumDefaultSlotsComponents.ts +++ b/packages/grid/x-data-grid-premium/src/constants/dataGridPremiumDefaultSlotsComponents.ts @@ -6,5 +6,5 @@ import materialSlots from '../material'; export const DATA_GRID_PREMIUM_DEFAULT_SLOTS_COMPONENTS: GridPremiumSlotsComponent = { ...DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS, ...materialSlots, - ColumnMenu: GridPremiumColumnMenu, + columnMenu: GridPremiumColumnMenu, }; diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationFunctions.ts b/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationFunctions.ts index a777f45a16d24..1dae2269107d4 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationFunctions.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationFunctions.ts @@ -2,12 +2,12 @@ import { GridValueFormatterParams } from '@mui/x-data-grid-pro'; import { isNumber } from '@mui/x-data-grid-pro/internals'; import { GridAggregationFunction } from './gridAggregationInterfaces'; -const sumAgg: GridAggregationFunction = { +const sumAgg: GridAggregationFunction = { apply: ({ values }) => { let sum = 0; for (let i = 0; i < values.length; i += 1) { const value = values[i]; - if (value != null) { + if (isNumber(value)) { sum += value; } } @@ -17,14 +17,23 @@ const sumAgg: GridAggregationFunction = { columnTypes: ['number'], }; -const avgAgg: GridAggregationFunction = { - apply: (params) => { - if (params.values.length === 0) { +const avgAgg: GridAggregationFunction = { + apply: ({ values }) => { + if (values.length === 0) { return null; } - const sum = sumAgg.apply(params) as number; - return sum / params.values.length; + let sum = 0; + let valuesCount = 0; + for (let i = 0; i < values.length; i += 1) { + const value = values[i]; + if (isNumber(value)) { + valuesCount += 1; + sum += value; + } + } + + return sum / valuesCount; }, columnTypes: ['number'], }; @@ -67,9 +76,9 @@ const maxAgg: GridAggregationFunction = { columnTypes: ['number', 'date', 'dateTime'], }; -const sizeAgg: GridAggregationFunction = { +const sizeAgg: GridAggregationFunction = { apply: ({ values }) => { - return values.length; + return values.filter((value) => typeof value !== 'undefined').length; }, valueFormatter: (params: GridValueFormatterParams) => { if (params.value == null || !isNumber(params.value)) { diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/wrapColumnWithAggregation.tsx b/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/wrapColumnWithAggregation.tsx index 6ebb2d7531f1a..1d03afb918667 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/wrapColumnWithAggregation.tsx +++ b/packages/grid/x-data-grid-premium/src/hooks/features/aggregation/wrapColumnWithAggregation.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { GridColDef, GridFilterOperator, GridRowId } from '@mui/x-data-grid-pro'; -import { GridBaseColDef, tagInternalFilter } from '@mui/x-data-grid-pro/internals'; +import { GridBaseColDef } from '@mui/x-data-grid-pro/internals'; import { GridApiPremium } from '../../../models/gridApiPremium'; import { GridAggregationCellMeta, @@ -130,42 +130,26 @@ const getWrappedFilterOperators: ColumnPropertyWrapper<'filterOperators'> = ({ }) => filterOperators!.map((operator) => { const baseGetApplyFilterFn = operator.getApplyFilterFn; - const baseGetApplyFilterFnV7 = operator.getApplyFilterFnV7; - const getApplyFilterFn: GridFilterOperator['getApplyFilterFn'] = - tagInternalFilter((filterItem, colDef) => { - const filterFn = baseGetApplyFilterFn(filterItem, colDef); - if (!filterFn) { - return null; + const getApplyFilterFn: GridFilterOperator['getApplyFilterFn'] = ( + filterItem, + colDef, + ) => { + const filterFn = baseGetApplyFilterFn(filterItem, colDef); + if (!filterFn) { + return null; + } + return (value, row, column, api) => { + if (getCellAggregationResult(apiRef.current.getRowId(row), column.field) != null) { + return true; } - return (params) => { - if (getCellAggregationResult(params.id, params.field) != null) { - return true; - } - return filterFn(params); - }; - }); - - const getApplyFilterFnV7: GridFilterOperator['getApplyFilterFnV7'] = - baseGetApplyFilterFnV7 === undefined - ? undefined - : tagInternalFilter((filterItem, colDef) => { - const filterFn = baseGetApplyFilterFnV7(filterItem, colDef); - if (!filterFn) { - return null; - } - return (value, row, column, api) => { - if (getCellAggregationResult(apiRef.current.getRowId(row), column.field) != null) { - return true; - } - return filterFn(value, row, column, api); - }; - }); + return filterFn(value, row, column, api); + }; + }; return { ...operator, getApplyFilterFn, - getApplyFilterFnV7, } as GridFilterOperator; }); @@ -177,11 +161,13 @@ const getWrappedRenderHeader: ColumnPropertyWrapper<'renderHeader'> = ({ aggregationRule, }) => { const wrappedRenderHeader: GridBaseColDef['renderHeader'] = (params) => { - const aggregation = { aggregationRule }; - if (renderHeader) { - return renderHeader({ ...params, aggregation }); - } - return ; + return ( + + ); }; return wrappedRenderHeader; diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts b/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts index ac622547f18e4..0419d88240657 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/cellSelection/useGridCellSelection.ts @@ -486,7 +486,7 @@ export const useGridCellSelection = ( newClasses.push(gridClasses['cell--rangeTop']); } - if (rowIndex < visibleRows.range.lastRowIndex) { + if (rowIndex + visibleRows.range.firstRowIndex < visibleRows.range.lastRowIndex) { const { id: nextRowId } = visibleRows.rows[rowIndex + 1]; if (!apiRef.current.unstable_isCellSelected(nextRowId, field)) { newClasses.push(gridClasses['cell--rangeBottom']); diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts b/packages/grid/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts index c4dc4ac80dfdf..2c36a29fc294b 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/clipboard/useGridClipboardImport.ts @@ -19,6 +19,7 @@ import { getActiveElement, GridPipeProcessor, useGridRegisterPipeProcessor, + getPublicApiRef, } 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'; @@ -379,7 +380,7 @@ export const useGridClipboardImport = ( defaultPasteResolver({ pastedData, - apiRef: { current: apiRef.current.getPublicApi() }, + apiRef: getPublicApiRef(apiRef), updateCell: (...args) => { cellUpdater.updateCell(...args); }, diff --git a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts index 1979146222f9c..64ca76ee5e549 100644 --- a/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts +++ b/packages/grid/x-data-grid-premium/src/hooks/features/rowGrouping/gridRowGroupingUtils.ts @@ -15,7 +15,6 @@ import { GridAggregatedFilterItemApplier, GridAggregatedFilterItemApplierResult, GridColumnRawLookup, - GridApiCommunity, } from '@mui/x-data-grid-pro/internals'; import { DataGridPremiumProcessedProps } from '../../../models/dataGridPremiumProps'; import { GridGroupingValueGetterParams } from '../../../models/gridGroupingValueGetterParams'; @@ -58,7 +57,7 @@ interface FilterRowTreeFromTreeDataParams { rowTree: GridRowTreeConfig; isRowMatchingFilters: GridAggregatedFilterItemApplier | null; filterModel: GridFilterModel; - apiRef: React.MutableRefObject; + apiRef: React.MutableRefObject; } /** diff --git a/packages/grid/x-data-grid-premium/src/material/index.ts b/packages/grid/x-data-grid-premium/src/material/index.ts index 26572aeaeb74d..cf4beb20825be 100644 --- a/packages/grid/x-data-grid-premium/src/material/index.ts +++ b/packages/grid/x-data-grid-premium/src/material/index.ts @@ -2,9 +2,9 @@ import type { GridPremiumIconSlotsComponent } from '../models'; import { GridWorkspacesIcon, GridGroupWorkIcon, GridFunctionsIcon } from './icons'; const iconsSlots: GridPremiumIconSlotsComponent = { - ColumnMenuUngroupIcon: GridWorkspacesIcon, - ColumnMenuGroupIcon: GridGroupWorkIcon, - ColumnMenuAggregationIcon: GridFunctionsIcon, + columnMenuUngroupIcon: GridWorkspacesIcon, + columnMenuGroupIcon: GridGroupWorkIcon, + columnMenuAggregationIcon: GridFunctionsIcon, }; const materialSlots = { diff --git a/packages/grid/x-data-grid-premium/src/models/dataGridPremiumProps.ts b/packages/grid/x-data-grid-premium/src/models/dataGridPremiumProps.ts index ffc20b0beb4a4..adab370ae5703 100644 --- a/packages/grid/x-data-grid-premium/src/models/dataGridPremiumProps.ts +++ b/packages/grid/x-data-grid-premium/src/models/dataGridPremiumProps.ts @@ -18,10 +18,7 @@ import type { GridAggregationFunction, GridAggregationPosition, } from '../hooks/features/aggregation'; -import { - GridPremiumSlotsComponent, - UncapitalizedGridPremiumSlotsComponent, -} from './gridPremiumSlotsComponent'; +import { GridPremiumSlotsComponent } from './gridPremiumSlotsComponent'; import { GridInitialStatePremium } from './gridStatePremium'; import { GridApiPremium } from './gridApiPremium'; import { GridCellSelectionModel } from '../hooks/features/cellSelection'; @@ -35,15 +32,10 @@ export interface GridExperimentalPremiumFeatures extends GridExperimentalProFeat export interface DataGridPremiumPropsWithComplexDefaultValueBeforeProcessing extends Pick { - /** - * Overridable components. - * @deprecated Use the `slots` prop instead. - */ - components?: Partial; /** * Overridable components. */ - slots?: Partial; + slots?: Partial; } /** @@ -59,7 +51,7 @@ export interface DataGridPremiumProps export interface DataGridPremiumPropsWithComplexDefaultValueAfterProcessing extends Pick { - slots: UncapitalizedGridPremiumSlotsComponent; + slots: GridPremiumSlotsComponent; } /** @@ -68,7 +60,7 @@ export interface DataGridPremiumPropsWithComplexDefaultValueAfterProcessing export interface DataGridPremiumProcessedProps extends DataGridPremiumPropsWithDefaultValue, DataGridPremiumPropsWithComplexDefaultValueAfterProcessing, - Omit {} + DataGridPremiumPropsWithoutDefaultValue {} export type DataGridPremiumForcedPropsKey = 'signature'; diff --git a/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts b/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts index 3577b6ca9c3bd..e79561d733b99 100644 --- a/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts +++ b/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts @@ -2,6 +2,7 @@ import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals'; import { GridApiCommon, GridColumnPinningApi, + GridColumnResizeApi, GridDetailPanelApi, GridDetailPanelPrivateApi, GridRowPinningApi, @@ -12,6 +13,7 @@ import { import { GridInitialStatePremium, GridStatePremium } from './gridStatePremium'; import type { GridRowGroupingApi, GridExcelExportApi, GridAggregationApi } from '../hooks'; import { GridCellSelectionApi } from '../hooks/features/cellSelection/gridCellSelectionInterfaces'; +import type { DataGridPremiumProcessedProps } from './dataGridPremiumProps'; /** * The api of `DataGridPremium`. @@ -21,6 +23,7 @@ export interface GridApiPremium extends GridApiCommon, GridRowProApi, GridColumnPinningApi, + GridColumnResizeApi, GridDetailPanelApi, GridRowGroupingApi, GridExcelExportApi, @@ -29,10 +32,9 @@ export interface GridApiPremium GridCellSelectionApi, // APIs that are private in Community plan, but public in Pro and Premium plans GridRowMultiSelectionApi, - GridColumnReorderApi, - GridRowProApi {} + GridColumnReorderApi {} export interface GridPrivateApiPremium extends GridApiPremium, - GridPrivateOnlyApiCommon, + GridPrivateOnlyApiCommon, GridDetailPanelPrivateApi {} diff --git a/packages/grid/x-data-grid-premium/src/models/gridPremiumIconSlotsComponent.ts b/packages/grid/x-data-grid-premium/src/models/gridPremiumIconSlotsComponent.ts index f09b91fbc5590..3730901b0d8a6 100644 --- a/packages/grid/x-data-grid-premium/src/models/gridPremiumIconSlotsComponent.ts +++ b/packages/grid/x-data-grid-premium/src/models/gridPremiumIconSlotsComponent.ts @@ -5,15 +5,15 @@ export interface GridPremiumIconSlotsComponent { * Icon displayed in column menu for ungrouping * @default GridWorkspacesIcon */ - ColumnMenuUngroupIcon: React.JSXElementConstructor; + columnMenuUngroupIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for grouping * @default GridGroupWorkIcon */ - ColumnMenuGroupIcon: React.JSXElementConstructor; + columnMenuGroupIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for aggregation * @default GridFunctionsIcon */ - ColumnMenuAggregationIcon: React.JSXElementConstructor; + columnMenuAggregationIcon: React.JSXElementConstructor; } diff --git a/packages/grid/x-data-grid-premium/src/models/gridPremiumSlotsComponent.ts b/packages/grid/x-data-grid-premium/src/models/gridPremiumSlotsComponent.ts index 79a039f7c61f7..b9e307b89652d 100644 --- a/packages/grid/x-data-grid-premium/src/models/gridPremiumSlotsComponent.ts +++ b/packages/grid/x-data-grid-premium/src/models/gridPremiumSlotsComponent.ts @@ -1,5 +1,4 @@ -import { GridProSlotsComponent, UncapitalizedGridProSlotsComponent } from '@mui/x-data-grid-pro'; -import { UncapitalizeObjectKeys } from '@mui/x-data-grid-pro/internals'; +import { GridProSlotsComponent } from '@mui/x-data-grid-pro'; import { GridPremiumIconSlotsComponent } from './gridPremiumIconSlotsComponent'; /** @@ -9,8 +8,3 @@ import { GridPremiumIconSlotsComponent } from './gridPremiumIconSlotsComponent'; export interface GridPremiumSlotsComponent extends GridProSlotsComponent, GridPremiumIconSlotsComponent {} - -// TODO: remove in v7 -export interface UncapitalizedGridPremiumSlotsComponent - extends UncapitalizedGridProSlotsComponent, - UncapitalizeObjectKeys {} diff --git a/packages/grid/x-data-grid-premium/src/tests/DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/DataGridPremium.test.tsx index 756e035038869..d6c0fa7588751 100644 --- a/packages/grid/x-data-grid-premium/src/tests/DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/DataGridPremium.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, act } from '@mui/monorepo/test/utils'; +import { createRenderer, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGridPremium as DataGrid, @@ -12,7 +12,7 @@ import { getColumnValues } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Quick Filter', () => { +describe(' - Quick filter', () => { const { render } = createRenderer(); const baselineProps = { diff --git a/packages/grid/x-data-grid-premium/src/tests/aggregation.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/aggregation.DataGridPremium.test.tsx index 3c64e8514fb3a..4023b59f4cb1b 100644 --- a/packages/grid/x-data-grid-premium/src/tests/aggregation.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/aggregation.DataGridPremium.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, screen, userEvent, within, act } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, userEvent, within, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn'; import { SinonSpy, spy } from 'sinon'; @@ -275,7 +275,7 @@ describe(' - Aggregation', () => { }); }); - describe('Tree Data', () => { + describe('Tree data', () => { function TreeDataTest(props: Omit) { return ( - Aggregation', () => { }); }); - describe('Column Menu', () => { + describe('Column menu', () => { it('should render select on aggregable column', () => { render(); @@ -765,4 +765,72 @@ describe(' - Aggregation', () => { ]); }); }); + + describe('built-in aggregation functions', () => { + describe('`sum`', () => { + it('should work with numbers', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.sum.apply({ + values: [0, 10, 12, 23], + field: 'value', + groupId: 0, + }), + ).to.equal(45); + }); + + it('should ignore non-numbers', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.sum.apply({ + values: [0, 10, 12, 23, 'a', '', undefined, null, NaN, {}, true], + field: 'value', + groupId: 0, + }), + ).to.equal(45); + }); + }); + + describe('`avg`', () => { + it('should work with numbers', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.avg.apply({ + values: [0, 10, 12, 23], + field: 'value', + groupId: 0, + }), + ).to.equal(11.25); + }); + + it('should ignore non-numbers', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.avg.apply({ + values: [0, 10, 12, 23, 'a', '', undefined, null, NaN, {}, true], + field: 'value', + groupId: 0, + }), + ).to.equal(11.25); + }); + }); + + describe('`size`', () => { + it('should work with any value types', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.size.apply({ + values: [23, '', 'a', NaN, {}, false, true], + field: 'value', + groupId: 0, + }), + ).to.equal(7); + }); + + it('should ignore undefined values', () => { + expect( + GRID_AGGREGATION_FUNCTIONS.size.apply({ + values: [23, '', 'a', NaN, {}, false, true, undefined], + field: 'value', + groupId: 0, + }), + ).to.equal(7); + }); + }); + }); }); diff --git a/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx index d7d05dbbacd23..2a17b8ed035c9 100644 --- a/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/cellSelection.DataGridPremium.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { stub, SinonStub } from 'sinon'; import { expect } from 'chai'; import { spyApi, getCell } from 'test/utils/helperFn'; -import { createRenderer, fireEvent, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent, screen } from '@mui-internal/test-utils'; import { DataGridPremium, DataGridPremiumProps, @@ -36,12 +36,12 @@ describe(' - Cell selection', () => {
                ); @@ -66,6 +66,26 @@ describe(' - Cell selection', () => { expect(cell11).to.have.class('Mui-selected'); }); + // https://github.com/mui/mui-x/issues/10777 + it('should work with the paginated grid', () => { + render( + , + ); + const cell01 = getCell(2, 0); + fireEvent.click(cell01); + expect(cell01).to.have.class('Mui-selected'); + fireEvent.click(screen.getByRole('button', { name: /next page/i })); + const cell02 = getCell(5, 0); + fireEvent.click(cell02); + expect(cell02).to.have.class('Mui-selected'); + }); + describe('Ctrl + click', () => { it('should add the clicked cells to the selection', () => { render(); diff --git a/packages/grid/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx index 70c9dbfaf8369..97eb968aee159 100644 --- a/packages/grid/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/clipboard.DataGridPremium.test.tsx @@ -7,7 +7,7 @@ import { GridColDef, } from '@mui/x-data-grid-premium'; // @ts-ignore Remove once the test utils are typed -import { createRenderer, fireEvent, userEvent, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, userEvent, waitFor } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { stub, SinonStub, spy } from 'sinon'; import { getCell, getColumnValues, sleep } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid-premium/src/tests/columns.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/columns.DataGridPremium.test.tsx index eb32d82a5e199..14267f4ac7dca 100644 --- a/packages/grid/x-data-grid-premium/src/tests/columns.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/columns.DataGridPremium.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { act, createRenderer, fireEvent } from '@mui/monorepo/test/utils'; +import { act, createRenderer, fireEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGridPremium, gridClasses } from '@mui/x-data-grid-premium'; import { getCell, getColumnHeaderCell } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid-premium/src/tests/exportExcel.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/exportExcel.DataGridPremium.test.tsx index 404c90c237498..2d9510dd59a33 100644 --- a/packages/grid/x-data-grid-premium/src/tests/exportExcel.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/exportExcel.DataGridPremium.test.tsx @@ -8,7 +8,7 @@ import { DataGridPremiumProps, GridActionsCellItem, } from '@mui/x-data-grid-premium'; -import { createRenderer, screen, fireEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, fireEvent, act } from '@mui-internal/test-utils'; import { spy, SinonSpy } from 'sinon'; import { expect } from 'chai'; import Excel from 'exceljs'; @@ -58,7 +58,7 @@ describe(' - Export Excel', () => { }); it('should display export option', () => { - render(); + render(); fireEvent.click(screen.getByRole('button', { name: 'Export' })); expect(screen.queryByRole('menu')).not.to.equal(null); diff --git a/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx index d99d0d1ee93ff..96495a5572c9d 100644 --- a/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/license.DataGridPremium.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import addYears from 'date-fns/addYears'; import { expect } from 'chai'; -import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, waitFor } from '@mui-internal/test-utils'; import { DataGridPremium } from '@mui/x-data-grid-premium'; import { generateLicense, LicenseInfo } from '@mui/x-license-pro'; diff --git a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx index 2a03b49efffe2..60fb45c1802ed 100644 --- a/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/rowGrouping.DataGridPremium.test.tsx @@ -6,7 +6,7 @@ import { act, userEvent, waitFor, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { microtasks, getColumnHeaderCell, @@ -69,7 +69,7 @@ const baselineProps: DataGridPremiumProps = { ], }; -describe(' - Row Grouping', () => { +describe(' - Row grouping', () => { const { render, clock } = createRenderer(); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-premium/src/tests/rowPinning.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/rowPinning.DataGridPremium.test.tsx index acca8e87e40d3..e0a103cbde2b7 100644 --- a/packages/grid/x-data-grid-premium/src/tests/rowPinning.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/rowPinning.DataGridPremium.test.tsx @@ -1,4 +1,4 @@ -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer } from '@mui-internal/test-utils'; import * as React from 'react'; import { expect } from 'chai'; import { diff --git a/packages/grid/x-data-grid-premium/src/tests/statePersistence.DataGridPremium.test.tsx b/packages/grid/x-data-grid-premium/src/tests/statePersistence.DataGridPremium.test.tsx index 7567a7a2c35ae..a8a9a8411d1e0 100644 --- a/packages/grid/x-data-grid-premium/src/tests/statePersistence.DataGridPremium.test.tsx +++ b/packages/grid/x-data-grid-premium/src/tests/statePersistence.DataGridPremium.test.tsx @@ -8,7 +8,7 @@ import { GridRowsProp, useGridApiRef, } from '@mui/x-data-grid-premium'; -import { createRenderer, act } from '@mui/monorepo/test/utils'; +import { createRenderer, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { getColumnValues } from 'test/utils/helperFn'; @@ -47,7 +47,7 @@ const FULL_INITIAL_STATE: GridInitialState = { }, }; -describe(' - State Persistence', () => { +describe(' - State persistence', () => { const { render } = createRenderer(); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-premium/tsconfig.json b/packages/grid/x-data-grid-premium/tsconfig.json index 3d20b7d6a4bb6..79824ab245468 100644 --- a/packages/grid/x-data-grid-premium/tsconfig.json +++ b/packages/grid/x-data-grid-premium/tsconfig.json @@ -6,7 +6,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../../node_modules/@mui/material/themeCssVarsAugmentation" ] } diff --git a/packages/grid/x-data-grid-pro/package.json b/packages/grid/x-data-grid-pro/package.json index 14e5dd5a1e34a..abedce9de2c20 100644 --- a/packages/grid/x-data-grid-pro/package.json +++ b/packages/grid/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "6.14.0", + "version": "6.18.0", "description": "The Pro plan edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -42,11 +42,11 @@ "directory": "packages/grid/x-data-grid-pro" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/utils": "^5.14.8", - "@mui/x-data-grid": "6.14.0", + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16", + "@mui/x-data-grid": "6.18.0", "@mui/x-license-pro": "6.10.2", - "@types/format-util": "^1.0.2", + "@types/format-util": "^1.0.3", "clsx": "^2.0.0", "prop-types": "^15.8.1", "reselect": "^4.1.8" diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 0231d8bb4d41c..39e2e4fdab1ef 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -59,6 +59,13 @@ interface DataGridProComponent { propTypes?: any; } +/** + * Demos: + * - [DataGridPro](https://mui.com/x/react-data-grid/demo/) + * + * API: + * - [DataGridPro API](https://mui.com/x/api/data-grid/data-grid-pro/) + */ export const DataGridPro = React.memo(DataGridProRaw) as DataGridProComponent; DataGridProRaw.propTypes = { @@ -90,6 +97,21 @@ DataGridProRaw.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. */ @@ -146,16 +168,6 @@ DataGridProRaw.propTypes = { * If defined, the grid will ignore the `hide` property in [[GridColDef]]. */ columnVisibilityModel: PropTypes.object, - /** - * Overridable components. - * @deprecated Use the `slots` prop instead. - */ - components: PropTypes.object, - /** - * Overridable components props dynamically passed to the component at rendering. - * @deprecated Use the `slotProps` prop instead. - */ - componentsProps: PropTypes.object, /** * If above 0, the row children will be expanded up to this depth. * If equal to -1, all the row children will be expanded. @@ -173,6 +185,11 @@ DataGridProRaw.propTypes = { detailPanelExpandedRowIds: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, ), + /** + * If `true`, column autosizing on header separator double-click is disabled. + * @default false + */ + disableAutosize: PropTypes.bool, /** * If `true`, the filtering will only be applied to the top level rows when grouping rows with the `treeData` prop. * @default false @@ -394,6 +411,12 @@ DataGridProRaw.propTypes = { * @default false */ hideFooterSelectedRowCount: PropTypes.bool, + /** + * If `true`, the diacritics (accents) are ignored when filtering or quick filtering. + * E.g. when filter value is `cafe`, the rows with `café` will be visible. + * @default false + */ + ignoreDiacritics: PropTypes.bool, /** * The initial state of the DataGridPro. * The data in it will be set in the state on initialization but will not be controlled. @@ -790,6 +813,13 @@ DataGridProRaw.propTypes = { * Controls the modes of the rows. */ rowModesModel: PropTypes.object, + /** + * The milliseconds delay to wait after measuring the row height before recalculating row positions. + * Setting it to a lower value could be useful when using dynamic row height, + * but might reduce performance when displaying a large number of rows. + * @default 166 + */ + rowPositionsDebounceMs: PropTypes.number, /** * If `true`, the reordering of rows is enabled. * @default false @@ -904,7 +934,7 @@ DataGridProRaw.propTypes = { /** * If `true`, the grid will not use `valueFormatter` when exporting to CSV or copying to clipboard. * If an object is provided, you can choose to ignore the `valueFormatter` for CSV export or clipboard export. - * @default: false + * @default false */ unstable_ignoreValueFormatterDuringExport: PropTypes.oneOfType([ PropTypes.shape({ diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index e9077e1996b1d..23220818d45aa 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -42,6 +42,8 @@ import { columnGroupsStateInitializer, headerFilteringStateInitializer, useGridHeaderFiltering, + virtualizationStateInitializer, + useGridVirtualization, } from '@mui/x-data-grid/internals'; import { GridApiPro, GridPrivateApiPro } from '../models/gridApiPro'; import { DataGridProProcessedProps } from '../models/dataGridProProps'; @@ -119,6 +121,7 @@ export const useDataGridProComponent = ( useGridInitializeState(rowsMetaStateInitializer, apiRef, props); useGridInitializeState(columnMenuStateInitializer, apiRef, props); useGridInitializeState(columnGroupsStateInitializer, apiRef, props); + useGridInitializeState(virtualizationStateInitializer, apiRef, props); useGridHeaderFiltering(apiRef, props); useGridTreeData(apiRef); @@ -153,6 +156,7 @@ export const useDataGridProComponent = ( useGridDimensions(apiRef, props); useGridEvents(apiRef, props); useGridStatePersistence(apiRef); + useGridVirtualization(apiRef, props); return apiRef; }; diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts index 9c991e4f24d8d..37fef66bce595 100644 --- a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts +++ b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts @@ -5,13 +5,13 @@ import { DATA_GRID_PROPS_DEFAULT_VALUES, GridValidRowModel, } from '@mui/x-data-grid'; -import { computeSlots, uncapitalizeObjectKeys, useProps } from '@mui/x-data-grid/internals'; +import { computeSlots, useProps } from '@mui/x-data-grid/internals'; import { DataGridProProps, DataGridProProcessedProps, DataGridProPropsWithDefaultValue, } from '../models/dataGridProProps'; -import { GridProSlotsComponent, UncapitalizedGridProSlotsComponent } from '../models'; +import { GridProSlotsComponent } from '../models'; import { DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS } from '../constants/dataGridProDefaultSlotsComponents'; /** @@ -22,6 +22,8 @@ export const DATA_GRID_PRO_PROPS_DEFAULT_VALUES: DataGridProPropsWithDefaultValu scrollEndThreshold: 80, treeData: false, defaultGroupingExpansionDepth: 0, + autosizeOnMount: false, + disableAutosize: false, disableColumnPinning: false, keepColumnPositionIfDraggedOutside: false, disableChildrenFiltering: false, @@ -32,10 +34,10 @@ export const DATA_GRID_PRO_PROPS_DEFAULT_VALUES: DataGridProPropsWithDefaultValu unstable_headerFilters: false, }; -const defaultSlots = uncapitalizeObjectKeys(DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS)!; +const defaultSlots = DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS; export const useDataGridProProps = (inProps: DataGridProProps) => { - const [components, componentsProps, themedProps] = useProps( + const themedProps = useProps( useThemeProps({ props: inProps, name: 'MuiDataGrid', @@ -47,14 +49,13 @@ export const useDataGridProProps = (inProps: DataGr [themedProps.localeText], ); - const slots = React.useMemo( + const slots = React.useMemo( () => computeSlots({ defaultSlots, slots: themedProps.slots, - components, }), - [components, themedProps.slots], + [themedProps.slots], ); return React.useMemo>( @@ -63,9 +64,8 @@ export const useDataGridProProps = (inProps: DataGr ...themedProps, localeText, slots, - slotProps: themedProps.slotProps ?? componentsProps, signature: 'DataGridPro', }), - [themedProps, localeText, slots, componentsProps], + [themedProps, localeText, slots], ); }; diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx index d3e9a234eca39..d49930dc7b1e4 100644 --- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx +++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterCell.tsx @@ -311,7 +311,6 @@ GridHeaderFilterCell.propTypes = { filterOperators: PropTypes.arrayOf( PropTypes.shape({ getApplyFilterFn: PropTypes.func.isRequired, - getApplyFilterFnV7: PropTypes.func, getValueAsString: PropTypes.func, headerLabel: PropTypes.string, InputComponent: PropTypes.elementType, diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenu.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenu.tsx index ccec6d54e1b24..409d1efa66012 100644 --- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenu.tsx +++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenu.tsx @@ -55,13 +55,7 @@ function GridHeaderFilterMenu({ } return ( - + {operators.map((op, i) => { const label = @@ -108,7 +102,6 @@ GridHeaderFilterMenu.propTypes = { operators: PropTypes.arrayOf( PropTypes.shape({ getApplyFilterFn: PropTypes.func.isRequired, - getApplyFilterFnV7: PropTypes.func, getValueAsString: PropTypes.func, headerLabel: PropTypes.string, InputComponent: PropTypes.elementType, diff --git a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenuContainer.tsx b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenuContainer.tsx index e5be2897031bc..4ce23b8f79b6f 100644 --- a/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenuContainer.tsx +++ b/packages/grid/x-data-grid-pro/src/components/headerFiltering/GridHeaderFilterMenuContainer.tsx @@ -96,7 +96,6 @@ GridHeaderFilterMenuContainer.propTypes = { operators: PropTypes.arrayOf( PropTypes.shape({ getApplyFilterFn: PropTypes.func.isRequired, - getApplyFilterFnV7: PropTypes.func, getValueAsString: PropTypes.func, headerLabel: PropTypes.string, InputComponent: PropTypes.elementType, diff --git a/packages/grid/x-data-grid-pro/src/constants/dataGridProDefaultSlotsComponents.ts b/packages/grid/x-data-grid-pro/src/constants/dataGridProDefaultSlotsComponents.ts index 9e098b64ec828..bcca11e477a6b 100644 --- a/packages/grid/x-data-grid-pro/src/constants/dataGridProDefaultSlotsComponents.ts +++ b/packages/grid/x-data-grid-pro/src/constants/dataGridProDefaultSlotsComponents.ts @@ -9,8 +9,8 @@ import materialSlots from '../material'; export const DATA_GRID_PRO_DEFAULT_SLOTS_COMPONENTS: GridProSlotsComponent = { ...DATA_GRID_DEFAULT_SLOTS_COMPONENTS, ...materialSlots, - ColumnMenu: GridProColumnMenu, - ColumnHeaders: GridColumnHeaders, - HeaderFilterCell: GridHeaderFilterCell, - HeaderFilterMenu: GridHeaderFilterMenu, + columnMenu: GridProColumnMenu, + columnHeaders: GridColumnHeaders, + headerFilterCell: GridHeaderFilterCell, + headerFilterMenu: GridHeaderFilterMenu, }; diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx b/packages/grid/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx index 47fd3d037b3cd..d580b99ec7d0c 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx +++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnPinning/useGridColumnPinning.tsx @@ -6,7 +6,6 @@ import { gridColumnsTotalWidthSelector, gridColumnPositionsSelector, gridVisibleColumnFieldsSelector, - gridClasses, useGridApiMethod, useGridApiEventHandler, GridEventListener, @@ -71,69 +70,6 @@ export const useGridColumnPinning = ( ): void => { const pinnedColumns = useGridSelector(apiRef, gridPinnedColumnsSelector); const theme = useTheme(); - // Each visible row (not to be confused with a filter result) is composed of a central .MuiDataGrid-row element - // and up to two additional .MuiDataGrid-row's, one for the columns pinned to the left and another - // for those on the right side. When hovering any of these elements, the :hover styles are applied only to - // the row element that was actually hovered, not its additional siblings. To make it look like a contiguous row, - // this method adds/removes the .Mui-hovered class to all of the row elements inside one visible row. - const updateHoveredClassOnSiblingRows = React.useCallback( - (event: React.MouseEvent) => { - if (props.disableColumnPinning) { - return; - } - - if (!Array.isArray(pinnedColumns.left) && !Array.isArray(pinnedColumns.right)) { - return; - } - - const nbLeftPinnedColumns = pinnedColumns.left?.length ?? 0; - const nbRightPinnedColumns = pinnedColumns.right?.length ?? 0; - if (nbLeftPinnedColumns + nbRightPinnedColumns === 0) { - return; - } - - const rowContainer = apiRef.current.virtualScrollerRef!.current!; - if (!rowContainer) { - return; - } - - const index = event.currentTarget.dataset.rowindex; - const rowElements = rowContainer.querySelectorAll( - `.${gridClasses.row}[data-rowindex="${index}"]`, - ); - rowElements.forEach((row) => { - // Ignore rows from other grid inside the hovered row - if ( - row.closest(`.${gridClasses.virtualScroller}`) === - apiRef.current.virtualScrollerRef!.current! - ) { - if (event.type === 'mouseenter') { - row.classList.add('Mui-hovered'); - } else { - row.classList.remove('Mui-hovered'); - } - } - }); - }, - [apiRef, pinnedColumns.left, pinnedColumns.right, props.disableColumnPinning], - ); - - const handleMouseEnter = React.useCallback>( - (params, event) => { - updateHoveredClassOnSiblingRows(event); - }, - [updateHoveredClassOnSiblingRows], - ); - - const handleMouseLeave = React.useCallback>( - (params, event) => { - updateHoveredClassOnSiblingRows(event); - }, - [updateHoveredClassOnSiblingRows], - ); - - useGridApiEventHandler(apiRef, 'rowMouseEnter', handleMouseEnter); - useGridApiEventHandler(apiRef, 'rowMouseLeave', handleMouseLeave); /** * PRE-PROCESSING diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts new file mode 100644 index 0000000000000..c19883f4368aa --- /dev/null +++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts @@ -0,0 +1,47 @@ +import { GridColDef } from '@mui/x-data-grid'; + +export const DEFAULT_GRID_AUTOSIZE_OPTIONS = { + includeHeaders: true, + includeOutliers: false, + outliersFactor: 1.5, + expand: false, +}; + +export type GridAutosizeOptions = { + /** + * The columns to autosize. By default, applies to all columns. + */ + columns?: GridColDef['field'][]; + /** + * If true, include the header widths in the calculation. + * @default false + */ + includeHeaders?: boolean; + /** + * If true, width outliers will be ignored. + * @default false + */ + includeOutliers?: boolean; + /** + * The IQR factor range to detect outliers. + * @default 1.5 + */ + outliersFactor?: number; + /** + * If the total width is less than the available width, expand columns to fill it. + * @default false + */ + expand?: boolean; +}; + +/** + * The Resize API interface that is available in the grid `apiRef`. + */ +export interface GridColumnResizeApi { + /** + * Auto-size the columns of the grid based on the cells' content and the space available. + * @param {GridAutosizeOptions} options The autosizing options + * @returns {Promise} A promise that resolves when autosizing is completed + */ + autosizeColumns: (options?: GridAutosizeOptions) => Promise; +} diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts index f4105375e68ff..beb30a48691a6 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts @@ -1,2 +1,3 @@ export * from './columnResizeSelector'; export * from './columnResizeState'; +export * from './gridColumnResizeApi'; diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx index 93161721c049d..eed1b3bb4b8ae 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx +++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx @@ -11,14 +11,22 @@ import { GridColumnResizeParams, useGridApiEventHandler, useGridApiOptionHandler, + useGridApiMethod, useGridNativeEventListener, useGridLogger, + useGridSelector, + gridVirtualizationColumnEnabledSelector, } from '@mui/x-data-grid'; import { clamp, findParentElementFromClassName, - GridStateInitializer, + gridColumnsStateSelector, + useOnMount, + useTimeout, + createControllablePromise, + ControllablePromise, GridStateColDef, + GridStateInitializer, } from '@mui/x-data-grid/internals'; import { useTheme, Direction } from '@mui/material/styles'; import { @@ -26,9 +34,18 @@ import { getFieldFromHeaderElem, findHeaderElementFromField, findGroupHeaderElementsFromField, + findGridHeader, + findGridCells, } from '../../../utils/domUtils'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; +import { + GridAutosizeOptions, + GridColumnResizeApi, + DEFAULT_GRID_AUTOSIZE_OPTIONS, +} from './gridColumnResizeApi'; + +type AutosizeOptionsRequired = Required; type ResizeDirection = keyof typeof GridColumnHeaderSeparatorSides; @@ -119,18 +136,145 @@ function getResizeDirection(element: HTMLElement, direction: Direction) { return side; } +function preventClick(event: MouseEvent) { + event.preventDefault(); + event.stopImmediatePropagation(); +} + +/** + * Checker that returns a promise that resolves when the column virtualization + * is disabled. + */ +function useColumnVirtualizationDisabled(apiRef: React.MutableRefObject) { + const promise = React.useRef(); + const selector = () => gridVirtualizationColumnEnabledSelector(apiRef); + const value = useGridSelector(apiRef, selector); + + React.useEffect(() => { + if (promise.current && value === false) { + promise.current.resolve(); + promise.current = undefined; + } + }); + + const asyncCheck = () => { + if (!promise.current) { + if (selector() === false) { + return Promise.resolve(); + } + promise.current = createControllablePromise(); + } + return promise.current; + }; + + return asyncCheck; +} + +/** + * Basic statistical outlier detection, checks if the value is `F * IQR` away from + * the Q1 and Q3 boundaries. IQR: interquartile range. + */ +function excludeOutliers(inputValues: number[], factor: number) { + if (inputValues.length < 4) { + return inputValues; + } + + const values = inputValues.slice(); + values.sort((a, b) => a - b); + + const q1 = values[Math.floor(values.length * 0.25)]; + const q3 = values[Math.floor(values.length * 0.75) - 1]; + 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. + // 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. + const deviation = iqr < 5 ? 5 : iqr * factor; + + return values.filter((v) => v > q1 - deviation && v < q3 + deviation); +} + +function extractColumnWidths( + apiRef: React.MutableRefObject, + options: AutosizeOptionsRequired, + columns: GridStateColDef[], +) { + const widthByField = {} as Record; + + const root = apiRef.current.rootElementRef!.current!; + root.classList.add(gridClasses.autosizing); + + columns.forEach((column) => { + const cells = findGridCells(apiRef.current, column.field); + + const widths = cells.map((cell) => { + const style = window.getComputedStyle(cell, null); + const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10); + const contentWidth = cell.firstElementChild?.getBoundingClientRect().width ?? 0; + return paddingWidth + contentWidth; + }); + + const filteredWidths = options.includeOutliers + ? widths + : excludeOutliers(widths, options.outliersFactor); + + if (options.includeHeaders) { + const header = findGridHeader(apiRef.current, column.field); + if (header) { + const title = header.querySelector(`.${gridClasses.columnHeaderTitle}`); + const content = header.querySelector(`.${gridClasses.columnHeaderTitleContainerContent}`)!; + const iconContainer = header.querySelector(`.${gridClasses.iconButtonContainer}`); + const menuContainer = header.querySelector(`.${gridClasses.menuIcon}`); + const element = title ?? content; + + const style = window.getComputedStyle(header, null); + const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10); + const contentWidth = element.scrollWidth + 1; + const width = + contentWidth + + paddingWidth + + (iconContainer?.clientWidth ?? 0) + + (menuContainer?.clientWidth ?? 0); + + filteredWidths.push(width); + } + } + + const hasColumnMin = column.minWidth !== -Infinity && column.minWidth !== undefined; + const hasColumnMax = column.maxWidth !== Infinity && column.maxWidth !== undefined; + + const min = hasColumnMin ? column.minWidth! : 0; + const max = hasColumnMax ? column.maxWidth! : Infinity; + const maxContent = filteredWidths.length === 0 ? 0 : Math.max(...filteredWidths); + + widthByField[column.field] = clamp(maxContent, min, max); + }); + + root.classList.remove(gridClasses.autosizing); + + return widthByField; +} + export const columnResizeStateInitializer: GridStateInitializer = (state) => ({ ...state, columnResize: { resizingColumnField: '' }, }); - /** * @requires useGridColumns (method, event) * TODO: improve experience for last column */ export const useGridColumnResize = ( apiRef: React.MutableRefObject, - props: Pick, + props: Pick< + DataGridProProcessedProps, + | 'autosizeOptions' + | 'autosizeOnMount' + | 'disableAutosize' + | 'onColumnResize' + | 'onColumnWidthChange' + >, ) => { const logger = useGridLogger(apiRef, 'useGridColumnResize'); @@ -147,7 +291,7 @@ export const useGridColumnResize = ( const initialOffsetToSeparator = React.useRef(); const resizeDirection = React.useRef(); - const stopResizeEventTimeout = React.useRef(); + const stopResizeEventTimeout = useTimeout(); const touchId = React.useRef(); const updateWidth = (newWidth: number) => { @@ -201,8 +345,7 @@ export const useGridColumnResize = ( ); } - clearTimeout(stopResizeEventTimeout.current); - stopResizeEventTimeout.current = setTimeout(() => { + stopResizeEventTimeout.start(0, () => { apiRef.current.publishEvent('columnResizeStop', null, nativeEvent); }); }; @@ -234,66 +377,6 @@ export const useGridColumnResize = ( apiRef.current.publishEvent('columnResize', params, nativeEvent); }); - const handleColumnResizeMouseDown: GridEventListener<'columnSeparatorMouseDown'> = - useEventCallback(({ colDef }, event) => { - // Only handle left clicks - if (event.button !== 0) { - return; - } - - // Skip if the column isn't resizable - if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) { - return; - } - - // Avoid text selection - event.preventDefault(); - - logger.debug(`Start Resize on col ${colDef.field}`); - apiRef.current.publishEvent('columnResizeStart', { field: colDef.field }, event); - - colDefRef.current = colDef as GridStateColDef; - colElementRef.current = - apiRef.current.columnHeadersContainerElementRef?.current!.querySelector( - `[data-field="${colDef.field}"]`, - )!; - - const headerFilterRowElement = apiRef.current.headerFiltersElementRef?.current; - - if (headerFilterRowElement) { - headerFilterElementRef.current = headerFilterRowElement.querySelector( - `[data-field="${colDef.field}"]`, - ) as HTMLDivElement; - } - - colGroupingElementRef.current = findGroupHeaderElementsFromField( - apiRef.current.columnHeadersContainerElementRef?.current!, - colDef.field, - ); - - colCellElementsRef.current = findGridCellElementsFromCol( - colElementRef.current, - apiRef.current, - ); - - const doc = ownerDocument(apiRef.current.rootElementRef!.current); - doc.body.style.cursor = 'col-resize'; - - resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction); - - initialOffsetToSeparator.current = computeOffsetToSeparator( - event.clientX, - colElementRef.current!.getBoundingClientRect(), - resizeDirection.current, - ); - - doc.addEventListener('mousemove', handleResizeMouseMove); - doc.addEventListener('mouseup', handleResizeMouseUp); - - // Fixes https://github.com/mui/mui-x/issues/4777 - colElementRef.current.style.pointerEvents = 'none'; - }); - const handleTouchEnd = useEventCallback((nativeEvent: any) => { const finger = trackFinger(nativeEvent, touchId.current); @@ -395,6 +478,11 @@ export const useGridColumnResize = ( doc.removeEventListener('mouseup', handleResizeMouseUp); doc.removeEventListener('touchmove', handleTouchMove); doc.removeEventListener('touchend', handleTouchEnd); + // The click event runs right after the mouseup event, we want to wait until it + // has been canceled before removing our handler. + setTimeout(() => { + doc.removeEventListener('click', preventClick, true); + }, 100); if (colElementRef.current) { colElementRef.current!.style.pointerEvents = 'unset'; } @@ -426,12 +514,172 @@ export const useGridColumnResize = ( apiRef.current.forceUpdate(); }, [apiRef]); - React.useEffect(() => { - return () => { - clearTimeout(stopResizeEventTimeout.current); - stopListening(); - }; - }, [apiRef, handleTouchStart, stopListening]); + const handleColumnResizeMouseDown: GridEventListener<'columnSeparatorMouseDown'> = + useEventCallback(({ colDef }, event) => { + // Only handle left clicks + if (event.button !== 0) { + return; + } + + // Skip if the column isn't resizable + if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) { + return; + } + + // Avoid text selection + event.preventDefault(); + + logger.debug(`Start Resize on col ${colDef.field}`); + apiRef.current.publishEvent('columnResizeStart', { field: colDef.field }, event); + + colDefRef.current = colDef as GridStateColDef; + colElementRef.current = + apiRef.current.columnHeadersContainerElementRef?.current!.querySelector( + `[data-field="${colDef.field}"]`, + )!; + + const headerFilterRowElement = apiRef.current.headerFiltersElementRef?.current; + + if (headerFilterRowElement) { + headerFilterElementRef.current = headerFilterRowElement.querySelector( + `[data-field="${colDef.field}"]`, + ) as HTMLDivElement; + } + + colGroupingElementRef.current = findGroupHeaderElementsFromField( + apiRef.current.columnHeadersContainerElementRef?.current!, + colDef.field, + ); + + colCellElementsRef.current = findGridCellElementsFromCol( + colElementRef.current, + apiRef.current, + ); + + const doc = ownerDocument(apiRef.current.rootElementRef!.current); + doc.body.style.cursor = 'col-resize'; + + resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction); + + initialOffsetToSeparator.current = computeOffsetToSeparator( + event.clientX, + colElementRef.current!.getBoundingClientRect(), + resizeDirection.current, + ); + + doc.addEventListener('mousemove', handleResizeMouseMove); + doc.addEventListener('mouseup', handleResizeMouseUp); + + // Prevent the click event if we have resized the column. + // Fixes https://github.com/mui/mui-x/issues/4777 + doc.addEventListener('click', preventClick, true); + }); + + const handleColumnSeparatorDoubleClick: GridEventListener<'columnSeparatorDoubleClick'> = + useEventCallback((params, event) => { + if (props.disableAutosize) { + return; + } + + // Only handle left clicks + if (event.button !== 0) { + return; + } + + const column = apiRef.current.state.columns.lookup[params.field]; + if (column.resizable === false) { + return; + } + + apiRef.current.autosizeColumns({ + ...props.autosizeOptions, + columns: [column.field], + }); + }); + + /** + * API METHODS + */ + + const columnVirtualizationDisabled = useColumnVirtualizationDisabled(apiRef); + const isAutosizingRef = React.useRef(false); + const autosizeColumns = React.useCallback( + async (userOptions) => { + const root = apiRef.current.rootElementRef?.current; + if (!root) { + return; + } + if (isAutosizingRef.current) { + return; + } + isAutosizingRef.current = true; + + const state = gridColumnsStateSelector(apiRef.current.state); + const options = { + ...DEFAULT_GRID_AUTOSIZE_OPTIONS, + ...userOptions, + columns: userOptions?.columns ?? state.orderedFields, + }; + options.columns = options.columns.filter((c) => state.columnVisibilityModel[c] !== false); + + const columns = options.columns.map((c) => apiRef.current.state.columns.lookup[c]); + + try { + apiRef.current.unstable_setColumnVirtualization(false); + await columnVirtualizationDisabled(); + + const widthByField = extractColumnWidths(apiRef, options, columns); + + const newColumns = columns.map((column) => ({ + ...column, + width: widthByField[column.field], + computedWidth: widthByField[column.field], + })); + + if (options.expand) { + const visibleColumns = state.orderedFields + .map((field) => state.lookup[field]) + .filter((c) => state.columnVisibilityModel[c.field] !== false); + + const totalWidth = visibleColumns.reduce( + (total, column) => + total + (widthByField[column.field] ?? column.computedWidth ?? column.width), + 0, + ); + const availableWidth = apiRef.current.getRootDimensions()?.viewportInnerSize.width ?? 0; + const remainingWidth = availableWidth - totalWidth; + + if (remainingWidth > 0) { + const widthPerColumn = remainingWidth / (newColumns.length || 1); + newColumns.forEach((column) => { + column.width += widthPerColumn; + column.computedWidth += widthPerColumn; + }); + } + } + + apiRef.current.updateColumns(newColumns); + } finally { + apiRef.current.unstable_setColumnVirtualization(true); + isAutosizingRef.current = false; + } + }, + [apiRef, columnVirtualizationDisabled], + ); + + /** + * EFFECTS + */ + + React.useEffect(() => stopListening, [stopListening]); + + useOnMount(() => { + if (props.autosizeOnMount) { + Promise.resolve().then(() => { + apiRef.current.autosizeColumns(props.autosizeOptions); + }); + } + }); useGridNativeEventListener( apiRef, @@ -441,9 +689,18 @@ export const useGridColumnResize = ( { passive: doesSupportTouchActionNone() }, ); - useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown); - useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart); + useGridApiMethod( + apiRef, + { + autosizeColumns, + }, + 'public', + ); + useGridApiEventHandler(apiRef, 'columnResizeStop', handleResizeStop); + useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart); + useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown); + useGridApiEventHandler(apiRef, 'columnSeparatorDoubleClick', handleColumnSeparatorDoubleClick); useGridApiOptionHandler(apiRef, 'columnResize', props.onColumnResize); useGridApiOptionHandler(apiRef, 'columnWidthChange', props.onColumnWidthChange); diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts b/packages/grid/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts index d90e2f8ab1606..6f19a0a3721f4 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/lazyLoader/useGridLazyLoader.ts @@ -36,9 +36,9 @@ function findSkeletonRowsSection({ while (!isSkeletonSectionFound && firstRowIndex < lastRowIndex) { const isStartingWithASkeletonRow = - apiRef.current.getRowNode(visibleRowsSection[startIndex].id)!.type === 'skeletonRow'; + apiRef.current.getRowNode(visibleRowsSection[startIndex].id)?.type === 'skeletonRow'; const isEndingWithASkeletonRow = - apiRef.current.getRowNode(visibleRowsSection[endIndex].id)!.type === 'skeletonRow'; + apiRef.current.getRowNode(visibleRowsSection[endIndex].id)?.type === 'skeletonRow'; if (isStartingWithASkeletonRow && isEndingWithASkeletonRow) { isSkeletonSectionFound = true; diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataGroupColDef.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataGroupColDef.ts index 16d3e080f728d..670d5681a60f9 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataGroupColDef.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataGroupColDef.ts @@ -13,7 +13,9 @@ export const GRID_TREE_DATA_GROUPING_COL_DEF: Omit - params.rowNode.type === 'group' ? params.rowNode.groupingKey : undefined, + params.rowNode.type === 'group' || params.rowNode.type === 'leaf' + ? params.rowNode.groupingKey + : undefined, }; export const GRID_TREE_DATA_GROUPING_FIELD = '__tree_data_group__'; diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts index 291adc3f39d74..9926ef35c3df5 100644 --- a/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts +++ b/packages/grid/x-data-grid-pro/src/hooks/features/treeData/gridTreeDataUtils.ts @@ -8,16 +8,16 @@ import { import { GridAggregatedFilterItemApplier, GridAggregatedFilterItemApplierResult, - GridApiCommunity, passFilterLogic, } from '@mui/x-data-grid/internals'; +import type { GridPrivateApiPro } from '../../../models/gridApiPro'; interface FilterRowTreeFromTreeDataParams { rowTree: GridRowTreeConfig; disableChildrenFiltering: boolean; isRowMatchingFilters: GridAggregatedFilterItemApplier | null; filterModel: GridFilterModel; - apiRef: React.MutableRefObject; + apiRef: React.MutableRefObject; } export const TREE_DATA_STRATEGY = 'tree-data'; diff --git a/packages/grid/x-data-grid-pro/src/material/index.ts b/packages/grid/x-data-grid-pro/src/material/index.ts index 28b3d08f2bcc7..a8a8366af9792 100644 --- a/packages/grid/x-data-grid-pro/src/material/index.ts +++ b/packages/grid/x-data-grid-pro/src/material/index.ts @@ -2,8 +2,8 @@ import type { GridProIconSlotsComponent } from '../models'; import { GridPushPinRightIcon, GridPushPinLeftIcon } from './icons'; const iconSlots: GridProIconSlotsComponent = { - ColumnMenuPinRightIcon: GridPushPinRightIcon, - ColumnMenuPinLeftIcon: GridPushPinLeftIcon, + columnMenuPinRightIcon: GridPushPinRightIcon, + columnMenuPinLeftIcon: GridPushPinLeftIcon, }; const materialSlots = { diff --git a/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts b/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts index 95301282803bb..4cb4260a30636 100644 --- a/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts +++ b/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts @@ -23,8 +23,9 @@ import { GridGroupingColDefOverrideParams, } from './gridGroupingColDefOverride'; import { GridInitialStatePro } from './gridStatePro'; -import { GridProSlotsComponent, UncapitalizedGridProSlotsComponent } from './gridProSlotsComponent'; +import { GridProSlotsComponent } from './gridProSlotsComponent'; import type { GridProSlotProps } from './gridProSlotProps'; +import { GridAutosizeOptions } from '../hooks'; export interface GridExperimentalProFeatures extends GridExperimentalFeatures { /** @@ -37,13 +38,8 @@ interface DataGridProPropsWithComplexDefaultValueBeforeProcessing extends Omit { /** * Overridable components. - * @deprecated Use the `slots` prop instead. */ - components?: Partial; - /** - * Overridable components. - */ - slots?: Partial; + slots?: Partial; } /** @@ -59,7 +55,7 @@ export interface DataGridProProps interface DataGridProPropsWithComplexDefaultValueAfterProcessing extends Omit { - slots: UncapitalizedGridProSlotsComponent; + slots: GridProSlotsComponent; } /** @@ -101,6 +97,16 @@ 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 @@ -157,6 +163,10 @@ 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. @@ -254,9 +264,4 @@ export interface DataGridProPropsWithoutDefaultValue= provided `rowCount` + */ + pageInfo?: { + hasNextPage?: boolean; + truncated?: number; + }; +} + +export interface DataSource { + /** + Fetcher Functions: + - `getRows` is required + - `updateRow` is optional + + `getRows` will be used by the grid to fetch data for the current page or children for the current parent group + It may return a `rowCount` to update the total count of rows in the grid along with the optional `pageInfo` + */ + getRows(params: GetRowsParams): Promise; + updateRow?(rows: GridRowModel): Promise; +} diff --git a/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts b/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts index bd5c6dddeccfc..1ad793c49c50e 100644 --- a/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts +++ b/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts @@ -8,10 +8,12 @@ import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals'; import { GridInitialStatePro, GridStatePro } from './gridStatePro'; import type { GridColumnPinningApi, + GridColumnResizeApi, GridDetailPanelApi, GridRowPinningApi, GridDetailPanelPrivateApi, } from '../hooks'; +import type { DataGridProProcessedProps } from './dataGridProProps'; /** * The api of `DataGridPro`. @@ -20,14 +22,14 @@ export interface GridApiPro extends GridApiCommon, GridRowProApi, GridColumnPinningApi, + GridColumnResizeApi, GridDetailPanelApi, GridRowPinningApi, // APIs that are private in Community plan, but public in Pro and Premium plans GridRowMultiSelectionApi, - GridColumnReorderApi, - GridRowProApi {} + GridColumnReorderApi {} export interface GridPrivateApiPro extends GridApiPro, - GridPrivateOnlyApiCommon, + GridPrivateOnlyApiCommon, GridDetailPanelPrivateApi {} diff --git a/packages/grid/x-data-grid-pro/src/models/gridGroupingColDefOverride.ts b/packages/grid/x-data-grid-pro/src/models/gridGroupingColDefOverride.ts index d85d87d91bb07..ab6f80261c46d 100644 --- a/packages/grid/x-data-grid-pro/src/models/gridGroupingColDefOverride.ts +++ b/packages/grid/x-data-grid-pro/src/models/gridGroupingColDefOverride.ts @@ -15,7 +15,7 @@ export interface GridGroupingColDefOverride * The field from which we want to apply the sorting and the filtering for the grouping column. * It is only useful when `props.rowGroupingColumnMode === "multiple"` to decide which grouping criteria should be used for sorting and filtering. * Do not have any effect when building the tree with the `props.treeData` feature. - * @default: The sorting and filtering is applied based on the leaf field in any, otherwise based on top level grouping criteria. + * @default The sorting and filtering is applied based on the leaf field in any, otherwise based on top level grouping criteria. */ mainGroupingCriteria?: string; @@ -27,7 +27,7 @@ export interface GridGroupingColDefOverride /** * If `true`, the grouping cells will not render the amount of descendants. - * @default: false + * @default false */ hideDescendantCount?: boolean; } diff --git a/packages/grid/x-data-grid-pro/src/models/gridProIconSlotsComponent.ts b/packages/grid/x-data-grid-pro/src/models/gridProIconSlotsComponent.ts index 634bb56455480..670a500efb11b 100644 --- a/packages/grid/x-data-grid-pro/src/models/gridProIconSlotsComponent.ts +++ b/packages/grid/x-data-grid-pro/src/models/gridProIconSlotsComponent.ts @@ -5,10 +5,10 @@ export interface GridProIconSlotsComponent { * Icon displayed in column menu for left pinning * @default GridPushPinLeftIcon */ - ColumnMenuPinLeftIcon: React.JSXElementConstructor; + columnMenuPinLeftIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for right pinning * @default GridPushPinRightIcon */ - ColumnMenuPinRightIcon: React.JSXElementConstructor; + columnMenuPinRightIcon: React.JSXElementConstructor; } diff --git a/packages/grid/x-data-grid-pro/src/models/gridProSlotsComponent.ts b/packages/grid/x-data-grid-pro/src/models/gridProSlotsComponent.ts index 51c7cdd00bbf9..619ea31423f77 100644 --- a/packages/grid/x-data-grid-pro/src/models/gridProSlotsComponent.ts +++ b/packages/grid/x-data-grid-pro/src/models/gridProSlotsComponent.ts @@ -1,5 +1,4 @@ import { GridSlotsComponent } from '@mui/x-data-grid'; -import { UncapitalizeObjectKeys } from '@mui/x-data-grid/internals'; import { GridProIconSlotsComponent } from './gridProIconSlotsComponent'; /** @@ -11,14 +10,10 @@ export interface GridProSlotsComponent extends GridSlotsComponent, GridProIconSl * Component responsible for showing menu adornment in Header filter row * @default GridHeaderFilterCell */ - HeaderFilterCell: React.JSXElementConstructor; + headerFilterCell: React.JSXElementConstructor; /** * Component responsible for showing menu in Header filter row * @default GridHeaderFilterMenu */ - HeaderFilterMenu: React.JSXElementConstructor | null; + headerFilterMenu: React.JSXElementConstructor | null; } - -// TODO: remove in v7 -export interface UncapitalizedGridProSlotsComponent - extends UncapitalizeObjectKeys {} diff --git a/packages/grid/x-data-grid-pro/src/models/index.ts b/packages/grid/x-data-grid-pro/src/models/index.ts index 58cfd0bf2f8eb..36deff5c944b6 100644 --- a/packages/grid/x-data-grid-pro/src/models/index.ts +++ b/packages/grid/x-data-grid-pro/src/models/index.ts @@ -1,3 +1,4 @@ +export * from './gridApiPro'; export * from './gridGroupingColDefOverride'; export * from './gridRowScrollEndParams'; export * from './gridRowOrderChangeParams'; diff --git a/packages/grid/x-data-grid-pro/src/tests/accessibility.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/accessibility.DataGridPro.test.tsx index 18bc9828f4307..2c54024362746 100644 --- a/packages/grid/x-data-grid-pro/src/tests/accessibility.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/accessibility.DataGridPro.test.tsx @@ -3,7 +3,7 @@ import { expect } from 'chai'; import axe from 'axe-core'; import { DataGridPro } from '@mui/x-data-grid-pro'; import { useBasicDemoData } from '@mui/x-data-grid-generator'; -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer } from '@mui-internal/test-utils'; function logViolations(violations: any) { if (violations.length !== 0) { diff --git a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx index 5336b9e2f83e1..7aa921856e0f9 100644 --- a/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/cellEditing.DataGridPro.test.tsx @@ -13,10 +13,10 @@ import { GridCellModes, } from '@mui/x-data-grid-pro'; import { getBasicGridData } from '@mui/x-data-grid-generator'; -import { createRenderer, fireEvent, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent } from '@mui-internal/test-utils'; import { getCell, spyApi } from 'test/utils/helperFn'; -describe(' - Cell Editing', () => { +describe(' - Cell editing', () => { const { render, clock } = createRenderer({ clock: 'fake' }); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-pro/src/tests/clipboard.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/clipboard.DataGridPro.test.tsx index 682f3986ce62d..fb2a4721dbf9e 100644 --- a/packages/grid/x-data-grid-pro/src/tests/clipboard.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/clipboard.DataGridPro.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { GridApi, useGridApiRef, DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; -import { createRenderer, fireEvent, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { stub, SinonStub } from 'sinon'; import { getCell } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid-pro/src/tests/columnHeaders.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnHeaders.DataGridPro.test.tsx index d40189b6e4924..6846a4fcfe298 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnHeaders.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnHeaders.DataGridPro.test.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { gridClasses, DataGridPro, DataGridProProps } from '@mui/x-data-grid-pro'; import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Column Headers', () => { +describe(' - Column headers', () => { const { render, clock } = createRenderer({ clock: 'fake' }); const baselineProps = { diff --git a/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx index 57a1a02ef3e94..44e0eb07521a9 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnPinning.DataGridPro.test.tsx @@ -8,6 +8,9 @@ import { DataGridProProps, gridClasses, GridPinnedPosition, + GridColumnGroupingModel, + GridColDef, + GRID_CHECKBOX_SELECTION_FIELD, } from '@mui/x-data-grid-pro'; import { useBasicDemoData, getBasicGridData } from '@mui/x-data-grid-generator'; import { @@ -17,7 +20,7 @@ import { createEvent, act, userEvent, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { getCell, getColumnHeaderCell, getColumnHeadersTextContent } from 'test/utils/helperFn'; // TODO Move to utils @@ -64,6 +67,9 @@ describe(' - Column pinning', () => { disconnect: () => { clearTimeout(timeout); }, + unobserve: () => { + clearTimeout(timeout); + }, }; } @@ -142,6 +148,34 @@ describe(' - Column pinning', () => { expect(renderZone!.querySelector('[data-rowindex="0"]')).not.to.have.class('Mui-hovered'); }); + // https://github.com/mui/mui-x/issues/10176 + it('should keep .Mui-hovered on the entire row when row is selected and deselected', () => { + render( + , + ); + const leftColumns = document.querySelector(`.${gridClasses['pinnedColumns--left']}`); + const rightColumns = document.querySelector(`.${gridClasses['pinnedColumns--right']}`); + const renderZone = document.querySelector(`.${gridClasses.virtualScrollerRenderZone}`); + const cell = getCell(0, 0); + + fireEvent.mouseEnter(cell); + expect(leftColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + expect(rightColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + expect(renderZone!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + + const checkbox = cell.querySelector('input[type="checkbox"]')!; + fireEvent.click(checkbox); + fireEvent.click(checkbox); + expect(leftColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + expect(rightColumns!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + expect(renderZone!.querySelector('[data-rowindex="0"]')).to.have.class('Mui-hovered'); + }); + it('should update the render zone offset after resize', function test() { if (isJSDOM) { // Need layouting @@ -821,4 +855,77 @@ describe(' - Column pinning', () => { expect(getColumnHeadersTextContent()).to.deep.equal(['id', 'price1M']); }); }); + + describe('Column grouping', () => { + const columns: GridColDef[] = [ + { field: 'id', headerName: 'ID', width: 90 }, + { + field: 'firstName', + headerName: 'First name', + width: 150, + }, + { + field: 'lastName', + headerName: 'Last name', + width: 150, + }, + { + field: 'age', + headerName: 'Age', + type: 'number', + width: 110, + }, + ]; + + const rows = [ + { id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 }, + { id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 }, + { id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 }, + { id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 }, + { id: 5, lastName: 'Targaryen', firstName: 'Daenerys', age: null }, + { id: 6, lastName: 'Melisandre', firstName: null, age: 150 }, + { id: 7, lastName: 'Clifford', firstName: 'Ferrara', age: 44 }, + { id: 8, lastName: 'Frances', firstName: 'Rossini', age: 36 }, + { id: 9, lastName: 'Roxie', firstName: 'Harvey', age: 65 }, + ]; + + const columnGroupingModel: GridColumnGroupingModel = [ + { + groupId: 'Internal', + description: '', + children: [{ field: 'id' }], + }, + { + groupId: 'Basic info', + children: [ + { + groupId: 'Full name', + children: [{ field: 'lastName' }, { field: 'firstName' }], + }, + { field: 'age' }, + ], + }, + ]; + + it('should create separate column groups for pinned and non-pinned columns having same column group', () => { + render( + , + ); + + const firstNameLastNameColumnGroupHeader = document.querySelector( + '[role="columnheader"][data-fields="|-firstName-|-lastName-|"]', + )!; + expect(firstNameLastNameColumnGroupHeader.textContent).to.equal('Basic info'); + const ageCellColumnGroupHeader = document.querySelector( + '[role="columnheader"][data-fields="|-age-|"]', + )!; + expect(ageCellColumnGroupHeader.textContent).to.equal('Basic info'); + }); + }); }); diff --git a/packages/grid/x-data-grid-pro/src/tests/columnReorder.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnReorder.DataGridPro.test.tsx index d566f208b09d1..10374c4da06d8 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnReorder.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnReorder.DataGridPro.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, fireEvent, createEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, createEvent, act } from '@mui-internal/test-utils'; import { getColumnHeadersTextContent, getColumnHeaderCell, diff --git a/packages/grid/x-data-grid-pro/src/tests/columnSpanning.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnSpanning.DataGridPro.test.tsx index e9454dca917f3..06391336b3c80 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnSpanning.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnSpanning.DataGridPro.test.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { createRenderer, fireEvent, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGridPro, GridApi, useGridApiRef, GridColDef, gridClasses } from '@mui/x-data-grid-pro'; import { getActiveCell, getCell, getColumnHeaderCell } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Column Spanning', () => { +describe(' - Column spanning', () => { const { render } = createRenderer({ clock: 'fake' }); const baselineProps = { diff --git a/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx index e56f110f2ff6a..a4226ca77e1e5 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { @@ -10,9 +10,10 @@ import { gridColumnLookupSelector, gridColumnFieldsSelector, GridApi, + GridAutosizeOptions, } from '@mui/x-data-grid-pro'; import { useGridPrivateApiContext } from '@mui/x-data-grid-pro/internals'; -import { getColumnHeaderCell, getCell } from 'test/utils/helperFn'; +import { getColumnHeaderCell, getCell, microtasks } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -410,6 +411,91 @@ describe(' - Columns', () => { }); }); + describe('autosizing', () => { + before(function beforeHook() { + if (isJSDOM) { + // Need layouting + this.skip(); + } + }); + + const rows = [ + { + id: 0, + brand: 'Nike', + }, + { + id: 1, + brand: 'Adidas', + }, + { + id: 2, + brand: 'Puma', + }, + { + id: 3, + brand: 'Lululemon Athletica', + }, + ]; + const columns = [ + { field: 'id', headerName: 'This is the ID column' }, + { field: 'brand', headerName: 'This is the brand column' }, + ]; + + const getWidths = () => { + return columns.map((_, i) => parseInt(getColumnHeaderCell(i).style.width, 10)); + }; + + it('should work through the API', async () => { + render(); + await apiRef.current.autosizeColumns(); + await microtasks(); + expect(getWidths()).to.deep.equal([213, 235]); + }); + + it('should work through double-clicking the separator', async () => { + render(); + const separator = document.querySelectorAll( + `.${gridClasses['columnSeparator--resizable']}`, + )[1]; + fireEvent.doubleClick(separator); + await microtasks(); + expect(getWidths()).to.deep.equal([100, 235]); + }); + + it('should work on mount', async () => { + render(); + await microtasks(); /* first effect after render */ + await microtasks(); /* async autosize operation */ + expect(getWidths()).to.deep.equal([213, 235]); + }); + + describe('options', () => { + const autosize = async (options: GridAutosizeOptions | undefined, widths: number[]) => { + render(); + await apiRef.current.autosizeColumns({ includeHeaders: false, ...options }); + await microtasks(); + expect(getWidths()).to.deep.equal(widths); + }; + + it('.columns works', async () => { + await autosize({ columns: [columns[0].field] }, [50, 100]); + }); + it('.includeHeaders works', async () => { + await autosize({ includeHeaders: true }, [213, 235]); + }); + it('.includeOutliers works', async () => { + await autosize({ includeOutliers: true }, [50, 144]); + }); + it('.outliersFactor works', async () => { + await autosize({ outliersFactor: 40 }, [50, 144]); + }); + it('.expand works', async () => { + await autosize({ expand: true }, [134, 148]); + }); + }); + }); + describe('column pipe processing', () => { type GridPrivateApiContextRef = ReturnType; it('should not loose column width when re-applying pipe processing', () => { @@ -418,7 +504,7 @@ describe(' - Columns', () => { privateApi = useGridPrivateApiContext(); return null; } - render(); + render(); act(() => apiRef.current.setColumnWidth('brand', 300)); expect(gridColumnLookupSelector(apiRef).brand.computedWidth).to.equal(300); @@ -436,7 +522,7 @@ describe(' - Columns', () => { , ); @@ -453,7 +539,7 @@ describe(' - Columns', () => { privateApi = useGridPrivateApiContext(); return null; } - render(); + render(); act(() => apiRef.current.updateColumns([{ field: 'id' }])); expect(gridColumnFieldsSelector(apiRef)).to.deep.equal(['__check__', 'brand', 'id']); diff --git a/packages/grid/x-data-grid-pro/src/tests/columnsVisibility.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columnsVisibility.DataGridPro.test.tsx index 824cdbe06c807..d6c9f05104e8a 100644 --- a/packages/grid/x-data-grid-pro/src/tests/columnsVisibility.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/columnsVisibility.DataGridPro.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { createRenderer, fireEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act } from '@mui-internal/test-utils'; import { DataGridPro, DataGridProProps, @@ -21,7 +21,7 @@ const rows: GridRowsProp = [{ id: 1 }]; const columns: GridColDef[] = [{ field: 'id' }, { field: 'idBis' }]; -describe(' - Columns Visibility', () => { +describe(' - Columns visibility', () => { const { render } = createRenderer({ clock: 'fake' }); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx index b443a67fb19ea..0f22b76842792 100644 --- a/packages/grid/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/components.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, EventType, fireEvent, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, EventType, fireEvent, userEvent } from '@mui-internal/test-utils'; import { spy } from 'sinon'; import { expect } from 'chai'; import { @@ -51,10 +51,10 @@ describe(' - Components', () => { ['onDragOver', 'cellDragOver'], ] as const ).forEach(([prop, event]) => { - it(`should still publish the '${event}' event when overriding the '${prop}' prop in components.cell`, () => { + it(`should still publish the '${event}' event when overriding the '${prop}' prop in slots.cell`, () => { const propHandler = spy(); const eventHandler = spy(); - render(); + render(); apiRef!.current.subscribeEvent(event, eventHandler); expect(propHandler.callCount).to.equal(0); @@ -77,10 +77,10 @@ describe(' - Components', () => { }); }); - it(`should still publish the 'cellKeyDown' event when overriding the 'onKeyDown' prop in components.cell`, () => { + it(`should still publish the 'cellKeyDown' event when overriding the 'onKeyDown' prop in slots.cell`, () => { const propHandler = spy(); const eventHandler = spy(); - render(); + render(); apiRef!.current.subscribeEvent('cellKeyDown', eventHandler); expect(propHandler.callCount).to.equal(0); @@ -100,10 +100,10 @@ describe(' - Components', () => { ['onDoubleClick', 'rowDoubleClick'], ] as const ).forEach(([prop, event]) => { - it(`should still publish the '${event}' event when overriding the '${prop}' prop in components.row`, () => { + it(`should still publish the '${event}' event when overriding the '${prop}' prop in slots.row`, () => { const propHandler = spy(); const eventHandler = spy(); - render(); + render(); apiRef!.current.subscribeEvent(event, eventHandler); expect(propHandler.callCount).to.equal(0); diff --git a/packages/grid/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx index 1e3d478134821..707482b36a1dd 100644 --- a/packages/grid/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/detailPanel.DataGridPro.test.tsx @@ -18,7 +18,7 @@ import { waitFor, act, userEvent, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { getRow, getCell, getColumnValues, getRows } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -693,7 +693,7 @@ describe(' - Detail panel', () => { getDetailPanelHeight={() => 0} nbRows={1} getDetailPanelContent={() =>
                } - componentsProps={{ + slotProps={{ row: { style: { color: 'yellow' } }, }} />, diff --git a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx index 62b77df7e944c..e1d80b9bfa54c 100644 --- a/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/editComponents.DataGridPro.test.tsx @@ -10,7 +10,7 @@ import { renderEditInputCell, renderEditSingleSelectCell, } from '@mui/x-data-grid-pro'; -import { act, createRenderer, fireEvent, screen, userEvent } from '@mui/monorepo/test/utils'; +import { act, createRenderer, fireEvent, screen, userEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { getCell, spyApi } from 'test/utils/helperFn'; import { spy, SinonSpy } from 'sinon'; @@ -37,7 +37,7 @@ const generateDate = ( return rawDate.getTime(); }; -describe(' - Edit Components', () => { +describe(' - Edit components', () => { const { render, clock } = createRenderer({ clock: 'fake' }); let apiRef: React.MutableRefObject; @@ -623,7 +623,7 @@ describe(' - Edit Components', () => { userEvent.mousePress(screen.queryAllByRole('option')[1]); clock.runToLast(); expect(screen.queryByRole('listbox')).to.equal(null); - fireEvent.keyDown(screen.getByRole('button', { name: 'Adidas' }), { key: 'Enter' }); + fireEvent.keyDown(screen.getByRole('combobox'), { key: 'Enter' }); expect(screen.queryByRole('listbox')).to.equal(null); resolveCallback!(); diff --git a/packages/grid/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx index 9190215e67b51..f596ed6ad2a16 100644 --- a/packages/grid/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/events.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGridPro, @@ -20,7 +20,7 @@ import { spy } from 'sinon'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Events Params', () => { +describe(' - Events params', () => { const { render, clock } = createRenderer(); const baselineProps: { rows: GridRowsProp; columns: GridColDef[] } = { diff --git a/packages/grid/x-data-grid-pro/src/tests/export.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/export.DataGridPro.test.tsx index 6a5cd4f56feab..fb3f9e9022938 100644 --- a/packages/grid/x-data-grid-pro/src/tests/export.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/export.DataGridPro.test.tsx @@ -5,7 +5,7 @@ import { GridApi, DataGridProProps, } from '@mui/x-data-grid-pro'; -import { createRenderer, act } from '@mui/monorepo/test/utils'; +import { createRenderer, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import * as React from 'react'; diff --git a/packages/grid/x-data-grid-pro/src/tests/filterPanel.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/filterPanel.DataGridPro.test.tsx index 0477796e293e3..d7f5333dfa1cd 100644 --- a/packages/grid/x-data-grid-pro/src/tests/filterPanel.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/filterPanel.DataGridPro.test.tsx @@ -7,7 +7,7 @@ import { GridApi, useGridApiRef, } from '@mui/x-data-grid-pro'; -import { createRenderer, act } from '@mui/monorepo/test/utils'; +import { createRenderer, act } from '@mui-internal/test-utils'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); diff --git a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx index 9e878e0a9e377..b28cea80f7428 100644 --- a/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/filtering.DataGridPro.test.tsx @@ -15,7 +15,7 @@ import { gridExpandedSortedRowEntriesSelector, gridClasses, } from '@mui/x-data-grid-pro'; -import { createRenderer, fireEvent, screen, act, within } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act, within } from '@mui-internal/test-utils'; import { expect } from 'chai'; import * as React from 'react'; import { spy } from 'sinon'; @@ -75,7 +75,7 @@ describe(' - Filter', () => { ], }; - it('componentsProps `filterColumns` and `getColumnForNewFilter` should allow custom filtering', () => { + it('slotProps `filterColumns` and `getColumnForNewFilter` should allow custom filtering', () => { const filterColumns = ({ field, columns, currentFilters }: FilterColumnsArgs) => { // remove already filtered fields from list of columns const filteredFields = currentFilters?.map((item) => item.field); @@ -103,8 +103,8 @@ describe(' - Filter', () => { openedPanelValue: GridPreferencePanelsValue.filters, }, }} - components={{ Toolbar: GridToolbar }} - componentsProps={{ + slots={{ toolbar: GridToolbar }} + slotProps={{ filterPanel: { filterFormProps: { filterColumns, @@ -132,8 +132,8 @@ describe(' - Filter', () => { openedPanelValue: GridPreferencePanelsValue.filters, }, }} - components={{ Toolbar: GridToolbar }} - componentsProps={{ + slots={{ toolbar: GridToolbar }} + slotProps={{ filterPanel: { getColumnForNewFilter, }, @@ -158,8 +158,8 @@ describe(' - Filter', () => { openedPanelValue: GridPreferencePanelsValue.filters, }, }} - components={{ Toolbar: GridToolbar }} - componentsProps={{ + slots={{ toolbar: GridToolbar }} + slotProps={{ filterPanel: { filterFormProps: { filterColumns, @@ -352,7 +352,7 @@ describe(' - Filter', () => { openedPanelValue: GridPreferencePanelsValue.filters, }, }} - componentsProps={{ + slotProps={{ filterPanel: { disableAddFilterButton: true, }, @@ -372,7 +372,7 @@ describe(' - Filter', () => { openedPanelValue: GridPreferencePanelsValue.filters, }, }} - componentsProps={{ + slotProps={{ filterPanel: { disableRemoveAllButton: true, }, diff --git a/packages/grid/x-data-grid-pro/src/tests/layout.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/layout.DataGridPro.test.tsx index d43165fce0c81..ded3a0d2b2108 100644 --- a/packages/grid/x-data-grid-pro/src/tests/layout.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/layout.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { GridApi, useGridApiRef, DataGridPro, ptBR, DataGridProProps } from '@mui/x-data-grid-pro'; diff --git a/packages/grid/x-data-grid-pro/src/tests/lazyLoader.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/lazyLoader.DataGridPro.test.tsx index b782cd3bc34e9..b144e2d8640f5 100644 --- a/packages/grid/x-data-grid-pro/src/tests/lazyLoader.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/lazyLoader.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act } from '@mui-internal/test-utils'; import { getColumnHeaderCell, getColumnValues, getRow } from 'test/utils/helperFn'; import { expect } from 'chai'; import { diff --git a/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx index 7306eb5992d8c..f2556186f2532 100644 --- a/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/license.DataGridPro.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, screen, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, waitFor } from '@mui-internal/test-utils'; import { DataGridPro } from '@mui/x-data-grid-pro'; import { LicenseInfo } from '@mui/x-license-pro'; diff --git a/packages/grid/x-data-grid-pro/src/tests/pagination.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/pagination.DataGridPro.test.tsx index 745fc9d5fc3c1..55d78d03024ff 100644 --- a/packages/grid/x-data-grid-pro/src/tests/pagination.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/pagination.DataGridPro.test.tsx @@ -1,4 +1,4 @@ -import { createRenderer, act } from '@mui/monorepo/test/utils'; +import { createRenderer, act } from '@mui-internal/test-utils'; import { getColumnValues } from 'test/utils/helperFn'; import * as React from 'react'; import { expect } from 'chai'; diff --git a/packages/grid/x-data-grid-pro/src/tests/printExport.DataGrid.test.tsx b/packages/grid/x-data-grid-pro/src/tests/printExport.DataGrid.test.tsx index d532723cba476..5a5433fe409f9 100644 --- a/packages/grid/x-data-grid-pro/src/tests/printExport.DataGrid.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/printExport.DataGrid.test.tsx @@ -9,7 +9,7 @@ import { DataGridProProps, } from '@mui/x-data-grid-pro'; import { getBasicGridData } from '@mui/x-data-grid-generator'; -import { createRenderer, screen, fireEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, fireEvent, act } from '@mui-internal/test-utils'; describe(' - Print export', () => { const { render, clock } = createRenderer(); diff --git a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx index dbd563bbc9f63..a08ebe77322c7 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rowEditing.DataGridPro.test.tsx @@ -13,10 +13,10 @@ import { } from '@mui/x-data-grid-pro'; import Portal from '@mui/material/Portal'; import { getBasicGridData } from '@mui/x-data-grid-generator'; -import { createRenderer, fireEvent, act, userEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent, screen } from '@mui-internal/test-utils'; import { getCell, getRow, spyApi } from 'test/utils/helperFn'; -describe(' - Row Editing', () => { +describe(' - Row editing', () => { const { render, clock } = createRenderer(); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-pro/src/tests/rowPinning.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowPinning.DataGridPro.test.tsx index bb80ce265f766..878eb683e1f4c 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rowPinning.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rowPinning.DataGridPro.test.tsx @@ -18,7 +18,7 @@ import { act, userEvent, waitFor, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { getActiveCell, getActiveColumnHeader, diff --git a/packages/grid/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx index 7aee64b05493c..3c56a545f6d04 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rowReorder.DataGridPro.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createRenderer, fireEvent, createEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, createEvent } from '@mui-internal/test-utils'; import { getCell, getRowsFieldContent } from 'test/utils/helperFn'; import { useGridApiRef, DataGridPro, gridClasses, GridApi } from '@mui/x-data-grid-pro'; import { useBasicDemoData } from '@mui/x-data-grid-generator'; diff --git a/packages/grid/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx index 0defbac668e35..1666112dd0cdb 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rowSelection.DataGridPro.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { getCell, getColumnValues, getRows } from 'test/utils/helperFn'; -import { createRenderer, fireEvent, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act } from '@mui-internal/test-utils'; import { GridApi, useGridApiRef, @@ -23,7 +23,7 @@ function getSelectedRowIds() { ); } -describe(' - Row Selection', () => { +describe(' - Row selection', () => { const { render } = createRenderer(); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx index 2791f94b63071..8f3d464f6356a 100644 --- a/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act, userEvent } from '@mui-internal/test-utils'; import { spy } from 'sinon'; import { expect } from 'chai'; import { diff --git a/packages/grid/x-data-grid-pro/src/tests/sorting.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/sorting.DataGridPro.test.tsx index cd77c3eb27eef..ab44035ffbf6c 100644 --- a/packages/grid/x-data-grid-pro/src/tests/sorting.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/sorting.DataGridPro.test.tsx @@ -7,7 +7,7 @@ import { useGridApiRef, GridColDef, } from '@mui/x-data-grid-pro'; -import { createRenderer, fireEvent, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { getColumnValues, getCell, getColumnHeaderCell } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid-pro/src/tests/state.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/state.DataGridPro.test.tsx index 266afb032ca1f..4b5090b4e277a 100644 --- a/packages/grid/x-data-grid-pro/src/tests/state.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/state.DataGridPro.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { getColumnValues } from 'test/utils/helperFn'; import { expect } from 'chai'; import { DataGridPro, useGridApiRef, GridApi, DataGridProProps } from '@mui/x-data-grid-pro'; diff --git a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx index a039498fff72b..f21003436f85e 100644 --- a/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/statePersistence.DataGridPro.test.tsx @@ -10,7 +10,7 @@ import { GridRowsProp, useGridApiRef, } from '@mui/x-data-grid-pro'; -import { createRenderer, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, act } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { getColumnHeaderCell, @@ -79,7 +79,7 @@ const FULL_INITIAL_STATE: GridInitialState = { }, }; -describe(' - State Persistence', () => { +describe(' - State persistence', () => { const { render, clock } = createRenderer({ clock: 'fake' }); let apiRef: React.MutableRefObject; diff --git a/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx index f83c86ef62d7e..3df9861f4a112 100644 --- a/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx +++ b/packages/grid/x-data-grid-pro/src/tests/treeData.DataGridPro.test.tsx @@ -1,4 +1,4 @@ -import { createRenderer, fireEvent, screen, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act, userEvent } from '@mui-internal/test-utils'; import { getCell, getColumnHeaderCell, @@ -56,7 +56,7 @@ const baselineProps: DataGridProProps = { getRowId: (row) => row.name, }; -describe(' - Tree Data', () => { +describe(' - Tree data', () => { const { render, clock } = createRenderer({ clock: 'fake' }); let apiRef: React.MutableRefObject; @@ -336,6 +336,27 @@ describe(' - Tree Data', () => { ); expect(getColumnValues(0)).to.deep.equal(['A', 'A', 'B', 'B', 'A', 'B', 'A', 'A', 'C']); }); + + // https://github.com/mui/mui-x/issues/9344 + it('should support valueFormatter', () => { + render( + `> ${value}` }} + defaultGroupingExpansionDepth={-1} + />, + ); + expect(getColumnValues(0)).to.deep.equal([ + '> A (2)', + '> A', + '> B', + '> B (4)', + '> A', + '> B (2)', + '> A (1)', + '> A', + '> C', + ]); + }); }); describe('row grouping column', () => { diff --git a/packages/grid/x-data-grid-pro/src/utils/domUtils.ts b/packages/grid/x-data-grid-pro/src/utils/domUtils.ts index 697c10999dee0..02e1b5ca563f3 100644 --- a/packages/grid/x-data-grid-pro/src/utils/domUtils.ts +++ b/packages/grid/x-data-grid-pro/src/utils/domUtils.ts @@ -56,3 +56,18 @@ export function findGridCellElementsFromCol(col: HTMLElement, api: GridPrivateAp 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!; + const selectorFor = (role: string) => + `:scope > div > div > div > [data-field="${field}"][role="${role}"]`; + // TODO(v7): Keep only the selector for the correct role + return Array.from( + container.querySelectorAll(`${selectorFor('cell')}, ${selectorFor('gridcell')}`), + ); +} diff --git a/packages/grid/x-data-grid-pro/tsconfig.json b/packages/grid/x-data-grid-pro/tsconfig.json index 3d20b7d6a4bb6..79824ab245468 100644 --- a/packages/grid/x-data-grid-pro/tsconfig.json +++ b/packages/grid/x-data-grid-pro/tsconfig.json @@ -6,7 +6,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../../node_modules/@mui/material/themeCssVarsAugmentation" ] } diff --git a/packages/grid/x-data-grid/package.json b/packages/grid/x-data-grid/package.json index 63f4b6237db47..c319be66aca0d 100644 --- a/packages/grid/x-data-grid/package.json +++ b/packages/grid/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "6.14.0", + "version": "6.18.0", "description": "The community edition of the data grid component (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -46,8 +46,8 @@ "directory": "packages/grid/x-data-grid" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/utils": "^5.14.8", + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16", "clsx": "^2.0.0", "prop-types": "^15.8.1", "reselect": "^4.1.8" diff --git a/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx index 22e3860741c39..cec284d7de13f 100644 --- a/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/grid/x-data-grid/src/DataGrid/DataGrid.tsx @@ -40,6 +40,13 @@ interface DataGridComponent { propTypes?: any; } +/** + * Demos: + * - [DataGrid](https://mui.com/x/react-data-grid/demo/) + * + * API: + * - [DataGrid API](https://mui.com/x/api/data-grid/data-grid/) + */ export const DataGrid = React.memo(DataGridRaw) as DataGridComponent; /** @@ -139,16 +146,6 @@ DataGridRaw.propTypes = { * If defined, the grid will ignore the `hide` property in [[GridColDef]]. */ columnVisibilityModel: PropTypes.object, - /** - * Overridable components. - * @deprecated Use `slots` instead. - */ - components: PropTypes.object, - /** - * Overridable components props dynamically passed to the component at rendering. - * @deprecated Use the `slotProps` prop instead. - */ - componentsProps: PropTypes.object, /** * Set the density of the grid. * @default "standard" @@ -294,6 +291,12 @@ DataGridRaw.propTypes = { * @default false */ hideFooterSelectedRowCount: PropTypes.bool, + /** + * If `true`, the diacritics (accents) are ignored when filtering or quick filtering. + * E.g. when filter value is `cafe`, the rows with `café` will be visible. + * @default false + */ + ignoreDiacritics: PropTypes.bool, /** * The initial state of the DataGrid. * The data in it will be set in the state on initialization but will not be controlled. @@ -624,6 +627,13 @@ DataGridRaw.propTypes = { * Controls the modes of the rows. */ rowModesModel: PropTypes.object, + /** + * The milliseconds delay to wait after measuring the row height before recalculating row positions. + * Setting it to a lower value could be useful when using dynamic row height, + * but might reduce performance when displaying a large number of rows. + * @default 166 + */ + rowPositionsDebounceMs: PropTypes.number, /** * Set of rows of type [[GridRowsProp]]. */ @@ -705,7 +715,7 @@ DataGridRaw.propTypes = { /** * If `true`, the grid will not use `valueFormatter` when exporting to CSV or copying to clipboard. * If an object is provided, you can choose to ignore the `valueFormatter` for CSV export or clipboard export. - * @default: false + * @default false */ unstable_ignoreValueFormatterDuringExport: PropTypes.oneOfType([ PropTypes.shape({ diff --git a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx index 2e0db8b05d205..17d5b9e59327a 100644 --- a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx +++ b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx @@ -42,12 +42,16 @@ import { useGridColumnGrouping, columnGroupsStateInitializer, } from '../hooks/features/columnGrouping/useGridColumnGrouping'; +import { + useGridVirtualization, + virtualizationStateInitializer, +} from '../hooks/features/virtualization'; export const useDataGridComponent = ( inputApiRef: React.MutableRefObject | undefined, props: DataGridProcessedProps, ) => { - const privateApiRef = useGridInitialization( + const apiRef = useGridInitialization( inputApiRef, props, ); @@ -55,49 +59,51 @@ export const useDataGridComponent = ( /** * Register all pre-processors called during state initialization here. */ - useGridRowSelectionPreProcessors(privateApiRef, props); - useGridRowsPreProcessors(privateApiRef); + useGridRowSelectionPreProcessors(apiRef, props); + useGridRowsPreProcessors(apiRef); /** * Register all state initializers here. */ - useGridInitializeState(rowSelectionStateInitializer, privateApiRef, props); - useGridInitializeState(columnsStateInitializer, privateApiRef, props); - useGridInitializeState(rowsStateInitializer, privateApiRef, props); - useGridInitializeState(editingStateInitializer, privateApiRef, props); - useGridInitializeState(focusStateInitializer, privateApiRef, props); - useGridInitializeState(sortingStateInitializer, privateApiRef, props); - useGridInitializeState(preferencePanelStateInitializer, privateApiRef, props); - useGridInitializeState(filterStateInitializer, privateApiRef, props); - useGridInitializeState(densityStateInitializer, privateApiRef, props); - useGridInitializeState(paginationStateInitializer, privateApiRef, props); - useGridInitializeState(rowsMetaStateInitializer, privateApiRef, props); - useGridInitializeState(columnMenuStateInitializer, privateApiRef, props); - useGridInitializeState(columnGroupsStateInitializer, privateApiRef, props); + useGridInitializeState(rowSelectionStateInitializer, apiRef, props); + useGridInitializeState(columnsStateInitializer, apiRef, props); + useGridInitializeState(rowsStateInitializer, apiRef, props); + useGridInitializeState(editingStateInitializer, apiRef, props); + useGridInitializeState(focusStateInitializer, apiRef, props); + useGridInitializeState(sortingStateInitializer, apiRef, props); + useGridInitializeState(preferencePanelStateInitializer, apiRef, props); + useGridInitializeState(filterStateInitializer, apiRef, props); + useGridInitializeState(densityStateInitializer, apiRef, props); + useGridInitializeState(paginationStateInitializer, apiRef, props); + useGridInitializeState(rowsMetaStateInitializer, apiRef, props); + useGridInitializeState(columnMenuStateInitializer, apiRef, props); + useGridInitializeState(columnGroupsStateInitializer, apiRef, props); + useGridInitializeState(virtualizationStateInitializer, apiRef, props); - useGridKeyboardNavigation(privateApiRef, props); - useGridRowSelection(privateApiRef, props); - useGridColumns(privateApiRef, props); - useGridRows(privateApiRef, props); - useGridParamsApi(privateApiRef, props); - useGridColumnSpanning(privateApiRef); - useGridColumnGrouping(privateApiRef, props); - useGridEditing(privateApiRef, props); - useGridFocus(privateApiRef, props); - useGridPreferencesPanel(privateApiRef, props); - useGridFilter(privateApiRef, props); - useGridSorting(privateApiRef, props); - useGridDensity(privateApiRef, props); - useGridPagination(privateApiRef, props); - useGridRowsMeta(privateApiRef, props); - useGridScroll(privateApiRef, props); - useGridColumnMenu(privateApiRef); - useGridCsvExport(privateApiRef, props); - useGridPrintExport(privateApiRef, props); - useGridClipboard(privateApiRef, props); - useGridDimensions(privateApiRef, props); - useGridEvents(privateApiRef, props); - useGridStatePersistence(privateApiRef); + useGridKeyboardNavigation(apiRef, props); + useGridRowSelection(apiRef, props); + useGridColumns(apiRef, props); + useGridRows(apiRef, props); + useGridParamsApi(apiRef, props); + useGridColumnSpanning(apiRef); + useGridColumnGrouping(apiRef, props); + useGridEditing(apiRef, props); + useGridFocus(apiRef, props); + useGridPreferencesPanel(apiRef, props); + useGridFilter(apiRef, props); + useGridSorting(apiRef, props); + useGridDensity(apiRef, props); + useGridPagination(apiRef, props); + useGridRowsMeta(apiRef, props); + useGridScroll(apiRef, props); + useGridColumnMenu(apiRef); + useGridCsvExport(apiRef, props); + useGridPrintExport(apiRef, props); + useGridClipboard(apiRef, props); + useGridDimensions(apiRef, props); + useGridEvents(apiRef, props); + useGridStatePersistence(apiRef); + useGridVirtualization(apiRef, props); - return privateApiRef; + return apiRef; }; diff --git a/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts b/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts index bbe85470742a1..57bf123e18980 100644 --- a/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts +++ b/packages/grid/x-data-grid/src/DataGrid/useDataGridProps.ts @@ -9,12 +9,7 @@ import { import { GRID_DEFAULT_LOCALE_TEXT } from '../constants'; import { DATA_GRID_DEFAULT_SLOTS_COMPONENTS } from '../constants/defaultGridSlotsComponents'; import { GridEditModes, GridSlotsComponent, GridValidRowModel } from '../models'; -import { - computeSlots, - useProps, - uncapitalizeObjectKeys, - UncapitalizeObjectKeys, -} from '../internals/utils'; +import { computeSlots, useProps } from '../internals/utils'; const DATA_GRID_FORCED_PROPS: { [key in DataGridForcedPropsKey]?: DataGridProcessedProps[key] } = { disableMultipleColumnsFiltering: true, @@ -62,6 +57,7 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { hideFooterPagination: false, hideFooterRowCount: false, hideFooterSelectedRowCount: false, + ignoreDiacritics: false, logger: console, logLevel: process.env.NODE_ENV === 'production' ? ('error' as const) : ('warn' as const), pagination: false, @@ -80,12 +76,13 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { keepColumnPositionIfDraggedOutside: false, unstable_ignoreValueFormatterDuringExport: false, clipboardCopyCellDelimiter: '\t', + rowPositionsDebounceMs: 166, }; -const defaultSlots = uncapitalizeObjectKeys(DATA_GRID_DEFAULT_SLOTS_COMPONENTS)!; +const defaultSlots = DATA_GRID_DEFAULT_SLOTS_COMPONENTS; export const useDataGridProps = (inProps: DataGridProps) => { - const [components, componentsProps, themedProps] = useProps( + const themedProps = useProps( useThemeProps({ props: inProps, name: 'MuiDataGrid', @@ -97,14 +94,13 @@ export const useDataGridProps = (inProps: DataGridP [themedProps.localeText], ); - const slots = React.useMemo>( + const slots = React.useMemo( () => computeSlots({ defaultSlots, slots: themedProps.slots, - components, }), - [components, themedProps.slots], + [themedProps.slots], ); return React.useMemo>( @@ -113,9 +109,8 @@ export const useDataGridProps = (inProps: DataGridP ...themedProps, localeText, slots, - slotProps: themedProps.slotProps ?? componentsProps, ...DATA_GRID_FORCED_PROPS, }), - [themedProps, localeText, slots, componentsProps], + [themedProps, localeText, slots], ); }; diff --git a/packages/grid/x-data-grid/src/colDef/gridActionsColDef.tsx b/packages/grid/x-data-grid/src/colDef/gridActionsColDef.tsx index ad197534905b9..5c27a57a04580 100644 --- a/packages/grid/x-data-grid/src/colDef/gridActionsColDef.tsx +++ b/packages/grid/x-data-grid/src/colDef/gridActionsColDef.tsx @@ -18,5 +18,4 @@ export const GRID_ACTIONS_COL_DEF: GridColTypeDef = { disableExport: true, renderCell: renderActionsCell, getApplyQuickFilterFn: undefined, - getApplyQuickFilterFnV7: undefined, }; diff --git a/packages/grid/x-data-grid/src/colDef/gridBooleanColDef.tsx b/packages/grid/x-data-grid/src/colDef/gridBooleanColDef.tsx index c208d7822275e..7c2abdc9416e8 100644 --- a/packages/grid/x-data-grid/src/colDef/gridBooleanColDef.tsx +++ b/packages/grid/x-data-grid/src/colDef/gridBooleanColDef.tsx @@ -42,7 +42,6 @@ export const GRID_BOOLEAN_COL_DEF: GridColTypeDef = { valueFormatter: gridBooleanFormatter, filterOperators: getGridBooleanOperators(), getApplyQuickFilterFn: undefined, - getApplyQuickFilterFnV7: undefined, // @ts-ignore aggregable: false, // @ts-ignore diff --git a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts index 9c93a623274d2..db8eb32f4ba1b 100644 --- a/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridBooleanOperators.ts @@ -1,22 +1,20 @@ import { GridFilterInputBoolean } from '../components/panel/filterPanel/GridFilterInputBoolean'; import { GridFilterItem } from '../models/gridFilterItem'; import { GridFilterOperator } from '../models/gridFilterOperator'; -import { convertLegacyOperators } from './utils'; -export const getGridBooleanOperators = (): GridFilterOperator[] => - convertLegacyOperators([ - { - value: 'is', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!filterItem.value) { - return null; - } +export const getGridBooleanOperators = (): GridFilterOperator[] => [ + { + value: 'is', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!filterItem.value) { + return null; + } - const valueAsBoolean = filterItem.value === 'true'; - return (value): boolean => { - return Boolean(value) === valueAsBoolean; - }; - }, - InputComponent: GridFilterInputBoolean, + const valueAsBoolean = filterItem.value === 'true'; + return (value): boolean => { + return Boolean(value) === valueAsBoolean; + }; }, - ]); + InputComponent: GridFilterInputBoolean, + }, +]; diff --git a/packages/grid/x-data-grid/src/colDef/gridCheckboxSelectionColDef.tsx b/packages/grid/x-data-grid/src/colDef/gridCheckboxSelectionColDef.tsx index 662782884b53c..f036ca4082ad0 100644 --- a/packages/grid/x-data-grid/src/colDef/gridCheckboxSelectionColDef.tsx +++ b/packages/grid/x-data-grid/src/colDef/gridCheckboxSelectionColDef.tsx @@ -21,7 +21,6 @@ export const GRID_CHECKBOX_SELECTION_COL_DEF: GridColDef = { disableReorder: true, disableExport: true, getApplyQuickFilterFn: undefined, - getApplyQuickFilterFnV7: undefined, valueGetter: (params) => { const selectionLookup = selectedIdsLookupSelector(params.api.state, params.api.instanceId); return selectionLookup[params.id] !== undefined; diff --git a/packages/grid/x-data-grid/src/colDef/gridDateColDef.ts b/packages/grid/x-data-grid/src/colDef/gridDateColDef.ts index cb09fa6d40cdd..18e9e29f3ffc6 100644 --- a/packages/grid/x-data-grid/src/colDef/gridDateColDef.ts +++ b/packages/grid/x-data-grid/src/colDef/gridDateColDef.ts @@ -50,8 +50,6 @@ export const GRID_DATE_COL_DEF: GridColTypeDef = { valueFormatter: gridDateFormatter, filterOperators: getGridDateOperators(), renderEditCell: renderEditDateCell, - getApplyQuickFilterFn: undefined, - getApplyQuickFilterFnV7: undefined, // @ts-ignore pastedValueParser: (value) => new Date(value), }; @@ -63,8 +61,6 @@ export const GRID_DATETIME_COL_DEF: GridColTypeDef = { valueFormatter: gridDateTimeFormatter, filterOperators: getGridDateOperators(true), renderEditCell: renderEditDateCell, - getApplyQuickFilterFn: undefined, - getApplyQuickFilterFnV7: undefined, // @ts-ignore pastedValueParser: (value) => new Date(value), }; diff --git a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts index ddfd18ec5fc97..5898f2745da13 100644 --- a/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridDateOperators.ts @@ -1,7 +1,6 @@ import { GridFilterInputDate } from '../components/panel/filterPanel/GridFilterInputDate'; import { GridFilterItem } from '../models/gridFilterItem'; -import { GridFilterOperator, GetApplyFilterFnV7 } from '../models/gridFilterOperator'; -import { convertLegacyOperators } from './utils'; +import { GridFilterOperator, GetApplyFilterFn } from '../models/gridFilterOperator'; const dateRegex = /(\d+)-(\d+)-(\d+)/; const dateTimeRegex = /(\d+)-(\d+)-(\d+)T(\d+):(\d+)/; @@ -11,7 +10,7 @@ function buildApplyFilterFn( compareFn: (value1: number, value2: number) => boolean, showTime?: boolean, keepHours?: boolean, -): ReturnType { +): ReturnType { if (!filterItem.value) { return null; } @@ -44,77 +43,76 @@ function buildApplyFilterFn( }; } -export const getGridDateOperators = (showTime?: boolean): GridFilterOperator[] => - convertLegacyOperators([ - { - value: 'is', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn(filterItem, (value1, value2) => value1 === value2, showTime); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, +export const getGridDateOperators = (showTime?: boolean): GridFilterOperator[] => [ + { + value: 'is', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn(filterItem, (value1, value2) => value1 === value2, showTime); }, - { - value: 'not', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn(filterItem, (value1, value2) => value1 !== value2, showTime); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'not', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn(filterItem, (value1, value2) => value1 !== value2, showTime); }, - { - value: 'after', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn(filterItem, (value1, value2) => value1 > value2, showTime); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'after', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn(filterItem, (value1, value2) => value1 > value2, showTime); }, - { - value: 'onOrAfter', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn(filterItem, (value1, value2) => value1 >= value2, showTime); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'onOrAfter', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn(filterItem, (value1, value2) => value1 >= value2, showTime); }, - { - value: 'before', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn( - filterItem, - (value1, value2) => value1 < value2, - showTime, - !showTime, - ); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'before', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn( + filterItem, + (value1, value2) => value1 < value2, + showTime, + !showTime, + ); }, - { - value: 'onOrBefore', - getApplyFilterFnV7: (filterItem) => { - return buildApplyFilterFn(filterItem, (value1, value2) => value1 <= value2, showTime); - }, - InputComponent: GridFilterInputDate, - InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'onOrBefore', + getApplyFilterFn: (filterItem) => { + return buildApplyFilterFn(filterItem, (value1, value2) => value1 <= value2, showTime); }, - { - value: 'isEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value == null; - }; - }, - requiresFilterValue: false, + InputComponent: GridFilterInputDate, + InputComponentProps: { type: showTime ? 'datetime-local' : 'date' }, + }, + { + value: 'isEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value == null; + }; }, - { - value: 'isNotEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value != null; - }; - }, - requiresFilterValue: false, + requiresFilterValue: false, + }, + { + value: 'isNotEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value != null; + }; }, - ]); + requiresFilterValue: false, + }, +]; diff --git a/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts b/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts index 46c1fc55bde4c..cca2a740ff04a 100644 --- a/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts +++ b/packages/grid/x-data-grid/src/colDef/gridNumericColDef.ts @@ -3,7 +3,6 @@ import { isNumber } from '../utils/utils'; import { getGridNumericOperators, getGridNumericQuickFilterFn } from './gridNumericOperators'; import { GRID_STRING_COL_DEF } from './gridStringColDef'; import { GridColTypeDef } from '../models/colDef/gridColDef'; -import { convertQuickFilterV7ToLegacy } from './utils'; export const GRID_NUMERIC_COL_DEF: GridColTypeDef = { ...GRID_STRING_COL_DEF, @@ -14,6 +13,5 @@ export const GRID_NUMERIC_COL_DEF: GridColTypeDef (value === '' ? null : Number(value)), valueFormatter: ({ value }) => (isNumber(value) ? value.toLocaleString() : value || ''), filterOperators: getGridNumericOperators(), - getApplyQuickFilterFn: convertQuickFilterV7ToLegacy(getGridNumericQuickFilterFn), - getApplyQuickFilterFnV7: getGridNumericQuickFilterFn, + getApplyQuickFilterFn: getGridNumericQuickFilterFn, }; diff --git a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts index 22147e4ea448e..ced58577c0ee9 100644 --- a/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridNumericOperators.ts @@ -1,8 +1,7 @@ import { GridFilterInputValue } from '../components/panel/filterPanel/GridFilterInputValue'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; import { GridFilterOperator } from '../models/gridFilterOperator'; -import type { GridApplyQuickFilterV7 } from '../models/colDef/gridColDef'; -import { convertLegacyOperators, tagInternalFilter } from './utils'; +import type { GetApplyQuickFilterFn } from '../models/colDef/gridColDef'; const parseNumericValue = (value: unknown) => { if (value == null) { @@ -12,150 +11,153 @@ const parseNumericValue = (value: unknown) => { return Number(value); }; -export const getGridNumericQuickFilterFn = tagInternalFilter( - (value: any): GridApplyQuickFilterV7 | null => { - if (value == null || Number.isNaN(value) || value === '') { - return null; - } +export const getGridNumericQuickFilterFn: GetApplyQuickFilterFn = ( + value, +) => { + if (value == null || Number.isNaN(value) || value === '') { + return null; + } - return (columnValue): boolean => { - return parseNumericValue(columnValue) === parseNumericValue(value); - }; - }, -); - -export const getGridNumericOperators = (): GridFilterOperator[] => - convertLegacyOperators([ - { - value: '=', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; - } + return (columnValue) => { + return parseNumericValue(columnValue) === parseNumericValue(value); + }; +}; - return (value): boolean => { - return parseNumericValue(value) === filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, +export const getGridNumericOperators = (): GridFilterOperator< + any, + number | string | null, + any +>[] => [ + { + value: '=', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + return parseNumericValue(value) === filterItem.value; + }; }, - { - value: '!=', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; - } - - return (value): boolean => { - return parseNumericValue(value) !== filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: '!=', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + return parseNumericValue(value) !== filterItem.value; + }; }, - { - value: '>', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: '>', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + if (value == null) { + return false; } - return (value): boolean => { - if (value == null) { - return false; - } - - return parseNumericValue(value)! > filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, + return parseNumericValue(value)! > filterItem.value; + }; }, - { - value: '>=', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: '>=', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + if (value == null) { + return false; } - return (value): boolean => { - if (value == null) { - return false; - } - - return parseNumericValue(value)! >= filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, + return parseNumericValue(value)! >= filterItem.value; + }; }, - { - value: '<', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: '<', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + if (value == null) { + return false; } - return (value): boolean => { - if (value == null) { - return false; - } - - return parseNumericValue(value)! < filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, + return parseNumericValue(value)! < filterItem.value; + }; }, - { - value: '<=', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || Number.isNaN(filterItem.value)) { - return null; + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: '<=', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || Number.isNaN(filterItem.value)) { + return null; + } + + return (value): boolean => { + if (value == null) { + return false; } - return (value): boolean => { - if (value == null) { - return false; - } - - return parseNumericValue(value)! <= filterItem.value; - }; - }, - InputComponent: GridFilterInputValue, - InputComponentProps: { type: 'number' }, + return parseNumericValue(value)! <= filterItem.value; + }; }, - { - value: 'isEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value == null; - }; - }, - requiresFilterValue: false, + InputComponent: GridFilterInputValue, + InputComponentProps: { type: 'number' }, + }, + { + value: 'isEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value == null; + }; }, - { - value: 'isNotEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value != null; - }; - }, - requiresFilterValue: false, + requiresFilterValue: false, + }, + { + value: 'isNotEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value != null; + }; }, - { - value: 'isAnyOf', - getApplyFilterFnV7: (filterItem) => { - if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { - return null; - } - - return (value): boolean => { - return value != null && filterItem.value.includes(Number(value)); - }; - }, - InputComponent: GridFilterInputMultipleValue, - InputComponentProps: { type: 'number' }, + requiresFilterValue: false, + }, + { + value: 'isAnyOf', + getApplyFilterFn: (filterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { + return null; + } + + return (value): boolean => { + return value != null && filterItem.value.includes(Number(value)); + }; }, - ]); + InputComponent: GridFilterInputMultipleValue, + InputComponentProps: { type: 'number' }, + }, +]; diff --git a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts index 59dcd552b43f9..bd603578ad6e1 100644 --- a/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridSingleSelectOperators.ts @@ -2,7 +2,6 @@ import { GridFilterInputSingleSelect } from '../components/panel/filterPanel/Gri import { GridFilterOperator } from '../models/gridFilterOperator'; import { GridFilterInputMultipleSingleSelect } from '../components/panel/filterPanel/GridFilterInputMultipleSingleSelect'; import { isObject } from '../utils/utils'; -import { convertLegacyOperators } from './utils'; const parseObjectValue = (value: unknown) => { if (value == null || !isObject<{ value: unknown }>(value)) { @@ -11,37 +10,36 @@ const parseObjectValue = (value: unknown) => { return value.value; }; -export const getGridSingleSelectOperators = (): GridFilterOperator[] => - convertLegacyOperators([ - { - value: 'is', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || filterItem.value === '') { - return null; - } - return (value): boolean => parseObjectValue(value) === parseObjectValue(filterItem.value); - }, - InputComponent: GridFilterInputSingleSelect, +export const getGridSingleSelectOperators = (): GridFilterOperator[] => [ + { + value: 'is', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || filterItem.value === '') { + return null; + } + return (value): boolean => parseObjectValue(value) === parseObjectValue(filterItem.value); }, - { - value: 'not', - getApplyFilterFnV7: (filterItem) => { - if (filterItem.value == null || filterItem.value === '') { - return null; - } - return (value): boolean => parseObjectValue(value) !== parseObjectValue(filterItem.value); - }, - InputComponent: GridFilterInputSingleSelect, + InputComponent: GridFilterInputSingleSelect, + }, + { + value: 'not', + getApplyFilterFn: (filterItem) => { + if (filterItem.value == null || filterItem.value === '') { + return null; + } + return (value): boolean => parseObjectValue(value) !== parseObjectValue(filterItem.value); }, - { - value: 'isAnyOf', - getApplyFilterFnV7: (filterItem) => { - if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { - return null; - } - const filterItemValues = filterItem.value.map(parseObjectValue); - return (value): boolean => filterItemValues.includes(parseObjectValue(value)); - }, - InputComponent: GridFilterInputMultipleSingleSelect, + InputComponent: GridFilterInputSingleSelect, + }, + { + value: 'isAnyOf', + getApplyFilterFn: (filterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { + return null; + } + const filterItemValues = filterItem.value.map(parseObjectValue); + return (value): boolean => filterItemValues.includes(parseObjectValue(value)); }, - ]); + InputComponent: GridFilterInputMultipleSingleSelect, + }, +]; diff --git a/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts b/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts index 5c1987c056c11..e9f764da95441 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringColDef.ts @@ -2,7 +2,6 @@ import { renderEditInputCell } from '../components/cell/GridEditInputCell'; import { gridStringOrNumberComparator } from '../hooks/features/sorting/gridSortingUtils'; import { GridColTypeDef } from '../models/colDef/gridColDef'; import { getGridStringOperators, getGridStringQuickFilterFn } from './gridStringOperators'; -import { convertQuickFilterV7ToLegacy } from './utils'; /** * TODO: Move pro and premium properties outside of this Community file @@ -25,6 +24,5 @@ export const GRID_STRING_COL_DEF: GridColTypeDef = { align: 'left', filterOperators: getGridStringOperators(), renderEditCell: renderEditInputCell, - getApplyQuickFilterFn: convertQuickFilterV7ToLegacy(getGridStringQuickFilterFn), - getApplyQuickFilterFnV7: getGridStringQuickFilterFn, + getApplyQuickFilterFn: getGridStringQuickFilterFn, }; diff --git a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts index 9a00deb28c33d..5d35dc96e6965 100644 --- a/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts +++ b/packages/grid/x-data-grid/src/colDef/gridStringOperators.ts @@ -1,124 +1,124 @@ import { GridFilterInputValue } from '../components/panel/filterPanel/GridFilterInputValue'; import { escapeRegExp } from '../utils/utils'; -import type { GridApplyQuickFilterV7 } from '../models/colDef/gridColDef'; +import type { GetApplyQuickFilterFn } from '../models/colDef/gridColDef'; import { GridFilterItem } from '../models/gridFilterItem'; import { GridFilterOperator } from '../models/gridFilterOperator'; import { GridFilterInputMultipleValue } from '../components/panel/filterPanel/GridFilterInputMultipleValue'; -import { convertLegacyOperators, tagInternalFilter } from './utils'; +import { removeDiacritics } from '../hooks/features/filter/gridFilterUtils'; -export const getGridStringQuickFilterFn = tagInternalFilter( - (value: any): GridApplyQuickFilterV7 | null => { - if (!value) { - return null; +export const getGridStringQuickFilterFn: GetApplyQuickFilterFn = (value) => { + if (!value) { + return null; + } + const filterRegex = new RegExp(escapeRegExp(value), 'i'); + return (_, row, column, apiRef) => { + let columnValue = apiRef.current.getRowFormattedValue(row, column); + if (apiRef.current.ignoreDiacritics) { + columnValue = removeDiacritics(columnValue); } - const filterRegex = new RegExp(escapeRegExp(value), 'i'); - return (_, row, column, apiRef): boolean => { - const columnValue = apiRef.current.getRowFormattedValue(row, column); - return columnValue != null ? filterRegex.test(columnValue.toString()) : false; - }; - }, -); + return columnValue != null ? filterRegex.test(columnValue.toString()) : false; + }; +}; export const getGridStringOperators = ( disableTrim: boolean = false, -): GridFilterOperator[] => - convertLegacyOperators([ - { - value: 'contains', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!filterItem.value) { - return null; - } - const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); +): GridFilterOperator[] => [ + { + value: 'contains', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!filterItem.value) { + return null; + } + const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); - const filterRegex = new RegExp(escapeRegExp(filterItemValue), 'i'); - return (value): boolean => { - return value != null ? filterRegex.test(String(value)) : false; - }; - }, - InputComponent: GridFilterInputValue, + const filterRegex = new RegExp(escapeRegExp(filterItemValue), 'i'); + return (value): boolean => { + return value != null ? filterRegex.test(String(value)) : false; + }; }, - { - value: 'equals', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!filterItem.value) { - return null; - } - const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); + InputComponent: GridFilterInputValue, + }, + { + value: 'equals', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!filterItem.value) { + return null; + } + const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); - const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return (value): boolean => { - return value != null ? collator.compare(filterItemValue, value.toString()) === 0 : false; - }; - }, - InputComponent: GridFilterInputValue, + const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); + return (value): boolean => { + return value != null ? collator.compare(filterItemValue, value.toString()) === 0 : false; + }; }, - { - value: 'startsWith', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!filterItem.value) { - return null; - } - const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); + InputComponent: GridFilterInputValue, + }, + { + value: 'startsWith', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!filterItem.value) { + return null; + } + const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); - const filterRegex = new RegExp(`^${escapeRegExp(filterItemValue)}.*$`, 'i'); - return (value): boolean => { - return value != null ? filterRegex.test(value.toString()) : false; - }; - }, - InputComponent: GridFilterInputValue, + const filterRegex = new RegExp(`^${escapeRegExp(filterItemValue)}.*$`, 'i'); + return (value): boolean => { + return value != null ? filterRegex.test(value.toString()) : false; + }; }, - { - value: 'endsWith', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!filterItem.value) { - return null; - } - const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); + InputComponent: GridFilterInputValue, + }, + { + value: 'endsWith', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!filterItem.value) { + return null; + } + const filterItemValue = disableTrim ? filterItem.value : filterItem.value.trim(); - const filterRegex = new RegExp(`.*${escapeRegExp(filterItemValue)}$`, 'i'); - return (value): boolean => { - return value != null ? filterRegex.test(value.toString()) : false; - }; - }, - InputComponent: GridFilterInputValue, + const filterRegex = new RegExp(`.*${escapeRegExp(filterItemValue)}$`, 'i'); + return (value): boolean => { + return value != null ? filterRegex.test(value.toString()) : false; + }; }, - { - value: 'isEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value === '' || value == null; - }; - }, - requiresFilterValue: false, + InputComponent: GridFilterInputValue, + }, + { + value: 'isEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value === '' || value == null; + }; }, - { - value: 'isNotEmpty', - getApplyFilterFnV7: () => { - return (value): boolean => { - return value !== '' && value != null; - }; - }, - requiresFilterValue: false, + requiresFilterValue: false, + }, + { + value: 'isNotEmpty', + getApplyFilterFn: () => { + return (value): boolean => { + return value !== '' && value != null; + }; }, - { - value: 'isAnyOf', - getApplyFilterFnV7: (filterItem: GridFilterItem) => { - if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { - return null; - } - const filterItemValue = disableTrim - ? filterItem.value - : filterItem.value.map((val) => val.trim()); - const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); + requiresFilterValue: false, + }, + { + value: 'isAnyOf', + getApplyFilterFn: (filterItem: GridFilterItem) => { + if (!Array.isArray(filterItem.value) || filterItem.value.length === 0) { + return null; + } + const filterItemValue = disableTrim + ? filterItem.value + : filterItem.value.map((val) => val.trim()); + const collator = new Intl.Collator(undefined, { sensitivity: 'base', usage: 'search' }); - return (value): boolean => - value != null - ? filterItemValue.some((filterValue: GridFilterItem['value']) => { - return collator.compare(filterValue, value.toString() || '') === 0; - }) - : false; - }, - InputComponent: GridFilterInputMultipleValue, + return (value): boolean => + value != null + ? filterItemValue.some((filterValue: GridFilterItem['value']) => { + return collator.compare(filterValue, value.toString() || '') === 0; + }) + : false; }, - ]); + InputComponent: GridFilterInputMultipleValue, + }, +]; diff --git a/packages/grid/x-data-grid/src/colDef/utils.ts b/packages/grid/x-data-grid/src/colDef/utils.ts deleted file mode 100644 index eadd8fbda44cc..0000000000000 --- a/packages/grid/x-data-grid/src/colDef/utils.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as React from 'react'; -import { GridApiCommunity } from '../models/api/gridApiCommunity'; -import { GetApplyFilterFnV7, GetApplyFilterFnLegacy, GridFilterOperator } from '../models'; -import { GetApplyQuickFilterFnV7, GetApplyQuickFilterFnLegacy } from '../models/colDef/gridColDef'; - -/** - * A global API ref, for v7-to-legacy converter - */ -export const GLOBAL_API_REF = { - current: null as null | React.MutableRefObject, -}; - -/** - * A tagger to determine if the filter is internal or custom user-supplied. - * To be a valid internal filter, the v7 function *must* be defined/redefined at - * the same time as the legacy one. - * https://github.com/mui/mui-x/pull/9254#discussion_r1231095551 - */ -export function tagInternalFilter(fn: T): T { - (fn as any).isInternal = true; - return fn; -} - -export function isInternalFilter(fn: Function | undefined): boolean { - return fn !== undefined && (fn as any).isInternal === true; -} - -export function convertFilterV7ToLegacy(fn: GetApplyFilterFnV7): GetApplyFilterFnLegacy { - return tagInternalFilter((filterItem, column) => { - const filterFn = fn(filterItem, column); - if (!filterFn) { - return filterFn; - } - return (cellParams): boolean => { - return filterFn(cellParams.value, cellParams.row, column, GLOBAL_API_REF.current!); - }; - }); -} - -export function convertLegacyOperators( - ops: Omit[], -): GridFilterOperator[] { - return ops.map((op) => { - return { - ...op, - getApplyFilterFn: convertFilterV7ToLegacy(op.getApplyFilterFnV7!), - getApplyFilterFnV7: tagInternalFilter(op.getApplyFilterFnV7!), - }; - }); -} - -export function convertQuickFilterV7ToLegacy( - fn: GetApplyQuickFilterFnV7, -): GetApplyQuickFilterFnLegacy { - return tagInternalFilter((filterItem, column, apiRef) => { - const filterFn = fn(filterItem, column, apiRef); - if (!filterFn) { - return filterFn; - } - return (cellParams): boolean => { - return filterFn(cellParams.value, cellParams.row, column, apiRef); - }; - }); -} diff --git a/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx b/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx index 2589c5423c7e1..e10e75dd2724f 100644 --- a/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx +++ b/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx @@ -5,30 +5,26 @@ import { GridVirtualScrollerRenderZone } from './virtualization/GridVirtualScrol import { useGridVirtualScroller } from '../hooks/features/virtualization/useGridVirtualScroller'; import { GridOverlays } from './base/GridOverlays'; -interface DataGridVirtualScrollerProps extends React.HTMLAttributes { - disableVirtualization?: boolean; -} +const DataGridVirtualScroller = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(function DataGridVirtualScroller(props, ref) { + const { className, ...other } = props; -const DataGridVirtualScroller = React.forwardRef( - function DataGridVirtualScroller(props, ref) { - const { className, disableVirtualization, ...other } = props; + const { getRootProps, getContentProps, getRenderZoneProps, getRows } = useGridVirtualScroller({ + ref, + }); - const { getRootProps, getContentProps, getRenderZoneProps, getRows } = useGridVirtualScroller({ - ref, - disableVirtualization, - }); - - return ( - - - - - {getRows()} - - - - ); - }, -); + return ( + + + + + {getRows()} + + + + ); +}); export { DataGridVirtualScroller }; diff --git a/packages/grid/x-data-grid/src/components/GridRow.tsx b/packages/grid/x-data-grid/src/components/GridRow.tsx index a27bef8934be8..3e21241918a59 100644 --- a/packages/grid/x-data-grid/src/components/GridRow.tsx +++ b/packages/grid/x-data-grid/src/components/GridRow.tsx @@ -103,6 +103,7 @@ function EmptyCell({ width }: { width: number }) { const GridRow = React.forwardRef(function GridRow(props, refProp) { const { selected, + hovered, rowId, row, index, @@ -124,6 +125,8 @@ const GridRow = React.forwardRef(function GridRow( onDoubleClick, onMouseEnter, onMouseLeave, + onMouseOut, + onMouseOver, ...other } = props; const apiRef = useGridApiContext(); @@ -141,6 +144,7 @@ const GridRow = React.forwardRef(function GridRow( const ownerState = { selected, + hovered, isLastVisible, classes: rootProps.classes, editing: apiRef.current.getRowMode(rowId) === GridRowModes.Edit, @@ -447,6 +451,8 @@ const GridRow = React.forwardRef(function GridRow( onDoubleClick: publish('rowDoubleClick', onDoubleClick), onMouseEnter: publish('rowMouseEnter', onMouseEnter), onMouseLeave: publish('rowMouseLeave', onMouseLeave), + onMouseOut: publish('rowMouseOut', onMouseOut), + onMouseOver: publish('rowMouseOver', onMouseOver), } : null; @@ -456,7 +462,7 @@ const GridRow = React.forwardRef(function GridRow( data-id={rowId} data-rowindex={index} role="row" - className={clsx(...rowClassNames, classes.root, className)} + className={clsx(...rowClassNames, classes.root, hovered ? 'Mui-hovered' : null, className)} aria-rowindex={ariaRowIndex} aria-selected={selected} style={style} diff --git a/packages/grid/x-data-grid/src/components/base/GridBody.tsx b/packages/grid/x-data-grid/src/components/base/GridBody.tsx index 320501ca349ed..8c0ea5762311a 100644 --- a/packages/grid/x-data-grid/src/components/base/GridBody.tsx +++ b/packages/grid/x-data-grid/src/components/base/GridBody.tsx @@ -32,7 +32,6 @@ interface GridBodyProps { VirtualScrollerComponent: React.JSXElementConstructor< React.HTMLAttributes & { ref: React.Ref; - disableVirtualization: boolean; } >; } @@ -76,10 +75,6 @@ function GridBody(props: GridBodyProps) { cellTabIndexState === null ); - const [isVirtualizationDisabled, setIsVirtualizationDisabled] = React.useState( - rootProps.disableVirtualization, - ); - useEnhancedEffect(() => { apiRef.current.computeSizeAndPublishResizeEvent(); @@ -111,27 +106,6 @@ function GridBody(props: GridBodyProps) { }; }, [apiRef]); - const disableVirtualization = React.useCallback(() => { - setIsVirtualizationDisabled(true); - }, []); - - const enableVirtualization = React.useCallback(() => { - setIsVirtualizationDisabled(false); - }, []); - - React.useEffect(() => { - setIsVirtualizationDisabled(rootProps.disableVirtualization); - }, [rootProps.disableVirtualization]); - - // The `useGridApiMethod` hook can't be used here, because it only installs the - // method if it doesn't exist yet. Once installed, it's never updated again. - // This break the methods above, since their closure comes from the first time - // they were installed. Which means that calling `setIsVirtualizationDisabled` - // will trigger a re-render, but it won't update the state. That can be solved - // by migrating the virtualization status to the global state. - apiRef.current.unstable_disableVirtualization = disableVirtualization; - apiRef.current.unstable_enableVirtualization = enableVirtualization; - const columnHeadersRef = React.useRef(null); const columnsContainerRef = React.useRef(null); const virtualScrollerRef = React.useRef(null); @@ -174,7 +148,6 @@ function GridBody(props: GridBodyProps) { // If this event is published while dimensions haven't been computed, // the `onFetchRows` prop won't be called during mount. ref={virtualScrollerRef} - disableVirtualization={isVirtualizationDisabled} /> )} diff --git a/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx b/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx index 103f594dc95c1..c42d8627b895e 100644 --- a/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx +++ b/packages/grid/x-data-grid/src/components/cell/GridActionsCell.tsx @@ -97,11 +97,13 @@ function GridActionsCell(props: GridActionsCellProps) { focus() { // If ignoreCallToFocus is true, then one of the buttons was clicked and the focus is already set if (!ignoreCallToFocus.current) { - setFocusedButtonIndex(0); + // find the first focusable button and pass the index to the state + const focusableButtonIndex = options.findIndex((o) => !o.props.disabled); + setFocusedButtonIndex(focusableButtonIndex); } }, }), - [], + [options], ); React.useEffect(() => { @@ -141,19 +143,26 @@ function GridActionsCell(props: GridActionsCellProps) { return; } + const getNewIndex = (index: number, direction: 'left' | 'right'): number => { + if (index < 0 || index > options.length) { + return index; + } + + // for rtl mode we need to reverse the direction + const rtlMod = theme.direction === 'rtl' ? -1 : 1; + const indexMod = (direction === 'left' ? -1 : 1) * rtlMod; + + // if the button that should receive focus is disabled go one more step + return options[index + indexMod]?.props.disabled + ? getNewIndex(index + indexMod, direction) + : index + indexMod; + }; + let newIndex: number = focusedButtonIndex; if (event.key === 'ArrowRight') { - if (theme.direction === 'rtl') { - newIndex -= 1; - } else { - newIndex += 1; - } + newIndex = getNewIndex(focusedButtonIndex, 'right'); } else if (event.key === 'ArrowLeft') { - if (theme.direction === 'rtl') { - newIndex += 1; - } else { - newIndex -= 1; - } + newIndex = getNewIndex(focusedButtonIndex, 'left'); } if (newIndex < 0 || newIndex >= numberOfButtons) { @@ -215,11 +224,11 @@ function GridActionsCell(props: GridActionsCellProps) { {menuButtons.length > 0 && ( & { isEditable?: boolean; isSelected?: boolean; + isSelectionMode?: boolean; classes?: DataGridProcessedProps['classes']; }; const useUtilityClasses = (ownerState: OwnerState) => { - const { align, showRightBorder, isEditable, isSelected, classes } = ownerState; + const { align, showRightBorder, isEditable, isSelected, isSelectionMode, classes } = ownerState; const slots = { root: [ @@ -114,6 +116,7 @@ const useUtilityClasses = (ownerState: OwnerState) => { isEditable && 'cell--editable', isSelected && 'selected', showRightBorder && 'cell--withRightBorder', + isSelectionMode && !isEditable && 'cell--selectionMode', 'withBorderColor', ], content: ['cellContent'], @@ -583,9 +586,13 @@ const GridCellV7 = React.forwardRef((props, ref const { cellMode, hasFocus, isEditable, value, formattedValue } = cellParamsWithAPI; - const managesOwnFocus = column.type === 'actions'; + const canManageOwnFocus = + column.type === 'actions' && + (column as GridActionsColDef) + .getActions?.(apiRef.current.getRowParams(rowId)) + .some((action) => !action.props.disabled); const tabIndex = - (cellMode === 'view' || !isEditable) && !managesOwnFocus ? cellParamsWithAPI.tabIndex : -1; + (cellMode === 'view' || !isEditable) && !canManageOwnFocus ? cellParamsWithAPI.tabIndex : -1; const { classes: rootClasses, getCellClassName } = rootProps; @@ -610,7 +617,16 @@ const GridCellV7 = React.forwardRef((props, ref const cellRef = React.useRef(null); const handleRef = useForkRef(ref, cellRef); const focusElementRef = React.useRef(null); - const ownerState = { align, showRightBorder, isEditable, classes: rootProps.classes, isSelected }; + // @ts-expect-error To access `unstable_cellSelection` flag as it's a `premium` feature + const isSelectionMode = rootProps.unstable_cellSelection ?? false; + const ownerState = { + align, + showRightBorder, + isEditable, + classes: rootProps.classes, + isSelected, + isSelectionMode, + }; const classes = useUtilityClasses(ownerState); const publishMouseUp = React.useCallback( @@ -761,7 +777,7 @@ const GridCellV7 = React.forwardRef((props, ref ); } - if (React.isValidElement(children) && managesOwnFocus) { + if (React.isValidElement(children) && canManageOwnFocus) { children = React.cloneElement(children, { focusElementRef }); } diff --git a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx index ba7777efbed80..9dcd1dde3a739 100644 --- a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx +++ b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx @@ -152,7 +152,10 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { ); const columnHeaderSeparatorProps = React.useMemo( - () => ({ onMouseDown: publish('columnSeparatorMouseDown') }), + () => ({ + onMouseDown: publish('columnSeparatorMouseDown'), + onDoubleClick: publish('columnSeparatorDoubleClick'), + }), [publish], ); @@ -189,7 +192,7 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) { /> ); - const sortingOrder: GridSortDirection[] = colDef.sortingOrder ?? rootProps.sortingOrder; + const sortingOrder: readonly GridSortDirection[] = colDef.sortingOrder ?? rootProps.sortingOrder; const columnTitleIconButtons = ( diff --git a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderSortIcon.tsx b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderSortIcon.tsx index b6f489f73a181..3ae5296646f7d 100644 --- a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderSortIcon.tsx +++ b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderSortIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import Badge from '@mui/material/Badge'; -import { UncapitalizedGridSlotsComponent } from '../../models/gridSlotsComponent'; +import { GridSlotsComponent } from '../../models/gridSlotsComponent'; import { GridSortDirection } from '../../models/gridSortModel'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; @@ -13,7 +13,7 @@ import { GridIconButtonContainer } from './GridIconButtonContainer'; export interface GridColumnHeaderSortIconProps { direction: GridSortDirection; index: number | undefined; - sortingOrder: GridSortDirection[]; + sortingOrder: readonly GridSortDirection[]; } type OwnerState = GridColumnHeaderSortIconProps & { @@ -31,10 +31,10 @@ const useUtilityClasses = (ownerState: OwnerState) => { }; function getIcon( - icons: UncapitalizedGridSlotsComponent, + icons: GridSlotsComponent, direction: GridSortDirection, className: string, - sortingOrder: GridSortDirection[], + sortingOrder: readonly GridSortDirection[], ) { let Icon; const iconProps: any = {}; diff --git a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts index 9312cc84def07..6d2b94793f464 100644 --- a/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts +++ b/packages/grid/x-data-grid/src/components/containers/GridRootStyles.ts @@ -56,6 +56,7 @@ export const GridRootStyles = styled('div', { [`&.${gridClasses['root--disableUserSelection']} .${gridClasses.cell}`]: styles['root--disableUserSelection'], }, + { [`&.${gridClasses.autosizing}`]: styles.autosizing }, { [`& .${gridClasses.editBooleanCell}`]: styles.editBooleanCell }, { [`& .${gridClasses['cell--editing']}`]: styles['cell--editing'] }, { [`& .${gridClasses['cell--textCenter']}`]: styles['cell--textCenter'] }, @@ -150,6 +151,15 @@ export const GridRootStyles = styled('div', { borderBottomColor: 'transparent', }, }, + [`&.${gridClasses.autosizing}`]: { + [`& .${gridClasses.columnHeaderTitleContainerContent} > *`]: { + overflow: 'visible !important', + }, + [`& .${gridClasses.cell} > *`]: { + overflow: 'visible !important', + whiteSpace: 'nowrap', + }, + }, [`& .${gridClasses['virtualScrollerContent--overflowed']} .${gridClasses['row--lastVisible']} .${gridClasses.cell}`]: { borderBottomColor: 'transparent', @@ -383,6 +393,9 @@ export const GridRootStyles = styled('div', { overflow: 'hidden', textOverflow: 'ellipsis', }, + [`& .${gridClasses.cell}.${gridClasses['cell--selectionMode']}`]: { + cursor: 'default', + }, [`& .${gridClasses.cell}.${gridClasses['cell--editing']}`]: { padding: 1, display: 'flex', diff --git a/packages/grid/x-data-grid/src/components/menu/GridMenu.tsx b/packages/grid/x-data-grid/src/components/menu/GridMenu.tsx index 6ea294286e633..cc00c12dae5d4 100644 --- a/packages/grid/x-data-grid/src/components/menu/GridMenu.tsx +++ b/packages/grid/x-data-grid/src/components/menu/GridMenu.tsx @@ -2,7 +2,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import ClickAwayListener, { ClickAwayListenerProps } from '@mui/material/ClickAwayListener'; -import { unstable_composeClasses as composeClasses, HTMLElementType } from '@mui/utils'; +import { + unstable_composeClasses as composeClasses, + unstable_useEnhancedEffect as useEnhancedEffect, + HTMLElementType, +} from '@mui/utils'; import Grow, { GrowProps } from '@mui/material/Grow'; import Paper from '@mui/material/Paper'; import Popper, { PopperProps } from '@mui/material/Popper'; @@ -53,7 +57,7 @@ const GridMenuRoot = styled(Popper, { export interface GridMenuProps extends Omit { open: boolean; target: HTMLElement | null; - onClickAway: ClickAwayListenerProps['onClickAway']; + onClose: (event?: Event) => void; position?: MenuPosition; onExited?: GrowProps['onExited']; children: React.ReactNode; @@ -65,11 +69,22 @@ const transformOrigin = { }; function GridMenu(props: GridMenuProps) { - const { open, target, onClickAway, children, position, className, onExited, ...other } = props; + const { open, target, onClose, children, position, className, onExited, ...other } = props; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); const classes = useUtilityClasses(rootProps); + const savedFocusRef = React.useRef(null); + useEnhancedEffect(() => { + if (open) { + savedFocusRef.current = + document.activeElement instanceof HTMLElement ? document.activeElement : null; + } else { + savedFocusRef.current?.focus?.(); + savedFocusRef.current = null; + } + }, [open]); + React.useEffect(() => { // Emit menuOpen or menuClose events const eventName = open ? 'menuOpen' : 'menuClose'; @@ -86,6 +101,13 @@ function GridMenu(props: GridMenuProps) { } }; + const handleClickAway: ClickAwayListenerProps['onClickAway'] = (event) => { + if (event.target && (target === event.target || target?.contains(event.target as Node))) { + return; + } + onClose(event); + }; + return ( {({ TransitionProps, placement }) => ( - + { + const hideMenu = useEventCallback((event?: Event) => { + if (event) { // Prevent triggering the sorting event.stopPropagation(); - if (!target?.contains(event.target as HTMLElement)) { - apiRef.current.hideColumnMenu(); + if (target?.contains(event.target as HTMLElement)) { + return; } - }, - [apiRef, target], - ); + } + apiRef.current.hideColumnMenu(); + }); if (!target || !colDef) { return null; @@ -49,7 +49,7 @@ function GridColumnHeaderMenu({ placement={`bottom-${colDef!.align === 'right' ? 'start' : 'end'}` as any} open={open} target={target} - onClickAway={hideMenu} + onClose={hideMenu} onExited={onExited} > ) => { @@ -42,6 +42,11 @@ function GridColumnMenuSortItem(props: GridColumnMenuItemProps) { return null; } + const getLabel = (key: 'columnMenuSortAsc' | 'columnMenuSortDesc') => { + const label = apiRef.current.getLocaleText(key); + return typeof label === 'function' ? label(colDef) : label; + }; + return ( {sortingOrder.includes('asc') && sortDirection !== 'asc' ? ( @@ -49,7 +54,7 @@ function GridColumnMenuSortItem(props: GridColumnMenuItemProps) { - {apiRef.current.getLocaleText('columnMenuSortAsc')} + {getLabel('columnMenuSortAsc')} ) : null} {sortingOrder.includes('desc') && sortDirection !== 'desc' ? ( @@ -57,7 +62,7 @@ function GridColumnMenuSortItem(props: GridColumnMenuItemProps) { - {apiRef.current.getLocaleText('columnMenuSortDesc')} + {getLabel('columnMenuSortDesc')} ) : null} {sortingOrder.includes(null) && sortDirection != null ? ( diff --git a/packages/grid/x-data-grid/src/components/panel/GridPanel.test.tsx b/packages/grid/x-data-grid/src/components/panel/GridPanel.test.tsx index e201e19b16dce..093099c0c7949 100644 --- a/packages/grid/x-data-grid/src/components/panel/GridPanel.test.tsx +++ b/packages/grid/x-data-grid/src/components/panel/GridPanel.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, describeConformance } from '@mui/monorepo/test/utils'; +import { createRenderer, describeConformance } from '@mui-internal/test-utils'; import { GridPanel, gridPanelClasses as classes, diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx index 1e13b9b5e0930..a2a1df4576181 100644 --- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx +++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterForm.tsx @@ -651,4 +651,11 @@ GridFilterForm.propTypes = { valueInputProps: PropTypes.any, } as any; +/** + * Demos: + * - [Filtering - overview](https://mui.com/x/react-data-grid/filtering/) + * + * API: + * - [GridFilterForm API](https://mui.com/x/api/data-grid/grid-filter-form/) + */ export { GridFilterForm }; diff --git a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterPanel.tsx b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterPanel.tsx index 7d46573d287ee..b01bc07eeab0c 100644 --- a/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterPanel.tsx +++ b/packages/grid/x-data-grid/src/components/panel/filterPanel/GridFilterPanel.tsx @@ -317,4 +317,11 @@ GridFilterPanel.propTypes = { ]), } as any; +/** + * Demos: + * - [Filtering - overview](https://mui.com/x/react-data-grid/filtering/) + * + * API: + * - [GridFilterPanel API](https://mui.com/x/api/data-grid/grid-filter-panel/) + */ export { GridFilterPanel, getGridFilter }; diff --git a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx index 61567ce612785..a203da246f717 100644 --- a/packages/grid/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx +++ b/packages/grid/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx @@ -10,7 +10,7 @@ import { isHideMenuKey, isTabKey } from '../../utils/keyboardUtils'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { GridDensityOption } from '../../models/api/gridDensityApi'; -import { GridMenu, GridMenuProps } from '../menu/GridMenu'; +import { GridMenu } from '../menu/GridMenu'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { gridClasses } from '../../constants/gridClasses'; @@ -60,14 +60,7 @@ export const GridToolbarDensitySelector = React.forwardRef !prevOpen); onClick?.(event); }; - const handleDensitySelectorClickAway: GridMenuProps['onClickAway'] = (event) => { - if ( - buttonRef.current === event.target || - // if user clicked on the icon - buttonRef.current?.contains(event.target as Element) - ) { - return; - } + const handleDensitySelectorClose = () => { setOpen(false); }; const handleDensityUpdate = (newDensity: GridDensity) => { @@ -120,7 +113,7 @@ export const GridToolbarDensitySelector = React.forwardRef { - if ( - buttonRef.current === event.target || - // if user clicked on the icon - buttonRef.current?.contains(event.target as Element) - ) { - return; - } - setOpen(false); - }; - if (children == null) { return null; } @@ -72,7 +61,7 @@ export const GridToolbarExportContainer = React.forwardRef ('MuiDataGrid', [ 'aggregationColumnHeader--alignRight', 'aggregationColumnHeaderLabel', 'autoHeight', + 'autosizing', 'booleanCell', 'cell--editable', 'cell--editing', @@ -557,6 +566,7 @@ export const gridClasses = generateUtilityClasses('MuiDataGrid', [ 'cell--rangeBottom', 'cell--rangeLeft', 'cell--rangeRight', + 'cell--selectionMode', 'cell', 'cellContent', 'cellCheckbox', diff --git a/packages/grid/x-data-grid/src/hooks/core/useGridInitialization.ts b/packages/grid/x-data-grid/src/hooks/core/useGridInitialization.ts index 77752d254535a..f7ea5d3008cc3 100644 --- a/packages/grid/x-data-grid/src/hooks/core/useGridInitialization.ts +++ b/packages/grid/x-data-grid/src/hooks/core/useGridInitialization.ts @@ -16,7 +16,7 @@ export const useGridInitialization = < Api extends GridApiCommon, >( inputApiRef: React.MutableRefObject | undefined, - props: Pick, + props: DataGridProcessedProps, ) => { const privateApiRef = useGridApiInitialization(inputApiRef, props); useGridLoggerFactory(privateApiRef, props); @@ -25,5 +25,7 @@ export const useGridInitialization = < useGridStrategyProcessing(privateApiRef); useGridLocaleText(privateApiRef, props); + privateApiRef.current.register('private', { rootProps: props }); + return privateApiRef; }; diff --git a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/gridColumnGroupsUtils.ts b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/gridColumnGroupsUtils.ts index 7cf0d16ac0047..5b66c4fde4b3c 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/gridColumnGroupsUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/gridColumnGroupsUtils.ts @@ -65,6 +65,7 @@ export const unwrapGroupingColumnModel = ( export const getColumnGroupsHeaderStructure = ( orderedColumns: string[], unwrappedGroupingModel: UnwrappedGroupingModel, + pinnedFields: { right?: string[]; left?: string[] }, ) => { const getParents = (field: string) => unwrappedGroupingModel[field] ?? []; @@ -74,6 +75,24 @@ export const getColumnGroupsHeaderStructure = ( const haveSameParents = (field1: string, field2: string, depth: number) => isDeepEqual(getParents(field1).slice(0, depth + 1), getParents(field2).slice(0, depth + 1)); + const haveDifferentContainers = (field1: string, field2: string) => { + if ( + pinnedFields?.left && + pinnedFields.left.includes(field1) && + !pinnedFields.left.includes(field2) + ) { + return true; + } + if ( + pinnedFields?.right && + !pinnedFields.right.includes(field1) && + pinnedFields.right.includes(field2) + ) { + return true; + } + return false; + }; + for (let depth = 0; depth < maxDepth; depth += 1) { const depthStructure = orderedColumns.reduce((structure, newField) => { const groupId = getParents(newField)[depth] ?? null; @@ -90,7 +109,12 @@ export const getColumnGroupsHeaderStructure = ( const prevField = lastGroup.columnFields[lastGroup.columnFields.length - 1]; const prevGroupId = lastGroup.groupId; - if (prevGroupId !== groupId || !haveSameParents(prevField, newField, depth)) { + if ( + prevGroupId !== groupId || + !haveSameParents(prevField, newField, depth) || + // Fix for https://github.com/mui/mui-x/issues/7041 + haveDifferentContainers(prevField, newField) + ) { // It's a new group return [ ...structure, diff --git a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts index f7255263dd7d2..f0d4f0699a259 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columnGrouping/useGridColumnGrouping.ts @@ -63,6 +63,8 @@ export const columnGroupsStateInitializer: GridStateInitializer< const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure( columnFields, unwrappedGroupingModel, + // @ts-expect-error Move this part to `Pro` package + apiRef.current.state.pinnedColumns ?? {}, ); const maxDepth = visibleColumnFields.length === 0 @@ -122,9 +124,13 @@ export const useGridColumnGrouping = ( apiRef.current.setState((state) => { const orderedFields = state.columns?.orderedFields ?? []; + // @ts-expect-error Move this logic to `Pro` package + const pinnedColumns = state.pinnedColumns ?? {}; + const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure( orderedFields as string[], unwrappedGroupingModel, + pinnedColumns, ); return { ...state, @@ -141,6 +147,8 @@ export const useGridColumnGrouping = ( if (!props.experimentalFeatures?.columnGrouping) { return; } + // @ts-expect-error Move this logic to `Pro` package + const pinnedColumns = apiRef.current.getPinnedColumns?.() ?? {}; const columnFields = gridColumnFieldsSelector(apiRef); const visibleColumnFields = gridVisibleColumnFieldsSelector(apiRef); const groupLookup = createGroupLookup(columnGroupingModel ?? []); @@ -148,6 +156,7 @@ export const useGridColumnGrouping = ( const columnGroupsHeaderStructure = getColumnGroupsHeaderStructure( columnFields, unwrappedGroupingModel, + pinnedColumns, ); const maxDepth = visibleColumnFields.length === 0 diff --git a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx index e1f9470b15fe1..7921587cb966c 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx @@ -1,8 +1,9 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; -import { styled, useTheme } from '@mui/system'; +import { styled, useTheme } from '@mui/material/styles'; import { defaultMemoize } from 'reselect'; +import { useGridSelector } from '../../utils'; import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext'; import { useGridRootProps } from '../../utils/useGridRootProps'; import { GridRenderContext } from '../../../models/params/gridScrollParams'; @@ -15,6 +16,7 @@ import { areRenderContextsEqual, getRenderableIndexes, } from '../virtualization/useGridVirtualScroller'; +import { gridVirtualizationColumnEnabledSelector } from '../virtualization'; import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader'; import { GridColumnGroup } from '../../../models/gridColumnGrouping'; import { GridStateColDef } from '../../../models/colDef/gridColDef'; @@ -100,6 +102,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { const [resizeCol, setResizeCol] = React.useState(''); const apiRef = useGridPrivateApiContext(); + const hasVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector); const rootProps = useGridRootProps(); const innerRef = React.useRef(null); @@ -243,7 +246,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { (params) => setDragCol(params.field), [], ); - const handleColumnReorderStop = React.useCallback>( () => setDragCol(''), [], @@ -276,20 +278,21 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => { buffer: rootProps.rowBuffer, }); - const firstColumnToRender = getFirstColumnIndexToRenderRef.current({ - firstColumnIndex: nextRenderContext!.firstColumnIndex, - minColumnIndex: minFirstColumn, - columnBuffer: rootProps.columnBuffer, - apiRef, - firstRowToRender, - lastRowToRender, - visibleRows: currentPage.rows, - }); - - const lastColumnToRender = Math.min( - nextRenderContext.lastColumnIndex! + rootProps.columnBuffer, - maxLastColumn, - ); + const firstColumnToRender = !hasVirtualization + ? 0 + : getFirstColumnIndexToRenderRef.current({ + firstColumnIndex: nextRenderContext!.firstColumnIndex, + minColumnIndex: minFirstColumn, + columnBuffer: rootProps.columnBuffer, + apiRef, + firstRowToRender, + lastRowToRender, + visibleRows: currentPage.rows, + }); + + const lastColumnToRender = !hasVirtualization + ? maxLastColumn + : Math.min(nextRenderContext.lastColumnIndex! + rootProps.columnBuffer, maxLastColumn); const renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender); diff --git a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsInterfaces.ts b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsInterfaces.ts index 40ee74e590d60..bf977f229f77c 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsInterfaces.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsInterfaces.ts @@ -1,4 +1,3 @@ -import type { GridRowId } from '../../../models'; import { GridColDef, GridStateColDef } from '../../../models/colDef/gridColDef'; import type { GridColumnDimensionProperties } from './gridColumnsUtils'; @@ -30,4 +29,4 @@ export type GridColumnsRawState = Omit & { export type GridHydrateColumnsValue = GridColumnsRawState; -export type GridColumnVisibilityModel = Record; +export type GridColumnVisibilityModel = Record; diff --git a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts index 9033f55d15e40..44e857f375c6b 100644 --- a/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/columns/gridColumnsUtils.ts @@ -94,7 +94,6 @@ export function computeFlexColumnsWidth({ flexColumnsLookup.all[column.field] && flexColumnsLookup.all[column.field].frozen === true ) { - // eslint-disable-next-line no-continue continue; } @@ -293,7 +292,7 @@ export const createColumnsState = ({ columnVisibilityModel = gridColumnVisibilityModelSelector(apiRef), keepOnlyColumnsToUpsert = false, }: { - columnsToUpsert: GridColDef[]; + columnsToUpsert: readonly GridColDef[]; initialState: GridColumnsInitialState | undefined; columnTypes: GridColumnTypesRecord; columnVisibilityModel?: GridColumnVisibilityModel; diff --git a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx index 87d108f92a674..04e1bdcf8ac94 100644 --- a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; import { unstable_ownerDocument as ownerDocument } from '@mui/utils'; -import { GridApiCommunity, GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { GridPrintExportApi } from '../../../models/api/gridPrintExportApi'; import { useGridLogger } from '../../utils/useGridLogger'; import { gridExpandedRowCountSelector } from '../filter/gridFilterSelector'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; -import { GridPrintExportOptions, GridPrintGetRowsToExportParams } from '../../../models/gridExport'; -import { GridRowId, GridValidRowModel } from '../../../models/gridRows'; +import { GridPrintExportOptions } from '../../../models/gridExport'; +import { GridValidRowModel } from '../../../models/gridRows'; import { GridInitialStateCommunity } from '../../../models/gridStateCommunity'; import { gridColumnDefinitionsSelector, @@ -15,7 +15,7 @@ import { import { gridClasses } from '../../../constants/gridClasses'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; -import { getColumnsToExport } from './utils'; +import { defaultGetRowsToExport, getColumnsToExport } from './utils'; import { mergeStateWithPaginationModel } from '../pagination/useGridPagination'; import { GridPipeProcessor, useGridRegisterPipeProcessor } from '../../core/pipeProcessing'; import { @@ -104,9 +104,7 @@ export const useGridPrintExport = ( ); const updateGridRowsForPrint = React.useCallback( - ( - getRowsToExport: (params: GridPrintGetRowsToExportParams) => GridRowId[], - ) => { + (getRowsToExport: NonNullable) => { const rowsToExportIds = getRowsToExport({ apiRef }); const newRows = rowsToExportIds.map((id) => apiRef.current.getRow(id)); apiRef.current.setRows(newRows); @@ -178,14 +176,12 @@ export const useGridPrintExport = ( // the footer is always being placed at the bottom of the page as if all rows are exported // so if getRowsToExport is being used to only export a subset of rows then we need to // adjust the footer position to be correctly placed at the bottom of the grid - if (options?.getRowsToExport) { - const gridFooterElement: HTMLElement | null = gridClone.querySelector( - `.${gridClasses.footerContainer}`, - ); - gridFooterElement!.style.position = 'absolute'; - gridFooterElement!.style.width = '100%'; - gridFooterElement!.style.top = `${computedTotalHeight - gridFooterElementHeight}px`; - } + const gridFooterElement: HTMLElement | null = gridClone.querySelector( + `.${gridClasses.footerContainer}`, + ); + gridFooterElement!.style.position = 'absolute'; + gridFooterElement!.style.width = '100%'; + gridFooterElement!.style.top = `${computedTotalHeight - gridFooterElementHeight}px`; // printDoc.body.appendChild(gridClone); should be enough but a clone isolation bug in Safari // prevents us to do it @@ -281,7 +277,7 @@ export const useGridPrintExport = ( apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current); } - apiRef.current.unstable_enableVirtualization(); + apiRef.current.unstable_setVirtualization(true); apiRef.current.setRows(previousRows.current); // Clear local state @@ -325,11 +321,9 @@ export const useGridPrintExport = ( options?.includeCheckboxes, ); - if (options?.getRowsToExport) { - updateGridRowsForPrint(options.getRowsToExport); - } + updateGridRowsForPrint(options?.getRowsToExport ?? defaultGetRowsToExport); - apiRef.current.unstable_disableVirtualization(); + apiRef.current.unstable_setVirtualization(false); await raf(); // wait for the state changes to take action const printWindow = buildPrintWindow(options?.fileName); if (process.env.NODE_ENV === 'test') { diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts index afc37156c034d..51cfec81bcdc3 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/filter/gridFilterUtils.ts @@ -1,17 +1,13 @@ import * as React from 'react'; import { - GridCellParams, GridColDef, GridFilterItem, GridFilterModel, GridLogicOperator, - GridRowId, - GridRowIdGetter, GridValidRowModel, } from '../../../models'; -import { GridApiCommunity } from '../../../models/api/gridApiCommunity'; +import type { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; -import { GLOBAL_API_REF, isInternalFilter } from '../../../colDef/utils'; import { getDefaultGridFilterModel, GridAggregatedFilterItemApplier, @@ -19,6 +15,7 @@ import { GridQuickFilterValueResult, } from './gridFilterState'; import { buildWarning } from '../../../utils/warning'; +import { getPublicApiRef } from '../../../utils/getPublicApiRef'; import { gridColumnFieldsSelector, gridColumnLookupSelector, @@ -26,8 +23,8 @@ import { } from '../columns'; // Fixes https://github.com/mui/mui-x/issues/10056 -const global = (typeof window === 'undefined' ? globalThis : window) as any; -const evalCode = global[atob('ZXZhbA==')] as (source: string) => T; +const globalScope = (typeof window === 'undefined' ? globalThis : window) as any; +const evalCode = globalScope[atob('ZXZhbA==')] as (source: string) => T; let hasEval: boolean; try { @@ -36,17 +33,10 @@ try { hasEval = false; } -type GridFilterItemApplier = - | { - v7: false; - fn: (rowId: GridRowId) => boolean; - item: GridFilterItem; - } - | { - v7: true; - fn: (row: GridValidRowModel) => boolean; - item: GridFilterItem; - }; +type GridFilterItemApplier = { + fn: (row: GridValidRowModel) => boolean; + item: GridFilterItem; +}; type GridFilterItemApplierNotAggregated = ( row: GridValidRowModel, @@ -56,13 +46,13 @@ type GridFilterItemApplierNotAggregated = ( /** * Adds default values to the optional fields of a filter items. * @param {GridFilterItem} item The raw filter item. - * @param {React.MutableRefObject} apiRef The API of the grid. + * @param {React.MutableRefObject} apiRef The API of the grid. * @return {GridFilterItem} The clean filter item with an uniq ID and an always-defined operator. * TODO: Make the typing reflect the different between GridFilterInputItem and GridFilterItem. */ export const cleanFilterItem = ( item: GridFilterItem, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, ) => { const cleanItem: GridFilterItem = { ...item }; @@ -101,7 +91,7 @@ const filterModelMissingItemOperatorWarning = buildWarning( export const sanitizeFilterModel = ( model: GridFilterModel, disableMultipleColumnsFiltering: boolean, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, ) => { const hasSeveralItems = model.items.length > 1; @@ -146,16 +136,23 @@ export const mergeStateWithFilterModel = ( filterModel: GridFilterModel, disableMultipleColumnsFiltering: boolean, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, ) => (filteringState: GridStateCommunity['filter']): GridStateCommunity['filter'] => ({ ...filteringState, filterModel: sanitizeFilterModel(filterModel, disableMultipleColumnsFiltering, apiRef), }); +export const removeDiacritics = (value: unknown) => { + if (typeof value === 'string') { + return value.normalize('NFD').replace(/[\u0300-\u036f]/g, ''); + } + return value; +}; + const getFilterCallbackFromItem = ( filterItem: GridFilterItem, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, ): GridFilterItemApplier | null => { if (!filterItem.field || !filterItem.operator) { return null; @@ -175,6 +172,13 @@ const getFilterCallbackFromItem = ( } else { parsedValue = filterItem.value; } + + const { ignoreDiacritics } = apiRef.current.rootProps; + + if (ignoreDiacritics) { + parsedValue = removeDiacritics(parsedValue); + } + const newFilterItem: GridFilterItem = { ...filterItem, value: parsedValue }; const filterOperators = column.filterOperators; @@ -191,38 +195,20 @@ const getFilterCallbackFromItem = ( ); } - const hasUserFunctionLegacy = !isInternalFilter(filterOperator.getApplyFilterFn); - const hasUserFunctionV7 = !isInternalFilter(filterOperator.getApplyFilterFnV7); + const publicApiRef = getPublicApiRef(apiRef); - if (filterOperator.getApplyFilterFnV7 && !(hasUserFunctionLegacy && !hasUserFunctionV7)) { - const applyFilterOnRow = filterOperator.getApplyFilterFnV7(newFilterItem, column)!; - if (typeof applyFilterOnRow !== 'function') { - return null; - } - return { - v7: true, - item: newFilterItem, - fn: (row: GridValidRowModel) => { - const value = apiRef.current.getRowValue(row, column); - return applyFilterOnRow(value, row, column, apiRef); - }, - }; - } - - const applyFilterOnRow = filterOperator.getApplyFilterFn!(newFilterItem, column)!; + const applyFilterOnRow = filterOperator.getApplyFilterFn(newFilterItem, column)!; if (typeof applyFilterOnRow !== 'function') { return null; } - return { - v7: false, item: newFilterItem, - fn: (rowId: GridRowId) => { - const params = apiRef.current.getCellParams(rowId, newFilterItem.field!); - GLOBAL_API_REF.current = apiRef; - const result = applyFilterOnRow(params); - GLOBAL_API_REF.current = null; - return result; + fn: (row: GridValidRowModel) => { + let value = apiRef.current.getRowValue(row, column); + if (ignoreDiacritics) { + value = removeDiacritics(value); + } + return applyFilterOnRow(value, row, column, publicApiRef); }, }; }; @@ -231,15 +217,13 @@ let filterItemsApplierId = 1; /** * Generates a method to easily check if a row is matching the current filter model. - * @param {GridRowIdGetter | undefined} getRowId The getter for row's id. * @param {GridFilterModel} filterModel The model with which we want to filter the rows. - * @param {React.MutableRefObject} apiRef The API of the grid. + * @param {React.MutableRefObject} apiRef The API of the grid. * @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters. */ -export const buildAggregatedFilterItemsApplier = ( - getRowId: GridRowIdGetter | undefined, +const buildAggregatedFilterItemsApplier = ( filterModel: GridFilterModel, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, disableEval: boolean, ): GridFilterItemApplierNotAggregated | null => { const { items } = filterModel; @@ -260,9 +244,7 @@ export const buildAggregatedFilterItemsApplier = ( for (let i = 0; i < appliers.length; i += 1) { const applier = appliers[i]; if (!shouldApplyFilter || shouldApplyFilter(applier.item.field)) { - resultPerItemId[applier.item.id!] = applier.v7 - ? applier.fn(row) - : applier.fn(getRowId ? getRowId(row) : row.id); + resultPerItemId[applier.item.id!] = applier.fn(row); } } @@ -272,7 +254,7 @@ export const buildAggregatedFilterItemsApplier = ( // We generate a new function with `eval()` to avoid expensive patterns for JS engines // such as a dynamic object assignment, e.g. `{ [dynamicKey]: value }`. - const filterItemTemplate = `(function filterItem$$(appliers, row, shouldApplyFilter) { + const filterItemTemplate = `(function filterItem$$(getRowId, appliers, row, shouldApplyFilter) { ${appliers .map( (applier, i) => @@ -289,11 +271,7 @@ export const buildAggregatedFilterItemsApplier = ( `${JSON.stringify(String(applier.item.id))}: !shouldApply${i} ? false : - ${ - applier.v7 - ? `appliers[${i}].fn(row)` - : `appliers[${i}].fn(${getRowId ? 'getRowId(row)' : 'row.id'})` - }, + appliers[${i}].fn(row), `, ) .join('\n')}}; @@ -305,7 +283,7 @@ export const buildAggregatedFilterItemsApplier = ( filterItemTemplate.replaceAll('$$', String(filterItemsApplierId)), ); const filterItem: GridFilterItemApplierNotAggregated = (row, shouldApplyItem) => { - return filterItemCore(appliers, row, shouldApplyItem); + return filterItemCore(apiRef.current.getRowId, appliers, row, shouldApplyItem); }; filterItemsApplierId += 1; @@ -314,15 +292,13 @@ export const buildAggregatedFilterItemsApplier = ( /** * Generates a method to easily check if a row is matching the current quick filter. - * @param {GridRowIdGetter | undefined} getRowId The getter for row's id. * @param {any[]} filterModel The model with which we want to filter the rows. - * @param {React.MutableRefObject} apiRef The API of the grid. + * @param {React.MutableRefObject} apiRef The API of the grid. * @returns {GridAggregatedFilterItemApplier | null} A method that checks if a row is matching the current filter model. If `null`, we consider that all the rows are matching the filters. */ -export const buildAggregatedQuickFilterApplier = ( - getRowId: GridRowIdGetter | undefined, +const buildAggregatedQuickFilterApplier = ( filterModel: GridFilterModel, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, ): GridFilterItemApplierNotAggregated | null => { const quickFilterValues = filterModel.quickFilterValues?.filter(Boolean) ?? []; if (quickFilterValues.length === 0) { @@ -338,43 +314,34 @@ export const buildAggregatedQuickFilterApplier = ( const appliersPerField = [] as { column: GridColDef; appliers: { - v7: boolean; fn: null | ((...args: any[]) => boolean); }[]; }[]; + const { ignoreDiacritics } = apiRef.current.rootProps; + const publicApiRef = getPublicApiRef(apiRef); + columnFields.forEach((field) => { const column = apiRef.current.getColumn(field); const getApplyQuickFilterFn = column?.getApplyQuickFilterFn; - const getApplyQuickFilterFnV7 = column?.getApplyQuickFilterFnV7; - const hasUserFunctionLegacy = !isInternalFilter(getApplyQuickFilterFn); - const hasUserFunctionV7 = !isInternalFilter(getApplyQuickFilterFnV7); - - if (getApplyQuickFilterFnV7 && !(hasUserFunctionLegacy && !hasUserFunctionV7)) { + if (getApplyQuickFilterFn) { appliersPerField.push({ column, - appliers: quickFilterValues.map((value) => ({ - v7: true, - fn: getApplyQuickFilterFnV7(value, column, apiRef), - })), - }); - } else if (getApplyQuickFilterFn) { - appliersPerField.push({ - column, - appliers: quickFilterValues.map((value) => ({ - v7: false, - fn: getApplyQuickFilterFn(value, column, apiRef), - })), + appliers: quickFilterValues.map((quickFilterValue) => { + const value = ignoreDiacritics ? removeDiacritics(quickFilterValue) : quickFilterValue; + return { + fn: getApplyQuickFilterFn(value, column, publicApiRef), + }; + }), }); } }); return function isRowMatchingQuickFilter(row, shouldApplyFilter) { const result = {} as GridQuickFilterValueResult; - const usedCellParams = {} as { [field: string]: GridCellParams }; - /* eslint-disable no-restricted-syntax, no-labels, no-continue */ + /* eslint-disable no-restricted-syntax, no-labels */ outer: for (let v = 0; v < quickFilterValues.length; v += 1) { const filterValue = quickFilterValues[v]; @@ -387,53 +354,41 @@ export const buildAggregatedQuickFilterApplier = ( } const applier = appliers[v]; - const value = apiRef.current.getRowValue(row, column); + let value = apiRef.current.getRowValue(row, column); if (applier.fn === null) { continue; } - if (applier.v7) { - const isMatching = applier.fn(value, row, column, apiRef); - if (isMatching) { - result[filterValue] = true; - continue outer; - } - } else { - const cellParams = - usedCellParams[field] ?? - apiRef.current.getCellParams(getRowId ? getRowId(row) : row.id, field); - usedCellParams[field] = cellParams; - - const isMatching = applier.fn(cellParams); - if (isMatching) { - result[filterValue] = true; - continue outer; - } + if (ignoreDiacritics) { + value = removeDiacritics(value); + } + const isMatching = applier.fn(value, row, column, publicApiRef); + if (isMatching) { + result[filterValue] = true; + continue outer; } } result[filterValue] = false; } - /* eslint-enable no-restricted-syntax, no-labels, no-continue */ + /* eslint-enable no-restricted-syntax, no-labels */ return result; }; }; export const buildAggregatedFilterApplier = ( - getRowId: GridRowIdGetter | undefined, filterModel: GridFilterModel, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, disableEval: boolean, ): GridAggregatedFilterItemApplier => { const isRowMatchingFilterItems = buildAggregatedFilterItemsApplier( - getRowId, filterModel, apiRef, disableEval, ); - const isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(getRowId, filterModel, apiRef); + const isRowMatchingQuickFilter = buildAggregatedQuickFilterApplier(filterModel, apiRef); return function isRowMatchingFilters(row, shouldApplyFilter, result) { result.passingFilterItems = isRowMatchingFilterItems?.(row, shouldApplyFilter) ?? null; @@ -449,7 +404,7 @@ type FilterCache = { const filterModelItems = ( cache: FilterCache, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, items: GridFilterItem[], ) => { if (!cache.cleanedFilterItems) { @@ -464,7 +419,7 @@ export const passFilterLogic = ( allFilterItemResults: (null | GridFilterItemResult)[], allQuickFilterResults: (null | GridQuickFilterValueResult)[], filterModel: GridFilterModel, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, cache: FilterCache, ): boolean => { const cleanedFilterItems = filterModelItems(cache, apiRef, filterModel.items); diff --git a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx index a35e9d1adc437..166e52a5ced0d 100644 --- a/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/filter/useGridFilter.tsx @@ -90,6 +90,7 @@ export const useGridFilter = ( | 'slotProps' | 'disableColumnFilter' | 'disableEval' + | 'ignoreDiacritics' >, ): void => { const logger = useGridLogger(apiRef, 'useGridFilter'); @@ -107,7 +108,7 @@ export const useGridFilter = ( const filterModel = gridFilterModelSelector(state, apiRef.current.instanceId); const isRowMatchingFilters = props.filterMode === 'client' - ? buildAggregatedFilterApplier(props.getRowId, filterModel, apiRef, props.disableEval) + ? buildAggregatedFilterApplier(filterModel, apiRef, props.disableEval) : null; const filteringResult = apiRef.current.applyStrategyProcessor('filtering', { @@ -131,7 +132,7 @@ export const useGridFilter = ( }; }); apiRef.current.publishEvent('filteredRowsSet'); - }, [apiRef, props.filterMode, props.getRowId, props.disableEval]); + }, [apiRef, props.filterMode, props.disableEval]); const addColumnMenuItem = React.useCallback>( (columnMenuItems, colDef) => { @@ -329,6 +330,7 @@ export const useGridFilter = ( showFilterPanel, hideFilterPanel, setQuickFilterValues, + ignoreDiacritics: props.ignoreDiacritics, }; useGridApiMethod(apiRef, filterApi, 'public'); diff --git a/packages/grid/x-data-grid/src/hooks/features/index.ts b/packages/grid/x-data-grid/src/hooks/features/index.ts index dc565a3269ebb..b5d62c7b80d5b 100644 --- a/packages/grid/x-data-grid/src/hooks/features/index.ts +++ b/packages/grid/x-data-grid/src/hooks/features/index.ts @@ -13,3 +13,4 @@ export * from './sorting'; export * from './dimensions'; export * from './statePersistence'; export * from './headerFiltering'; +export * from './virtualization'; diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts index 2562e1c3d932f..849560f833b0a 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRows.ts @@ -440,7 +440,7 @@ export const useGridRows = ( tree[GRID_ROOT_GROUP_ID] = { ...rootGroup, children: rootGroupChildren }; // Removes potential remaining skeleton rows from the dataRowIds. - const dataRowIds = rootGroupChildren.filter((childId) => tree[childId].type === 'leaf'); + const dataRowIds = rootGroupChildren.filter((childId) => tree[childId]?.type === 'leaf'); apiRef.current.caches.rows.dataRowIdToModelLookup = dataRowIdToModelLookup; apiRef.current.caches.rows.dataRowIdToIdLookup = dataRowIdToIdLookup; diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts index 3ae59a3fe64a9..5a62e9ded7f5d 100644 --- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts +++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts @@ -69,6 +69,7 @@ export const useGridRowsMeta = ( | 'pagination' | 'paginationMode' | 'rowHeight' + | 'rowPositionsDebounceMs' >, ): void => { const { getRowHeight: getRowHeightProp, getRowSpacing, getEstimatedRowHeight } = props; @@ -256,8 +257,8 @@ export const useGridRowsMeta = ( ); const debouncedHydrateRowsMeta = React.useMemo( - () => debounce(hydrateRowsMeta), - [hydrateRowsMeta], + () => debounce(hydrateRowsMeta, props.rowPositionsDebounceMs), + [hydrateRowsMeta, props.rowPositionsDebounceMs], ); const storeMeasuredRowHeight = React.useCallback< diff --git a/packages/grid/x-data-grid/src/hooks/features/sorting/gridSortingUtils.ts b/packages/grid/x-data-grid/src/hooks/features/sorting/gridSortingUtils.ts index e3ab32cbf8d4f..43d5f1a82f464 100644 --- a/packages/grid/x-data-grid/src/hooks/features/sorting/gridSortingUtils.ts +++ b/packages/grid/x-data-grid/src/hooks/features/sorting/gridSortingUtils.ts @@ -146,7 +146,7 @@ export const buildAggregatedSortingApplier = ( }; export const getNextGridSortDirection = ( - sortingOrder: GridSortDirection[], + sortingOrder: readonly GridSortDirection[], current?: GridSortDirection, ) => { const currentIdx = sortingOrder.indexOf(current); diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts b/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts new file mode 100644 index 0000000000000..4fed29c696b7f --- /dev/null +++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts @@ -0,0 +1,26 @@ +import { createSelector } from '../../../utils/createSelector'; +import { GridStateCommunity } from '../../../models/gridStateCommunity'; + +/** + * Get the columns state + * @category Virtualization + */ +export const gridVirtualizationSelector = (state: GridStateCommunity) => state.virtualization; + +/** + * Get the enabled state for virtualization + * @category Virtualization + */ +export const gridVirtualizationEnabledSelector = createSelector( + gridVirtualizationSelector, + (state) => state.enabled, +); + +/** + * Get the enabled state for virtualization + * @category Virtualization + */ +export const gridVirtualizationColumnEnabledSelector = createSelector( + gridVirtualizationSelector, + (state) => state.enabledForColumns, +); diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts b/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts new file mode 100644 index 0000000000000..19098c42dfda5 --- /dev/null +++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts @@ -0,0 +1,2 @@ +export * from './useGridVirtualization'; +export * from './gridVirtualizationSelectors'; diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index dee0de795da83..30cb17e519947 100644 --- a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -28,6 +28,10 @@ import { GridStateColDef } from '../../../models/colDef/gridColDef'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; import { getMinimalContentHeight } from '../rows/gridRowsUtils'; import { GridRowProps } from '../../../components/GridRow'; +import { + gridVirtualizationEnabledSelector, + gridVirtualizationColumnEnabledSelector, +} from './gridVirtualizationSelectors'; // Uses binary search to avoid looping through all possible positions export function binarySearch( @@ -98,7 +102,6 @@ export const areRenderContextsEqual = ( interface UseGridVirtualScrollerProps { ref: React.Ref; - disableVirtualization?: boolean; renderZoneMinColumnIndex?: number; renderZoneMaxColumnIndex?: number; onRenderZonePositioning?: (params: { top: number; left: number }) => void; @@ -118,10 +121,11 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { const apiRef = useGridPrivateApiContext(); const rootProps = useGridRootProps(); const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector); + const enabled = useGridSelector(apiRef, gridVirtualizationEnabledSelector); + const enabledForColumns = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector); const { ref, - disableVirtualization, onRenderZonePositioning, renderZoneMinColumnIndex = 0, renderZoneMaxColumnIndex = visibleColumns.length, @@ -139,7 +143,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { const renderZoneRef = React.useRef(null); const rootRef = React.useRef(null); const handleRef = useForkRef(ref, rootRef); - const [renderContext, setRenderContext] = React.useState(null); + const [renderContext, setRenderContextState] = React.useState(null); const prevRenderContext = React.useRef(renderContext); const scrollPosition = React.useRef({ top: 0, left: 0 }); const [containerDimensions, setContainerDimensions] = React.useState({ @@ -147,6 +151,12 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { height: null, }); const prevTotalWidth = React.useRef(columnsTotalWidth); + // Each visible row (not to be confused with a filter result) is composed of a central row element + // and up to two additional row elements for pinned columns (left and right). + // When hovering any of these elements, the :hover styles are applied only to the row element that + // was actually hovered, not its additional siblings. To make it look like a contiguous row, + // we add/remove the .Mui-hovered class to all of the row elements inside one visible row. + const [hoveredRowId, setHoveredRowId] = React.useState(null); const rowStyleCache = React.useRef>(Object.create(null)); const prevGetRowProps = React.useRef(); @@ -232,7 +242,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { ); const computeRenderContext = React.useCallback(() => { - if (disableVirtualization) { + if (!enabled) { return { firstRowIndex: 0, lastRowIndex: currentPage.rows.length, @@ -251,26 +261,32 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { ? firstRowIndex + currentPage.rows.length : getNearestIndexToRender(top + containerDimensions.height!); - let hasRowWithAutoHeight = false; let firstColumnIndex = 0; let lastColumnIndex = columnPositions.length; - const [firstRowToRender, lastRowToRender] = getRenderableIndexes({ - firstIndex: firstRowIndex, - lastIndex: lastRowIndex, - minFirstIndex: 0, - maxLastIndex: currentPage.rows.length, - buffer: rootProps.rowBuffer, - }); + if (enabledForColumns) { + let hasRowWithAutoHeight = false; - for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { - const row = currentPage.rows[i]; - hasRowWithAutoHeight = apiRef.current.rowHasAutoHeight(row.id); - } + const [firstRowToRender, lastRowToRender] = getRenderableIndexes({ + firstIndex: firstRowIndex, + lastIndex: lastRowIndex, + minFirstIndex: 0, + maxLastIndex: currentPage.rows.length, + buffer: rootProps.rowBuffer, + }); + + for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { + const row = currentPage.rows[i]; + hasRowWithAutoHeight = apiRef.current.rowHasAutoHeight(row.id); + } - if (!hasRowWithAutoHeight) { - firstColumnIndex = binarySearch(Math.abs(left), columnPositions); - lastColumnIndex = binarySearch(Math.abs(left) + containerDimensions.width!, columnPositions); + if (!hasRowWithAutoHeight) { + firstColumnIndex = binarySearch(Math.abs(left), columnPositions); + lastColumnIndex = binarySearch( + Math.abs(left) + containerDimensions.width!, + columnPositions, + ); + } } return { @@ -280,7 +296,8 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { lastColumnIndex, }; }, [ - disableVirtualization, + enabled, + enabledForColumns, getNearestIndexToRender, rowsMeta.positions.length, rootProps.autoHeight, @@ -293,14 +310,14 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { ]); useEnhancedEffect(() => { - if (disableVirtualization) { - renderZoneRef.current!.style.transform = `translate3d(0px, 0px, 0px)`; - } else { + if (enabled) { // TODO a scroll reset should not be necessary rootRef.current!.scrollLeft = 0; rootRef.current!.scrollTop = 0; + } else { + renderZoneRef.current!.style.transform = `translate3d(0px, 0px, 0px)`; } - }, [disableVirtualization]); + }, [enabled]); useEnhancedEffect(() => { setContainerDimensions({ @@ -367,7 +384,9 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { ], ); - const updateRenderContext = React.useCallback( + const getRenderContext = React.useCallback(() => prevRenderContext.current!, []); + + const setRenderContext = React.useCallback( (nextRenderContext: GridRenderContext) => { if ( prevRenderContext.current && @@ -376,7 +395,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { updateRenderZonePosition(nextRenderContext); return; } - setRenderContext(nextRenderContext); + setRenderContextState(nextRenderContext); updateRenderZonePosition(nextRenderContext); @@ -397,7 +416,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { }, [ apiRef, - setRenderContext, + setRenderContextState, prevRenderContext, currentPage.rows.length, rootProps.rowBuffer, @@ -411,12 +430,12 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { } const initialRenderContext = computeRenderContext(); - updateRenderContext(initialRenderContext); + setRenderContext(initialRenderContext); const { top, left } = scrollPosition.current!; const params = { top, left, renderContext: initialRenderContext }; apiRef.current.publishEvent('scrollPositionChange', params); - }, [apiRef, computeRenderContext, containerDimensions.width, updateRenderContext]); + }, [apiRef, computeRenderContext, containerDimensions.width, setRenderContext]); const handleScroll = useEventCallback((event: React.UIEvent) => { const { scrollTop, scrollLeft } = event.currentTarget; @@ -439,9 +458,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { } // When virtualization is disabled, the context never changes during scroll - const nextRenderContext = disableVirtualization - ? prevRenderContext.current - : computeRenderContext(); + const nextRenderContext = enabled ? computeRenderContext() : prevRenderContext.current; const topRowsScrolledSincePreviousRender = Math.abs( nextRenderContext.firstRowIndex - prevRenderContext.current.firstRowIndex, @@ -477,7 +494,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { if (shouldSetState) { // Prevents batching render context changes ReactDOM.flushSync(() => { - updateRenderContext(nextRenderContext); + setRenderContext(nextRenderContext); }); prevTotalWidth.current = columnsTotalWidth; } @@ -498,6 +515,19 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { return -1; }, [cellFocus, currentPage.rows]); + useGridApiEventHandler(apiRef, 'rowMouseOver', (params, event) => { + if (event.currentTarget.contains(event.relatedTarget as Node)) { + return; + } + setHoveredRowId(params.id ?? null); + }); + useGridApiEventHandler(apiRef, 'rowMouseOut', (params, event) => { + if (event.currentTarget.contains(event.relatedTarget as Node)) { + return; + } + setHoveredRowId(null); + }); + const getRows = ( params: { renderContext: GridRenderContext | null; @@ -524,8 +554,8 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { return null; } - const rowBuffer = !disableVirtualization ? rootProps.rowBuffer : 0; - const columnBuffer = !disableVirtualization ? rootProps.columnBuffer : 0; + const rowBuffer = enabled ? rootProps.rowBuffer : 0; + const columnBuffer = enabled ? rootProps.columnBuffer : 0; const [firstRowToRender, lastRowToRender] = getRenderableIndexes({ firstIndex: nextRenderContext.firstRowIndex, @@ -633,6 +663,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { } const rows: React.JSX.Element[] = []; + let isRowWithFocusedCellRendered = false; for (let i = 0; i < renderedRows.length; i += 1) { const { id, model } = renderedRows[i]; @@ -683,6 +714,14 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { rowStyleCache.current[id] = style; } + let index = rowIndexOffset + (currentPage?.range?.firstRowIndex || 0) + firstRowToRender + i; + if (isRowWithFocusedCellNotInRange && cellFocus?.id === id) { + index = indexOfRowWithFocusedCell; + isRowWithFocusedCellRendered = true; + } else if (isRowWithFocusedCellRendered) { + index -= 1; + } + rows.push( { firstColumnToRender={firstColumnToRender} lastColumnToRender={lastColumnToRender} selected={isSelected} - index={rowIndexOffset + (currentPage?.range?.firstRowIndex || 0) + firstRowToRender + i} + index={index} containerWidth={availableSpace} isLastVisible={lastVisibleRowIndex} position={position} {...rowProps} {...rootRowProps} + hovered={hoveredRowId === id} style={rowStyleCache.current[id]} />, ); @@ -769,11 +809,9 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => { return style; }, [needsHorizontalScrollbar, rootProps.autoHeight]); - const getRenderContext = React.useCallback((): GridRenderContext => { - return prevRenderContext.current!; - }, []); - - apiRef.current.register('private', { getRenderContext }); + apiRef.current.register('private', { + getRenderContext, + }); return { renderContext, diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx new file mode 100644 index 0000000000000..7634600ac1da8 --- /dev/null +++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; +import { useGridApiMethod } from '../../utils/useGridApiMethod'; +import { GridStateInitializer } from '../../utils/useGridInitializeState'; + +type RootProps = Pick; + +export type GridVirtualizationState = { + enabled: boolean; + enabledForColumns: boolean; +}; + +export const virtualizationStateInitializer: GridStateInitializer = (state, props) => { + const virtualization = { + enabled: !props.disableVirtualization, + enabledForColumns: true, + }; + + return { + ...state, + virtualization, + }; +}; + +export function useGridVirtualization( + apiRef: React.MutableRefObject, + props: RootProps, +): void { + /* + * API METHODS + */ + + const setVirtualization = (enabled: boolean) => { + apiRef.current.setState((state) => ({ + ...state, + virtualization: { + ...state.virtualization, + enabled, + }, + })); + }; + + const setColumnVirtualization = (enabled: boolean) => { + apiRef.current.setState((state) => ({ + ...state, + virtualization: { + ...state.virtualization, + enabledForColumns: enabled, + }, + })); + }; + + const api = { + unstable_setVirtualization: setVirtualization, + unstable_setColumnVirtualization: setColumnVirtualization, + }; + + useGridApiMethod(apiRef, api, 'public'); + + /* + * EFFECTS + */ + + /* eslint-disable react-hooks/exhaustive-deps */ + React.useEffect(() => { + setVirtualization(!props.disableVirtualization); + }, [props.disableVirtualization]); + /* eslint-enable react-hooks/exhaustive-deps */ +} diff --git a/packages/grid/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx b/packages/grid/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx index 644a145a8decf..8619de8bf66f7 100644 --- a/packages/grid/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx +++ b/packages/grid/x-data-grid/src/hooks/utils/useGridApiEventHandler.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer } from '@mui-internal/test-utils'; import { sleep } from 'test/utils/helperFn'; import { createUseGridApiEventHandler } from './useGridApiEventHandler'; import { FinalizationRegistryBasedCleanupTracking } from '../../utils/cleanupTracking/FinalizationRegistryBasedCleanupTracking'; diff --git a/packages/grid/x-data-grid/src/internals/index.ts b/packages/grid/x-data-grid/src/internals/index.ts index 40a9d112efdad..d584dbe1c7730 100644 --- a/packages/grid/x-data-grid/src/internals/index.ts +++ b/packages/grid/x-data-grid/src/internals/index.ts @@ -39,6 +39,7 @@ export { export { useGridColumns, columnsStateInitializer } from '../hooks/features/columns/useGridColumns'; export { getTotalHeaderHeight } from '../hooks/features/columns/gridColumnsUtils'; export { useGridColumnSpanning } from '../hooks/features/columns/useGridColumnSpanning'; +export { gridColumnsStateSelector } from '../hooks/features/columns/gridColumnsSelector'; export { useGridColumnGrouping, columnGroupsStateInitializer, @@ -113,6 +114,7 @@ export { useGridVirtualScroller, getRenderableIndexes, } from '../hooks/features/virtualization/useGridVirtualScroller'; +export * from '../hooks/features/virtualization'; export { useTimeout } from '../hooks/utils/useTimeout'; export { useGridVisibleRows, getVisibleRows } from '../hooks/utils/useGridVisibleRows'; @@ -128,6 +130,7 @@ export type { } from '../models/props/DataGridProps'; export { getColumnsToExport, defaultGetRowsToExport } from '../hooks/features/export/utils'; +export * from '../utils/createControllablePromise'; export { createSelector, createSelectorMemoized, @@ -138,13 +141,14 @@ export { isNavigationKey } from '../utils/keyboardUtils'; export { clamp, isDeepEqual, isNumber, isFunction, isObject } from '../utils/utils'; export { buildWarning } from '../utils/warning'; export { exportAs } from '../utils/exportAs'; +export * from '../utils/getPublicApiRef'; export type { GridPrivateOnlyApiCommon } from '../models/api/gridApiCommon'; export { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext'; +export * from '../hooks/utils/useOnMount'; export type { GridApiCommunity } from '../models/api/gridApiCommunity'; export type { GridApiCaches } from '../models/gridApiCaches'; export { serializeCellValue } from '../hooks/features/export/serializers/csvSerializer'; -export * from '../colDef/utils'; export * from './utils'; diff --git a/packages/grid/x-data-grid/src/internals/utils/computeSlots.ts b/packages/grid/x-data-grid/src/internals/utils/computeSlots.ts index 1f27d53bf0c90..6e8c849a45090 100644 --- a/packages/grid/x-data-grid/src/internals/utils/computeSlots.ts +++ b/packages/grid/x-data-grid/src/internals/utils/computeSlots.ts @@ -1,22 +1,24 @@ -import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from './slotsMigration'; - -// TODO v7: Remove `components` and usages of `UncapitalizeObjectKeys` type -// after converting keys in Grid(Pro|Premium)SlotsComponent to camelCase. -// https://github.com/mui/mui-x/issues/7940 export function computeSlots({ defaultSlots, slots, - components, }: { - defaultSlots: UncapitalizeObjectKeys; - slots?: UncapitalizeObjectKeys>; - components?: Partial; -}): UncapitalizeObjectKeys { - const overrides = slots ?? (components ? uncapitalizeObjectKeys(components) : null); + defaultSlots: SlotComponents; + slots?: Partial; +}): SlotComponents { + const overrides = slots; if (!overrides || Object.keys(overrides).length === 0) { return defaultSlots; } - return { ...defaultSlots, ...overrides }; + const result = { ...defaultSlots }; + Object.keys(overrides).forEach((key) => { + const k = key as keyof typeof overrides; + + if (overrides[k] !== undefined) { + result[k] = overrides[k] as any; + } + }); + + return result; } diff --git a/packages/grid/x-data-grid/src/internals/utils/index.ts b/packages/grid/x-data-grid/src/internals/utils/index.ts index 788efa1eb792d..e8f120cd75d8c 100644 --- a/packages/grid/x-data-grid/src/internals/utils/index.ts +++ b/packages/grid/x-data-grid/src/internals/utils/index.ts @@ -1,3 +1,2 @@ export * from './computeSlots'; -export * from './slotsMigration'; export * from './useProps'; diff --git a/packages/grid/x-data-grid/src/internals/utils/slotsMigration.ts b/packages/grid/x-data-grid/src/internals/utils/slotsMigration.ts deleted file mode 100644 index b1dadaba01bc9..0000000000000 --- a/packages/grid/x-data-grid/src/internals/utils/slotsMigration.ts +++ /dev/null @@ -1,22 +0,0 @@ -// TODO v7: This file exist only to simplify typing between -// components/componentsProps and slots/slotProps -// Should be deleted when components/componentsProps are removed - -export type UncapitalizeObjectKeys = { - [K in keyof T as K extends string ? Uncapitalize : K]: T[K]; -}; - -export const uncapitalizeObjectKeys = ( - capitalizedObject: TInputType, -) => { - if (capitalizedObject === undefined) { - return undefined; - } - return Object.keys(capitalizedObject).reduce( - (acc, key) => ({ - ...acc, - [`${key.charAt(0).toLowerCase()}${key.slice(1)}`]: capitalizedObject[key as keyof TInputType], - }), - {} as UncapitalizeObjectKeys, - ); -}; diff --git a/packages/grid/x-data-grid/src/internals/utils/useProps.ts b/packages/grid/x-data-grid/src/internals/utils/useProps.ts index 44e5d810f5634..0f876b37a31f6 100644 --- a/packages/grid/x-data-grid/src/internals/utils/useProps.ts +++ b/packages/grid/x-data-grid/src/internals/utils/useProps.ts @@ -1,11 +1,4 @@ import * as React from 'react'; -import { GridSlotsComponentsProps } from '../../models/gridSlotsComponentsProps'; -import { GridSlotsComponent } from '../../models'; - -interface WithComponents { - components?: Partial; - componentsProps?: GridSlotsComponentsProps; -} /** Gathers props for the root element into a single `.forwardedProps` field */ function groupForwardedProps< @@ -38,9 +31,9 @@ function groupForwardedProps< return newProps as T; } -export function useProps(allProps: T) { +export function useProps>(allProps: T) { return React.useMemo(() => { - const { components, componentsProps, ...themedProps } = allProps; - return [components, componentsProps, groupForwardedProps(themedProps)] as const; + const { ...themedProps } = allProps; + return groupForwardedProps(themedProps); }, [allProps]); } diff --git a/packages/grid/x-data-grid/src/joy/icons.tsx b/packages/grid/x-data-grid/src/joy/icons.tsx index d2870263671d4..c69419a057752 100644 --- a/packages/grid/x-data-grid/src/joy/icons.tsx +++ b/packages/grid/x-data-grid/src/joy/icons.tsx @@ -16,7 +16,6 @@ import SvgIcon, { SvgIconProps } from '@mui/joy/SvgIcon'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; import { GridSortDirection } from '../models/gridSortModel'; import { GridIconSlotsComponent } from '../models/gridIconSlotsComponent'; -import { UncapitalizeObjectKeys } from '../internals/utils'; function createSvgIcon(path: React.ReactNode, displayName: string): typeof SvgIcon { const fontSizeMap = { small: 'lg', medium: 'xl', large: 'xl2', inherit: undefined } as const; @@ -305,7 +304,7 @@ const GridColumnUnsortedIcon = React.memo(function GridColumnHeaderSortIcon( return Icon ? : null; }); -const joyIconSlots: UncapitalizeObjectKeys = { +const joyIconSlots: GridIconSlotsComponent = { booleanCellTrueIcon: GridCheckIcon, booleanCellFalseIcon: GridCloseIcon, columnMenuIcon: GridTripleDotsVerticalIcon, diff --git a/packages/grid/x-data-grid/src/joy/joySlots.tsx b/packages/grid/x-data-grid/src/joy/joySlots.tsx index d69d62cd26736..7e245b9180657 100644 --- a/packages/grid/x-data-grid/src/joy/joySlots.tsx +++ b/packages/grid/x-data-grid/src/joy/joySlots.tsx @@ -16,7 +16,6 @@ import JoyCircularProgress from '@mui/joy/CircularProgress'; import JoyTooltip from '@mui/joy/Tooltip'; import { unstable_useForkRef as useForkRef } from '@mui/utils'; import joyIconSlots, { GridKeyboardArrowRight, GridKeyboardArrowLeft } from './icons'; -import type { UncapitalizeObjectKeys } from '../internals/utils'; import type { GridSlotsComponent, GridSlotsComponentsProps } from '../models'; import { useGridApiContext } from '../hooks/utils/useGridApiContext'; import { useGridRootProps } from '../hooks/utils/useGridRootProps'; @@ -229,7 +228,7 @@ const Select = React.forwardRef< }, ref, ) => { - const handleChange: JoySelectProps['onChange'] = (event, newValue) => { + const handleChange: JoySelectProps['onChange'] = (event, newValue) => { if (event && onChange) { // Same as in https://github.com/mui/material-ui/blob/e5558282a8f36856aef1299f3a36f3235e92e770/packages/mui-material/src/Select/SelectInput.js#L288-L300 @@ -251,7 +250,7 @@ const Select = React.forwardRef< return ( )} + {...(props as JoySelectProps)} listboxOpen={open} onListboxOpenChange={(isOpen) => { if (isOpen) { @@ -360,7 +359,7 @@ const Pagination = React.forwardRef< const pageSizeOptions = isPageSizeIncludedInPageSizeOptions() ? rootProps.pageSizeOptions : []; - const handleChangeRowsPerPage: JoySelectProps['onChange'] = (event, newValue) => { + const handleChangeRowsPerPage: JoySelectProps['onChange'] = (event, newValue) => { const newPageSize = Number(newValue); apiRef.current.setPageSize(newPageSize); }; @@ -434,7 +433,7 @@ const LoadingOverlay = React.forwardRef< ); }); -const joySlots: UncapitalizeObjectKeys> = { +const joySlots: Partial = { ...joyIconSlots, baseCheckbox: Checkbox, baseTextField: TextField, diff --git a/packages/grid/x-data-grid/src/locales/arSD.ts b/packages/grid/x-data-grid/src/locales/arSD.ts index 0be78dd5d5e9b..73b6a3140c813 100644 --- a/packages/grid/x-data-grid/src/locales/arSD.ts +++ b/packages/grid/x-data-grid/src/locales/arSD.ts @@ -47,7 +47,7 @@ const arSDGrid: Partial = { // Filter panel text filterPanelAddFilter: 'إضافة مرشِح', - // filterPanelRemoveAll: 'Remove all', + filterPanelRemoveAll: 'حذف الكل', filterPanelDeleteIconLabel: 'حذف', filterPanelLogicOperator: 'عامل منطقي', filterPanelOperator: 'عامل', @@ -71,33 +71,33 @@ const arSDGrid: Partial = { filterOperatorIsEmpty: 'خالي', filterOperatorIsNotEmpty: 'غير خالي', filterOperatorIsAnyOf: 'أي من', - // 'filterOperator=': '=', - // 'filterOperator!=': '!=', - // 'filterOperator>': '>', - // 'filterOperator>=': '>=', - // 'filterOperator<': '<', - // 'filterOperator<=': '<=', + 'filterOperator=': '=', + 'filterOperator!=': '!=', + 'filterOperator>': '>', + 'filterOperator>=': '>=', + 'filterOperator<': '<', + 'filterOperator<=': '<=', // Header filter operators text - // headerFilterOperatorContains: 'Contains', - // headerFilterOperatorEquals: 'Equals', - // headerFilterOperatorStartsWith: 'Starts with', - // headerFilterOperatorEndsWith: 'Ends with', - // headerFilterOperatorIs: 'Is', - // headerFilterOperatorNot: 'Is not', - // headerFilterOperatorAfter: 'Is after', - // headerFilterOperatorOnOrAfter: 'Is on or after', - // headerFilterOperatorBefore: 'Is before', - // headerFilterOperatorOnOrBefore: 'Is on or before', - // headerFilterOperatorIsEmpty: 'Is empty', - // headerFilterOperatorIsNotEmpty: 'Is not empty', - // headerFilterOperatorIsAnyOf: 'Is any of', - // 'headerFilterOperator=': 'Equals', - // 'headerFilterOperator!=': 'Not equals', - // 'headerFilterOperator>': 'Greater than', - // 'headerFilterOperator>=': 'Greater than or equal to', - // 'headerFilterOperator<': 'Less than', - // 'headerFilterOperator<=': 'Less than or equal to', + headerFilterOperatorContains: 'يحتوي على', + headerFilterOperatorEquals: 'يساوي', + headerFilterOperatorStartsWith: 'يبدأ ب', + headerFilterOperatorEndsWith: 'ينتهي ب', + headerFilterOperatorIs: 'هو', + headerFilterOperatorNot: 'هو ليس', + headerFilterOperatorAfter: 'يقع بعد', + headerFilterOperatorOnOrAfter: 'هو على او بعد', + headerFilterOperatorBefore: 'يقع قبل', + headerFilterOperatorOnOrBefore: 'هو على او بعد', + headerFilterOperatorIsEmpty: 'هو فارغ', + headerFilterOperatorIsNotEmpty: 'هو ليس فارغ', + headerFilterOperatorIsAnyOf: 'هو أي من', + 'headerFilterOperator=': 'يساوي', + 'headerFilterOperator!=': 'لا يساوي', + 'headerFilterOperator>': 'أكبر من', + 'headerFilterOperator>=': 'أكبر من او يساوي', + 'headerFilterOperator<': 'اصغر من', + 'headerFilterOperator<=': 'اصغر من او يساوي', // Filter values text filterValueAny: 'أي', diff --git a/packages/grid/x-data-grid/src/locales/csCZ.ts b/packages/grid/x-data-grid/src/locales/csCZ.ts index a553a6a7661b1..b4eafa7dab0d9 100644 --- a/packages/grid/x-data-grid/src/locales/csCZ.ts +++ b/packages/grid/x-data-grid/src/locales/csCZ.ts @@ -174,8 +174,8 @@ const csCZGrid: Partial = { actionsCellMore: 'více', // Column pinning text - pinToLeft: 'Připnout na levo', - pinToRight: 'Připnout na pravo', + pinToLeft: 'Připnout vlevo', + pinToRight: 'Připnout vpravo', unpin: 'Odepnout', // Tree Data diff --git a/packages/grid/x-data-grid/src/locales/ptBR.ts b/packages/grid/x-data-grid/src/locales/ptBR.ts index 504aaa348cac6..7a304071cdddf 100644 --- a/packages/grid/x-data-grid/src/locales/ptBR.ts +++ b/packages/grid/x-data-grid/src/locales/ptBR.ts @@ -71,12 +71,12 @@ const ptBRGrid: Partial = { filterOperatorIsEmpty: 'está vazio', filterOperatorIsNotEmpty: 'não está vazio', filterOperatorIsAnyOf: 'é qualquer um dos', - // 'filterOperator=': '=', - // 'filterOperator!=': '!=', - // 'filterOperator>': '>', - // 'filterOperator>=': '>=', - // 'filterOperator<': '<', - // 'filterOperator<=': '<=', + 'filterOperator=': 'igual à', + 'filterOperator!=': 'diferente de', + 'filterOperator>': 'maior que', + 'filterOperator>=': 'maior ou igual que', + 'filterOperator<': 'menor que', + 'filterOperator<=': 'menor ou igual que', // Header filter operators text headerFilterOperatorContains: 'Contém', @@ -85,13 +85,13 @@ const ptBRGrid: Partial = { headerFilterOperatorEndsWith: 'Termina com', headerFilterOperatorIs: 'É', headerFilterOperatorNot: 'Não é', - // headerFilterOperatorAfter: 'Is after', - // headerFilterOperatorOnOrAfter: 'Is on or after', - // headerFilterOperatorBefore: 'Is before', - // headerFilterOperatorOnOrBefore: 'Is on or before', - // headerFilterOperatorIsEmpty: 'Is empty', - // headerFilterOperatorIsNotEmpty: 'Is not empty', - // headerFilterOperatorIsAnyOf: 'Is any of', + headerFilterOperatorAfter: 'Depois de', + headerFilterOperatorOnOrAfter: 'Está entre ou depois de', + headerFilterOperatorBefore: 'Antes de', + headerFilterOperatorOnOrBefore: 'Está entre ou antes de', + headerFilterOperatorIsEmpty: 'É vazio', + headerFilterOperatorIsNotEmpty: 'Não é vazio', + headerFilterOperatorIsAnyOf: 'É algum', 'headerFilterOperator=': 'Igual', 'headerFilterOperator!=': 'Não igual', 'headerFilterOperator>': 'Maior que', diff --git a/packages/grid/x-data-grid/src/locales/roRO.ts b/packages/grid/x-data-grid/src/locales/roRO.ts index 4a7421fdd0478..6c3dd716d4a3a 100644 --- a/packages/grid/x-data-grid/src/locales/roRO.ts +++ b/packages/grid/x-data-grid/src/locales/roRO.ts @@ -86,7 +86,7 @@ const roROGrid: Partial = { headerFilterOperatorIs: 'Este', headerFilterOperatorNot: 'Nu este', headerFilterOperatorAfter: 'Este după', - headerFilterOperatorOnOrAfter: 'Este la sau „după”', + headerFilterOperatorOnOrAfter: 'Este la sau după', headerFilterOperatorBefore: 'Este înainte de', headerFilterOperatorOnOrBefore: 'este la sau înainte de', headerFilterOperatorIsEmpty: 'Este gol', diff --git a/packages/grid/x-data-grid/src/locales/ruRU.ts b/packages/grid/x-data-grid/src/locales/ruRU.ts index 744b7e5f41049..74d0ca6a6dfdc 100644 --- a/packages/grid/x-data-grid/src/locales/ruRU.ts +++ b/packages/grid/x-data-grid/src/locales/ruRU.ts @@ -55,7 +55,7 @@ const ruRUGrid: Partial = { // Filter panel text filterPanelAddFilter: 'Добавить фильтр', - // filterPanelRemoveAll: 'Remove all', + filterPanelRemoveAll: 'Очистить фильтр', filterPanelDeleteIconLabel: 'Удалить', filterPanelLogicOperator: 'Логические операторы', filterPanelOperator: 'Операторы', @@ -79,33 +79,33 @@ const ruRUGrid: Partial = { filterOperatorIsEmpty: 'пустой', filterOperatorIsNotEmpty: 'не пустой', filterOperatorIsAnyOf: 'любой из', - // 'filterOperator=': '=', - // 'filterOperator!=': '!=', - // 'filterOperator>': '>', - // 'filterOperator>=': '>=', - // 'filterOperator<': '<', - // 'filterOperator<=': '<=', + 'filterOperator=': '=', + 'filterOperator!=': '!=', + 'filterOperator>': '>', + 'filterOperator>=': '>=', + 'filterOperator<': '<', + 'filterOperator<=': '<=', // Header filter operators text - // headerFilterOperatorContains: 'Contains', - // headerFilterOperatorEquals: 'Equals', - // headerFilterOperatorStartsWith: 'Starts with', - // headerFilterOperatorEndsWith: 'Ends with', - // headerFilterOperatorIs: 'Is', - // headerFilterOperatorNot: 'Is not', - // headerFilterOperatorAfter: 'Is after', - // headerFilterOperatorOnOrAfter: 'Is on or after', - // headerFilterOperatorBefore: 'Is before', - // headerFilterOperatorOnOrBefore: 'Is on or before', - // headerFilterOperatorIsEmpty: 'Is empty', - // headerFilterOperatorIsNotEmpty: 'Is not empty', - // headerFilterOperatorIsAnyOf: 'Is any of', - // 'headerFilterOperator=': 'Equals', - // 'headerFilterOperator!=': 'Not equals', - // 'headerFilterOperator>': 'Greater than', - // 'headerFilterOperator>=': 'Greater than or equal to', - // 'headerFilterOperator<': 'Less than', - // 'headerFilterOperator<=': 'Less than or equal to', + headerFilterOperatorContains: 'содержит', + headerFilterOperatorEquals: 'равен', + headerFilterOperatorStartsWith: 'начинается с', + headerFilterOperatorEndsWith: 'заканчивается на', + headerFilterOperatorIs: 'равен', + headerFilterOperatorNot: 'не равен', + headerFilterOperatorAfter: 'больше чем', + headerFilterOperatorOnOrAfter: 'больше или равно', + headerFilterOperatorBefore: 'меньше чем', + headerFilterOperatorOnOrBefore: 'меньше или равно', + headerFilterOperatorIsEmpty: 'пустой', + headerFilterOperatorIsNotEmpty: 'не пустой', + headerFilterOperatorIsAnyOf: 'любой из', + 'headerFilterOperator=': 'содержит', + 'headerFilterOperator!=': 'не содержит', + 'headerFilterOperator>': 'больше чем', + 'headerFilterOperator>=': 'больше или равно', + 'headerFilterOperator<': 'меньше чем', + 'headerFilterOperator<=': 'меньше или равно', // Filter values text filterValueAny: 'любой', diff --git a/packages/grid/x-data-grid/src/material/index.ts b/packages/grid/x-data-grid/src/material/index.ts index e58d09f5adfa0..4690784e16777 100644 --- a/packages/grid/x-data-grid/src/material/index.ts +++ b/packages/grid/x-data-grid/src/material/index.ts @@ -43,58 +43,58 @@ import type { GridBaseSlots } from '../models/gridSlotsComponent'; import MUISelectOption from './components/MUISelectOption'; const iconSlots: GridIconSlotsComponent = { - BooleanCellTrueIcon: GridCheckIcon, - BooleanCellFalseIcon: GridCloseIcon, - ColumnMenuIcon: GridTripleDotsVerticalIcon, - OpenFilterButtonIcon: GridFilterListIcon, - FilterPanelDeleteIcon: GridCloseIcon, - ColumnFilteredIcon: GridFilterAltIcon, - ColumnSelectorIcon: GridColumnIcon, - ColumnUnsortedIcon: GridColumnUnsortedIcon, - ColumnSortedAscendingIcon: GridArrowUpwardIcon, - ColumnSortedDescendingIcon: GridArrowDownwardIcon, - ColumnResizeIcon: GridSeparatorIcon, - DensityCompactIcon: GridViewHeadlineIcon, - DensityStandardIcon: GridTableRowsIcon, - DensityComfortableIcon: GridViewStreamIcon, - ExportIcon: GridSaveAltIcon, - MoreActionsIcon: GridMoreVertIcon, - TreeDataCollapseIcon: GridExpandMoreIcon, - TreeDataExpandIcon: GridKeyboardArrowRight, - GroupingCriteriaCollapseIcon: GridExpandMoreIcon, - GroupingCriteriaExpandIcon: GridKeyboardArrowRight, - DetailPanelExpandIcon: GridAddIcon, - DetailPanelCollapseIcon: GridRemoveIcon, - RowReorderIcon: GridDragIcon, - QuickFilterIcon: GridSearchIcon, - QuickFilterClearIcon: GridCloseIcon, - ColumnMenuHideIcon: GridVisibilityOffIcon, - ColumnMenuSortAscendingIcon: GridArrowUpwardIcon, - ColumnMenuSortDescendingIcon: GridArrowDownwardIcon, - ColumnMenuFilterIcon: GridFilterAltIcon, - ColumnMenuManageColumnsIcon: GridViewColumnIcon, - ColumnMenuClearIcon: GridClearIcon, - LoadIcon: GridLoadIcon, - FilterPanelAddIcon: GridAddIcon, - FilterPanelRemoveAllIcon: GridDeleteForeverIcon, - ColumnReorderIcon: GridDragIcon, + booleanCellTrueIcon: GridCheckIcon, + booleanCellFalseIcon: GridCloseIcon, + columnMenuIcon: GridTripleDotsVerticalIcon, + openFilterButtonIcon: GridFilterListIcon, + filterPanelDeleteIcon: GridCloseIcon, + columnFilteredIcon: GridFilterAltIcon, + columnSelectorIcon: GridColumnIcon, + columnUnsortedIcon: GridColumnUnsortedIcon, + columnSortedAscendingIcon: GridArrowUpwardIcon, + columnSortedDescendingIcon: GridArrowDownwardIcon, + columnResizeIcon: GridSeparatorIcon, + densityCompactIcon: GridViewHeadlineIcon, + densityStandardIcon: GridTableRowsIcon, + densityComfortableIcon: GridViewStreamIcon, + exportIcon: GridSaveAltIcon, + moreActionsIcon: GridMoreVertIcon, + treeDataCollapseIcon: GridExpandMoreIcon, + treeDataExpandIcon: GridKeyboardArrowRight, + groupingCriteriaCollapseIcon: GridExpandMoreIcon, + groupingCriteriaExpandIcon: GridKeyboardArrowRight, + detailPanelExpandIcon: GridAddIcon, + detailPanelCollapseIcon: GridRemoveIcon, + rowReorderIcon: GridDragIcon, + quickFilterIcon: GridSearchIcon, + quickFilterClearIcon: GridCloseIcon, + columnMenuHideIcon: GridVisibilityOffIcon, + columnMenuSortAscendingIcon: GridArrowUpwardIcon, + columnMenuSortDescendingIcon: GridArrowDownwardIcon, + columnMenuFilterIcon: GridFilterAltIcon, + columnMenuManageColumnsIcon: GridViewColumnIcon, + columnMenuClearIcon: GridClearIcon, + loadIcon: GridLoadIcon, + filterPanelAddIcon: GridAddIcon, + filterPanelRemoveAllIcon: GridDeleteForeverIcon, + columnReorderIcon: GridDragIcon, }; const materialSlots: GridBaseSlots & GridIconSlotsComponent = { ...iconSlots, - BaseCheckbox: MUICheckbox, - BaseTextField: MUITextField, - BaseFormControl: MUIFormControl, - BaseSelect: MUISelect, - BaseSwitch: MUISwitch, - BaseButton: MUIButton, - BaseIconButton: MUIIconButton, - BaseInputAdornment: MUIInputAdornment, - BaseTooltip: MUITooltip, - BasePopper: MUIPopper, - BaseInputLabel: MUIInputLabel, - BaseSelectOption: MUISelectOption, - BaseChip: MUIChip, + baseCheckbox: MUICheckbox, + baseTextField: MUITextField, + baseFormControl: MUIFormControl, + baseSelect: MUISelect, + baseSwitch: MUISwitch, + baseButton: MUIButton, + baseIconButton: MUIIconButton, + baseInputAdornment: MUIInputAdornment, + baseTooltip: MUITooltip, + basePopper: MUIPopper, + baseInputLabel: MUIInputLabel, + baseSelectOption: MUISelectOption, + baseChip: MUIChip, }; export default materialSlots; diff --git a/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts b/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts index 17c98ff59b273..091732aeb64fb 100644 --- a/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts +++ b/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts @@ -10,7 +10,6 @@ import { GridLocaleTextApi } from './gridLocaleTextApi'; import type { GridParamsApi } from './gridParamsApi'; import { GridPreferencesPanelApi } from './gridPreferencesPanelApi'; import { GridPrintExportApi } from './gridPrintExportApi'; -import { GridDisableVirtualizationApi } from './gridDisableVirtualizationApi'; import { GridRowApi } from './gridRowApi'; import { GridRowsMetaApi, GridRowsMetaPrivateApi } from './gridRowsMetaApi'; import { GridRowSelectionApi } from './gridRowSelectionApi'; @@ -18,7 +17,7 @@ import { GridSortApi } from './gridSortApi'; import { GridStateApi, GridStatePrivateApi } from './gridStateApi'; import { GridLoggerApi } from './gridLoggerApi'; import { GridScrollApi } from './gridScrollApi'; -import { GridVirtualScrollerApi } from './gridVirtualScrollerApi'; +import { GridVirtualizationApi, GridVirtualizationPrivateApi } from './gridVirtualizationApi'; import type { GridPipeProcessingApi, GridPipeProcessingPrivateApi, @@ -34,6 +33,7 @@ import type { GridStatePersistenceApi } from '../../hooks/features/statePersiste import { GridColumnGroupingApi } from './gridColumnGroupingApi'; import type { GridInitialStateCommunity, GridStateCommunity } from '../gridStateCommunity'; import { GridHeaderFilteringApi, GridHeaderFilteringPrivateApi } from './gridHeaderFilteringApi'; +import type { DataGridProcessedProps } from '../props/DataGridProps'; export interface GridApiCommon< GridState extends GridStateCommunity = any, @@ -56,7 +56,7 @@ export interface GridApiCommon< GridColumnMenuApi, GridPreferencesPanelApi, GridPrintExportApi, - GridDisableVirtualizationApi, + GridVirtualizationApi, GridLocaleTextApi, GridScrollApi, GridColumnSpanningApi, @@ -68,19 +68,20 @@ export interface GridApiCommon< export interface GridPrivateOnlyApiCommon< Api extends GridApiCommon, PrivateApi extends GridPrivateApiCommon, -> extends GridCorePrivateApi, + Props extends DataGridProcessedProps, +> extends GridCorePrivateApi, GridStatePrivateApi, GridPipeProcessingPrivateApi, GridStrategyProcessingApi, GridColumnSpanningPrivateApi, GridRowsMetaPrivateApi, GridDimensionsPrivateApi, - GridVirtualScrollerApi, GridEditingPrivateApi, GridLoggerApi, GridFocusPrivateApi, - GridHeaderFilteringPrivateApi {} + GridHeaderFilteringPrivateApi, + GridVirtualizationPrivateApi {} export interface GridPrivateApiCommon extends GridApiCommon, - GridPrivateOnlyApiCommon {} + GridPrivateOnlyApiCommon {} diff --git a/packages/grid/x-data-grid/src/models/api/gridApiCommunity.ts b/packages/grid/x-data-grid/src/models/api/gridApiCommunity.ts index 7f785d2b29ec4..308949c6d24bc 100644 --- a/packages/grid/x-data-grid/src/models/api/gridApiCommunity.ts +++ b/packages/grid/x-data-grid/src/models/api/gridApiCommunity.ts @@ -1,4 +1,5 @@ import type { GridInitialStateCommunity, GridStateCommunity } from '../gridStateCommunity'; +import type { DataGridProcessedProps } from '../props/DataGridProps'; import type { GridApiCommon, GridPrivateOnlyApiCommon } from './gridApiCommon'; import type { GridColumnReorderApi } from './gridColumnApi'; import { GridRowProApi } from './gridRowApi'; @@ -12,7 +13,7 @@ export interface GridApiCommunity export interface GridPrivateApiCommunity extends GridApiCommunity, - GridPrivateOnlyApiCommon, + GridPrivateOnlyApiCommon, // APIs that are private in Community plan, but public in Pro and Premium plans GridRowMultiSelectionApi, GridColumnReorderApi, diff --git a/packages/grid/x-data-grid/src/models/api/gridCoreApi.ts b/packages/grid/x-data-grid/src/models/api/gridCoreApi.ts index e69a0917f7a66..405eeca6e2244 100644 --- a/packages/grid/x-data-grid/src/models/api/gridCoreApi.ts +++ b/packages/grid/x-data-grid/src/models/api/gridCoreApi.ts @@ -4,6 +4,7 @@ import { Store } from '../../utils/Store'; import { EventManager, EventListenerOptions } from '../../utils/EventManager'; import { GridApiCaches } from '../gridApiCaches'; import type { GridApiCommon, GridPrivateApiCommon } from './gridApiCommon'; +import type { DataGridProcessedProps } from '../props/DataGridProps'; /** * The core API interface that is available in the grid `apiRef`. @@ -48,6 +49,7 @@ export interface GridCoreApi { export interface GridCorePrivateApi< GridPublicApi extends GridApiCommon, GridPrivateApi extends GridPrivateApiCommon, + GridProps extends DataGridProcessedProps, > { /** * The caches used by hooks and state initializers. @@ -87,6 +89,11 @@ export interface GridCorePrivateApi< * @returns {GridPublicApi} The public api. */ getPublicApi: () => GridPublicApi; + /** + * Allows to access the root props outside of the React component. + * Do not use in React components - use the `useGridRootProps` hook instead. + */ + rootProps: GridProps; /** * The React ref of the grid column container virtualized div element. */ diff --git a/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts b/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts deleted file mode 100644 index 1c31999395d87..0000000000000 --- a/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * The API to disable the virtualization that is available in the grid [[apiRef]]. - */ -export interface GridDisableVirtualizationApi { - /** - * Disables grid's virtualization. - * @ignore - do not document. Remove before releasing v5 stable version. - */ - unstable_disableVirtualization: () => void; - /** - * Enables grid's virtualization. - * @ignore - do not document. Remove before releasing v5 stable version. - */ - unstable_enableVirtualization: () => void; -} diff --git a/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts b/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts index 3140625018972..c2e64a3b953b1 100644 --- a/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts +++ b/packages/grid/x-data-grid/src/models/api/gridEditingApi.ts @@ -183,7 +183,7 @@ export interface GridCellEditingApi extends GridEditingSharedApi { startCellEditMode(params: GridStartCellEditModeParams): void; /** * Puts the cell corresponding to the given row id and field into view mode and updates the original row with the new value stored. - * If `params.ignoreModifications` is `false` it will discard the modifications made. + * If `params.ignoreModifications` is `true` it will discard the modifications made. * @param {GridStopCellEditModeParams} params The row id and field of the cell to stop editing. */ stopCellEditMode(params: GridStopCellEditModeParams): void; @@ -223,7 +223,7 @@ export interface GridRowEditingApi extends GridEditingSharedApi { startRowEditMode(params: GridStartRowEditModeParams): void; /** * Puts the row corresponding to the given id and into view mode and updates the original row with the new values stored. - * If `params.ignoreModifications` is `false` it will discard the modifications made. + * If `params.ignoreModifications` is `true` it will discard the modifications made. * @param {GridStopCellEditModeParams} params The row id and field of the cell to stop editing. */ stopRowEditMode(params: GridStopRowEditModeParams): void; diff --git a/packages/grid/x-data-grid/src/models/api/gridFilterApi.ts b/packages/grid/x-data-grid/src/models/api/gridFilterApi.ts index 4b9967b63eb5e..16f3b6194f991 100644 --- a/packages/grid/x-data-grid/src/models/api/gridFilterApi.ts +++ b/packages/grid/x-data-grid/src/models/api/gridFilterApi.ts @@ -1,6 +1,7 @@ import { GridFilterModel } from '../gridFilterModel'; import { GridFilterItem, GridLogicOperator } from '../gridFilterItem'; import { GridControlledStateReasonLookup } from '../events'; +import { DataGridProcessedProps } from '../props/DataGridProps'; /** * The filter API interface that is available in the grid [[apiRef]]. @@ -52,8 +53,12 @@ export interface GridFilterApi { reason?: GridControlledStateReasonLookup['filter'], ) => void; /** - * Set the quick filter values ot the one given by `values` + * Set the quick filter values to the one given by `values` * @param {any[]} values The list of element to quick filter */ setQuickFilterValues: (values: any[]) => void; + /** + * Returns the value of the `ignoreDiacritics` prop. + */ + ignoreDiacritics: DataGridProcessedProps['ignoreDiacritics']; } diff --git a/packages/grid/x-data-grid/src/models/api/gridLocaleTextApi.ts b/packages/grid/x-data-grid/src/models/api/gridLocaleTextApi.ts index c20e868e6dd62..5ca230b313f71 100644 --- a/packages/grid/x-data-grid/src/models/api/gridLocaleTextApi.ts +++ b/packages/grid/x-data-grid/src/models/api/gridLocaleTextApi.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { ComponentsPropsList } from '@mui/material/styles'; +import { GridColDef } from '../colDef'; /** * Set the types of the texts in the grid. @@ -112,8 +113,8 @@ export interface GridLocaleText { columnMenuFilter: React.ReactNode; columnMenuHideColumn: React.ReactNode; columnMenuUnsort: React.ReactNode; - columnMenuSortAsc: React.ReactNode; - columnMenuSortDesc: React.ReactNode; + columnMenuSortAsc: React.ReactNode | ((colDef: GridColDef) => React.ReactNode); + columnMenuSortDesc: React.ReactNode | ((colDef: GridColDef) => React.ReactNode); // Column header text columnHeaderFiltersTooltipActive: (count: number) => React.ReactNode; diff --git a/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts b/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts deleted file mode 100644 index a91668ed1b3cb..0000000000000 --- a/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { GridRenderContext } from '../params/gridScrollParams'; - -export interface GridVirtualScrollerApi { - /** - * Get the current grid rendering context. - * @returns {GridRenderContext} The `GridRenderContext`. - */ - getRenderContext: () => GridRenderContext; -} diff --git a/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts b/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts new file mode 100644 index 0000000000000..6a5d787e72d39 --- /dev/null +++ b/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts @@ -0,0 +1,22 @@ +import { GridRenderContext } from '../params'; + +export interface GridVirtualizationApi { + /** + * Enable/disable virtualization. + * @param {boolean} enabled The enabled value for virtualization + */ + unstable_setVirtualization: (enabled: boolean) => void; + /** + * Enable/disable column virtualization. + * @param {boolean} enabled The enabled value for column virtualization + */ + unstable_setColumnVirtualization: (enabled: boolean) => void; +} + +export interface GridVirtualizationPrivateApi { + /** + * Get the current grid rendering context. + * @returns {GridRenderContext} The `GridRenderContext`. + */ + getRenderContext: () => GridRenderContext; +} diff --git a/packages/grid/x-data-grid/src/models/api/index.ts b/packages/grid/x-data-grid/src/models/api/index.ts index f84f56ee69a17..709885eea44ad 100644 --- a/packages/grid/x-data-grid/src/models/api/index.ts +++ b/packages/grid/x-data-grid/src/models/api/index.ts @@ -16,10 +16,9 @@ export * from './gridFilterApi'; export * from './gridColumnMenuApi'; export * from './gridPreferencesPanelApi'; export * from './gridPrintExportApi'; -export * from './gridDisableVirtualizationApi'; export * from './gridCallbackDetails'; export * from './gridScrollApi'; -export * from './gridVirtualScrollerApi'; +export * from './gridVirtualizationApi'; export type { GridApiCommon } from './gridApiCommon'; export type { GridEditingApi, GridCellModesModel, GridRowModesModel } from './gridEditingApi'; diff --git a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts index 3b58faa2a5e3f..f925f7d4714af 100644 --- a/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts +++ b/packages/grid/x-data-grid/src/models/colDef/gridColDef.ts @@ -32,28 +32,18 @@ export type ValueOptions = string | number | { value: any; label: string } | Rec */ export type GridKeyValue = string | number | boolean; -export type GridApplyQuickFilterV7 = ( +export type GridApplyQuickFilter = ( value: V, row: R, column: GridColDef, apiRef: React.MutableRefObject, ) => boolean; -export type GetApplyQuickFilterFnLegacy< - R extends GridValidRowModel = GridValidRowModel, - V = any, - F = V, -> = ( +export type GetApplyQuickFilterFn = ( value: any, - colDef: GridStateColDef, + colDef: GridStateColDef, apiRef: React.MutableRefObject, -) => null | ((params: GridCellParams) => boolean); - -export type GetApplyQuickFilterFnV7 = ( - value: any, - colDef: GridStateColDef, - apiRef: React.MutableRefObject, -) => null | GridApplyQuickFilterV7; +) => null | GridApplyQuickFilter; /** * Column Definition base interface. @@ -238,18 +228,9 @@ export interface GridBaseColDef} apiRef Deprecated: The API of the grid. - * @returns {null | ((params: GridCellParams) => boolean)} The function to call to check if a row pass this filter value or not. - */ - getApplyQuickFilterFn?: GetApplyQuickFilterFnLegacy; - /** - * The callback that generates a filtering function for a given quick filter value. - * This function can return `null` to skip filtering for this value and column. - * @param {any} value The value with which we want to filter the column. - * @param {GridStateColDef} colDef The column from which we want to filter the rows. - * @param {React.MutableRefObject} apiRef Deprecated: The API of the grid. - * @returns {null | GridApplyQuickFilterV7} The function to call to check if a row pass this filter value or not. + * @returns {null | GridApplyQuickFilter} The function to call to check if a row pass this filter value or not. */ - getApplyQuickFilterFnV7?: GetApplyQuickFilterFnV7; + getApplyQuickFilterFn?: GetApplyQuickFilterFn; /** * If `true`, this column cannot be reordered. * @default false diff --git a/packages/grid/x-data-grid/src/models/colDef/index.ts b/packages/grid/x-data-grid/src/models/colDef/index.ts index 32ecadea3bd49..da8753ee10322 100644 --- a/packages/grid/x-data-grid/src/models/colDef/index.ts +++ b/packages/grid/x-data-grid/src/models/colDef/index.ts @@ -8,6 +8,7 @@ export type { GridColumnsMeta, GridSingleSelectColDef, GridActionsColDef, + GetApplyQuickFilterFn, } from './gridColDef'; // Other types diff --git a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts index 294cba7f29c59..c893e7597582e 100644 --- a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts +++ b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts @@ -44,6 +44,14 @@ export interface GridRowEventLookup { * Fired when the mouse leaves the row. Called with a [[GridRowParams]] object. */ rowMouseLeave: { params: GridRowParams; event: React.MouseEvent }; + /** + * @ignore - do not document. + */ + rowMouseOut: { params: GridRowParams; event: React.MouseEvent }; + /** + * @ignore - do not document. + */ + rowMouseOver: { params: GridRowParams; event: React.MouseEvent }; /** * Fired when the user starts dragging a row. It's mapped to the `dragstart` DOM event. * @ignore - do not document. @@ -175,6 +183,14 @@ export interface GridColumnHeaderEventLookup { params: GridColumnHeaderParams; event: React.DragEvent; }; + /** + * Fired when a `dblclick` DOM event happens in the column header separator. + * @ignore - do not document. + */ + columnSeparatorDoubleClick: { + params: GridColumnHeaderParams; + event: React.MouseEvent; + }; /** * Fired when a `mousedown` DOM event happens in the column header separator. * @ignore - do not document. diff --git a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts index 4198fd92d6150..d74fe2c2ec786 100644 --- a/packages/grid/x-data-grid/src/models/gridFilterOperator.ts +++ b/packages/grid/x-data-grid/src/models/gridFilterOperator.ts @@ -1,30 +1,20 @@ import * as React from 'react'; import { GridFilterItem } from './gridFilterItem'; -import { GridCellParams } from './params/gridCellParams'; import type { GridColDef } from './colDef/gridColDef'; import type { GridValidRowModel } from './gridRows'; import type { GridApiCommunity } from './api/gridApiCommunity'; -type ApplyFilterFnLegacy = ( - params: GridCellParams, -) => boolean; - -type ApplyFilterFnV7 = ( +type ApplyFilterFn = ( value: V, row: R, column: GridColDef, apiRef: React.MutableRefObject, ) => boolean; -export type GetApplyFilterFnV7 = ( - filterItem: GridFilterItem, - column: GridColDef, -) => null | ApplyFilterFnV7; - -export type GetApplyFilterFnLegacy = ( +export type GetApplyFilterFn = ( filterItem: GridFilterItem, column: GridColDef, -) => null | ApplyFilterFnLegacy; +) => null | ApplyFilterFn; /** * Filter operator definition interface. @@ -50,18 +40,9 @@ export interface GridFilterOperator; - /** - * The callback that generates a filtering function for a given filter item and column. - * This function can return `null` to skip filtering for this item and column. - * This function uses the more performant v7 API. - * @param {GridFilterItem} filterItem The filter item with which we want to filter the column. - * @param {GridColDef} column The column from which we want to filter the rows. - * @returns {null | ApplyFilterFnV7} The function to call to check if a row pass this filter item or not. + * @returns {null | ApplyFilterFn} The function to call to check if a row pass this filter item or not. */ - getApplyFilterFnV7?: GetApplyFilterFnV7; + getApplyFilterFn: GetApplyFilterFn; /** * The input component to render in the filter panel for this filter operator. */ diff --git a/packages/grid/x-data-grid/src/models/gridIconSlotsComponent.ts b/packages/grid/x-data-grid/src/models/gridIconSlotsComponent.ts index a64cdcb16fdb7..90d024a3ea006 100644 --- a/packages/grid/x-data-grid/src/models/gridIconSlotsComponent.ts +++ b/packages/grid/x-data-grid/src/models/gridIconSlotsComponent.ts @@ -2,182 +2,181 @@ import * as React from 'react'; /** * Set of icons used in the grid component UI. - * TODO: Differentiate community and pro interface */ export interface GridIconSlotsComponent { /** * Icon displayed on the boolean cell to represent the true value. * @default GridCheckIcon */ - BooleanCellTrueIcon: React.JSXElementConstructor; + booleanCellTrueIcon: React.JSXElementConstructor; /** * Icon displayed on the boolean cell to represent the false value. * @default GridCloseIcon */ - BooleanCellFalseIcon: React.JSXElementConstructor; + booleanCellFalseIcon: React.JSXElementConstructor; /** * Icon displayed on the side of the column header title to display the filter input component. * @default GridTripleDotsVerticalIcon */ - ColumnMenuIcon: React.JSXElementConstructor; + columnMenuIcon: React.JSXElementConstructor; /** * Icon displayed on the open filter button present in the toolbar by default. * @default GridFilterListIcon */ - OpenFilterButtonIcon: React.JSXElementConstructor; + openFilterButtonIcon: React.JSXElementConstructor; /** * Icon displayed on the column header menu to show that a filter has been applied to the column. * @default GridFilterAltIcon */ - ColumnFilteredIcon: React.JSXElementConstructor; + columnFilteredIcon: React.JSXElementConstructor; /** * Icon displayed on the column menu selector tab. * @default GridColumnIcon */ - ColumnSelectorIcon: React.JSXElementConstructor; + columnSelectorIcon: React.JSXElementConstructor; /** * Icon displayed on the side of the column header title when unsorted. * @default GridColumnUnsortedIcon */ - ColumnUnsortedIcon: React.JSXElementConstructor | null; + columnUnsortedIcon: React.JSXElementConstructor | null; /** * Icon displayed on the side of the column header title when sorted in ascending order. * @default GridArrowUpwardIcon */ - ColumnSortedAscendingIcon: React.JSXElementConstructor | null; + columnSortedAscendingIcon: React.JSXElementConstructor | null; /** * Icon displayed on the side of the column header title when sorted in descending order. * @default GridArrowDownwardIcon */ - ColumnSortedDescendingIcon: React.JSXElementConstructor | null; + columnSortedDescendingIcon: React.JSXElementConstructor | null; /** * Icon displayed in between two column headers that allows to resize the column header. * @default GridSeparatorIcon */ - ColumnResizeIcon: React.JSXElementConstructor; + columnResizeIcon: React.JSXElementConstructor; /** * Icon displayed on the compact density option in the toolbar. * @default GridViewHeadlineIcon */ - DensityCompactIcon: React.JSXElementConstructor; + densityCompactIcon: React.JSXElementConstructor; /** * Icon displayed on the standard density option in the toolbar. * @default GridTableRowsIcon */ - DensityStandardIcon: React.JSXElementConstructor; + densityStandardIcon: React.JSXElementConstructor; /** * Icon displayed on the "comfortable" density option in the toolbar. * @default GridViewStreamIcon */ - DensityComfortableIcon: React.JSXElementConstructor; + densityComfortableIcon: React.JSXElementConstructor; /** * Icon displayed on the open export button present in the toolbar by default. * @default GridSaveAltIcon */ - ExportIcon: React.JSXElementConstructor; + exportIcon: React.JSXElementConstructor; /** * Icon displayed on the `actions` column type to open the menu. * @default GridMoreVertIcon */ - MoreActionsIcon: React.JSXElementConstructor; + moreActionsIcon: React.JSXElementConstructor; /** * Icon displayed on the tree data toggling column when the children are collapsed * @default GridKeyboardArrowRight */ - TreeDataExpandIcon: React.JSXElementConstructor; + treeDataExpandIcon: React.JSXElementConstructor; /** * Icon displayed on the tree data toggling column when the children are expanded * @default GridExpandMoreIcon */ - TreeDataCollapseIcon: React.JSXElementConstructor; + treeDataCollapseIcon: React.JSXElementConstructor; /** * Icon displayed on the grouping column when the children are collapsed * @default GridKeyboardArrowRight */ - GroupingCriteriaExpandIcon: React.JSXElementConstructor; + groupingCriteriaExpandIcon: React.JSXElementConstructor; /** * Icon displayed on the grouping column when the children are expanded * @default GridExpandMoreIcon */ - GroupingCriteriaCollapseIcon: React.JSXElementConstructor; + groupingCriteriaCollapseIcon: React.JSXElementConstructor; /** * Icon displayed on the detail panel toggle column when collapsed. * @default GridAddIcon */ - DetailPanelExpandIcon: React.JSXElementConstructor; + detailPanelExpandIcon: React.JSXElementConstructor; /** * Icon displayed on the detail panel toggle column when expanded. * @default GridRemoveIcon */ - DetailPanelCollapseIcon: React.JSXElementConstructor; + detailPanelCollapseIcon: React.JSXElementConstructor; /** * Icon displayed for deleting the filter from filter panel. * @default GridAddIcon */ - FilterPanelAddIcon: React.JSXElementConstructor; + filterPanelAddIcon: React.JSXElementConstructor; /** * Icon displayed for deleting the filter from filter panel. * @default GridDeleteIcon */ - FilterPanelDeleteIcon: React.JSXElementConstructor; + filterPanelDeleteIcon: React.JSXElementConstructor; /** * Icon displayed for deleting all the active filters from filter panel. * @default GridDeleteForeverIcon */ - FilterPanelRemoveAllIcon: React.JSXElementConstructor; + filterPanelRemoveAllIcon: React.JSXElementConstructor; /** * Icon displayed on the `reorder` column type to reorder a row. * @default GridDragIcon */ - RowReorderIcon: React.JSXElementConstructor; + rowReorderIcon: React.JSXElementConstructor; /** * Icon displayed on the quick filter input. * @default GridSearchIcon */ - QuickFilterIcon: React.JSXElementConstructor; + quickFilterIcon: React.JSXElementConstructor; /** * Icon displayed on the quick filter reset input. * @default GridCloseIcon */ - QuickFilterClearIcon: React.JSXElementConstructor; + quickFilterClearIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for hiding column * @default GridVisibilityOffIcon */ - ColumnMenuHideIcon: React.JSXElementConstructor; + columnMenuHideIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for ascending sort * @default GridArrowUpwardIcon */ - ColumnMenuSortAscendingIcon: React.JSXElementConstructor; + columnMenuSortAscendingIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for descending sort * @default GridArrowDownwardIcon */ - ColumnMenuSortDescendingIcon: React.JSXElementConstructor; + columnMenuSortDescendingIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for filter * @default GridFilterAltIcon */ - ColumnMenuFilterIcon: React.JSXElementConstructor; + columnMenuFilterIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for showing all columns * @default GridViewColumnIcon */ - ColumnMenuManageColumnsIcon: React.JSXElementConstructor; + columnMenuManageColumnsIcon: React.JSXElementConstructor; /** * Icon displayed in column menu for clearing values * @default GridClearIcon */ - ColumnMenuClearIcon: React.JSXElementConstructor; + columnMenuClearIcon: React.JSXElementConstructor; /** * Icon displayed on the input while processing. * @default GridLoadIcon */ - LoadIcon: React.JSXElementConstructor; + loadIcon: React.JSXElementConstructor; /** * Icon displayed on the column reorder button. * @default GridDragIcon */ - ColumnReorderIcon: React.JSXElementConstructor; + columnReorderIcon: React.JSXElementConstructor; } diff --git a/packages/grid/x-data-grid/src/models/gridSlotsComponent.ts b/packages/grid/x-data-grid/src/models/gridSlotsComponent.ts index 44f3c9e88c688..f6c062230c551 100644 --- a/packages/grid/x-data-grid/src/models/gridSlotsComponent.ts +++ b/packages/grid/x-data-grid/src/models/gridSlotsComponent.ts @@ -1,5 +1,4 @@ import * as React from 'react'; -import type { UncapitalizeObjectKeys } from '../internals/utils'; import type { GridIconSlotsComponent } from './gridIconSlotsComponent'; export interface GridBaseSlots { @@ -7,71 +6,69 @@ export interface GridBaseSlots { * The custom Checkbox component used in the grid for both header and cells. * @default Checkbox */ - BaseCheckbox: React.JSXElementConstructor; + baseCheckbox: React.JSXElementConstructor; /** * The custom Chip component used in the grid. * @default Chip */ - BaseChip: React.JSXElementConstructor; + baseChip: React.JSXElementConstructor; /** * The custom InputAdornment component used in the grid. * @default InputAdornment */ - BaseInputAdornment: React.JSXElementConstructor; + baseInputAdornment: React.JSXElementConstructor; /** * The custom TextField component used in the grid. * @default TextField */ - BaseTextField: React.JSXElementConstructor; + baseTextField: React.JSXElementConstructor; /** * The custom FormControl component used in the grid. * @default FormControl */ - BaseFormControl: React.JSXElementConstructor; + baseFormControl: React.JSXElementConstructor; /** * The custom Select component used in the grid. * @default Select */ - BaseSelect: React.JSXElementConstructor; + baseSelect: React.JSXElementConstructor; /** * The custom Switch component used in the grid. * @default Switch */ - BaseSwitch: React.JSXElementConstructor; + baseSwitch: React.JSXElementConstructor; /** * The custom Button component used in the grid. * @default Button */ - BaseButton: React.JSXElementConstructor; + baseButton: React.JSXElementConstructor; /** * The custom IconButton component used in the grid. * @default IconButton */ - BaseIconButton: React.JSXElementConstructor; + baseIconButton: React.JSXElementConstructor; /** * The custom Tooltip component used in the grid. * @default Tooltip */ - BaseTooltip: React.JSXElementConstructor; + baseTooltip: React.JSXElementConstructor; /** * The custom Popper component used in the grid. * @default Popper */ - BasePopper: React.JSXElementConstructor; + basePopper: React.JSXElementConstructor; /** * The custom InputLabel component used in the grid. * @default InputLabel */ - BaseInputLabel: React.JSXElementConstructor; + baseInputLabel: React.JSXElementConstructor; /** * The custom SelectOption component used in the grid. * @default MenuItem */ - BaseSelectOption: React.JSXElementConstructor; + baseSelectOption: React.JSXElementConstructor; } -// TODO v7: camelCase GridSlotsComponent, the `componenets` prop is going away. - /** * Grid components React prop interface containing all the overridable components. */ @@ -80,93 +77,90 @@ export interface GridSlotsComponent extends GridBaseSlots, GridIconSlotsComponen * The custom Chip component used in the grid. * @default Chip */ - BaseChip: React.JSXElementConstructor; + baseChip: React.JSXElementConstructor; /** * Component rendered for each cell. * @default GridCell */ - Cell: React.JSXElementConstructor; + cell: React.JSXElementConstructor; /** * Component rendered for each skeleton cell. * @default GridSkeletonCell */ - SkeletonCell: React.JSXElementConstructor; + skeletonCell: React.JSXElementConstructor; /** * Filter icon component rendered in each column header. * @default GridColumnHeaderFilterIconButton */ - ColumnHeaderFilterIconButton: React.JSXElementConstructor; + columnHeaderFilterIconButton: React.JSXElementConstructor; /** * Column menu component rendered by clicking on the 3 dots "kebab" icon in column headers. * @default GridColumnMenu */ - ColumnMenu: React.JSXElementConstructor; + columnMenu: React.JSXElementConstructor; /** * Component responsible for rendering the column headers. * @default DataGridColumnHeaders */ - ColumnHeaders: React.JSXElementConstructor; + columnHeaders: React.JSXElementConstructor; /** * Footer component rendered at the bottom of the grid viewport. * @default GridFooter */ - Footer: React.JSXElementConstructor; + footer: React.JSXElementConstructor; /** * Row count component rendered in the footer * @default GridRowCount */ - FooterRowCount: React.JSXElementConstructor; + footerRowCount: React.JSXElementConstructor; /** * Toolbar component rendered inside the Header component. * @default null */ - Toolbar: React.JSXElementConstructor | null; + toolbar: React.JSXElementConstructor | null; /** * PreferencesPanel component rendered inside the Header component. * @default GridPreferencesPanel */ - PreferencesPanel: React.JSXElementConstructor; + preferencesPanel: React.JSXElementConstructor; /** * Loading overlay component rendered when the grid is in a loading state. * @default GridLoadingOverlay */ - LoadingOverlay: React.JSXElementConstructor; + loadingOverlay: React.JSXElementConstructor; /** * No results overlay component rendered when the grid has no results after filtering. * @default GridNoResultsOverlay */ - NoResultsOverlay: React.JSXElementConstructor; + noResultsOverlay: React.JSXElementConstructor; /** * No rows overlay component rendered when the grid has no rows. * @default GridNoRowsOverlay */ - NoRowsOverlay: React.JSXElementConstructor; + noRowsOverlay: React.JSXElementConstructor; /** * Pagination component rendered in the grid footer by default. * @default Pagination */ - Pagination: React.JSXElementConstructor | null; + pagination: React.JSXElementConstructor | null; /** * Filter panel component rendered when clicking the filter button. * @default GridFilterPanel */ - FilterPanel: React.JSXElementConstructor; + filterPanel: React.JSXElementConstructor; /** * GridColumns panel component rendered when clicking the columns button. * @default GridColumnsPanel */ - ColumnsPanel: React.JSXElementConstructor; + columnsPanel: React.JSXElementConstructor; /** * Panel component wrapping the filters and columns panels. * @default GridPanel */ - Panel: React.JSXElementConstructor; + panel: React.JSXElementConstructor; /** * Component rendered for each row. * @default GridRow */ - Row: React.JSXElementConstructor; + row: React.JSXElementConstructor; } - -export interface UncapitalizedGridSlotsComponent - extends UncapitalizeObjectKeys {} diff --git a/packages/grid/x-data-grid/src/models/gridStateCommunity.ts b/packages/grid/x-data-grid/src/models/gridStateCommunity.ts index ac5bf733807a1..a0ee71bdf0c2b 100644 --- a/packages/grid/x-data-grid/src/models/gridStateCommunity.ts +++ b/packages/grid/x-data-grid/src/models/gridStateCommunity.ts @@ -15,6 +15,7 @@ import type { GridSortingInitialState, GridSortingState, GridTabIndexState, + GridVirtualizationState, } from '../hooks'; import type { GridRowsMetaState } from '../hooks/features/rows/gridRowsMetaState'; import type { GridEditingState } from './gridEditRowModel'; @@ -42,6 +43,7 @@ export interface GridStateCommunity { filter: GridFilterState; preferencePanel: GridPreferencePanelState; density: GridDensityState; + virtualization: GridVirtualizationState; } /** diff --git a/packages/grid/x-data-grid/src/models/index.ts b/packages/grid/x-data-grid/src/models/index.ts index 56dedd78b6d35..4ea52fabce63f 100644 --- a/packages/grid/x-data-grid/src/models/index.ts +++ b/packages/grid/x-data-grid/src/models/index.ts @@ -16,7 +16,7 @@ export * from './gridCell'; export * from './gridColumnHeaderClass'; export * from './api'; export * from './gridIconSlotsComponent'; -export type { GridSlotsComponent, UncapitalizedGridSlotsComponent } from './gridSlotsComponent'; +export type { GridSlotsComponent } from './gridSlotsComponent'; export * from './gridSlotsComponentsProps'; export * from './gridDensity'; export * from './logger'; diff --git a/packages/grid/x-data-grid/src/models/props/DataGridProps.ts b/packages/grid/x-data-grid/src/models/props/DataGridProps.ts index 647e04f9dae38..31c3c31728514 100644 --- a/packages/grid/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/grid/x-data-grid/src/models/props/DataGridProps.ts @@ -31,7 +31,6 @@ import { GridColumnVisibilityModel } from '../../hooks/features/columns/gridColu import { GridCellModesModel, GridRowModesModel } from '../api/gridEditingApi'; import { GridColumnGroupingModel } from '../gridColumnGrouping'; import { GridPaginationModel } from '../gridPaginationProps'; -import { UncapitalizeObjectKeys } from '../../internals/utils'; export interface GridExperimentalFeatures { /** @@ -69,7 +68,7 @@ export type DataGridProps = Omit< export interface DataGridProcessedProps extends DataGridPropsWithDefaultValues, DataGridPropsWithComplexDefaultValueAfterProcessing, - Omit, 'componentsProps'> {} + DataGridPropsWithoutDefaultValue {} /** * The props of the `DataGrid` component after the pre-processing phase that the user should not be able to override. @@ -92,7 +91,7 @@ export type DataGridForcedPropsKey = * The `DataGrid` options with a default value that must be merged with the value given through props. */ export interface DataGridPropsWithComplexDefaultValueAfterProcessing { - slots: UncapitalizeObjectKeys; + slots: GridSlotsComponent; localeText: GridLocaleText; } @@ -102,13 +101,8 @@ export interface DataGridPropsWithComplexDefaultValueAfterProcessing { export interface DataGridPropsWithComplexDefaultValueBeforeProcessing { /** * Overridable components. - * @deprecated Use `slots` instead. */ - components?: Partial; - /** - * Overridable components. - */ - slots?: UncapitalizeObjectKeys>; + slots?: Partial; /** * Set the locale text of the grid. * You can find all the translation keys supported in [the source](https://github.com/mui/mui-x/blob/HEAD/packages/grid/x-data-grid/src/constants/localeTextConstants.ts) in the GitHub repository. @@ -267,6 +261,12 @@ export interface DataGridPropsWithDefaultValues { * @default false */ hideFooterSelectedRowCount: boolean; + /** + * If `true`, the diacritics (accents) are ignored when filtering or quick filtering. + * E.g. when filter value is `cafe`, the rows with `café` will be visible. + * @default false + */ + ignoreDiacritics: boolean; /** * If `true`, the selection model will retain selected rows that do not exist. * Useful when using server side pagination and row selections need to be retained @@ -325,7 +325,7 @@ export interface DataGridPropsWithDefaultValues { * The order of the sorting sequence. * @default ['asc', 'desc', null] */ - sortingOrder: GridSortDirection[]; + sortingOrder: readonly GridSortDirection[]; /** * Sorting can be processed on the server or client-side. * Set it to 'client' if you would like to handle sorting on the client-side. @@ -358,7 +358,7 @@ export interface DataGridPropsWithDefaultValues { /** * If `true`, the grid will not use `valueFormatter` when exporting to CSV or copying to clipboard. * If an object is provided, you can choose to ignore the `valueFormatter` for CSV export or clipboard export. - * @default: false + * @default false */ unstable_ignoreValueFormatterDuringExport: | boolean @@ -371,6 +371,13 @@ export interface DataGridPropsWithDefaultValues { * @default '\t' */ clipboardCopyCellDelimiter: string; + /** + * The milliseconds delay to wait after measuring the row height before recalculating row positions. + * Setting it to a lower value could be useful when using dynamic row height, + * but might reduce performance when displaying a large number of rows. + * @default 166 + */ + rowPositionsDebounceMs: number; } /** @@ -705,7 +712,7 @@ export interface DataGridPropsWithoutDefaultValue[]; + columns: readonly GridColDef[]; /** * Return the id of a given [[GridRowModel]]. */ @@ -732,11 +739,6 @@ export interface DataGridPropsWithoutDefaultValue - Column Headers', () => { +describe(' - Column headers', () => { const { render, clock } = createRenderer({ clock: 'fake' }); const baselineProps = { @@ -50,7 +50,7 @@ describe(' - Column Headers', () => { }); }); - describe('Column Menu', () => { + describe('Column menu', () => { it('should allow to hide column', () => { render(
                diff --git a/packages/grid/x-data-grid/src/tests/columnSpanning.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/columnSpanning.DataGrid.test.tsx index 449c975019c0a..36165bbcff92a 100644 --- a/packages/grid/x-data-grid/src/tests/columnSpanning.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/columnSpanning.DataGrid.test.tsx @@ -1,12 +1,12 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, within, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, within, userEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGrid, gridClasses, GridColDef } from '@mui/x-data-grid'; import { getCell, getActiveCell, getColumnHeaderCell } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Column Spanning', () => { +describe(' - Column spanning', () => { const { render, clock } = createRenderer({ clock: 'fake' }); const baselineProps = { diff --git a/packages/grid/x-data-grid/src/tests/columns.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/columns.DataGrid.test.tsx index 824d1b13e16da..c5f26f0911ddf 100644 --- a/packages/grid/x-data-grid/src/tests/columns.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/columns.DataGrid.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer } from '@mui-internal/test-utils'; import { DataGrid, DataGridProps, GridRowsProp, GridColDef } from '@mui/x-data-grid'; import { getCell, getColumnHeaderCell, getColumnHeadersTextContent } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid/src/tests/columns.spec.tsx b/packages/grid/x-data-grid/src/tests/columns.spec.tsx index bf780e6bb80fc..ac5a5ddc14235 100644 --- a/packages/grid/x-data-grid/src/tests/columns.spec.tsx +++ b/packages/grid/x-data-grid/src/tests/columns.spec.tsx @@ -149,3 +149,10 @@ function CellParamsFormattedValue() { /> ); } + +const constBrandColumns = [{ field: 'brand' }] as const; +const constEmptyRows = [] as const; + +function ConstProps() { + return ; +} diff --git a/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx index 0e241faaed698..8e49966b85150 100644 --- a/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/columnsGrouping.DataGrid.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, ErrorBoundary, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, ErrorBoundary, screen } from '@mui-internal/test-utils'; import { DataGrid, DataGridProps, GridRowModel, GridColDef } from '@mui/x-data-grid'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); diff --git a/packages/grid/x-data-grid/src/tests/columnsVisibility.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/columnsVisibility.DataGrid.test.tsx index a95ee66420f0e..21be4634b256c 100644 --- a/packages/grid/x-data-grid/src/tests/columnsVisibility.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/columnsVisibility.DataGrid.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { DataGrid, DataGridProps, GridRowsProp, GridColDef, GridToolbar } from '@mui/x-data-grid'; import { getColumnHeadersTextContent } from 'test/utils/helperFn'; @@ -11,7 +11,7 @@ const rows: GridRowsProp = [{ id: 1, idBis: 1 }]; const columns: GridColDef[] = [{ field: 'id' }, { field: 'idBis' }]; -describe(' - Columns Visibility', () => { +describe(' - Columns visibility', () => { const { render } = createRenderer(); function TestDataGrid( @@ -46,8 +46,8 @@ describe(' - Columns Visibility', () => { it('should update the visible columns when props.onColumnVisibilityModelChange and props.columnVisibilityModel are not defined', () => { render( , ); @@ -62,8 +62,8 @@ describe(' - Columns Visibility', () => { const onColumnVisibilityModelChange = spy(); render( , @@ -83,8 +83,8 @@ describe(' - Columns Visibility', () => { const onColumnVisibilityModelChange = spy(); render( - Columns Visibility', () => { const onColumnVisibilityModelChange = spy(); render( - Columns Visibility', () => { it('should not hide non hideable columns when toggling all columns', () => { render( , @@ -148,7 +148,9 @@ describe(' - Columns Visibility', () => { it('should not show hidden non hideable columns when "Show all" is clicked', () => { render( - Columns Visibility', () => { it('should not show non-hideable columns when "Hide all" is clicked', () => { render( - Columns Visibility', () => { columnVisibilityModel: { idBis: false }, }, }} - components={{ - Toolbar: GridToolbar, + slots={{ + toolbar: GridToolbar, }} />, ); @@ -259,10 +263,10 @@ describe(' - Columns Visibility', () => { it('should autofocus the first switch element in columns panel when `autoFocusSearchField` disabled', () => { render( - Columns Visibility', () => { it('should hide `Hide all` in columns panel when `disableHideAllButton` is `true`', () => { render( - Columns Visibility', () => { it('should hide `Show all` in columns panel when `disableShowAllButton` is `true`', () => { render( - Export', () => { const { render, clock } = createRenderer({ clock: 'fake' }); @@ -37,7 +37,7 @@ describe(' - Export', () => { describe('component: GridToolbar', () => { it('should export with the default csvOptions', async () => { - render(); + render(); fireEvent.click(screen.getByRole('button', { name: 'Export' })); clock.runToLast(); expect(screen.queryByRole('menu')).not.to.equal(null); @@ -50,8 +50,8 @@ describe(' - Export', () => { it('should apply custom csvOptions', async () => { render( , ); fireEvent.click(screen.getByRole('button', { name: 'Export' })); @@ -66,8 +66,8 @@ describe(' - Export', () => { it('should disable csv export when passing `csvOptions.disableToolbarButton`', () => { render( , ); fireEvent.click(screen.getByRole('button', { name: 'Export' })); @@ -79,7 +79,7 @@ describe(' - Export', () => { describe('component: GridToolbarExport', () => { it('should export with the default csvOptions', async () => { - render( }} />); + render( }} />); fireEvent.click(screen.getByRole('button', { name: 'Export' })); clock.runToLast(); expect(screen.queryByRole('menu')).not.to.equal(null); @@ -92,7 +92,7 @@ describe(' - Export', () => { it('should apply custom csvOptions', async () => { render( }} + slots={{ toolbar: () => }} />, ); fireEvent.click(screen.getByRole('button', { name: 'Export' })); @@ -107,8 +107,8 @@ describe(' - Export', () => { it('should disable csv export when passing `csvOptions.disableToolbarButton`', async () => { render( , + slots={{ + toolbar: () => , }} />, ); diff --git a/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx index 44f9e654c0a56..b1c09140fc953 100644 --- a/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/filterPanel.DataGrid.test.tsx @@ -4,12 +4,12 @@ import { spy } from 'sinon'; import { DataGrid, DataGridProps, - GridCellParams, GridFilterInputValue, GridFilterInputValueProps, + GridFilterOperator, GridPreferencePanelsValue, } from '@mui/x-data-grid'; -import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { getColumnHeaderCell, getColumnValues } from 'test/utils/helperFn'; function setColumnValue(columnValue: string) { @@ -103,14 +103,13 @@ describe(' - Filter panel', () => { sensitivity: 'base', usage: 'search', }); - return (params: GridCellParams): boolean => { - const value = params.value!; + return (value) => { return collator.compare(filterItem.value, (value && value.toString()) || '') === 0; }; }, InputComponent: GridFilterInputValue, }, - ], + ] as GridFilterOperator[], }, { field: 'isPublished', type: 'boolean' }, { diff --git a/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx index e5ab37fa5443d..506ea4c286263 100644 --- a/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/filtering.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGrid, @@ -10,8 +10,6 @@ import { GridPreferencePanelsValue, GridToolbar, GridFilterOperator, - GRID_STRING_COL_DEF, - getGridStringOperators, } from '@mui/x-data-grid'; import { getColumnValues } from 'test/utils/helperFn'; import { spy } from 'sinon'; @@ -44,13 +42,11 @@ describe(' - Filter', () => { let disableEval = false; function testEval(fn: Function) { - return () => { - disableEval = false; - fn(); - disableEval = true; - fn(); - disableEval = false; - }; + disableEval = false; + fn(); + disableEval = true; + fn(); + disableEval = false; } function TestCase(props: Partial) { @@ -412,6 +408,52 @@ describe(' - Filter', () => { '1', ]); }); + + describe('ignoreDiacritics', () => { + function DiacriticsTestCase({ + filterValue, + ...props + }: Partial & { filterValue: GridFilterItem['value'] }) { + return ( + + ); + } + + it('should not ignore diacritics by default', () => { + testEval(() => { + const { unmount } = render(); + expect(getColumnValues(0)).to.deep.equal([]); + unmount(); + }); + + testEval(() => { + const { unmount } = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + unmount(); + }); + }); + + it('should ignore diacritics when `ignoreDiacritics` is enabled', () => { + testEval(() => { + const { unmount } = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + unmount(); + }); + + testEval(() => { + const { unmount } = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + unmount(); + }); + }); + }); }); describe('column type: number', () => { @@ -1181,7 +1223,7 @@ describe(' - Filter', () => { type: 'number', }, ]} - components={{ Toolbar: GridToolbarFilterButton }} + slots={{ toolbar: GridToolbarFilterButton }} />, ); @@ -1228,24 +1270,24 @@ describe(' - Filter', () => { label: 'Contains', value: 'contains', getApplyFilterFn: (filterItem) => { - return (params) => { + return (value) => { if ( !filterItem.field || !filterItem.value || !filterItem.operator || - !params.value + !value ) { return null; } - return params.value.includes(filterItem.value); + return value.includes(filterItem.value); }; }, getValueAsString: (value) => `"${value}" text string`, }, - ], + ] as GridFilterOperator[], }, ]} - components={{ Toolbar: GridToolbarFilterButton }} + slots={{ toolbar: GridToolbarFilterButton }} />
                , ); @@ -1263,171 +1305,6 @@ describe(' - Filter', () => { }); }); - describe('v7 filter compatibility', () => { - const getRows = (operator: GridFilterOperator) => { - const { unmount } = render( - , - ); - - const values = getColumnValues(0); - unmount(); - return values; - }; - - it('works with internal filters', () => { - const operator: GridFilterOperator = { - value: 'equals', - getApplyFilterFn: getGridStringOperators().find((o) => o.value === 'equals')! - .getApplyFilterFn, - getApplyFilterFnV7: getGridStringOperators().find((o) => o.value === 'equals')! - .getApplyFilterFnV7, - }; - - expect(getRows(operator)).to.deep.equal(['UK']); - }); - - it('works with custom getApplyFilterFn', () => { - const operator: GridFilterOperator = { - value: 'equals', - getApplyFilterFn: () => { - return (params): boolean => { - return params.value === 'Canada'; - }; - }, - getApplyFilterFnV7: getGridStringOperators().find((o) => o.value === 'equals')! - .getApplyFilterFnV7, - }; - - expect(getRows(operator)).to.deep.equal(['Canada']); - }); - - it('works with custom getApplyFilterFn and getApplyFilterFnV7', () => { - const operator: GridFilterOperator = { - value: 'equals', - getApplyFilterFn: () => { - return (params): boolean => { - return params.value === 'Canada'; - }; - }, - getApplyFilterFnV7: () => { - return (value): boolean => { - return value === 'Spain'; - }; - }, - }; - - expect(getRows(operator)).to.deep.equal(['Spain']); - }); - - it('works with custom getApplyFilterFnV7', () => { - const operator: GridFilterOperator = { - value: 'equals', - getApplyFilterFn: getGridStringOperators().find((o) => o.value === 'equals')! - .getApplyFilterFn, - getApplyFilterFnV7: () => { - return (value): boolean => { - return value === 'Spain'; - }; - }, - }; - - expect(getRows(operator)).to.deep.equal(['Spain']); - }); - }); - - describe('v7 quick filter compatibility', () => { - const getRows = (colDef: Partial) => { - const { unmount } = render( - , - ); - - const values = getColumnValues(0); - unmount(); - return values; - }; - - it('works with internal filters', () => { - const colDef: Partial = { - getApplyQuickFilterFn: GRID_STRING_COL_DEF.getApplyQuickFilterFn, - getApplyQuickFilterFnV7: GRID_STRING_COL_DEF.getApplyQuickFilterFnV7, - }; - expect(getRows(colDef)).to.deep.equal(['UK']); - }); - - it('works with custom getApplyFilterFn', () => { - const colDef: Partial = { - getApplyQuickFilterFn: () => { - return (params) => { - return params.value === 'Canada'; - }; - }, - getApplyQuickFilterFnV7: GRID_STRING_COL_DEF.getApplyQuickFilterFnV7, - }; - expect(getRows(colDef)).to.deep.equal(['Canada']); - }); - - it('works with custom getApplyFilterFn and getApplyFilterFnV7', () => { - const colDef: Partial = { - getApplyQuickFilterFn: () => { - return (params) => { - return params.value === 'Canada'; - }; - }, - getApplyQuickFilterFnV7: () => { - return (value) => { - return value === 'Spain'; - }; - }, - }; - expect(getRows(colDef)).to.deep.equal(['Spain']); - }); - - it('works with custom getApplyFilterFnV7', () => { - const colDef: Partial = { - getApplyQuickFilterFn: GRID_STRING_COL_DEF.getApplyQuickFilterFn, - getApplyQuickFilterFnV7: () => { - return (value) => { - return value === 'Spain'; - }; - }, - }; - expect(getRows(colDef)).to.deep.equal(['Spain']); - }); - }); - it('should translate operators dynamically in toolbar without crashing ', () => { expect(() => { return ( @@ -1450,8 +1327,8 @@ describe(' - Filter', () => { }, ], }} - components={{ - Toolbar: GridToolbar, + slots={{ + toolbar: GridToolbar, }} />
                diff --git a/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx index b9a4b8cbfe453..1cd7a93ec6bff 100644 --- a/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/keyboard.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act, userEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act, userEvent } from '@mui-internal/test-utils'; import { spy } from 'sinon'; import { expect } from 'chai'; import { @@ -10,8 +10,9 @@ import { getColumnValues, getRow, } from 'test/utils/helperFn'; -import { DataGrid, DataGridProps, GridColDef } from '@mui/x-data-grid'; +import { DataGrid, DataGridProps, GridActionsCellItem, GridColDef } from '@mui/x-data-grid'; import { useBasicDemoData, getBasicGridData } from '@mui/x-data-grid-generator'; +import RestoreIcon from '@mui/icons-material/Restore'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); @@ -716,6 +717,113 @@ describe(' - Keyboard', () => { expect(virtualScroller.scrollLeft).to.equal(0); }); + it('should focus actions cell with one disabled item', () => { + const columns = [ + { + field: 'actions', + type: 'actions', + getActions: () => [ + } id={'action_1'} disabled />, + } id={'action_2'} />, + ], + }, + { field: 'id', width: 400 }, + { field: 'name' }, + ]; + const rows = [ + { id: 1, name: 'John' }, + { id: 2, name: 'Doe' }, + ]; + + render( +
                + +
                , + ); + + const cell = getCell(0, 1); + userEvent.mousePress(cell); + + fireEvent.keyDown(cell, { key: 'ArrowLeft' }); + expect(getActiveCell()).to.equal(`0-0`); + + // expect the only focusable button to be the active element + expect(document.activeElement?.id).to.equal('action_2'); + }); + + it('should focus actions cell with all items disabled', () => { + const columns = [ + { + field: 'actions', + type: 'actions', + getActions: () => [ + } id={'action_1'} disabled />, + } id={'action_2'} disabled />, + ], + }, + { field: 'id', width: 400 }, + { field: 'name' }, + ]; + const rows = [ + { id: 1, name: 'John' }, + { id: 2, name: 'Doe' }, + ]; + + render( +
                + +
                , + ); + + const cell = getCell(0, 1); + userEvent.mousePress(cell); + + fireEvent.keyDown(cell, { key: 'ArrowLeft' }); + expect(getActiveCell()).to.equal(`0-0`); + }); + + it('should be able to navigate the actions', () => { + const columns = [ + { + field: 'actions', + type: 'actions', + getActions: () => [ + } id={'action_1'} disabled />, + } id={'action_2'} />, + } id={'action_3'} disabled />, + } id={'action_4'} disabled />, + } id={'action_5'} />, + ], + }, + { field: 'id', width: 400 }, + { field: 'name' }, + ]; + const rows = [ + { id: 1, name: 'John' }, + { id: 2, name: 'Doe' }, + ]; + + render( +
                + +
                , + ); + + const cell = getCell(0, 1); + userEvent.mousePress(cell); + + fireEvent.keyDown(cell, { key: 'ArrowLeft' }); + expect(getActiveCell()).to.equal(`0-0`); + + // expect the only focusable button to be the active element + expect(document.activeElement?.id).to.equal('action_2'); + + fireEvent.keyDown(document.activeElement!, { key: 'ArrowRight' }); + + // expect the only focusable button to be the active element + expect(document.activeElement?.id).to.equal('action_5'); + }); + it('should not throw when moving into an empty grid', async () => { const columns = [{ field: 'id', width: 400 }, { field: 'name' }]; const rows = [] as any[]; diff --git a/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx index af9a37c187e1f..5c0173f4fa250 100644 --- a/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/layout.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, screen, ErrorBoundary, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, ErrorBoundary, waitFor } from '@mui-internal/test-utils'; import { stub, spy } from 'sinon'; import { expect } from 'chai'; import { @@ -14,7 +14,7 @@ import { useBasicDemoData } from '@mui/x-data-grid-generator'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { getColumnHeaderCell, getColumnValues, getCell, getRow, sleep } from 'test/utils/helperFn'; -describe(' - Layout & Warnings', () => { +describe(' - Layout & warnings', () => { const { clock, render } = createRenderer(); const baselineProps = { @@ -886,8 +886,8 @@ describe(' - Layout & Warnings', () => {
                @@ -914,8 +914,8 @@ describe(' - Layout & Warnings', () => {
                @@ -1048,7 +1048,7 @@ describe(' - Layout & Warnings', () => { function TestCase(props: Partial) { return (
                - +
                ); } @@ -1063,7 +1063,7 @@ describe(' - Layout & Warnings', () => { function TestCase(props: Partial) { return (
                - +
                ); } diff --git a/packages/grid/x-data-grid/src/tests/pagination.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/pagination.DataGrid.test.tsx index d714aad74cd08..bc5a7cf594602 100644 --- a/packages/grid/x-data-grid/src/tests/pagination.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/pagination.DataGrid.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy, stub, SinonStub, SinonSpy } from 'sinon'; import { expect } from 'chai'; -import { createRenderer, fireEvent, screen, userEvent, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, userEvent, waitFor } from '@mui-internal/test-utils'; import { DataGrid, DataGridProps, diff --git a/packages/grid/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx index 0cb32dc3c6b85..f8f6e0df1ab23 100644 --- a/packages/grid/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/quickFiltering.DataGrid.test.tsx @@ -1,10 +1,11 @@ import * as React from 'react'; -import { createRenderer, screen, fireEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, fireEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { DataGrid, DataGridProps, + GetApplyQuickFilterFn, GridFilterModel, GridLogicOperator, GridToolbar, @@ -14,7 +15,7 @@ import { getColumnValues, sleep } from 'test/utils/helperFn'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); -describe(' - Quick Filter', () => { +describe(' - Quick filter', () => { const { render, clock } = createRenderer(); const baselineProps = { @@ -270,13 +271,13 @@ describe(' - Quick Filter', () => { }); it('should apply filters on column visibility change when quickFilterExcludeHiddenColumns=true', () => { - const getApplyQuickFilterFnV7Spy = spy(getGridStringQuickFilterFn); + const getApplyQuickFilterFnSpy = spy(getGridStringQuickFilterFn); const { setProps } = render( - Quick Filter', () => { ); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); setProps({ columnVisibilityModel: { brand: false } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal([]); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(3); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(3); setProps({ columnVisibilityModel: { brand: true } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(4); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(4); }); it('should not apply filters on column visibility change when quickFilterExcludeHiddenColumns=true but no quick filter values', () => { - const getApplyQuickFilterFnV7Spy = spy(getGridStringQuickFilterFn); + const getApplyQuickFilterFnSpy = spy(getGridStringQuickFilterFn); const { setProps } = render( - Quick Filter', () => { ); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(0); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(0); setProps({ columnVisibilityModel: { brand: false } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(0); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(0); setProps({ columnVisibilityModel: { brand: true } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['0', '1', '2']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(0); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(0); }); it('should not apply filters on column visibility change when quickFilterExcludeHiddenColumns=false', () => { - const getApplyQuickFilterFnV7Spy = spy(getGridStringQuickFilterFn); + const getApplyQuickFilterFnSpy = spy(getGridStringQuickFilterFn); const { setProps } = render( - Quick Filter', () => { ); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); setProps({ columnVisibilityModel: { brand: false } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); setProps({ columnVisibilityModel: { brand: true } }); clock.runToLast(); expect(getColumnValues(0)).to.deep.equal(['1']); - expect(getApplyQuickFilterFnV7Spy.callCount).to.equal(2); + expect(getApplyQuickFilterFnSpy.callCount).to.equal(2); }); }); @@ -437,6 +438,47 @@ describe(' - Quick Filter', () => { expect(getRows({ quickFilterValues: ['+55 44444444'] })).to.deep.equal(['France (fr)']); expect(getRows({ quickFilterValues: ['5544444444'] })).to.deep.equal([]); }); + + describe('ignoreDiacritics', () => { + function DiacriticsTestCase({ + quickFilterValues, + ...props + }: Partial & { + quickFilterValues: GridFilterModel['quickFilterValues']; + }) { + return ( + + ); + } + + it('should not ignore diacritics by default', () => { + let renderer = render(); + expect(getColumnValues(0)).to.deep.equal([]); + renderer.unmount(); + + renderer = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + renderer.unmount(); + }); + + it('should ignore diacritics when `ignoreDiacritics` is enabled', () => { + let renderer = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + renderer.unmount(); + + renderer = render(); + expect(getColumnValues(0)).to.deep.equal(['Apă']); + renderer.unmount(); + }); + }); }); describe('column type: number', () => { @@ -610,14 +652,14 @@ describe(' - Quick Filter', () => { // https://github.com/mui/mui-x/issues/9666 it('should not fail when the data changes', () => { - function getApplyQuickFilterFn(value: any) { + const getApplyQuickFilterFn: GetApplyQuickFilterFn = (value) => { if (!value) { return null; } - return (params: any) => { - return String(params?.value).toLowerCase().includes(String(value).toLowerCase()); + return (cellValue) => { + return String(cellValue).toLowerCase().includes(String(value).toLowerCase()); }; - } + }; const { setProps } = render( - Row Selection', () => { +describe(' - Row selection', () => { const { render, clock } = createRenderer(); const defaultData = getBasicGridData(4, 2); diff --git a/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx index c8cb659bca435..1c505ce0ecfdc 100644 --- a/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx @@ -7,7 +7,7 @@ import { userEvent, ErrorBoundary, waitFor, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import clsx from 'clsx'; import { expect } from 'chai'; import { spy, stub } from 'sinon'; @@ -1112,4 +1112,48 @@ describe(' - Rows', () => { expect(getColumnValues(0)).to.deep.equal(['Apple', 'Atari']); }); }); + + // https://github.com/mui/mui-x/issues/10373 + it('should set proper `data-rowindex` and `aria-rowindex` when focused row is out of the viewport', async function test() { + if (isJSDOM) { + // needs virtualization + this.skip(); + } + render( +
                + +
                , + ); + + const cell = getCell(0, 0); + userEvent.mousePress(cell); + + const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!; + virtualScroller.scrollTop = 1000; + virtualScroller.dispatchEvent(new Event('scroll')); + + const focusedRow = getRow(0); + expect(focusedRow.getAttribute('data-id')).to.equal('0'); + expect(focusedRow.getAttribute('data-rowindex')).to.equal('0'); + expect(focusedRow.getAttribute('aria-rowindex')).to.equal('2'); // 1-based, 1 is the header + + const lastRow = getRow(9); + expect(lastRow.getAttribute('data-id')).to.equal('9'); + expect(lastRow.getAttribute('data-rowindex')).to.equal('9'); + expect(lastRow.getAttribute('aria-rowindex')).to.equal('11'); // 1-based, 1 is the header + }); }); diff --git a/packages/grid/x-data-grid/src/tests/components.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/slots.DataGrid.test.tsx similarity index 81% rename from packages/grid/x-data-grid/src/tests/components.DataGrid.test.tsx rename to packages/grid/x-data-grid/src/tests/slots.DataGrid.test.tsx index b34915df5b861..3d3a1c1ce9a90 100644 --- a/packages/grid/x-data-grid/src/tests/components.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/slots.DataGrid.test.tsx @@ -1,11 +1,11 @@ import * as React from 'react'; -import { createRenderer, ErrorBoundary, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, ErrorBoundary, fireEvent, screen } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { spy } from 'sinon'; import { DataGrid, GridOverlay } from '@mui/x-data-grid'; import { getCell, getRow } from 'test/utils/helperFn'; -describe(' - Components', () => { +describe(' - Slots', () => { const { render } = createRenderer(); const baselineProps = { @@ -42,43 +42,43 @@ describe(' - Components', () => { } render(
                - +
                , ); expect(document.querySelectorAll('.customFooter').length).to.equal(0); }); }); - describe('componentsProps', () => { - it('should pass the props from componentsProps.cell to the cell', () => { + describe('slotProps', () => { + it('should pass the props from slotProps.cell to the cell', () => { render(
                , ); expect(getCell(0, 0)).to.have.attr('data-name', 'foobar'); }); - it('should pass the props from componentsProps.row to the row', () => { + it('should pass the props from slotProps.row to the row', () => { render(
                , ); expect(getRow(0)).to.have.attr('data-name', 'foobar'); }); - it('should pass the props from componentsProps.columnHeaderFilterIconButton to the column header filter icon', () => { + it('should pass the props from slotProps.columnHeaderFilterIconButton to the column header filter icon', () => { const onClick = spy(); render(
                @@ -89,7 +89,7 @@ describe(' - Components', () => { items: [{ field: 'brand', operator: 'contains', value: 'a' }], }} disableVirtualization - componentsProps={{ columnHeaderFilterIconButton: { onClick } }} + slotProps={{ columnHeaderFilterIconButton: { onClick } }} />
                , ); @@ -101,16 +101,16 @@ describe(' - Components', () => { }); }); - describe('components', () => { - it('should render the cell with the component given in components.Cell', () => { + describe('slots', () => { + it('should render the cell with the component given in slots.Cell', () => { render(
                ( + slots={{ + cell: ({ rowIndex, colIndex }) => ( ), }} @@ -120,14 +120,14 @@ describe(' - Components', () => { expect(getCell(0, 0).tagName).to.equal('SPAN'); }); - it('should render the row with the component given in components.Row', () => { + it('should render the row with the component given in slots.Row', () => { render(
                }} + slots={{ row: ({ index }) => }} />
                , ); diff --git a/packages/grid/x-data-grid/src/tests/sorting.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/sorting.DataGrid.test.tsx index aca6e0b84d832..da0251d355e69 100644 --- a/packages/grid/x-data-grid/src/tests/sorting.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/sorting.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act, waitFor } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act, waitFor } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { DataGrid, DataGridProps, GridSortModel, useGridApiRef, GridApi } from '@mui/x-data-grid'; import { getColumnValues, getColumnHeaderCell } from 'test/utils/helperFn'; diff --git a/packages/grid/x-data-grid/src/tests/toolbar.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/toolbar.DataGrid.test.tsx index 7260822b8bbbf..13713c7d58c27 100644 --- a/packages/grid/x-data-grid/src/tests/toolbar.DataGrid.test.tsx +++ b/packages/grid/x-data-grid/src/tests/toolbar.DataGrid.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, fireEvent, screen, act } from '@mui/monorepo/test/utils'; +import { createRenderer, fireEvent, screen, act } from '@mui-internal/test-utils'; import { getColumnHeadersTextContent } from 'test/utils/helperFn'; import { expect } from 'chai'; import { @@ -52,8 +52,8 @@ describe(' - Toolbar', () => {
                @@ -79,8 +79,8 @@ describe(' - Toolbar', () => {
                @@ -156,8 +156,8 @@ describe(' - Toolbar', () => {
                , @@ -176,8 +176,8 @@ describe(' - Toolbar', () => {
                , @@ -206,8 +206,8 @@ describe(' - Toolbar', () => { - Toolbar', () => {
                , @@ -273,10 +273,10 @@ describe(' - Toolbar', () => { = Promise & { + resolve: T extends unknown ? (value?: T) => void : (value: T) => void; + reject: (reason?: any) => void; +}; + +export function createControllablePromise() { + let resolve: ControllablePromise['resolve']; + let reject: ControllablePromise['reject']; + const promise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }) as ControllablePromise; + promise.resolve = resolve!; + promise.reject = reject!; + return promise; +} diff --git a/packages/grid/x-data-grid/src/utils/getPublicApiRef.ts b/packages/grid/x-data-grid/src/utils/getPublicApiRef.ts new file mode 100644 index 0000000000000..0f1a980ad93ba --- /dev/null +++ b/packages/grid/x-data-grid/src/utils/getPublicApiRef.ts @@ -0,0 +1,9 @@ +import type { GridPrivateApiCommunity } from '../models/api/gridApiCommunity'; + +export function getPublicApiRef( + apiRef: React.MutableRefObject, +) { + return { current: apiRef.current.getPublicApi() } as React.MutableRefObject< + ReturnType + >; +} diff --git a/packages/grid/x-data-grid/src/utils/utils.ts b/packages/grid/x-data-grid/src/utils/utils.ts index 48cf9c74e05a4..a0bb4d8bec329 100644 --- a/packages/grid/x-data-grid/src/utils/utils.ts +++ b/packages/grid/x-data-grid/src/utils/utils.ts @@ -1,5 +1,5 @@ -export function isNumber(value: any): value is number { - return typeof value === 'number'; +export function isNumber(value: unknown): value is number { + return typeof value === 'number' && !Number.isNaN(value); } export function isFunction(value: any): value is Function { diff --git a/packages/grid/x-data-grid/tsconfig.json b/packages/grid/x-data-grid/tsconfig.json index 3d20b7d6a4bb6..79824ab245468 100644 --- a/packages/grid/x-data-grid/tsconfig.json +++ b/packages/grid/x-data-grid/tsconfig.json @@ -6,7 +6,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../../node_modules/@mui/material/themeCssVarsAugmentation" ] } diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index affa868def69a..43bfbf829840f 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts", - "version": "6.0.0-alpha.11", + "version": "6.18.0", "description": "The community edition of the charts components (MUI X).", "author": "MUI Team", "main": "./src/index.js", @@ -38,8 +38,10 @@ "directory": "packages/x-charts" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/base": "^5.0.0-beta.14", + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@react-spring/rafz": "^9.7.3", + "@react-spring/web": "^9.7.3", "clsx": "^2.0.0", "d3-color": "^3.1.0", "d3-scale": "^4.0.2", @@ -63,9 +65,9 @@ } }, "devDependencies": { - "@types/d3-color": "^3.1.0", - "@types/d3-scale": "^4.0.4", - "@types/d3-shape": "^3.1.2" + "@types/d3-color": "^3.1.2", + "@types/d3-scale": "^4.0.6", + "@types/d3-shape": "^3.1.4" }, "exports": { ".": { diff --git a/packages/x-charts/src/BarChart/BarChart.tsx b/packages/x-charts/src/BarChart/BarChart.tsx index 50e4801625316..d1cf642ce2dc7 100644 --- a/packages/x-charts/src/BarChart/BarChart.tsx +++ b/packages/x-charts/src/BarChart/BarChart.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import useId from '@mui/utils/useId'; import PropTypes from 'prop-types'; -import { BarPlot, BarPlotSlotComponentProps, BarPlotSlotsComponent } from './BarPlot'; +import { BarPlot, BarPlotProps, BarPlotSlotProps, BarPlotSlots } from './BarPlot'; import { ResponsiveChartContainer, ResponsiveChartContainerProps, @@ -10,46 +10,75 @@ import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis'; import { BarSeriesType } from '../models/seriesType/bar'; import { MakeOptional } from '../models/helpers'; import { DEFAULT_X_AXIS_KEY, DEFAULT_Y_AXIS_KEY } from '../constants'; -import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip'; +import { + ChartsTooltip, + ChartsTooltipProps, + ChartsTooltipSlotProps, + ChartsTooltipSlots, +} from '../ChartsTooltip'; import { ChartsLegend, ChartsLegendProps, - ChartsLegendSlotsComponent, - ChartsLegendSlotComponentProps, + ChartsLegendSlots, + ChartsLegendSlotProps, } from '../ChartsLegend'; import { ChartsAxisHighlight, ChartsAxisHighlightProps } from '../ChartsAxisHighlight'; import { ChartsClipPath } from '../ChartsClipPath'; -import { ChartsAxisSlotsComponent, ChartsAxisSlotComponentProps } from '../models/axis'; +import { ChartsAxisSlots, ChartsAxisSlotProps } from '../models/axis'; -export interface BarChartSlotsComponent - extends ChartsAxisSlotsComponent, - BarPlotSlotsComponent, - ChartsLegendSlotsComponent {} -export interface BarChartSlotComponentProps - extends ChartsAxisSlotComponentProps, - BarPlotSlotComponentProps, - ChartsLegendSlotComponentProps {} +export interface BarChartSlots + extends ChartsAxisSlots, + BarPlotSlots, + ChartsLegendSlots, + ChartsTooltipSlots {} +export interface BarChartSlotProps + extends ChartsAxisSlotProps, + BarPlotSlotProps, + ChartsLegendSlotProps, + ChartsTooltipSlotProps {} export interface BarChartProps extends Omit, - Omit { + Omit, + Pick { series: MakeOptional[]; tooltip?: ChartsTooltipProps; + /** + * Object `{ x, y }` that defines how the charts highlight the mouse position along the x- and y-axes. + * The two properties accept the following values: + * - 'none': display nothing. + * - 'line': display a line at the current mouse position. + * - 'band': display a band at the current mouse position. Only available with band scale. + */ axisHighlight?: ChartsAxisHighlightProps; + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend?: ChartsLegendProps; /** * Overridable component slots. * @default {} */ - slots?: BarChartSlotsComponent; + slots?: BarChartSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: BarChartSlotComponentProps; + slotProps?: BarChartSlotProps; layout?: BarSeriesType['layout']; } +/** + * Demos: + * + * - [Bars](https://mui.com/x/react-charts/bars/) + * - [Bar demonstration](https://mui.com/x/react-charts/bar-demo/) + * - [Stacking](https://mui.com/x/react-charts/stacking/) + * + * API: + * + * - [BarChart API](https://mui.com/x/api/charts/bar-chart/) + */ const BarChart = React.forwardRef(function BarChart(props: BarChartProps, ref) { const { xAxis, @@ -69,6 +98,7 @@ const BarChart = React.forwardRef(function BarChart(props: BarChartProps, ref) { leftAxis, rightAxis, bottomAxis, + skipAnimation, children, slots, slotProps, @@ -120,7 +150,7 @@ const BarChart = React.forwardRef(function BarChart(props: BarChartProps, ref) { } > - + { return composeClasses(slots, getBarElementUtilityClass, classes); }; -export const BarElementPath = styled('rect', { +export const BarElementPath = styled(animated.rect, { name: 'MuiBarElement', slot: 'Root', overridesResolver: (_, styles) => styles.root, @@ -92,6 +93,7 @@ export function BarElement(props: BarElementProps) { highlightScope, slots, slotProps, + style, ...other } = props; const getInteractionItemProps = useInteractionItemProps(highlightScope); @@ -123,6 +125,7 @@ export function BarElement(props: BarElementProps) { additionalProps: { ...other, ...getInteractionItemProps({ type: 'bar', seriesId: id, dataIndex }), + style, className: classes.root, }, ownerState, diff --git a/packages/x-charts/src/BarChart/BarPlot.tsx b/packages/x-charts/src/BarChart/BarPlot.tsx index ae421c63d9aaf..8858ab5cb80ef 100644 --- a/packages/x-charts/src/BarChart/BarPlot.tsx +++ b/packages/x-charts/src/BarChart/BarPlot.tsx @@ -1,9 +1,13 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { useTransition } from '@react-spring/web'; import { SeriesContext } from '../context/SeriesContextProvider'; import { CartesianContext } from '../context/CartesianContextProvider'; import { BarElement, BarElementProps } from './BarElement'; import { isBandScaleConfig } from '../models/axis'; +import { FormatterResult } from '../models/seriesType/config'; +import { HighlightScope } from '../context/HighlightProvider'; +import { BarSeriesType } from '../models'; /** * Solution of the equations @@ -37,105 +41,182 @@ function getBandSize({ }; } -export interface BarPlotSlotsComponent { +export interface BarPlotSlots { bar?: React.JSXElementConstructor; } -export interface BarPlotSlotComponentProps { +export interface BarPlotSlotProps { bar?: Partial; } -export interface BarPlotProps extends Pick {} +export interface BarPlotProps extends Pick { + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation?: boolean; +} -function BarPlot(props: BarPlotProps) { - const seriesData = React.useContext(SeriesContext).bar; +interface CompletedBarData { + bottom: number; + top: number; + seriesId: string; + dataIndex: number; + layout: BarSeriesType['layout']; + x: number; + y: number; + xOrigin: number; + yOrigin: number; + height: number; + width: number; + color: string; + highlightScope?: Partial; +} + +const useCompletedData = (): CompletedBarData[] => { + const seriesData = + React.useContext(SeriesContext).bar ?? + ({ series: {}, stackingGroups: [], seriesOrder: [] } as FormatterResult<'bar'>); const axisData = React.useContext(CartesianContext); - if (seriesData === undefined) { - return null; - } const { series, stackingGroups } = seriesData; const { xAxis, yAxis, xAxisIds, yAxisIds } = axisData; const defaultXAxisId = xAxisIds[0]; const defaultYAxisId = yAxisIds[0]; + const data = stackingGroups.flatMap(({ ids: groupIds }, groupIndex) => { + return groupIds.flatMap((seriesId) => { + const xAxisKey = series[seriesId].xAxisKey ?? defaultXAxisId; + const yAxisKey = series[seriesId].yAxisKey ?? defaultYAxisId; + + const xAxisConfig = xAxis[xAxisKey]; + const yAxisConfig = yAxis[yAxisKey]; + + const verticalLayout = series[seriesId].layout === 'vertical'; + let baseScaleConfig; + if (verticalLayout) { + if (!isBandScaleConfig(xAxisConfig)) { + throw new Error( + `Axis with id "${xAxisKey}" shoud be of type "band" to display the bar series of id "${seriesId}"`, + ); + } + if (xAxis[xAxisKey].data === undefined) { + throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); + } + baseScaleConfig = xAxisConfig; + } else { + if (!isBandScaleConfig(yAxisConfig)) { + throw new Error( + `Axis with id "${yAxisKey}" shoud be of type "band" to display the bar series of id "${seriesId}"`, + ); + } + + if (yAxis[yAxisKey].data === undefined) { + throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); + } + baseScaleConfig = yAxisConfig; + } + + const xScale = xAxisConfig.scale; + const yScale = yAxisConfig.scale; + + const bandWidth = baseScaleConfig.scale.bandwidth(); + + const { barWidth, offset } = getBandSize({ + bandWidth, + numberOfGroups: stackingGroups.length, + gapRatio: baseScaleConfig.barGapRatio, + }); + const barOffset = groupIndex * (barWidth + offset); + + const { stackedData, color } = series[seriesId]; + + return stackedData.map((values, dataIndex: number) => { + const bottom = Math.min(...values); + const top = Math.max(...values); + + return { + bottom, + top, + seriesId, + dataIndex, + layout: series[seriesId].layout, + x: verticalLayout + ? xScale(xAxis[xAxisKey].data?.[dataIndex])! + barOffset + : xScale(bottom)!, + y: verticalLayout ? yScale(top)! : yScale(yAxis[yAxisKey].data?.[dataIndex])! + barOffset, + xOrigin: xScale(0)!, + yOrigin: yScale(0)!, + height: verticalLayout ? Math.abs(yScale(bottom)! - yScale(top)!) : barWidth, + width: verticalLayout ? barWidth : Math.abs(xScale(bottom)! - xScale(top)!), + color, + highlightScope: series[seriesId].highlightScope, + }; + }); + }); + }); + + return data; +}; + +const getOutStyle = ({ layout, yOrigin, x, width, y, xOrigin, height }: CompletedBarData) => ({ + ...(layout === 'vertical' + ? { + y: yOrigin, + x, + height: 0, + width, + } + : { + y, + x: xOrigin, + height, + width: 0, + }), +}); + +const getInStyle = ({ x, width, y, height }: CompletedBarData) => ({ + y, + x, + height, + width, +}); + +/** + * Demos: + * + * - [Bars](https://mui.com/x/react-charts/bars/) + * - [Bar demonstration](https://mui.com/x/react-charts/bar-demo/) + * - [Stacking](https://mui.com/x/react-charts/stacking/) + * + * API: + * + * - [BarPlot API](https://mui.com/x/api/charts/bar-plot/) + */ +function BarPlot(props: BarPlotProps) { + const completedData = useCompletedData(); + const { skipAnimation, ...other } = props; + + const transition = useTransition(completedData, { + keys: (bar) => `${bar.seriesId}-${bar.dataIndex}`, + from: getOutStyle, + leave: getOutStyle, + enter: getInStyle, + update: getInStyle, + immediate: skipAnimation, + }); return ( - {stackingGroups.flatMap(({ ids: groupIds }, groupIndex) => { - return groupIds.flatMap((seriesId) => { - const xAxisKey = series[seriesId].xAxisKey ?? defaultXAxisId; - const yAxisKey = series[seriesId].yAxisKey ?? defaultYAxisId; - - const xAxisConfig = xAxis[xAxisKey]; - const yAxisConfig = yAxis[yAxisKey]; - - const verticalLayout = series[seriesId].layout === 'vertical'; - let baseScaleConfig; - if (verticalLayout) { - if (!isBandScaleConfig(xAxisConfig)) { - throw new Error( - `Axis with id "${xAxisKey}" shoud be of type "band" to display the bar series of id "${seriesId}"`, - ); - } - if (xAxis[xAxisKey].data === undefined) { - throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); - } - baseScaleConfig = xAxisConfig; - } else { - if (!isBandScaleConfig(yAxisConfig)) { - throw new Error( - `Axis with id "${yAxisKey}" shoud be of type "band" to display the bar series of id "${seriesId}"`, - ); - } - - if (yAxis[yAxisKey].data === undefined) { - throw new Error(`Axis with id "${xAxisKey}" shoud have data property`); - } - baseScaleConfig = yAxisConfig; - } - - const xScale = xAxisConfig.scale; - const yScale = yAxisConfig.scale; - - const bandWidth = baseScaleConfig.scale.bandwidth(); - - const { barWidth, offset } = getBandSize({ - bandWidth, - numberOfGroups: stackingGroups.length, - gapRatio: baseScaleConfig.barGapRatio, - }); - const barOffset = groupIndex * (barWidth + offset); - - const { stackedData, color } = series[seriesId]; - - return stackedData.map((values, dataIndex: number) => { - const baseline = Math.min(...values); - const value = Math.max(...values); - return ( - - ); - }); - }); - })} + {transition((style, { seriesId, dataIndex, color, highlightScope }) => ( + + ))} ); } @@ -145,6 +226,11 @@ BarPlot.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation: PropTypes.bool, /** * The props used for each component slot. * @default {} diff --git a/packages/x-charts/src/BarChart/formatter.ts b/packages/x-charts/src/BarChart/formatter.ts index 36cd67328f1da..8850ffc1f0619 100644 --- a/packages/x-charts/src/BarChart/formatter.ts +++ b/packages/x-charts/src/BarChart/formatter.ts @@ -61,7 +61,7 @@ const formatter: Formatter<'bar'> = (params, dataset) => { return { seriesOrder, stackingGroups, - series: defaultizeValueFormatter(completedSeries, (v) => v.toLocaleString()), + series: defaultizeValueFormatter(completedSeries, (v) => v?.toLocaleString()), }; }; diff --git a/packages/x-charts/src/ChartContainer/index.tsx b/packages/x-charts/src/ChartContainer/index.tsx index a84819f32798d..534922106176e 100644 --- a/packages/x-charts/src/ChartContainer/index.tsx +++ b/packages/x-charts/src/ChartContainer/index.tsx @@ -6,6 +6,7 @@ import { SeriesContextProviderProps, } from '../context/SeriesContextProvider'; import { InteractionProvider } from '../context/InteractionProvider'; +import { useReducedMotion } from '../hooks/useReducedMotion'; import { ChartsSurface, ChartsSurfaceProps } from '../ChartsSurface'; import { CartesianContextProvider, @@ -45,6 +46,8 @@ export const ChartContainer = React.forwardRef(function ChartContainer( const svgRef = React.useRef(null); const handleRef = useForkRef(ref, svgRef); + useReducedMotion(); // a11y reduce motion (see: https://react-spring.dev/docs/utilities/use-reduced-motion) + return ( diff --git a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx index 805c512a5648e..a5d14935188d8 100644 --- a/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx +++ b/packages/x-charts/src/ChartsAxis/ChartsAxis.tsx @@ -5,8 +5,8 @@ import { CartesianContext } from '../context/CartesianContextProvider'; import { ChartsXAxis } from '../ChartsXAxis'; import { ChartsYAxis } from '../ChartsYAxis'; import { - ChartsAxisSlotComponentProps, - ChartsAxisSlotsComponent, + ChartsAxisSlotProps, + ChartsAxisSlots, ChartsXAxisProps, ChartsYAxisProps, } from '../models/axis'; @@ -40,12 +40,12 @@ export interface ChartsAxisProps { * Overridable component slots. * @default {} */ - slots?: ChartsAxisSlotsComponent; + slots?: ChartsAxisSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: ChartsAxisSlotComponentProps; + slotProps?: ChartsAxisSlotProps; } const getAxisId = ( @@ -55,15 +55,15 @@ const getAxisId = ( return null; } if (typeof propsValue === 'object') { - return propsValue.axisId; + return propsValue.axisId ?? null; } return propsValue; }; const mergeProps = ( axisConfig: undefined | null | string | ChartsXAxisProps | ChartsYAxisProps, - slots?: Partial, - slotProps?: Partial, + slots?: Partial, + slotProps?: Partial, ) => { return typeof axisConfig === 'object' ? { @@ -74,6 +74,15 @@ const mergeProps = ( : { slots, slotProps }; }; +/** + * Demos: + * + * - [Axis](https://mui.com/x/react-charts/axis/) + * + * API: + * + * - [ChartsAxis API](https://mui.com/x/api/charts/charts-axis/) + */ function ChartsAxis(props: ChartsAxisProps) { const { topAxis, leftAxis, rightAxis, bottomAxis, slots, slotProps } = props; const { xAxis, xAxisIds, yAxis, yAxisIds } = React.useContext(CartesianContext); @@ -125,18 +134,26 @@ ChartsAxis.propTypes = { */ bottomAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -151,18 +168,26 @@ ChartsAxis.propTypes = { */ leftAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -177,18 +202,26 @@ ChartsAxis.propTypes = { */ rightAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -213,18 +246,26 @@ ChartsAxis.propTypes = { */ topAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/ChartsAxis/axisClasses.ts b/packages/x-charts/src/ChartsAxis/axisClasses.ts index 81f4e42632a8c..4bc68967b1fb8 100644 --- a/packages/x-charts/src/ChartsAxis/axisClasses.ts +++ b/packages/x-charts/src/ChartsAxis/axisClasses.ts @@ -14,11 +14,11 @@ export interface ChartsAxisClasses { tick: string; /** Styles applied to ticks label. */ tickLabel: string; - /** Styles applied to the axis label. */ + /** Styles applied to the group containing the axis label. */ label: string; - /** Styles applied to x axes. */ + /** Styles applied to x-axes. */ directionX: string; - /** Styles applied to y axes. */ + /** Styles applied to y-axes. */ directionY: string; /** Styles applied to the top axis. */ top: string; diff --git a/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx b/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx index 2fd89189953d0..cbdc637297109 100644 --- a/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx +++ b/packages/x-charts/src/ChartsAxisHighlight/ChartsAxisHighlight.tsx @@ -1,10 +1,54 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import composeClasses from '@mui/utils/composeClasses'; +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; +import { styled } from '@mui/material/styles'; import { InteractionContext } from '../context/InteractionProvider'; import { CartesianContext } from '../context/CartesianContextProvider'; import { getValueToPositionMapper } from '../hooks/useScale'; import { isBandScale } from '../internals/isBandScale'; +export interface ChartsAxisHighlightClasses { + /** Styles applied to the root element. */ + root: string; +} + +export type ChartsAxisHighlightClassKey = keyof ChartsAxisHighlightClasses; + +export function getAxisHighlightUtilityClass(slot: string) { + return generateUtilityClass('MuiChartsAxisHighlight', slot); +} + +export const chartsAxisHighlightClasses: ChartsAxisHighlightClasses = generateUtilityClasses( + 'MuiChartsAxisHighlight', + ['root'], +); + +const useUtilityClasses = () => { + const slots = { + root: ['root'], + }; + + return composeClasses(slots, getAxisHighlightUtilityClass); +}; + +export const ChartsAxisHighlightPath = styled('path', { + name: 'MuiChartsAxisHighlight', + slot: 'Root', + overridesResolver: (_, styles) => styles.root, +})<{ ownerState: { axisHighlight: AxisHighlight } }>(({ ownerState, theme }) => ({ + pointerEvents: 'none', + ...(ownerState.axisHighlight === 'band' && { + fill: theme.palette.mode === 'light' ? 'gray' : 'white', + fillOpacity: 0.1, + }), + ...(ownerState.axisHighlight === 'line' && { + strokeDasharray: '5 2', + stroke: theme.palette.mode === 'light' ? '#000000' : '#ffffff', + }), +})); + type AxisHighlight = 'none' | 'line' | 'band'; export type ChartsAxisHighlightProps = { @@ -12,9 +56,19 @@ export type ChartsAxisHighlightProps = { y?: AxisHighlight; }; +/** + * Demos: + * + * - [Custom components](https://mui.com/x/react-charts/components/) + * + * API: + * + * - [ChartsAxisHighlight API](https://mui.com/x/api/charts/charts-axis-highlight/) + */ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) { const { x: xAxisHighlight, y: yAxisHighlight } = props; const { xAxisIds, xAxis, yAxisIds, yAxis } = React.useContext(CartesianContext); + const classes = useUtilityClasses(); const USED_X_AXIS_ID = xAxisIds[0]; const USED_Y_AXIS_ID = yAxisIds[0]; @@ -29,50 +83,46 @@ function ChartsAxisHighlight(props: ChartsAxisHighlightProps) { return ( {xAxisHighlight === 'band' && axis.x !== null && isBandScale(xScale) && ( - )} {yAxisHighlight === 'band' && axis.y !== null && isBandScale(yScale) && ( - )} {xAxisHighlight === 'line' && axis.x !== null && ( - )} {yAxisHighlight === 'line' && axis.y !== null && ( - )} diff --git a/packages/x-charts/src/ChartsClipPath/ChartsClipPath.tsx b/packages/x-charts/src/ChartsClipPath/ChartsClipPath.tsx index 4b947e38fc402..fc674f7038c0a 100644 --- a/packages/x-charts/src/ChartsClipPath/ChartsClipPath.tsx +++ b/packages/x-charts/src/ChartsClipPath/ChartsClipPath.tsx @@ -7,6 +7,11 @@ export type ChartsClipPathProps = { offset?: { top?: number; right?: number; bottom?: number; left?: number }; }; +/** + * API: + * + * - [ChartsClipPath API](https://mui.com/x/api/charts/charts-clip-path/) + */ function ChartsClipPath(props: ChartsClipPathProps) { const { id, offset: offsetProps } = props; const { left, top, width, height } = React.useContext(DrawingContext); diff --git a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx index 695c7235f7472..4d81033e4e9c0 100644 --- a/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx +++ b/packages/x-charts/src/ChartsLegend/ChartsLegend.tsx @@ -1,50 +1,56 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; import { useSlotProps } from '@mui/base/utils'; +import { NoSsr } from '@mui/base/NoSsr'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme, styled } from '@mui/material/styles'; import { DrawingArea, DrawingContext } from '../context/DrawingProvider'; -import { AnchorPosition, SizingParams, getSeriesToDisplay } from './utils'; +import { AnchorPosition, Direction, getSeriesToDisplay } from './utils'; import { FormattedSeries, SeriesContext } from '../context/SeriesContextProvider'; -import { ChartsLegendClasses, getChartsLegendUtilityClass } from './chartsLegendClasses'; +import { ChartsLegendClasses, getLegendUtilityClass } from './chartsLegendClasses'; import { DefaultizedProps } from '../models/helpers'; -import { ChartSeriesDefaultized, LegendParams } from '../models/seriesType/config'; +import { LegendParams } from '../models/seriesType/config'; +import { ChartsText, ChartsTextStyle, getWordsByLines } from '../internals/components/ChartsText'; +import { CardinalDirections } from '../models/layout'; -export interface ChartsLegendSlotsComponent { +export interface ChartsLegendSlots { legend?: React.JSXElementConstructor; } -export interface ChartsLegendSlotComponentProps { +export interface ChartsLegendSlotProps { legend?: Partial; } export type ChartsLegendProps = { position?: AnchorPosition; - offset?: Partial<{ x: number; y: number }>; /** * Override or extend the styles applied to the component. */ classes?: Partial; /** * Set to true to hide the legend. + * @default false */ hidden?: boolean; + /** + * The direction of the legend layout. + * The default depends on the chart. + */ + direction?: Direction; /** * Overridable component slots. * @default {} */ - slots?: ChartsLegendSlotsComponent; + slots?: ChartsLegendSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: ChartsLegendSlotComponentProps; -} & SizingParams; + slotProps?: ChartsLegendSlotProps; +}; type DefaultizedChartsLegendProps = DefaultizedProps; -type SeriesLegendOwnerState = ChartSeriesDefaultized & - Pick & { seriesIndex: number }; - const useUtilityClasses = (ownerState: DefaultizedChartsLegendProps & { theme: Theme }) => { const { classes, direction } = ownerState; const slots = { @@ -54,132 +60,27 @@ const useUtilityClasses = (ownerState: DefaultizedChartsLegendProps & { theme: T series: ['series'], }; - return composeClasses(slots, getChartsLegendUtilityClass, classes); + return composeClasses(slots, getLegendUtilityClass, classes); }; export type ChartsLegendRootOwnerState = { position: AnchorPosition; - direction: 'row' | 'column'; + direction: Direction; drawingArea: DrawingArea; offsetX?: number; offsetY?: number; seriesNumber: number; }; -function getTranslePosition({ - position, - drawingArea, -}: Omit) { - let xValue: string; - switch (position.horizontal) { - case 'left': - xValue = `calc(var(--ChartsLegend-rootOffsetX, 0px) + ${drawingArea.left}px - var(--ChartsLegend-rootWidth))`; - break; - case 'middle': - xValue = `calc(var(--ChartsLegend-rootOffsetX, 0px) + ${ - drawingArea.left + drawingArea.width / 2 - }px - 0.5 * var(--ChartsLegend-rootWidth))`; - break; - default: - xValue = `calc(var(--ChartsLegend-rootOffsetX, 0px) + ${ - drawingArea.left + drawingArea.width - }px)`; - break; - } - let yValue: string; - switch (position.vertical) { - case 'top': - yValue = `calc(var(--ChartsLegend-rootOffsetY, 0px) + ${drawingArea.top}px - var(--ChartsLegend-rootHeight))`; - break; - case 'middle': - yValue = `calc(var(--ChartsLegend-rootOffsetY, 0px) + ${ - drawingArea.top + drawingArea.height / 2 - }px - 0.5 * var(--ChartsLegend-rootHeight))`; - break; - default: - yValue = `calc(var(--ChartsLegend-rootOffsetY, 0px) + ${ - drawingArea.top + drawingArea.height - }px)`; - break; - } - return { transform: `translate(${xValue}, ${yValue})` }; -} - export const ChartsLegendRoot = styled('g', { name: 'MuiChartsLegend', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})<{ ownerState: ChartsLegendRootOwnerState }>(({ ownerState }) => { - const { direction, drawingArea, offsetX, offsetY, seriesNumber, position } = ownerState; - - return { - '--ChartsLegend-rootOffsetX': typeof offsetX === 'number' ? `${offsetX}px` : undefined, - '--ChartsLegend-rootOffsetY': typeof offsetY === 'number' ? `${offsetY}px` : undefined, - '--ChartsLegend-rootWidth': - direction === 'row' - ? `calc(var(--ChartsLegend-itemWidth) * ${seriesNumber} + var(--ChartsLegend-rootSpacing) * ${ - seriesNumber - 1 - } )` - : 'var(--ChartsLegend-itemWidth)', - '--ChartsLegend-rootHeight': - direction === 'row' - ? 'var(--ChartsLegend-itemMarkSize)' - : `calc(var(--ChartsLegend-itemMarkSize) * ${seriesNumber} + var(--ChartsLegend-rootSpacing) * ${ - seriesNumber - 1 - } )`, - ...getTranslePosition({ position, drawingArea, offsetX, offsetY }), - }; -}); - -export const ChartsSeriesLegendGroup = styled('g', { - name: 'MuiChartsLegend', - slot: 'ChartsSeriesLegendGroup', - overridesResolver: (props, styles) => styles.series, -})<{ ownerState: SeriesLegendOwnerState }>(({ ownerState }) => { - const { direction, seriesIndex } = ownerState; - - if (direction === 'row') { - return { - transform: `translate(calc(${seriesIndex} * (var(--ChartsLegend-itemWidth) + var(--ChartsLegend-rootSpacing))), 0)`, - }; - } - return { - transform: `translate(0, calc(${seriesIndex} * (var(--ChartsLegend-itemMarkSize) + var(--ChartsLegend-rootSpacing))))`, - }; -}); - -export const ChartsLegendMark = styled('rect', { - name: 'MuiChartsLegend', - slot: 'Mark', - overridesResolver: (props, styles) => styles.mark, -})<{ ownerState: { color: string } }>(({ ownerState }) => ({ - x: 0, - y: 0, - width: 'var(--ChartsLegend-itemMarkSize)', - height: 'var(--ChartsLegend-itemMarkSize)', - fill: ownerState.color, -})); -export const ChartsLegendLabel = styled('text', { - name: 'MuiChartsLegend', - slot: 'Label', - overridesResolver: (props, styles) => styles.label, -})(({ theme }) => ({ - ...theme.typography.body1, - color: 'inherit', - transform: `translate( - calc(var(--ChartsLegend-itemMarkSize) + var(--ChartsLegend-labelSpacing)), - calc(0.5 * var(--ChartsLegend-itemMarkSize)) - )`, - fill: (theme.vars || theme).palette.text.primary, - dominantBaseline: 'central', -})); +})({}); const defaultProps = { position: { horizontal: 'middle', vertical: 'top' }, direction: 'row', - markSize: 20, - itemWidth: 100, - spacing: 2, } as const; export interface LegendRendererProps @@ -187,49 +88,381 @@ export interface LegendRendererProps series: FormattedSeries; seriesToDisplay: LegendParams[]; drawingArea: DrawingArea; - classes: Record<'label' | 'mark' | 'series' | 'root', string>; + classes: Record<'mark' | 'series' | 'root', string>; + /** + * Style applied to legend labels. + * @default theme.typography.subtitle1 + */ + labelStyle?: ChartsTextStyle; + /** + * Width of the item mark (in px). + * @default 20 + */ + itemMarkWidth?: number; + /** + * Height of the item mark (in px). + * @default 20 + */ + itemMarkHeight?: number; + /** + * Space between the mark and the label (in px). + * @default 5 + */ + markGap?: number; + /** + * Space between two legend items (in px). + * @default 10 + */ + itemGap?: number; + /** + * Legend padding (in px). + * Can either be a single number, or an object with top, left, bottom, right properties. + * @default 0 + */ + padding?: number | Partial>; } +/** + * Transforms number or partial padding object to a defaultized padding object. + */ +const getStandardizedPadding = (padding: LegendRendererProps['padding']) => { + if (typeof padding === 'number') { + return { + left: padding, + right: padding, + top: padding, + bottom: padding, + }; + } + return { + left: 0, + right: 0, + top: 0, + bottom: 0, + ...padding, + }; +}; + function DefaultChartsLegend(props: LegendRendererProps) { - const { hidden, position, direction, offset, series, seriesToDisplay, drawingArea, classes } = - props; + const { + hidden, + position, + direction, + seriesToDisplay, + drawingArea, + classes, + itemMarkWidth = 20, + itemMarkHeight = 20, + markGap = 5, + itemGap = 10, + padding: paddingProps = 10, + labelStyle: inLabelStyle, + } = props; + const theme = useTheme(); + + const labelStyle = React.useMemo( + () => + ({ + ...theme.typography.subtitle1, + color: 'inherit', + dominantBaseline: 'central', + textAnchor: 'start', + fill: (theme.vars || theme).palette.text.primary, + lineHeight: 1, + ...inLabelStyle, + } as ChartsTextStyle), // To say to TS that the dominantBaseline and textAnchor are correct + [inLabelStyle, theme], + ); + + const padding = React.useMemo(() => getStandardizedPadding(paddingProps), [paddingProps]); + + const getItemSpace = React.useCallback( + (label: string, inStyle: ChartsTextStyle = {}) => { + const { rotate, dominantBaseline, ...style } = inStyle; + const linesSize = getWordsByLines({ style, needsComputation: true, text: label }); + const innerSize = { + innerWidth: itemMarkWidth + markGap + Math.max(...linesSize.map((size) => size.width)), + innerHeight: Math.max(itemMarkHeight, linesSize.length * linesSize[0].height), + }; + + return { + ...innerSize, + outerWidth: innerSize.innerWidth + itemGap, + outerHeight: innerSize.innerHeight + itemGap, + }; + }, + [itemGap, itemMarkHeight, itemMarkWidth, markGap], + ); + + const totalWidth = drawingArea.left + drawingArea.width + drawingArea.right; + const totalHeight = drawingArea.top + drawingArea.height + drawingArea.bottom; + const availableWidth = totalWidth - padding.left - padding.right; + const availableHeight = totalHeight - padding.top - padding.bottom; + + const seriesWithPosition = React.useMemo(() => { + // Start at 0, 0. Will be modified later by padding and position. + let x = 0; + let y = 0; + + // total values used to align legend later. + let totalWidthUsed = 0; + let totalHeightUsed = 0; + let rowIndex = 0; + const rowMaxHeight = [0]; + + const seriesWithRawPosition = seriesToDisplay.map(({ label, ...other }) => { + const itemSpace = getItemSpace(label, labelStyle); + const rep = { + ...other, + label, + positionX: x, + positionY: y, + innerHeight: itemSpace.innerHeight, + innerWidth: itemSpace.innerWidth, + outerHeight: itemSpace.outerHeight, + outerWidth: itemSpace.outerWidth, + rowIndex, + }; + + if (direction === 'row') { + if (x + itemSpace.innerWidth > availableWidth) { + // This legend item would create overflow along the x-axis, so we start a new row. + x = 0; + y += rowMaxHeight[rowIndex]; + rowIndex += 1; + if (rowMaxHeight.length <= rowIndex) { + rowMaxHeight.push(0); + } + rep.positionX = x; + rep.positionY = y; + rep.rowIndex = rowIndex; + } + totalWidthUsed = Math.max(totalWidthUsed, x + itemSpace.outerWidth); + totalHeightUsed = Math.max(totalHeightUsed, y + itemSpace.outerHeight); + rowMaxHeight[rowIndex] = Math.max(rowMaxHeight[rowIndex], itemSpace.outerHeight); + + x += itemSpace.outerWidth; + } + + if (direction === 'column') { + if (y + itemSpace.innerHeight > availableHeight) { + // This legend item would create overflow along the y-axis, so we start a new column. + x = totalWidthUsed + itemGap; + y = 0; + rowIndex = 0; + rep.positionX = x; + rep.positionY = y; + rep.rowIndex = rowIndex; + } + if (rowMaxHeight.length <= rowIndex) { + rowMaxHeight.push(0); + } + totalWidthUsed = Math.max(totalWidthUsed, x + itemSpace.outerWidth); + totalHeightUsed = Math.max(totalHeightUsed, y + itemSpace.outerHeight); + + rowIndex += 1; + y += itemSpace.outerHeight; + } + + return rep; + }); + + // Move the legend according to padding and position + let gapX = 0; + let gapY = 0; + switch (position.horizontal) { + case 'left': + gapX = padding.left; + break; + case 'right': + gapX = totalWidth - padding.right - totalWidthUsed; + break; + default: + gapX = (totalWidth - totalWidthUsed) / 2; + break; + } + switch (position.vertical) { + case 'top': + gapY = padding.top; + break; + case 'bottom': + gapY = totalHeight - padding.bottom - totalHeightUsed; + break; + default: + gapY = (totalHeight - totalHeightUsed) / 2; + break; + } + 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, + ]); if (hidden) { return null; } + return ( - - {seriesToDisplay.map(({ id, label, color }, seriesIndex) => ( - - - {label} - - ))} - + + + {seriesWithPosition.map(({ id, label, color, positionX, positionY }) => ( + + + + + ))} + + ); } -export function ChartsLegend(inProps: ChartsLegendProps) { +DefaultChartsLegend.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object.isRequired, + /** + * The direction of the legend layout. + * The default depends on the chart. + */ + direction: PropTypes.oneOf(['column', 'row']).isRequired, + drawingArea: PropTypes.shape({ + bottom: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + left: PropTypes.number.isRequired, + right: PropTypes.number.isRequired, + top: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + }).isRequired, + /** + * Set to true to hide the legend. + */ + hidden: PropTypes.bool, + /** + * Space between two legend items (in px). + * @default 10 + */ + itemGap: PropTypes.number, + /** + * Height of the item mark (in px). + * @default 20 + */ + itemMarkHeight: PropTypes.number, + /** + * Width of the item mark (in px). + * @default 20 + */ + itemMarkWidth: PropTypes.number, + /** + * Style applied to legend labels. + * @default theme.typography.subtitle1 + */ + labelStyle: PropTypes.object, + /** + * Space between the mark and the label (in px). + * @default 5 + */ + markGap: PropTypes.number, + /** + * Legend padding (in px). + * Can either be a single number, or an object with top, left, bottom, right properties. + * @default 0 + */ + padding: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + bottom: PropTypes.number, + left: PropTypes.number, + right: PropTypes.number, + top: PropTypes.number, + }), + ]), + position: PropTypes.shape({ + horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, + vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, + }).isRequired, + series: PropTypes.shape({ + bar: PropTypes.shape({ + series: PropTypes.object.isRequired, + seriesOrder: PropTypes.arrayOf(PropTypes.string).isRequired, + stackingGroups: PropTypes.arrayOf( + PropTypes.shape({ + ids: PropTypes.arrayOf(PropTypes.string).isRequired, + stackingOffset: PropTypes.func.isRequired, + stackingOrder: PropTypes.func.isRequired, + }), + ).isRequired, + }), + line: PropTypes.shape({ + series: PropTypes.object.isRequired, + seriesOrder: PropTypes.arrayOf(PropTypes.string).isRequired, + stackingGroups: PropTypes.arrayOf( + PropTypes.shape({ + ids: PropTypes.arrayOf(PropTypes.string).isRequired, + stackingOffset: PropTypes.func.isRequired, + stackingOrder: PropTypes.func.isRequired, + }), + ).isRequired, + }), + pie: PropTypes.shape({ + series: PropTypes.object.isRequired, + seriesOrder: PropTypes.arrayOf(PropTypes.string).isRequired, + }), + scatter: PropTypes.shape({ + series: PropTypes.object.isRequired, + seriesOrder: PropTypes.arrayOf(PropTypes.string).isRequired, + }), + }).isRequired, + seriesToDisplay: PropTypes.arrayOf( + PropTypes.shape({ + color: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + }), + ).isRequired, +} as any; + +function ChartsLegend(inProps: ChartsLegendProps) { const props: DefaultizedChartsLegendProps = useThemeProps({ props: { ...defaultProps, ...inProps }, name: 'MuiChartsLegend', }); - const { position, direction, offset, hidden, slots, slotProps } = props; + const { position, direction, hidden, slots, slotProps } = props; const theme = useTheme(); const classes = useUtilityClasses({ ...props, theme }); @@ -245,7 +478,6 @@ export function ChartsLegend(inProps: ChartsLegendProps) { additionalProps: { position, direction, - offset, classes, drawingArea, series, @@ -257,3 +489,40 @@ export function ChartsLegend(inProps: ChartsLegendProps) { return ; } + +ChartsLegend.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * The direction of the legend layout. + * The default depends on the chart. + */ + direction: PropTypes.oneOf(['column', 'row']), + /** + * Set to true to hide the legend. + * @default false + */ + hidden: PropTypes.bool, + position: PropTypes.shape({ + horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, + vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, + }), + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, +} as any; + +export { ChartsLegend }; diff --git a/packages/x-charts/src/ChartsLegend/chartsLegendClasses.ts b/packages/x-charts/src/ChartsLegend/chartsLegendClasses.ts index cebe5a885a229..5d710e5540569 100644 --- a/packages/x-charts/src/ChartsLegend/chartsLegendClasses.ts +++ b/packages/x-charts/src/ChartsLegend/chartsLegendClasses.ts @@ -20,7 +20,7 @@ export interface ChartsLegendClasses { export type ChartsLegendClassKey = keyof ChartsLegendClasses; -export function getChartsLegendUtilityClass(slot: string) { +export function getLegendUtilityClass(slot: string) { return generateUtilityClass('MuiChartsLegend', slot); } diff --git a/packages/x-charts/src/ChartsLegend/utils.ts b/packages/x-charts/src/ChartsLegend/utils.ts index 65a2eee68296b..a5da75c6985b7 100644 --- a/packages/x-charts/src/ChartsLegend/utils.ts +++ b/packages/x-charts/src/ChartsLegend/utils.ts @@ -10,12 +10,7 @@ export type AnchorY = 'top' | 'bottom' | 'middle'; export type AnchorPosition = { horizontal: AnchorX; vertical: AnchorY }; -export type SizingParams = { - direction?: 'row' | 'column'; - markSize?: number; - itemWidth?: number; - spacing?: number; -}; +export type Direction = 'row' | 'column'; const legendGetter: { [T in ChartSeriesType]: LegendGetter } = { bar: getBarLegend, diff --git a/packages/x-charts/src/ChartsReferenceLine/ChartsReferenceLine.tsx b/packages/x-charts/src/ChartsReferenceLine/ChartsReferenceLine.tsx new file mode 100644 index 0000000000000..a8c9f1a6c0694 --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/ChartsReferenceLine.tsx @@ -0,0 +1,82 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { ChartsXReferenceLine, ChartsXReferenceLineProps } from './ChartsXReferenceLine'; +import { ChartsYReferenceLine, ChartsYReferenceLineProps } from './ChartsYReferenceLine'; +import { XOR } from '../internals/utils'; + +type ChartsReferenceLineProps = XOR< + ChartsXReferenceLineProps, + ChartsYReferenceLineProps +>; + +function ChartsReferenceLine(props: ChartsReferenceLineProps) { + if (props.x !== undefined && props.y !== undefined) { + throw new Error('MUI-X: The ChartsReferenceLine can not have both `x` and `y` props set.'); + } + + if (props.x === undefined && props.y === undefined) { + throw new Error('MUI-X: The ChartsReferenceLine should have a value in `x` or `y` prop.'); + } + + if (props.x !== undefined) { + return ; + } + return ; +} + +ChartsReferenceLine.propTypes = { + // ----------------------------- Warning -------------------------------- + // | These PropTypes are generated from the TypeScript type definitions | + // | To update them edit the TypeScript types and run "yarn proptypes" | + // ---------------------------------------------------------------------- + /** + * The id of the axis used for the reference value. + * @default The `id` of the first defined axis. + */ + axisId: PropTypes.string, + /** + * Override or extend the styles applied to the component. + */ + classes: PropTypes.object, + /** + * The label to display along the reference line. + */ + label: PropTypes.string, + /** + * The alignment if the label is in the chart drawing area. + * @default 'middle' + */ + labelAlign: PropTypes.oneOf(['end', 'middle', 'start']), + /** + * The style applied to the label. + */ + labelStyle: PropTypes.object, + /** + * The style applied to the line. + */ + lineStyle: PropTypes.object, + /** + * Additional space arround the label in px. + * Can be a number or an object `{ x, y }` to distinguish space with the reference line and space with axes. + * @default 5 + */ + spacing: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.shape({ + x: PropTypes.number, + y: PropTypes.number, + }), + ]), + /** + * The x value associated with the reference line. + * If defined the reference line will be vertical. + */ + x: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number, PropTypes.string]), + /** + * The y value associated with the reference line. + * If defined the reference line will be horizontal. + */ + y: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number, PropTypes.string]), +} as any; + +export { ChartsReferenceLine }; diff --git a/packages/x-charts/src/ChartsReferenceLine/ChartsXReferenceLine.tsx b/packages/x-charts/src/ChartsReferenceLine/ChartsXReferenceLine.tsx new file mode 100644 index 0000000000000..1cb83fa8fbe81 --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/ChartsXReferenceLine.tsx @@ -0,0 +1,133 @@ +import * as React from 'react'; +import composeClasses from '@mui/utils/composeClasses'; +import { useDrawingArea, useXScale } from '../hooks'; +import { CommonChartsReferenceLineProps, ReferenceLineRoot } from './common'; +import { ChartsText } from '../internals/components/ChartsText'; +import { + ChartsReferenceLineClasses, + getReferenceLineUtilityClass, +} from './chartsReferenceLineClasses'; + +export type ChartsXReferenceLineProps< + TValue extends string | number | Date = string | number | Date, +> = CommonChartsReferenceLineProps & { + /** + * The x value associated with the reference line. + * If defined the reference line will be vertical. + */ + x: TValue; +}; + +type GetTextPlacementParams = { + top: number; + height: number; + spacingY: number; +} & Pick; + +const getTextParams = ({ + top, + height, + spacingY, + labelAlign = 'middle', +}: GetTextPlacementParams) => { + switch (labelAlign) { + case 'start': + return { + y: top + spacingY, + style: { + dominantBaseline: 'hanging', + textAnchor: 'start', + } as const, + }; + + case 'end': + return { + y: top + height - spacingY, + style: { + dominantBaseline: 'auto', + textAnchor: 'start', + } as const, + }; + + default: + return { + y: top + height / 2, + style: { + dominantBaseline: 'central', + textAnchor: 'start', + } as const, + }; + } +}; + +export function getXReferenceLineClasses(classes?: Partial) { + return composeClasses( + { + root: ['root', 'vertical'], + line: ['line'], + label: ['label'], + }, + getReferenceLineUtilityClass, + classes, + ); +} + +let warnedOnce = false; + +function ChartsXReferenceLine(props: ChartsXReferenceLineProps) { + const { + x, + label = '', + spacing = 5, + classes: inClasses, + labelAlign, + lineStyle, + labelStyle, + axisId, + } = props; + + const { top, height } = useDrawingArea(); + const xAxisScale = useXScale(axisId); + + const xPosition = xAxisScale(x as any); + + if (xPosition === undefined) { + if (process.env.NODE_ENV !== 'production') { + if (!warnedOnce) { + warnedOnce = true; + console.error( + `MUI X: the value ${x} does not exist in the data of x axis with id ${axisId}.`, + ); + } + } + return null; + } + const d = `M ${xPosition} ${top} l 0 ${height}`; + + const classes = getXReferenceLineClasses(inClasses); + + const spacingX = typeof spacing === 'object' ? spacing.x ?? 0 : spacing; + const spacingY = typeof spacing === 'object' ? spacing.y ?? 0 : spacing; + + const textParams = { + x: xPosition + spacingX, + text: label, + fontSize: 12, + ...getTextParams({ + top, + height, + spacingY, + labelAlign, + }), + className: classes.label, + }; + + return ( + + + + + ); +} + +export { ChartsXReferenceLine }; diff --git a/packages/x-charts/src/ChartsReferenceLine/ChartsYReferenceLine.tsx b/packages/x-charts/src/ChartsReferenceLine/ChartsYReferenceLine.tsx new file mode 100644 index 0000000000000..9486bef8f9813 --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/ChartsYReferenceLine.tsx @@ -0,0 +1,133 @@ +import * as React from 'react'; +import composeClasses from '@mui/utils/composeClasses'; +import { useDrawingArea, useYScale } from '../hooks'; +import { CommonChartsReferenceLineProps, ReferenceLineRoot } from './common'; +import { ChartsText } from '../internals/components/ChartsText'; +import { + ChartsReferenceLineClasses, + getReferenceLineUtilityClass, +} from './chartsReferenceLineClasses'; + +export type ChartsYReferenceLineProps< + TValue extends string | number | Date = string | number | Date, +> = CommonChartsReferenceLineProps & { + /** + * The y value associated with the reference line. + * If defined the reference line will be horizontal. + */ + y: TValue; +}; + +type GetTextPlacementParams = { + left: number; + width: number; + spacingX: number; +} & Pick; + +const getTextParams = ({ + left, + width, + spacingX, + labelAlign = 'middle', +}: GetTextPlacementParams) => { + switch (labelAlign) { + case 'start': + return { + x: left + spacingX, + style: { + dominantBaseline: 'auto', + textAnchor: 'start', + } as const, + }; + + case 'end': + return { + x: left + width - spacingX, + style: { + dominantBaseline: 'auto', + textAnchor: 'end', + } as const, + }; + + default: + return { + x: left + width / 2, + style: { + dominantBaseline: 'auto', + textAnchor: 'middle', + } as const, + }; + } +}; + +let warnedOnce = false; + +export function getYReferenceLineClasses(classes?: Partial) { + return composeClasses( + { + root: ['root', 'horizontal'], + line: ['line'], + label: ['label'], + }, + getReferenceLineUtilityClass, + classes, + ); +} + +function ChartsYReferenceLine(props: ChartsYReferenceLineProps) { + const { + y, + label = '', + spacing = 5, + classes: inClasses, + labelAlign, + lineStyle, + labelStyle, + axisId, + } = props; + + const { left, width } = useDrawingArea(); + const yAxisScale = useYScale(axisId); + + const yPosition = yAxisScale(y as any); + + if (yPosition === undefined) { + if (process.env.NODE_ENV !== 'production') { + if (!warnedOnce) { + warnedOnce = true; + console.error( + `MUI X: the value ${y} does not exist in the data of y axis with id ${axisId}.`, + ); + } + } + return null; + } + + const d = `M ${left} ${yPosition} l ${width} 0`; + + const classes = getYReferenceLineClasses(inClasses); + + const spacingX = typeof spacing === 'object' ? spacing.x ?? 0 : spacing; + const spacingY = typeof spacing === 'object' ? spacing.y ?? 0 : spacing; + + const textParams = { + y: yPosition - spacingY, + text: label, + fontSize: 12, + ...getTextParams({ + left, + width, + spacingX, + labelAlign, + }), + className: classes.label, + }; + return ( + + + + + ); +} + +export { ChartsYReferenceLine }; diff --git a/packages/x-charts/src/ChartsReferenceLine/chartsReferenceLineClasses.ts b/packages/x-charts/src/ChartsReferenceLine/chartsReferenceLineClasses.ts new file mode 100644 index 0000000000000..882500714bc89 --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/chartsReferenceLineClasses.ts @@ -0,0 +1,26 @@ +import generateUtilityClass from '@mui/utils/generateUtilityClass'; +import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; + +export interface ChartsReferenceLineClasses { + /** Styles applied to the root element. */ + root: string; + /** Styles applied to the root element if the reference line is vertical. */ + vertical: string; + /** Styles applied to the root element if the reference line is horizontal. */ + horizontal: string; + /** Styles applied to the reference line. */ + line: string; + /** Styles applied to the reference label. */ + label: string; +} + +export type ChartsReferenceLineClassKey = keyof ChartsReferenceLineClasses; + +export function getReferenceLineUtilityClass(slot: string) { + return generateUtilityClass('MuiChartsReferenceLine', slot); +} + +export const referenceLineClasses: ChartsReferenceLineClasses = generateUtilityClasses( + 'MuiChartsReferenceLine', + ['root', 'vertical', 'horizontal', 'line', 'label'], +); diff --git a/packages/x-charts/src/ChartsReferenceLine/common.tsx b/packages/x-charts/src/ChartsReferenceLine/common.tsx new file mode 100644 index 0000000000000..535bce12785d0 --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/common.tsx @@ -0,0 +1,55 @@ +import { styled } from '@mui/material/styles'; +import { referenceLineClasses, ChartsReferenceLineClasses } from './chartsReferenceLineClasses'; +import { ChartsTextStyle } from '../internals/components/ChartsText'; + +export type CommonChartsReferenceLineProps = { + /** + * The alignment if the label is in the chart drawing area. + * @default 'middle' + */ + labelAlign?: 'start' | 'middle' | 'end'; + /** + * The label to display along the reference line. + */ + label?: string; + /** + * Additional space arround the label in px. + * Can be a number or an object `{ x, y }` to distinguish space with the reference line and space with axes. + * @default 5 + */ + spacing?: number | { x?: number; y?: number }; + /** + * The id of the axis used for the reference value. + * @default The `id` of the first defined axis. + */ + axisId?: string; + /** + * The style applied to the label. + */ + labelStyle?: ChartsTextStyle; + /** + * The style applied to the line. + */ + lineStyle?: React.CSSProperties; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; +}; + +export const ReferenceLineRoot = styled('g')(({ theme }) => ({ + [`& .${referenceLineClasses.line}`]: { + fill: 'none', + stroke: (theme.vars || theme).palette.text.primary, + shapeRendering: 'crispEdges', + strokeWidth: 1, + pointerEvents: 'none', + }, + [`& .${referenceLineClasses.label}`]: { + fill: (theme.vars || theme).palette.text.primary, + stroke: 'none', + pointerEvents: 'none', + fontSize: 12, + ...theme.typography.body1, + }, +})); diff --git a/packages/x-charts/src/ChartsReferenceLine/index.tsx b/packages/x-charts/src/ChartsReferenceLine/index.tsx new file mode 100644 index 0000000000000..630541c15e4ae --- /dev/null +++ b/packages/x-charts/src/ChartsReferenceLine/index.tsx @@ -0,0 +1,2 @@ +export * from './ChartsReferenceLine'; +export * from './chartsReferenceLineClasses'; diff --git a/packages/x-charts/src/ChartsSurface.tsx b/packages/x-charts/src/ChartsSurface.tsx index 894f569868ee1..8f0e9dfb0d61f 100644 --- a/packages/x-charts/src/ChartsSurface.tsx +++ b/packages/x-charts/src/ChartsSurface.tsx @@ -9,7 +9,13 @@ type ViewBox = { height?: number; }; export interface ChartsSurfaceProps { + /** + * The width of the chart in px. + */ width: number; + /** + * The height of the chart in px. + */ height: number; viewBox?: ViewBox; className?: string; @@ -17,6 +23,11 @@ export interface ChartsSurfaceProps { desc?: string; sx?: SxProps; children?: React.ReactNode; + /** + * If `true`, the charts will not listen to the mouse move event. + * It might break interactive features, but will improve performance. + * @default false + */ disableAxisListener?: boolean; } @@ -34,7 +45,6 @@ export const ChartsSurface = React.forwardRef viewBox, disableAxisListener = false, className, - sx, ...other } = props; const svgView = { width, height, x: 0, y: 0, ...viewBox }; @@ -47,16 +57,6 @@ export const ChartsSurface = React.forwardRef height={height} viewBox={`${svgView.x} ${svgView.y} ${svgView.width} ${svgView.height}`} ref={ref} - sx={[ - { - '--ChartsLegend-itemWidth': '100px', - '--ChartsLegend-itemMarkSize': '20px', - '--ChartsLegend-rootSpacing': '5px', - '--ChartsLegend-labelSpacing': '5px', - '--ChartsLegend-rootOffsetY': '-20px', - }, - ...(Array.isArray(sx) ? sx : [sx]), - ]} {...other} > {props.title} diff --git a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx index 80cc72387291e..16ae820abcc07 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsAxisTooltipContent.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { SxProps, Theme } from '@mui/material/styles'; import Typography from '@mui/material/Typography'; +import { useSlotProps } from '@mui/base/utils'; import { AxisInteractionData } from '../context/InteractionProvider'; import { SeriesContext } from '../context/SeriesContextProvider'; import { CartesianContext } from '../context/CartesianContextProvider'; @@ -67,21 +68,27 @@ export function DefaultChartsAxisContent(props: ChartsAxisContentProps) { )} - {series.map(({ color, id, label, valueFormatter, data }: ChartSeriesDefaultized) => ( - - - - + {series.map(({ color, id, label, valueFormatter, data }: ChartSeriesDefaultized) => { + const formattedValue = valueFormatter(data[dataIndex]); + if (formattedValue == null) { + return null; + } + return ( + + + + - - {label ? {label} : null} - + + {label ? {label} : null} + - - {valueFormatter(data[dataIndex])} - - - ))} + + {formattedValue} + + + ); + })} @@ -91,10 +98,11 @@ export function DefaultChartsAxisContent(props: ChartsAxisContentProps) { export function ChartsAxisTooltipContent(props: { axisData: AxisInteractionData; content?: React.ElementType; + contentProps?: Partial; sx?: SxProps; classes: ChartsAxisContentProps['classes']; }) { - const { content, axisData, sx, classes } = props; + const { content, contentProps, axisData, sx, classes } = props; const isXaxis = (axisData.x && axisData.x.index) !== undefined; @@ -129,15 +137,19 @@ export function ChartsAxisTooltipContent(props: { }, [USED_AXIS_ID, isXaxis, xAxis, yAxis]); const Content = content ?? DefaultChartsAxisContent; - return ( - - ); + const chartTooltipContentProps = useSlotProps({ + elementType: Content, + externalSlotProps: contentProps, + additionalProps: { + axisData, + series: relevantSeries, + axis: relevantAxis, + dataIndex, + axisValue, + sx, + classes, + }, + ownerState: {}, + }); + return ; } diff --git a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx index 405565b72120b..18c0543b326e9 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsItemTooltipContent.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { SxProps, Theme } from '@mui/material/styles'; +import { useSlotProps } from '@mui/base/utils'; import { ItemInteractionData } from '../context/InteractionProvider'; import { SeriesContext } from '../context/SeriesContextProvider'; import { ChartSeriesDefaultized, ChartSeriesType } from '../models/seriesType/config'; @@ -12,7 +13,7 @@ import { } from './ChartsTooltipTable'; import { ChartsTooltipClasses } from './tooltipClasses'; -export type ChartsItemContentProps = { +export type ChartsItemContentProps = { /** * The data used to identify the triggered item. */ @@ -28,7 +29,7 @@ export type ChartsItemContentProps = { sx?: SxProps; }; -export function DefaultChartsItemContent( +export function DefaultChartsItemContent( props: ChartsItemContentProps, ) { const { series, itemData, sx, classes } = props; @@ -72,16 +73,27 @@ export function DefaultChartsItemContent( export function ChartsItemTooltipContent(props: { itemData: ItemInteractionData; content?: React.ElementType>; + contentProps?: Partial>; sx?: SxProps; classes: ChartsItemContentProps['classes']; }) { - const { content, itemData, sx, classes } = props; + const { content, itemData, sx, classes, contentProps } = props; const series = React.useContext(SeriesContext)[itemData.type]!.series[ itemData.seriesId ] as ChartSeriesDefaultized; const Content = content ?? DefaultChartsItemContent; - - return ; + const chartTooltipContentProps = useSlotProps({ + elementType: Content, + externalSlotProps: contentProps, + additionalProps: { + itemData, + series, + sx, + classes, + }, + ownerState: {}, + }); + return ; } diff --git a/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx b/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx index cf4f0d5e48257..dba501a5a0814 100644 --- a/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx +++ b/packages/x-charts/src/ChartsTooltip/ChartsTooltip.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; -import { Popper } from '@mui/base/Popper'; +import composeClasses from '@mui/utils/composeClasses'; +import { styled } from '@mui/material/styles'; +import { Popper, PopperProps } from '@mui/base/Popper'; import { NoSsr } from '@mui/base/NoSsr'; +import { useSlotProps } from '@mui/base/utils'; import { AxisInteractionData, InteractionContext, @@ -14,6 +16,18 @@ import { ChartsItemContentProps, ChartsItemTooltipContent } from './ChartsItemTo import { ChartsAxisContentProps, ChartsAxisTooltipContent } from './ChartsAxisTooltipContent'; import { ChartsTooltipClasses, getTooltipUtilityClass } from './tooltipClasses'; +export interface ChartsTooltipSlots { + popper?: React.ElementType; + axisContent?: React.ElementType; + itemContent?: React.ElementType; +} + +export interface ChartsTooltipSlotProps { + popper?: Partial; + axisContent?: Partial; + itemContent?: Partial; +} + export type ChartsTooltipProps = { /** * Select the kind of tooltip to display @@ -25,16 +39,28 @@ export type ChartsTooltipProps = { trigger?: TriggerOptions; /** * Component to override the tooltip content when triger is set to 'item'. + * @deprecated Use slots.itemContent instead */ itemContent?: React.ElementType>; /** * Component to override the tooltip content when triger is set to 'axis'. + * @deprecated Use slots.axisContent instead */ axisContent?: React.ElementType; /** * Override or extend the styles applied to the component. */ classes?: Partial; + /** + * Overridable component slots. + * @default {} + */ + slots?: ChartsTooltipSlots; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: ChartsTooltipSlotProps; }; const useUtilityClasses = (ownerState: { classes: ChartsTooltipProps['classes'] }) => { @@ -50,8 +76,26 @@ const useUtilityClasses = (ownerState: { classes: ChartsTooltipProps['classes'] return composeClasses(slots, getTooltipUtilityClass, classes); }; +const ChartsTooltipRoot = styled(Popper, { + name: 'MuiChartsTooltip', + slot: 'Root', + overridesResolver: (_, styles) => styles.root, +})(({ theme }) => ({ + pointerEvents: 'none', + zIndex: theme.zIndex.modal, +})); + +/** + * Demos: + * + * - [ChartsTooltip](https://mui.com/x/react-charts/tooltip/) + * + * API: + * + * - [ChartsTooltip API](https://mui.com/x/api/charts/charts-tool-tip/) + */ function ChartsTooltip(props: ChartsTooltipProps) { - const { trigger = 'axis', itemContent, axisContent } = props; + const { trigger = 'axis', itemContent, axisContent, slots, slotProps } = props; const mousePosition = useMouseTracker(); @@ -64,34 +108,44 @@ function ChartsTooltip(props: ChartsTooltipProps) { const classes = useUtilityClasses({ classes: props.classes }); + const PopperComponent = slots?.popper ?? ChartsTooltipRoot; + const popperProps = useSlotProps({ + elementType: PopperComponent, + externalSlotProps: slotProps?.popper, + additionalProps: { + open: popperOpen, + placement: 'right-start' as const, + anchorEl: generateVirtualElement(mousePosition), + }, + ownerState: {}, + }); + if (trigger === 'none') { return null; } + return ( {popperOpen && ( - + {trigger === 'item' ? ( } - content={itemContent} + content={slots?.itemContent ?? itemContent} + contentProps={slotProps?.itemContent} sx={{ mx: 2 }} classes={classes} /> ) : ( )} - + )} ); @@ -104,6 +158,7 @@ ChartsTooltip.propTypes = { // ---------------------------------------------------------------------- /** * Component to override the tooltip content when triger is set to 'axis'. + * @deprecated Use slots.axisContent instead */ axisContent: PropTypes.elementType, /** @@ -112,8 +167,19 @@ ChartsTooltip.propTypes = { classes: PropTypes.object, /** * Component to override the tooltip content when triger is set to 'item'. + * @deprecated Use slots.itemContent instead */ itemContent: PropTypes.elementType, + /** + * The props used for each component slot. + * @default {} + */ + slotProps: PropTypes.object, + /** + * Overridable component slots. + * @default {} + */ + slots: PropTypes.object, /** * Select the kind of tooltip to display * - 'item': Shows data about the item below the mouse. diff --git a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx index 0817932005666..2cda7958494ec 100644 --- a/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx +++ b/packages/x-charts/src/ChartsXAxis/ChartsXAxis.tsx @@ -1,19 +1,17 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { useSlotProps } from '@mui/base/utils'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme } from '@mui/material/styles'; import { CartesianContext } from '../context/CartesianContextProvider'; import { DrawingContext } from '../context/DrawingProvider'; -import useTicks from '../hooks/useTicks'; +import useTicks, { TickItemType } from '../hooks/useTicks'; import { ChartsXAxisProps } from '../models/axis'; import { getAxisUtilityClass } from '../ChartsAxis/axisClasses'; -import { - ChartsLine, - ChartsTick, - ChartsTickLabel, - ChartsLabel, - AxisRoot, -} from '../internals/components/AxisSharedComponents'; +import { AxisRoot } from '../internals/components/AxisSharedComponents'; +import { ChartsText, ChartsTextProps, getWordsByLines } from '../internals/components/ChartsText'; +import { getMinXTranslation } from '../internals/geometry'; +import { useMounted } from '../hooks/useMounted'; const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => { const { classes, position } = ownerState; @@ -29,35 +27,100 @@ const useUtilityClasses = (ownerState: ChartsXAxisProps & { theme: Theme }) => { return composeClasses(slots, getAxisUtilityClass, classes); }; +type LabelExtraData = { width: number; height: number; skipLabel?: boolean }; + +function addLabelDimension( + xTicks: TickItemType[], + { + tickLabelStyle: style, + tickLabelInterval, + isMounted, + }: Pick & { isMounted: boolean }, +): (TickItemType & LabelExtraData)[] { + const withDimension = xTicks.map((tick) => { + if (!isMounted || tick.formattedValue === undefined) { + return { ...tick, width: 0, height: 0 }; + } + const tickSizes = getWordsByLines({ style, needsComputation: true, text: tick.formattedValue }); + return { + ...tick, + width: Math.max(...tickSizes.map((size) => size.width)), + height: Math.max(tickSizes.length * tickSizes[0].height), + }; + }); + + if (typeof tickLabelInterval === 'function') { + return withDimension.map((item, index) => ({ + ...item, + skipLabel: !tickLabelInterval(item.value, index), + })); + } + + // Filter label to avoid overlap + let textStart = 0; + let textEnd = 0; + + return withDimension.map((item, labelIndex) => { + const { width, offset, labelOffset, height } = item; + + const distance = getMinXTranslation(width, height, style?.angle); + const textPosition = offset + labelOffset; + const gapRatio = 1.2; // Ratio applied to the minimal distance to add some margin. + + textStart = textPosition - (gapRatio * distance) / 2; + if (labelIndex > 0 && textStart < textEnd) { + // Except for the first label, we skip all label that overlap with the last accepted. + // Notice that the early return prevents `textEnd` from being updated. + return { ...item, skipLabel: true }; + } + textEnd = textPosition + (gapRatio * distance) / 2; + return item; + }); +} + const defaultProps = { position: 'bottom', disableLine: false, disableTicks: false, - tickFontSize: 12, - labelFontSize: 14, tickSize: 6, } as const; +/** + * Demos: + * + * - [Axis](https://mui.com/x/react-charts/axis/) + * + * API: + * + * - [ChartsXAxis API](https://mui.com/x/api/charts/charts-x-axis/) + */ function ChartsXAxis(inProps: ChartsXAxisProps) { const props = useThemeProps({ props: { ...defaultProps, ...inProps }, name: 'MuiChartsXAxis' }); + const { xAxisIds } = React.useContext(CartesianContext); const { xAxis: { - [props.axisId]: { scale: xScale, ticksNumber, ...settings }, + [props.axisId ?? xAxisIds[0]]: { scale: xScale, tickNumber, ...settings }, }, } = React.useContext(CartesianContext); + const isMounted = useMounted(); + const defaultizedProps = { ...defaultProps, ...settings, ...props }; const { position, disableLine, disableTicks, - tickFontSize, + tickLabelStyle, label, + labelStyle, + tickFontSize, labelFontSize, tickSize: tickSizeProp, valueFormatter, slots, slotProps, + tickInterval, + tickLabelInterval, } = defaultizedProps; const theme = useTheme(); @@ -67,18 +130,55 @@ function ChartsXAxis(inProps: ChartsXAxisProps) { const tickSize = disableTicks ? 4 : tickSizeProp; - const xTicks = useTicks({ scale: xScale, ticksNumber, valueFormatter }); - const positionSigne = position === 'bottom' ? 1 : -1; + const positionSign = position === 'bottom' ? 1 : -1; + + const Line = slots?.axisLine ?? 'line'; + const Tick = slots?.axisTick ?? 'line'; + const TickLabel = slots?.axisTickLabel ?? ChartsText; + const Label = slots?.axisLabel ?? ChartsText; + + const axisTickLabelProps = useSlotProps({ + elementType: TickLabel, + externalSlotProps: slotProps?.axisTickLabel, + additionalProps: { + style: { + textAnchor: 'middle', + dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', + fontSize: tickFontSize ?? 12, + ...tickLabelStyle, + }, + className: classes.tickLabel, + } as Partial, + className: classes.tickLabel, + ownerState: {}, + }); + + const xTicks = useTicks({ scale: xScale, tickNumber, valueFormatter, tickInterval }); + + const xTicksWithDimension = addLabelDimension(xTicks, { + tickLabelStyle: axisTickLabelProps.style, + tickLabelInterval, + isMounted, + }); const labelRefPoint = { x: left + width / 2, - y: positionSigne * (tickFontSize + tickSize + 10), + y: positionSign * (tickSize + 22), }; - const Line = slots?.axisLine ?? ChartsLine; - const Tick = slots?.axisTick ?? ChartsTick; - const TickLabel = slots?.axisTickLabel ?? ChartsTickLabel; - const Label = slots?.axisLabel ?? ChartsLabel; + const axisLabelProps = useSlotProps({ + elementType: Label, + externalSlotProps: slotProps?.axisLabel, + additionalProps: { + style: { + fontSize: labelFontSize ?? 14, + textAnchor: 'middle', + dominantBaseline: position === 'bottom' ? 'hanging' : 'auto', + ...labelStyle, + }, + } as Partial, + ownerState: {}, + }); return ( )} - {xTicks.map(({ formattedValue, offset, labelOffset }, index) => { + {xTicksWithDimension.map(({ formattedValue, offset, labelOffset, skipLabel }, index) => { const xTickLabel = labelOffset ?? 0; - const yTickLabel = positionSigne * (tickSize + 3); + const yTickLabel = positionSign * (tickSize + 3); return ( {!disableTicks && ( )} - {formattedValue !== undefined && ( + {formattedValue !== undefined && !skipLabel && ( - {formattedValue} - + {...axisTickLabelProps} + text={formattedValue.toString()} + /> )} ); })} {label && ( - + + )} ); @@ -148,9 +234,10 @@ ChartsXAxis.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- /** - * Id of the axis to render. + * The id of the axis to render. + * If undefined, it will be the first defined axis. */ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, /** * Override or extend the styles applied to the component. */ @@ -177,8 +264,13 @@ ChartsXAxis.propTypes = { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize: PropTypes.number, + /** + * The style applied to the axis label. + */ + labelStyle: PropTypes.object, /** * Position of the axis. */ @@ -201,8 +293,28 @@ ChartsXAxis.propTypes = { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize: PropTypes.number, + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The style applied to ticks text. + */ + tickLabelStyle: PropTypes.object, /** * Maximal step between two ticks. * When using time data, the value is assumed to be in ms. diff --git a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx index 1e0d5227a2748..f22632a0a8a13 100644 --- a/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx +++ b/packages/x-charts/src/ChartsYAxis/ChartsYAxis.tsx @@ -1,18 +1,14 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { useSlotProps } from '@mui/base/utils'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { useThemeProps, useTheme, Theme } from '@mui/material/styles'; import { CartesianContext } from '../context/CartesianContextProvider'; import { DrawingContext } from '../context/DrawingProvider'; import useTicks from '../hooks/useTicks'; import { ChartsYAxisProps } from '../models/axis'; -import { - ChartsLine, - ChartsTick, - ChartsTickLabel, - ChartsLabel, - AxisRoot, -} from '../internals/components/AxisSharedComponents'; +import { AxisRoot } from '../internals/components/AxisSharedComponents'; +import { ChartsText, ChartsTextProps } from '../internals/components/ChartsText'; import { getAxisUtilityClass } from '../ChartsAxis/axisClasses'; const useUtilityClasses = (ownerState: ChartsYAxisProps & { theme: Theme }) => { @@ -38,11 +34,21 @@ const defaultProps = { tickSize: 6, } as const; +/** + * Demos: + * + * - [Axis](https://mui.com/x/react-charts/axis/) + * + * API: + * + * - [ChartsYAxis API](https://mui.com/x/api/charts/charts-y-axis/) + */ function ChartsYAxis(inProps: ChartsYAxisProps) { const props = useThemeProps({ props: { ...defaultProps, ...inProps }, name: 'MuiChartsYAxis' }); + const { yAxisIds } = React.useContext(CartesianContext); const { yAxis: { - [props.axisId]: { scale: yScale, ticksNumber, ...settings }, + [props.axisId ?? yAxisIds[0]]: { scale: yScale, tickNumber, ...settings }, }, } = React.useContext(CartesianContext); @@ -67,19 +73,47 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { const tickSize = disableTicks ? 4 : tickSizeProp; - const yTicks = useTicks({ scale: yScale, ticksNumber, valueFormatter }); + const yTicks = useTicks({ scale: yScale, tickNumber, valueFormatter }); - const positionSigne = position === 'right' ? 1 : -1; + const positionSign = position === 'right' ? 1 : -1; const labelRefPoint = { - x: positionSigne * (tickFontSize + tickSize + 10), + x: positionSign * (tickFontSize + tickSize + 10), y: top + height / 2, }; - const Line = slots?.axisLine ?? ChartsLine; - const Tick = slots?.axisTick ?? ChartsTick; - const TickLabel = slots?.axisTickLabel ?? ChartsTickLabel; - const Label = slots?.axisLabel ?? ChartsLabel; + const Line = slots?.axisLine ?? 'line'; + const Tick = slots?.axisTick ?? 'line'; + const TickLabel = slots?.axisTickLabel ?? ChartsText; + const Label = slots?.axisLabel ?? ChartsText; + + const axisTickLabelProps = useSlotProps({ + elementType: TickLabel, + externalSlotProps: slotProps?.axisTickLabel, + additionalProps: { + style: { + fontSize: tickFontSize, + textAnchor: position === 'right' ? 'start' : 'end', + dominantBaseline: 'central', + }, + className: classes.tickLabel, + } as Partial, + ownerState: {}, + }); + + const axisLabelProps = useSlotProps({ + elementType: Label, + externalSlotProps: slotProps?.axisLabel, + additionalProps: { + style: { + fontSize: labelFontSize, + angle: positionSign * 90, + textAnchor: 'middle', + dominantBaseline: 'auto', + } as Partial['style'], + } as Partial, + ownerState: {}, + }); return ( { - const xTickLabel = positionSigne * (tickSize + 2); + const xTickLabel = positionSign * (tickSize + 2); const yTickLabel = labelOffset; return ( {!disableTicks && ( @@ -112,33 +146,18 @@ function ChartsYAxis(inProps: ChartsYAxisProps) { - {formattedValue.toLocaleString()} - + text={formattedValue.toString()} + {...axisTickLabelProps} + /> )} ); })} {label && ( - + + )} ); @@ -150,9 +169,10 @@ ChartsYAxis.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- /** - * Id of the axis to render. + * The id of the axis to render. + * If undefined, it will be the first defined axis. */ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, /** * Override or extend the styles applied to the component. */ @@ -179,8 +199,13 @@ ChartsYAxis.propTypes = { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize: PropTypes.number, + /** + * The style applied to the axis label. + */ + labelStyle: PropTypes.object, /** * Position of the axis. */ @@ -203,8 +228,28 @@ ChartsYAxis.propTypes = { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize: PropTypes.number, + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + /** + * The style applied to ticks text. + */ + tickLabelStyle: PropTypes.object, /** * Maximal step between two ticks. * When using time data, the value is assumed to be in ms. diff --git a/packages/x-charts/src/LineChart/AreaElement.tsx b/packages/x-charts/src/LineChart/AreaElement.tsx index 7f9bd4c07f85c..d9610d9ec7848 100644 --- a/packages/x-charts/src/LineChart/AreaElement.tsx +++ b/packages/x-charts/src/LineChart/AreaElement.tsx @@ -108,6 +108,16 @@ export type AreaElementProps = Omit; } -export interface AreaPlotSlotComponentProps { +export interface AreaPlotSlotProps { area?: Partial; } @@ -19,6 +19,17 @@ export interface AreaPlotProps extends React.SVGAttributes, Pick {} +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Areas demonstration](https://mui.com/x/react-charts/areas-demo/) + * - [Stacking](https://mui.com/x/react-charts/stacking/) + * + * API: + * + * - [AreaPlot API](https://mui.com/x/api/charts/area-plot/) + */ function AreaPlot(props: AreaPlotProps) { const { slots, slotProps, ...other } = props; @@ -41,28 +52,41 @@ function AreaPlot(props: AreaPlotProps) { xAxisKey = defaultXAxisId, yAxisKey = defaultYAxisId, stackedData, + data, + connectNulls, } = series[seriesId]; const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; - if (xData === undefined) { - throw new Error( - `Axis of id "${xAxisKey}" should have data property to be able to display a line plot.`, - ); + if (process.env.NODE_ENV !== 'production') { + if (xData === undefined) { + throw new Error( + `Axis of id "${xAxisKey}" should have data property to be able to display a line plot.`, + ); + } + if (xData.length < stackedData.length) { + throw new Error( + `MUI: data length of the x axis (${xData.length} items) is lower than the length of series (${stackedData.length} items)`, + ); + } } const areaPath = d3Area<{ x: any; - y: any[]; + y: [number, number]; }>() .x((d) => xScale(d.x)) - .y0((d) => yScale(d.y[0])) - .y1((d) => yScale(d.y[1])); + .defined((_, i) => connectNulls || data[i] != null) + .y0((d) => d.y && yScale(d.y[0])!) + .y1((d) => d.y && yScale(d.y[1])!); const curve = getCurveFactory(series[seriesId].curve); - const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] })); + const formattedData = xData?.map((x, index) => ({ x, y: stackedData[index] })) ?? []; + const d3Data = connectNulls + ? formattedData.filter((_, i) => data[i] != null) + : formattedData; return ( !!series[seriesId].area && ( diff --git a/packages/x-charts/src/LineChart/LineChart.tsx b/packages/x-charts/src/LineChart/LineChart.tsx index e0ebdcc3ce804..29ab513796071 100644 --- a/packages/x-charts/src/LineChart/LineChart.tsx +++ b/packages/x-charts/src/LineChart/LineChart.tsx @@ -1,54 +1,71 @@ import * as React from 'react'; import useId from '@mui/utils/useId'; import PropTypes from 'prop-types'; -import { AreaPlot, AreaPlotSlotComponentProps, AreaPlotSlotsComponent } from './AreaPlot'; -import { LinePlot, LinePlotSlotComponentProps, LinePlotSlotsComponent } from './LinePlot'; +import { AreaPlot, AreaPlotSlotProps, AreaPlotSlots } from './AreaPlot'; +import { LinePlot, LinePlotSlotProps, LinePlotSlots } from './LinePlot'; import { ResponsiveChartContainer, ResponsiveChartContainerProps, } from '../ResponsiveChartContainer'; -import { MarkPlot, MarkPlotSlotComponentProps, MarkPlotSlotsComponent } from './MarkPlot'; +import { MarkPlot, MarkPlotSlotProps, MarkPlotSlots } from './MarkPlot'; import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis/ChartsAxis'; import { LineSeriesType } from '../models/seriesType/line'; import { MakeOptional } from '../models/helpers'; import { DEFAULT_X_AXIS_KEY } from '../constants'; -import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip'; +import { + ChartsTooltip, + ChartsTooltipProps, + ChartsTooltipSlotProps, + ChartsTooltipSlots, +} from '../ChartsTooltip'; import { ChartsLegend, ChartsLegendProps, - ChartsLegendSlotComponentProps, - ChartsLegendSlotsComponent, + ChartsLegendSlotProps, + ChartsLegendSlots, } from '../ChartsLegend'; import { ChartsAxisHighlight, ChartsAxisHighlightProps } from '../ChartsAxisHighlight'; import { ChartsClipPath } from '../ChartsClipPath'; -import { ChartsAxisSlotComponentProps, ChartsAxisSlotsComponent } from '../models/axis'; +import { ChartsAxisSlotProps, ChartsAxisSlots } from '../models/axis'; import { LineHighlightPlot, - LineHighlightPlotSlotsComponent, - LineHighlightPlotSlotComponentProps, + LineHighlightPlotSlots, + LineHighlightPlotSlotProps, } from './LineHighlightPlot'; -export interface LineChartSlotsComponent - extends ChartsAxisSlotsComponent, - AreaPlotSlotsComponent, - LinePlotSlotsComponent, - MarkPlotSlotsComponent, - LineHighlightPlotSlotsComponent, - ChartsLegendSlotsComponent {} -export interface LineChartSlotComponentProps - extends ChartsAxisSlotComponentProps, - AreaPlotSlotComponentProps, - LinePlotSlotComponentProps, - MarkPlotSlotComponentProps, - LineHighlightPlotSlotComponentProps, - ChartsLegendSlotComponentProps {} +export interface LineChartSlots + extends ChartsAxisSlots, + AreaPlotSlots, + LinePlotSlots, + MarkPlotSlots, + LineHighlightPlotSlots, + ChartsLegendSlots, + ChartsTooltipSlots {} +export interface LineChartSlotProps + extends ChartsAxisSlotProps, + AreaPlotSlotProps, + LinePlotSlotProps, + MarkPlotSlotProps, + LineHighlightPlotSlotProps, + ChartsLegendSlotProps, + ChartsTooltipSlotProps {} export interface LineChartProps extends Omit, Omit { series: MakeOptional[]; tooltip?: ChartsTooltipProps; + /** + * Object `{ x, y }` that defines how the charts highlight the mouse position along the x- and y-axes. + * The two properties accept the following values: + * - 'none': display nothing. + * - 'line': display a line at the current mouse position. + * - 'band': display a band at the current mouse position. Only available with band scale. + */ axisHighlight?: ChartsAxisHighlightProps; + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend?: ChartsLegendProps; /** * If `true`, render the line highlight item. @@ -58,13 +75,24 @@ export interface LineChartProps * Overridable component slots. * @default {} */ - slots?: LineChartSlotsComponent; + slots?: LineChartSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: LineChartSlotComponentProps; + slotProps?: LineChartSlotProps; } + +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Line demonstration](https://mui.com/x/react-charts/line-demo/) + * + * API: + * + * - [LineChart API](https://mui.com/x/api/charts/line-chart/) + */ const LineChart = React.forwardRef(function LineChart(props: LineChartProps, ref) { const { xAxis, @@ -139,7 +167,7 @@ const LineChart = React.forwardRef(function LineChart(props: LineChartProps, ref - + {children} @@ -151,6 +179,13 @@ LineChart.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- + /** + * Object `{ x, y }` that defines how the charts highlight the mouse position along the x- and y-axes. + * The two properties accept the following values: + * - 'none': display nothing. + * - 'line': display a line at the current mouse position. + * - 'band': display a band at the current mouse position. Only available with band scale. + */ axisHighlight: PropTypes.shape({ x: PropTypes.oneOf(['band', 'line', 'none']), y: PropTypes.oneOf(['band', 'line', 'none']), @@ -162,18 +197,26 @@ LineChart.propTypes = { */ bottomAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -187,13 +230,25 @@ LineChart.propTypes = { * Color palette used to colorize multiple series. */ colors: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.func]), + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset: PropTypes.arrayOf(PropTypes.object), desc: PropTypes.string, + /** + * If `true`, the charts will not listen to the mouse move event. + * It might break interactive features, but will improve performance. + * @default false + */ disableAxisListener: PropTypes.bool, /** * If `true`, render the line highlight item. */ disableLineItemHighlight: PropTypes.bool, + /** + * The height of the chart in px. If not defined, it takes the height of the parent element. + * @default undefined + */ height: PropTypes.number, /** * Indicate which axis to display the left of the charts. @@ -202,18 +257,26 @@ LineChart.propTypes = { */ leftAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -221,24 +284,26 @@ LineChart.propTypes = { }), PropTypes.string, ]), + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend: PropTypes.shape({ classes: PropTypes.object, direction: PropTypes.oneOf(['column', 'row']), hidden: PropTypes.bool, - itemWidth: PropTypes.number, - markSize: PropTypes.number, - offset: PropTypes.shape({ - x: PropTypes.number, - y: PropTypes.number, - }), position: PropTypes.shape({ horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, }), slotProps: PropTypes.object, slots: PropTypes.object, - spacing: PropTypes.number, }), + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ margin: PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, @@ -252,18 +317,26 @@ LineChart.propTypes = { */ rightAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -275,6 +348,7 @@ LineChart.propTypes = { PropTypes.shape({ area: PropTypes.bool, color: PropTypes.string, + connectNulls: PropTypes.bool, curve: PropTypes.oneOf([ 'catmullRom', 'linear', @@ -331,6 +405,8 @@ LineChart.propTypes = { axisContent: PropTypes.elementType, classes: PropTypes.object, itemContent: PropTypes.elementType, + slotProps: PropTypes.object, + slots: PropTypes.object, trigger: PropTypes.oneOf(['axis', 'item', 'none']), }), /** @@ -340,18 +416,26 @@ LineChart.propTypes = { */ topAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -365,7 +449,15 @@ LineChart.propTypes = { x: PropTypes.number, y: PropTypes.number, }), + /** + * The width of the chart in px. If not defined, it takes the width of the parent element. + * @default undefined + */ width: PropTypes.number, + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_X_AXIS_KEY`. + */ xAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -379,6 +471,7 @@ LineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -387,6 +480,13 @@ LineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -394,6 +494,10 @@ LineChart.propTypes = { valueFormatter: PropTypes.func, }), ), + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_Y_AXIS_KEY`. + */ yAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -407,6 +511,7 @@ LineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -415,6 +520,13 @@ LineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/LineChart/LineElement.tsx b/packages/x-charts/src/LineChart/LineElement.tsx index e9d0dfe16f8a6..14d13e28598fb 100644 --- a/packages/x-charts/src/LineChart/LineElement.tsx +++ b/packages/x-charts/src/LineChart/LineElement.tsx @@ -110,6 +110,16 @@ export type LineElementProps = Omit & {}; +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Line demonstration](https://mui.com/x/react-charts/line-demo/) + * + * API: + * + * - [LineHighlightElement API](https://mui.com/x/api/charts/line-highlight-element/) + */ function LineHighlightElement(props: LineHighlightElementProps) { const { x, y, id, classes: innerClasses, color, ...other } = props; diff --git a/packages/x-charts/src/LineChart/LineHighlightPlot.tsx b/packages/x-charts/src/LineChart/LineHighlightPlot.tsx index 4703913d2fcda..c610d5a744a34 100644 --- a/packages/x-charts/src/LineChart/LineHighlightPlot.tsx +++ b/packages/x-charts/src/LineChart/LineHighlightPlot.tsx @@ -6,11 +6,11 @@ import { LineHighlightElement, LineHighlightElementProps } from './LineHighlight import { getValueToPositionMapper } from '../hooks/useScale'; import { InteractionContext } from '../context/InteractionProvider'; -export interface LineHighlightPlotSlotsComponent { +export interface LineHighlightPlotSlots { lineHighlight?: React.JSXElementConstructor; } -export interface LineHighlightPlotSlotComponentProps { +export interface LineHighlightPlotSlotProps { lineHighlight?: Partial; } @@ -19,14 +19,24 @@ export interface LineHighlightPlotProps extends React.SVGAttributes; } -export interface LinePlotSlotComponentProps { +export interface LinePlotSlotProps { line?: Partial; } @@ -19,6 +19,16 @@ export interface LinePlotProps extends React.SVGAttributes, Pick {} +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Line demonstration](https://mui.com/x/react-charts/line-demo/) + * + * API: + * + * - [LinePlot API](https://mui.com/x/api/charts/line-plot/) + */ function LinePlot(props: LinePlotProps) { const { slots, slotProps, ...other } = props; const seriesData = React.useContext(SeriesContext).line; @@ -40,34 +50,40 @@ function LinePlot(props: LinePlotProps) { xAxisKey = defaultXAxisId, yAxisKey = defaultYAxisId, stackedData, + data, + connectNulls, } = series[seriesId]; const xScale = getValueToPositionMapper(xAxis[xAxisKey].scale); const yScale = yAxis[yAxisKey].scale; const xData = xAxis[xAxisKey].data; - if (xData === undefined) { - throw new Error( - `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, - ); + if (process.env.NODE_ENV !== 'production') { + if (xData === undefined) { + throw new Error( + `Axis of id "${xAxisKey}" should have data property to be able to display a line plot`, + ); + } + if (xData.length < stackedData.length) { + throw new Error( + `MUI: data length of the x axis (${xData.length} items) is lower than the length of series (${stackedData.length} items)`, + ); + } } const linePath = d3Line<{ x: any; - y: any[]; + y: [number, number]; }>() .x((d) => xScale(d.x)) - .y((d) => yScale(d.y[1])); + .defined((_, i) => connectNulls || data[i] != null) + .y((d) => yScale(d.y[1])!); - if (process.env.NODE_ENV !== 'production') { - if (xData.length !== stackedData.length) { - throw new Error( - `MUI: data length of the x axis (${xData.length} items) does not match length of series (${stackedData.length} items)`, - ); - } - } const curve = getCurveFactory(series[seriesId].curve); - const d3Data = xData?.map((x, index) => ({ x, y: stackedData[index] ?? [0, 0] })); + const formattedData = xData?.map((x, index) => ({ x, y: stackedData[index] })) ?? []; + const d3Data = connectNulls + ? formattedData.filter((_, i) => data[i] != null) + : formattedData; return ( ; }; +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Line demonstration](https://mui.com/x/react-charts/line-demo/) + * + * API: + * + * - [MarkElement API](https://mui.com/x/api/charts/mark-element/) + */ function MarkElement(props: MarkElementProps) { const { x, diff --git a/packages/x-charts/src/LineChart/MarkPlot.tsx b/packages/x-charts/src/LineChart/MarkPlot.tsx index 243be93e32d84..51c057316d587 100644 --- a/packages/x-charts/src/LineChart/MarkPlot.tsx +++ b/packages/x-charts/src/LineChart/MarkPlot.tsx @@ -5,11 +5,11 @@ import { CartesianContext } from '../context/CartesianContextProvider'; import { MarkElement, MarkElementProps } from './MarkElement'; import { getValueToPositionMapper } from '../hooks/useScale'; -export interface MarkPlotSlotsComponent { +export interface MarkPlotSlots { mark?: React.JSXElementConstructor; } -export interface MarkPlotSlotComponentProps { +export interface MarkPlotSlotProps { mark?: Partial; } @@ -18,14 +18,24 @@ export interface MarkPlotProps extends React.SVGAttributes { * Overridable component slots. * @default {} */ - slots?: MarkPlotSlotsComponent; + slots?: MarkPlotSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: MarkPlotSlotComponentProps; + slotProps?: MarkPlotSlotProps; } +/** + * Demos: + * + * - [Lines](https://mui.com/x/react-charts/lines/) + * - [Line demonstration](https://mui.com/x/react-charts/line-demo/) + * + * API: + * + * - [MarkPlot API](https://mui.com/x/api/charts/mark-plot/) + */ function MarkPlot(props: MarkPlotProps) { const { slots, slotProps, ...other } = props; @@ -50,6 +60,7 @@ function MarkPlot(props: MarkPlotProps) { xAxisKey = defaultXAxisId, yAxisKey = defaultYAxisId, stackedData, + data, showMark = true, } = series[seriesId]; @@ -82,25 +93,37 @@ function MarkPlot(props: MarkPlotProps) { return xData ?.map((x, index) => { - const y = stackedData[index][1]; + const value = data[index] == null ? null : stackedData[index][1]; return { x: xScale(x), - y: yScale(y), + y: value === null ? null : yScale(value)!, position: x, - value: y, + value, index, }; }) - .filter(isInRange) - .map(({ x, y, index, position, value }) => { - return showMark === true || - showMark({ - x, - y, - index, - position, - value, - }) ? ( + .filter(({ x, y, index, position, value }) => { + if (value === null || y === null) { + // Remove missing data point + return false; + } + if (!isInRange({ x, y })) { + // Remove out of range + return false; + } + if (showMark === true) { + return true; + } + return showMark({ + x, + y, + index, + position, + value, + }); + }) + .map(({ x, y, index }) => { + return ( - ) : null; + ); }); }); })} diff --git a/packages/x-charts/src/LineChart/formatter.ts b/packages/x-charts/src/LineChart/formatter.ts index 57d1839bf33f1..e120d166f5613 100644 --- a/packages/x-charts/src/LineChart/formatter.ts +++ b/packages/x-charts/src/LineChart/formatter.ts @@ -10,7 +10,7 @@ const formatter: Formatter<'line'> = (params, dataset) => { const stackingGroups = getStackingGroups(params); // Create a data set with format adapted to d3 - const d3Dataset: { [id: string]: number }[] = dataset ?? []; + const d3Dataset: { [id: string]: number | null }[] = dataset ?? []; seriesOrder.forEach((id) => { const data = series[id].data; if (data !== undefined) { @@ -21,7 +21,7 @@ const formatter: Formatter<'line'> = (params, dataset) => { d3Dataset[index][id] = value; } }); - } else if (dataset === undefined) { + } else if (dataset === undefined && process.env.NODE_ENV !== 'production') { throw new Error( [ `MUI: line series with id='${id}' has no data.`, @@ -36,7 +36,7 @@ const formatter: Formatter<'line'> = (params, dataset) => { stackingGroups.forEach((stackingGroup) => { // Get stacked values, and derive the domain const { ids, stackingOrder, stackingOffset } = stackingGroup; - const stackedSeries = d3Stack() + const stackedSeries = d3Stack() .keys( ids.map((id) => { // Use dataKey if needed and available @@ -44,6 +44,7 @@ const formatter: Formatter<'line'> = (params, dataset) => { return series[id].data === undefined && dataKey !== undefined ? dataKey : id; }), ) + .value((d, key) => d[key] ?? 0) // defaultize null value to 0 .order(stackingOrder) .offset(stackingOffset)(d3Dataset); @@ -60,7 +61,7 @@ const formatter: Formatter<'line'> = (params, dataset) => { return { seriesOrder, stackingGroups, - series: defaultizeValueFormatter(completedSeries, (v) => v.toLocaleString()), + series: defaultizeValueFormatter(completedSeries, (v) => v?.toLocaleString()), }; }; diff --git a/packages/x-charts/src/PieChart/PieArc.tsx b/packages/x-charts/src/PieChart/PieArc.tsx index b91c05d706615..07445f14198e9 100644 --- a/packages/x-charts/src/PieChart/PieArc.tsx +++ b/packages/x-charts/src/PieChart/PieArc.tsx @@ -1,18 +1,12 @@ import * as React from 'react'; -import { arc as d3Arc, PieArcDatum as D3PieArcDatum } from 'd3-shape'; -import PropTypes from 'prop-types'; +import { arc as d3Arc } from 'd3-shape'; +import { animated, SpringValue, to } from '@react-spring/web'; import composeClasses from '@mui/utils/composeClasses'; import generateUtilityClass from '@mui/utils/generateUtilityClass'; import { styled } from '@mui/material/styles'; import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import { InteractionContext } from '../context/InteractionProvider'; -import { - getIsFaded, - getIsHighlighted, - useInteractionItemProps, -} from '../hooks/useInteractionItemProps'; import { HighlightScope } from '../context/HighlightProvider'; -import { PieSeriesType } from '../models/seriesType/pie'; +import { useInteractionItemProps } from '../hooks/useInteractionItemProps'; export interface PieArcClasses { /** Styles applied to the root element. */ @@ -53,27 +47,26 @@ const useUtilityClasses = (ownerState: PieArcOwnerState) => { return composeClasses(slots, getPieArcUtilityClass, classes); }; -const PieArcRoot = styled('path', { +const PieArcRoot = styled(animated.path, { name: 'MuiPieArc', slot: 'Root', overridesResolver: (_, styles) => styles.arc, -})<{ ownerState: PieArcOwnerState }>(({ ownerState, theme }) => ({ +})<{ ownerState: PieArcOwnerState }>(({ theme }) => ({ stroke: (theme.vars || theme).palette.background.paper, strokeWidth: 1, strokeLinejoin: 'round', - fill: ownerState.color, - opacity: ownerState.isFaded ? 0.3 : 1, })); -export type PieArcProps = Omit & - React.ComponentPropsWithoutRef<'path'> & - D3PieArcDatum & { +export type PieArcProps = PieArcOwnerState & + React.ComponentPropsWithoutRef<'path'> & { + startAngle: SpringValue; + endAngle: SpringValue; + innerRadius: SpringValue; + outerRadius: SpringValue; + cornerRadius: SpringValue; + paddingAngle: SpringValue; highlightScope?: Partial; - innerRadius: PieSeriesType['innerRadius']; - outerRadius: number; - cornerRadius: PieSeriesType['cornerRadius']; - highlighted: PieSeriesType['highlighted']; - faded: PieSeriesType['faded']; + onClick?: (event: React.MouseEvent) => void; }; export default function PieArc(props: PieArcProps) { @@ -83,27 +76,18 @@ export default function PieArc(props: PieArcProps) { classes: innerClasses, color, highlightScope, - innerRadius: baseInnerRadius = 0, - outerRadius: baseOuterRadius, - cornerRadius: baseCornerRadius = 0, - highlighted, - faded = { additionalRadius: -5 }, + onClick, + isFaded, + isHighlighted, + startAngle, + endAngle, + paddingAngle, + innerRadius, + outerRadius, + cornerRadius, ...other } = props; - const getInteractionItemProps = useInteractionItemProps(highlightScope); - - const { item } = React.useContext(InteractionContext); - - const isHighlighted = getIsHighlighted( - item, - { type: 'pie', seriesId: id, dataIndex }, - highlightScope, - ); - - const isFaded = - !isHighlighted && getIsFaded(item, { type: 'pie', seriesId: id, dataIndex }, highlightScope); - const ownerState = { id, dataIndex, @@ -114,46 +98,27 @@ export default function PieArc(props: PieArcProps) { }; const classes = useUtilityClasses(ownerState); - const attibuesOverride = { - additionalRadius: 0, - ...((isFaded && faded) || (isHighlighted && highlighted) || {}), - }; - const innerRadius = Math.max(0, attibuesOverride.innerRadius ?? baseInnerRadius); - - const outerRadius = Math.max( - 0, - attibuesOverride.outerRadius ?? baseOuterRadius + attibuesOverride.additionalRadius, - ); - const cornerRadius = attibuesOverride.cornerRadius ?? baseCornerRadius; + const getInteractionItemProps = useInteractionItemProps(highlightScope); return ( + d3Arc().cornerRadius(cR)({ + padAngle: pA, + startAngle: sA, + endAngle: eA, + innerRadius: iR, + outerRadius: oR, + })!, + )} + onClick={onClick} + cursor={onClick ? 'pointer' : 'unset'} ownerState={ownerState} className={classes.root} + {...other} {...getInteractionItemProps({ type: 'pie', seriesId: id, dataIndex })} /> ); } - -PieArc.propTypes = { - // ----------------------------- Warning -------------------------------- - // | These PropTypes are generated from the TypeScript type definitions | - // | To update them edit the TypeScript types and run "yarn proptypes" | - // ---------------------------------------------------------------------- - classes: PropTypes.object, - cornerRadius: PropTypes.number, - dataIndex: PropTypes.number.isRequired, - highlightScope: PropTypes.shape({ - faded: PropTypes.oneOf(['global', 'none', 'series']), - highlighted: PropTypes.oneOf(['item', 'none', 'series']), - }), - innerRadius: PropTypes.number, - outerRadius: PropTypes.number.isRequired, -} as any; diff --git a/packages/x-charts/src/PieChart/PieArcLabel.tsx b/packages/x-charts/src/PieChart/PieArcLabel.tsx index d698393291f6f..ae41ede0319b4 100644 --- a/packages/x-charts/src/PieChart/PieArcLabel.tsx +++ b/packages/x-charts/src/PieChart/PieArcLabel.tsx @@ -1,14 +1,11 @@ import * as React from 'react'; -import { arc as d3Arc, PieArcDatum as D3PieArcDatum } from 'd3-shape'; +import { animated, SpringValue, to } from '@react-spring/web'; +import { arc as d3Arc } from 'd3-shape'; import PropTypes from 'prop-types'; import composeClasses from '@mui/utils/composeClasses'; import generateUtilityClass from '@mui/utils/generateUtilityClass'; import { styled } from '@mui/material/styles'; import generateUtilityClasses from '@mui/utils/generateUtilityClasses'; -import { InteractionContext } from '../context/InteractionProvider'; -import { getIsFaded, getIsHighlighted } from '../hooks/useInteractionItemProps'; -import { HighlightScope } from '../context/HighlightProvider'; -import { PieSeriesType } from '../models/seriesType/pie'; export interface PieArcLabelClasses { /** Styles applied to the root element. */ @@ -23,7 +20,6 @@ export type PieArcLabelClassKey = keyof PieArcLabelClasses; interface PieArcLabelOwnerState { id: string; - dataIndex: number; color: string; isFaded: boolean; isHighlighted: boolean; @@ -49,54 +45,78 @@ const useUtilityClasses = (ownerState: PieArcLabelOwnerState) => { return composeClasses(slots, getPieArcLabelUtilityClass, classes); }; -const PieArcLabelRoot = styled('text', { +const PieArcLabelRoot = styled(animated.text, { name: 'MuiPieArcLabel', slot: 'Root', overridesResolver: (_, styles) => styles.root, })(({ theme }) => ({ fill: (theme.vars || theme).palette.text.primary, textAnchor: 'middle', + dominantBaseline: 'middle', })); -export type PieArcLabelProps = Omit & - React.ComponentPropsWithoutRef<'path'> & - D3PieArcDatum & { - highlightScope?: Partial; - innerRadius: PieSeriesType['innerRadius']; - outerRadius: number; - cornerRadius: PieSeriesType['cornerRadius']; +export type PieArcLabelProps = PieArcLabelOwnerState & + React.ComponentPropsWithoutRef<'text'> & { + startAngle: SpringValue; + endAngle: SpringValue; + innerRadius: SpringValue; + outerRadius: SpringValue; + cornerRadius: SpringValue; + paddingAngle: SpringValue; } & { formattedArcLabel?: string | null; }; +/** + * Helper to compute label position. + * It's not an inline function because we need it in inerpolation. + */ +const getLabelPosition = + (formattedArcLabel: string | null | undefined, variable: 'x' | 'y') => + ( + startAngle: number, + endAngle: number, + padAngle: number, + innerRadius: number, + outerRadius: number, + cornerRadius: number, + ) => { + if (!formattedArcLabel) { + return 0; + } + const [x, y] = d3Arc().cornerRadius(cornerRadius).centroid({ + padAngle, + startAngle, + endAngle, + innerRadius, + outerRadius, + })!; + if (variable === 'x') { + return x; + } + return y; + }; + export default function PieArcLabel(props: PieArcLabelProps) { const { id, - dataIndex, classes: innerClasses, color, - highlightScope, - innerRadius = 0, + startAngle, + endAngle, + paddingAngle, + innerRadius, outerRadius, - cornerRadius = 0, + cornerRadius, formattedArcLabel, + isHighlighted, + isFaded, + style, ...other } = props; - const { item } = React.useContext(InteractionContext); - - const isHighlighted = getIsHighlighted( - item, - { type: 'pie', seriesId: id, dataIndex }, - highlightScope, - ); - - const isFaded = - !isHighlighted && getIsFaded(item, { type: 'pie', seriesId: id, dataIndex }, highlightScope); - const ownerState = { id, - dataIndex, classes: innerClasses, color, isFaded, @@ -104,13 +124,22 @@ export default function PieArcLabel(props: PieArcLabelProps) { }; const classes = useUtilityClasses(ownerState); - const arcLabelPosition = formattedArcLabel - ? d3Arc() - .cornerRadius(cornerRadius) - .centroid({ ...other, innerRadius, outerRadius }) - : [0, 0]; return ( - + {formattedArcLabel} ); diff --git a/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx b/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx new file mode 100644 index 0000000000000..64e20d33e960f --- /dev/null +++ b/packages/x-charts/src/PieChart/PieArcLabelPlot.tsx @@ -0,0 +1,159 @@ +import * as React from 'react'; +import { useTransition } from '@react-spring/web'; +import { + DefaultizedPieSeriesType, + DefaultizedPieValueType, + PieSeriesType, +} from '../models/seriesType/pie'; +import { defaultLabelTransitionConfig } from './dataTransform/transition'; +import { + AnimatedObject, + ValueWithHighlight, + useTransformData, +} from './dataTransform/useTransformData'; +import PieArcLabel, { PieArcLabelProps } from './PieArcLabel'; +import { DefaultizedProps } from '../models/helpers'; + +const RATIO = 180 / Math.PI; + +function getItemLabel( + arcLabel: PieSeriesType['arcLabel'], + arcLabelMinAngle: number, + item: DefaultizedPieValueType, +) { + if (!arcLabel) { + return null; + } + const angle = (item.endAngle - item.startAngle) * RATIO; + if (angle < arcLabelMinAngle) { + return null; + } + + if (typeof arcLabel === 'string') { + return item[arcLabel]?.toString(); + } + + return arcLabel(item); +} + +export interface PieArcLabelPlotSlots { + pieArcLabel?: React.JSXElementConstructor; +} + +export interface PieArcLabelPlotSlotProps { + pieArcLabel?: Partial; +} + +export interface PieArcLabelPlotProps + extends DefaultizedProps< + Pick< + DefaultizedPieSeriesType, + | 'data' + | 'faded' + | 'highlighted' + | 'innerRadius' + | 'outerRadius' + | 'cornerRadius' + | 'paddingAngle' + | 'arcLabel' + | 'arcLabelMinAngle' + | 'id' + | 'highlightScope' + >, + 'outerRadius' + > { + /** + * Overridable component slots. + * @default {} + */ + slots?: PieArcLabelPlotSlots; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: PieArcLabelPlotSlotProps; + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation?: boolean; +} + +export function PieArcLabelPlot(props: PieArcLabelPlotProps) { + const { + slots, + slotProps, + innerRadius = 0, + outerRadius, + cornerRadius = 0, + paddingAngle = 0, + id, + highlightScope, + highlighted, + faded = { additionalRadius: -5 }, + data, + arcLabel, + arcLabelMinAngle = 0, + skipAnimation, + ...other + } = props; + + const transformedData = useTransformData({ + innerRadius, + outerRadius, + cornerRadius, + paddingAngle, + id, + highlightScope, + highlighted, + faded, + data, + }); + const transition = useTransition(transformedData, { + ...defaultLabelTransitionConfig, + immediate: skipAnimation, + }); + + if (data.length === 0) { + return null; + } + + const ArcLabel = slots?.pieArcLabel ?? PieArcLabel; + + return ( + + {transition( + ( + { + startAngle, + endAngle, + paddingAngle: pA, + innerRadius: iR, + outerRadius: oR, + cornerRadius: cR, + ...style + }, + item, + ) => { + return ( + + ); + }, + )} + + ); +} diff --git a/packages/x-charts/src/PieChart/PieArcPlot.tsx b/packages/x-charts/src/PieChart/PieArcPlot.tsx new file mode 100644 index 0000000000000..6307d3c2cc7ab --- /dev/null +++ b/packages/x-charts/src/PieChart/PieArcPlot.tsx @@ -0,0 +1,154 @@ +import * as React from 'react'; +import { useTransition } from '@react-spring/web'; +import PieArc, { PieArcProps } from './PieArc'; +import { + DefaultizedPieSeriesType, + DefaultizedPieValueType, + PieItemIdentifier, +} from '../models/seriesType/pie'; +import { defaultTransitionConfig } from './dataTransform/transition'; +import { + AnimatedObject, + ValueWithHighlight, + useTransformData, +} from './dataTransform/useTransformData'; +import { DefaultizedProps } from '../models/helpers'; + +export interface PieArcPlotSlots { + pieArc?: React.JSXElementConstructor; +} + +export interface PieArcPlotSlotProps { + pieArc?: Partial; +} + +export interface PieArcPlotProps + extends DefaultizedProps< + Pick< + DefaultizedPieSeriesType, + | 'data' + | 'faded' + | 'highlighted' + | 'innerRadius' + | 'outerRadius' + | 'cornerRadius' + | 'paddingAngle' + | 'id' + | 'highlightScope' + >, + 'outerRadius' + > { + /** + * Overridable component slots. + * @default {} + */ + slots?: PieArcPlotSlots; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: PieArcPlotSlotProps; + /** + * Callback fired when a pie item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {PieItemIdentifier} pieItemIdentifier The pie item identifier. + * @param {DefaultizedPieValueType} item The pie item. + */ + onClick?: ( + event: React.MouseEvent, + pieItemIdentifier: PieItemIdentifier, + item: DefaultizedPieValueType, + ) => void; + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation?: boolean; +} + +export function PieArcPlot(props: PieArcPlotProps) { + const { + slots, + slotProps, + innerRadius = 0, + outerRadius, + cornerRadius = 0, + paddingAngle = 0, + id, + highlightScope, + highlighted, + faded = { additionalRadius: -5 }, + data, + onClick, + skipAnimation, + ...other + } = props; + + const transformedData = useTransformData({ + innerRadius, + outerRadius, + cornerRadius, + paddingAngle, + id, + highlightScope, + highlighted, + faded, + data, + }); + const transition = useTransition(transformedData, { + ...defaultTransitionConfig, + immediate: skipAnimation, + }); + + if (data.length === 0) { + return null; + } + + const Arc = slots?.pieArc ?? PieArc; + + return ( + + {transition( + ( + { + startAngle, + endAngle, + paddingAngle: pA, + innerRadius: iR, + outerRadius: oR, + cornerRadius: cR, + ...style + }, + item, + _, + index, + ) => { + return ( + { + onClick(event, { type: 'pie', seriesId: id, dataIndex: index }, item); + }) + } + {...slotProps?.pieArc} + /> + ); + }, + )} + + ); +} diff --git a/packages/x-charts/src/PieChart/PieChart.tsx b/packages/x-charts/src/PieChart/PieChart.tsx index 03a889fd0d890..e73ab4f30b59e 100644 --- a/packages/x-charts/src/PieChart/PieChart.tsx +++ b/packages/x-charts/src/PieChart/PieChart.tsx @@ -8,44 +8,68 @@ import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis/ChartsAxis'; import { PieSeriesType } from '../models/seriesType'; import { MakeOptional } from '../models/helpers'; import { DEFAULT_X_AXIS_KEY } from '../constants'; -import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip'; +import { + ChartsTooltip, + ChartsTooltipProps, + ChartsTooltipSlotProps, + ChartsTooltipSlots, +} from '../ChartsTooltip'; import { ChartsLegend, ChartsLegendProps, - ChartsLegendSlotComponentProps, - ChartsLegendSlotsComponent, + ChartsLegendSlotProps, + ChartsLegendSlots, } from '../ChartsLegend'; import { ChartsAxisHighlight, ChartsAxisHighlightProps } from '../ChartsAxisHighlight'; -import { PiePlot, PiePlotSlotComponentProps, PiePlotSlotsComponent } from './PiePlot'; +import { PiePlot, PiePlotProps, PiePlotSlotProps, PiePlotSlots } from './PiePlot'; import { PieValueType } from '../models/seriesType/pie'; -import { ChartsAxisSlotsComponent, ChartsAxisSlotComponentProps } from '../models/axis'; +import { ChartsAxisSlots, ChartsAxisSlotProps } from '../models/axis'; + +export interface PieChartSlots + extends ChartsAxisSlots, + PiePlotSlots, + ChartsLegendSlots, + ChartsTooltipSlots {} -export interface PieChartSlotsComponent - extends ChartsAxisSlotsComponent, - PiePlotSlotsComponent, - ChartsLegendSlotsComponent {} -export interface PieChartSlotComponentProps - extends ChartsAxisSlotComponentProps, - PiePlotSlotComponentProps, - ChartsLegendSlotComponentProps {} +export interface PieChartSlotProps + extends ChartsAxisSlotProps, + PiePlotSlotProps, + ChartsLegendSlotProps, + ChartsTooltipSlotProps {} export interface PieChartProps extends Omit, - Omit { + Omit, + Pick { series: MakeOptional>, 'type'>[]; tooltip?: ChartsTooltipProps; axisHighlight?: ChartsAxisHighlightProps; + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend?: ChartsLegendProps; + onClick?: PiePlotProps['onClick']; - slots?: PieChartSlotsComponent; + slots?: PieChartSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: PieChartSlotComponentProps; + slotProps?: PieChartSlotProps; } const defaultMargin = { top: 5, bottom: 5, left: 5, right: 100 }; + +/** + * Demos: + * + * - [Pie](https://mui.com/x/react-charts/pie/) + * - [Pie demonstration](https://mui.com/x/react-charts/pie-demo/) + * + * API: + * + * - [PieChart API](https://mui.com/x/api/charts/pie-chart/) + */ function PieChart(props: PieChartProps) { const { xAxis, @@ -58,6 +82,7 @@ function PieChart(props: PieChartProps) { sx, tooltip = { trigger: 'item' }, axisHighlight = { x: 'none', y: 'none' }, + skipAnimation, legend = { direction: 'column', position: { vertical: 'middle', horizontal: 'right' } }, topAxis = null, leftAxis = null, @@ -66,6 +91,7 @@ function PieChart(props: PieChartProps) { children, slots, slotProps, + onClick, } = props; const margin = { ...defaultMargin, ...marginProps }; @@ -101,7 +127,12 @@ function PieChart(props: PieChartProps) { slots={slots} slotProps={slotProps} /> - + @@ -126,18 +157,26 @@ PieChart.propTypes = { */ bottomAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -151,9 +190,21 @@ PieChart.propTypes = { * Color palette used to colorize multiple series. */ colors: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.func]), + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset: PropTypes.arrayOf(PropTypes.object), desc: PropTypes.string, + /** + * If `true`, the charts will not listen to the mouse move event. + * It might break interactive features, but will improve performance. + * @default false + */ disableAxisListener: PropTypes.bool, + /** + * The height of the chart in px. If not defined, it takes the height of the parent element. + * @default undefined + */ height: PropTypes.number, /** * Indicate which axis to display the left of the charts. @@ -162,18 +213,26 @@ PieChart.propTypes = { */ leftAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -181,30 +240,33 @@ PieChart.propTypes = { }), PropTypes.string, ]), + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend: PropTypes.shape({ classes: PropTypes.object, direction: PropTypes.oneOf(['column', 'row']), hidden: PropTypes.bool, - itemWidth: PropTypes.number, - markSize: PropTypes.number, - offset: PropTypes.shape({ - x: PropTypes.number, - y: PropTypes.number, - }), position: PropTypes.shape({ horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, }), slotProps: PropTypes.object, slots: PropTypes.object, - spacing: PropTypes.number, }), + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ margin: PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, right: PropTypes.number, top: PropTypes.number, }), + onClick: PropTypes.func, /** * Indicate which axis to display the right of the charts. * Can be a string (the id of the axis) or an object `ChartsYAxisProps`. @@ -212,18 +274,26 @@ PieChart.propTypes = { */ rightAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -253,15 +323,19 @@ PieChart.propTypes = { endAngle: PropTypes.number, faded: PropTypes.shape({ additionalRadius: PropTypes.number, + color: PropTypes.string, cornerRadius: PropTypes.number, innerRadius: PropTypes.number, outerRadius: PropTypes.number, + paddingAngle: PropTypes.number, }), highlighted: PropTypes.shape({ additionalRadius: PropTypes.number, + color: PropTypes.string, cornerRadius: PropTypes.number, innerRadius: PropTypes.number, outerRadius: PropTypes.number, + paddingAngle: PropTypes.number, }), highlightScope: PropTypes.shape({ faded: PropTypes.oneOf(['global', 'none', 'series']), @@ -280,6 +354,11 @@ PieChart.propTypes = { valueFormatter: PropTypes.func, }), ).isRequired, + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation: PropTypes.bool, /** * The props used for each component slot. * @default {} @@ -296,6 +375,8 @@ PieChart.propTypes = { axisContent: PropTypes.elementType, classes: PropTypes.object, itemContent: PropTypes.elementType, + slotProps: PropTypes.object, + slots: PropTypes.object, trigger: PropTypes.oneOf(['axis', 'item', 'none']), }), /** @@ -305,18 +386,26 @@ PieChart.propTypes = { */ topAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -330,7 +419,15 @@ PieChart.propTypes = { x: PropTypes.number, y: PropTypes.number, }), + /** + * The width of the chart in px. If not defined, it takes the width of the parent element. + * @default undefined + */ width: PropTypes.number, + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_X_AXIS_KEY`. + */ xAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -344,6 +441,7 @@ PieChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -352,6 +450,13 @@ PieChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -359,6 +464,10 @@ PieChart.propTypes = { valueFormatter: PropTypes.func, }), ), + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_Y_AXIS_KEY`. + */ yAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -372,6 +481,7 @@ PieChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -380,6 +490,13 @@ PieChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/PieChart/PiePlot.tsx b/packages/x-charts/src/PieChart/PiePlot.tsx index c8a1a11e8662e..d97e037995525 100644 --- a/packages/x-charts/src/PieChart/PiePlot.tsx +++ b/packages/x-charts/src/PieChart/PiePlot.tsx @@ -1,58 +1,45 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { SeriesContext } from '../context/SeriesContextProvider'; -import PieArc, { PieArcProps } from './PieArc'; -import PieArcLabel, { PieArcLabelProps } from './PieArcLabel'; import { DrawingContext } from '../context/DrawingProvider'; -import { DefaultizedPieValueType, PieSeriesType } from '../models/seriesType/pie'; +import { PieArcPlot, PieArcPlotProps, PieArcPlotSlotProps, PieArcPlotSlots } from './PieArcPlot'; +import { PieArcLabelPlotSlots, PieArcLabelPlotSlotProps, PieArcLabelPlot } from './PieArcLabelPlot'; -const RATIO = 180 / Math.PI; +export interface PiePlotSlots extends PieArcPlotSlots, PieArcLabelPlotSlots {} -function getItemLabel( - arcLabel: PieSeriesType['arcLabel'], - arcLabelMinAngle: number, - item: DefaultizedPieValueType, -) { - if (!arcLabel) { - return null; - } - const angle = (item.endAngle - item.startAngle) * RATIO; - if (angle < arcLabelMinAngle) { - return null; - } - - if (typeof arcLabel === 'string') { - return item[arcLabel]?.toString(); - } - - return arcLabel(item); -} - -export interface PiePlotSlotsComponent { - pieArc?: React.JSXElementConstructor; - pieArcLabel?: React.JSXElementConstructor; -} +export interface PiePlotSlotProps extends PieArcPlotSlotProps, PieArcLabelPlotSlotProps {} -export interface PiePlotSlotComponentProps { - pieArc?: Partial; - pieArcLabel?: Partial; -} - -export interface PiePlotProps { +export interface PiePlotProps extends Pick { /** * Overridable component slots. * @default {} */ - slots?: PiePlotSlotsComponent; + slots?: PiePlotSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: PiePlotSlotComponentProps; + slotProps?: PiePlotSlotProps; + /** + * Callback fired when a pie item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {PieItemIdentifier} pieItemIdentifier The pie item identifier. + * @param {DefaultizedPieValueType} item The pie item. + */ } +/** + * Demos: + * + * - [Pie](https://mui.com/x/react-charts/pie/) + * - [Pie demonstration](https://mui.com/x/react-charts/pie-demo/) + * + * API: + * + * - [PiePlot API](https://mui.com/x/api/charts/pie-plot/) + */ function PiePlot(props: PiePlotProps) { - const { slots, slotProps } = props; + const { skipAnimation, slots, slotProps, onClick } = props; const seriesData = React.useContext(SeriesContext).pie; const { left, top, width, height } = React.useContext(DrawingContext); @@ -67,8 +54,6 @@ function PiePlot(props: PiePlotProps) { }; const { series, seriesOrder } = seriesData; - const Arc = slots?.pieArc ?? PieArc; - const ArcLabel = slots?.pieArcLabel ?? PieArcLabel; return ( {seriesOrder.map((seriesId) => { @@ -76,13 +61,13 @@ function PiePlot(props: PiePlotProps) { innerRadius, outerRadius, cornerRadius, - arcLabel, - arcLabelMinAngle = 0, + paddingAngle, data, cx, cy, highlighted, faded, + highlightScope, } = series[seriesId]; return ( - - {data.map((item, index) => { - return ( - - ); - })} - {data.map((item, index) => { - return ( - - ); - })} - + + + ); + })} + {seriesOrder.map((seriesId) => { + const { + innerRadius, + outerRadius, + cornerRadius, + paddingAngle, + arcLabel, + arcLabelMinAngle, + data, + cx, + cy, + highlightScope, + } = series[seriesId]; + return ( + + ); })} @@ -140,6 +138,18 @@ PiePlot.propTypes = { // | These PropTypes are generated from the TypeScript type definitions | // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- + /** + * Callback fired when a pie item is clicked. + * @param {React.MouseEvent} event The event source of the callback. + * @param {PieItemIdentifier} pieItemIdentifier The pie item identifier. + * @param {DefaultizedPieValueType} item The pie item. + */ + onClick: PropTypes.func, + /** + * If `true`, animations are skiped. + * @default false + */ + skipAnimation: PropTypes.bool, /** * The props used for each component slot. * @default {} diff --git a/packages/x-charts/src/PieChart/dataTransform/transition.ts b/packages/x-charts/src/PieChart/dataTransform/transition.ts new file mode 100644 index 0000000000000..8a0ac1457f25f --- /dev/null +++ b/packages/x-charts/src/PieChart/dataTransform/transition.ts @@ -0,0 +1,103 @@ +import { UseTransitionProps } from '@react-spring/web'; +import { ValueWithHighlight } from './useTransformData'; + +export const defaultTransitionConfig: UseTransitionProps = { + keys: (item) => item.id, + from: ({ + innerRadius, + outerRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + color, + isFaded, + }) => ({ + innerRadius, + outerRadius: (innerRadius + outerRadius) / 2, + cornerRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + paddingAngle, + fill: color, + opacity: isFaded ? 0.3 : 1, + }), + leave: ({ innerRadius, startAngle, endAngle }) => ({ + innerRadius, + outerRadius: innerRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + }), + enter: ({ innerRadius, outerRadius, startAngle, endAngle }) => ({ + innerRadius, + outerRadius, + + startAngle, + endAngle, + }), + update: ({ + innerRadius, + outerRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + color, + isFaded, + }) => ({ + innerRadius, + outerRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + fill: color, + opacity: isFaded ? 0.3 : 1, + }), + config: { + tension: 120, + friction: 14, + clamp: true, + }, +}; + +export const defaultLabelTransitionConfig: UseTransitionProps = { + keys: (item) => item.id, + from: ({ innerRadius, outerRadius, cornerRadius, startAngle, endAngle, paddingAngle }) => ({ + innerRadius, + outerRadius: (innerRadius + outerRadius) / 2, + cornerRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + paddingAngle, + opacity: 0, + }), + leave: ({ innerRadius, startAngle, endAngle }) => ({ + innerRadius, + outerRadius: innerRadius, + startAngle: (startAngle + endAngle) / 2, + endAngle: (startAngle + endAngle) / 2, + opacity: 0, + }), + enter: ({ innerRadius, outerRadius, startAngle, endAngle }) => ({ + innerRadius, + outerRadius, + startAngle, + endAngle, + opacity: 1, + }), + update: ({ innerRadius, outerRadius, cornerRadius, startAngle, endAngle, paddingAngle }) => ({ + innerRadius, + outerRadius, + cornerRadius, + startAngle, + endAngle, + paddingAngle, + opacity: 1, + }), + config: { + tension: 120, + friction: 14, + clamp: true, + }, +}; diff --git a/packages/x-charts/src/PieChart/dataTransform/useTransformData.ts b/packages/x-charts/src/PieChart/dataTransform/useTransformData.ts new file mode 100644 index 0000000000000..ce0802519f322 --- /dev/null +++ b/packages/x-charts/src/PieChart/dataTransform/useTransformData.ts @@ -0,0 +1,113 @@ +import * as React from 'react'; +import { InteractionContext } from '../../context/InteractionProvider'; +import { DefaultizedPieSeriesType, DefaultizedPieValueType } from '../../models/seriesType/pie'; +import { getIsHighlighted, getIsFaded } from '../../hooks/useInteractionItemProps'; +import { DefaultizedProps } from '../../models/helpers'; + +export interface AnimatedObject { + innerRadius: number; + outerRadius: number; + cornerRadius: number; + startAngle: number; + endAngle: number; + paddingAngle: number; +} + +export interface ValueWithHighlight extends DefaultizedPieValueType, AnimatedObject { + isFaded: boolean; + isHighlighted: boolean; +} + +export function useTransformData( + series: DefaultizedProps< + Pick< + DefaultizedPieSeriesType, + | 'innerRadius' + | 'outerRadius' + | 'cornerRadius' + | 'paddingAngle' + | 'id' + | 'highlightScope' + | 'highlighted' + | 'faded' + | 'data' + >, + 'outerRadius' + >, +) { + const { + id: seriesId, + highlightScope, + data, + faded, + highlighted, + paddingAngle: basePaddingAngle = 0, + innerRadius: baseInnerRadius = 0, + outerRadius: baseOuterRadius, + cornerRadius: baseCornerRadius = 0, + } = series; + + const { item: highlightedItem } = React.useContext(InteractionContext); + + const getHighlightStatus = React.useCallback( + (dataIndex: number) => { + const isHighlighted = getIsHighlighted( + highlightedItem, + { type: 'pie', seriesId, dataIndex }, + highlightScope, + ); + const isFaded = + !isHighlighted && + getIsFaded(highlightedItem, { type: 'pie', seriesId, dataIndex }, highlightScope); + + return { isHighlighted, isFaded }; + }, + [highlightScope, highlightedItem, seriesId], + ); + + const dataWithHighlight: ValueWithHighlight[] = React.useMemo( + () => + data.map((item, itemIndex) => { + const { isHighlighted, isFaded } = getHighlightStatus(itemIndex); + + const attibuesOverride = { + additionalRadius: 0, + ...((isFaded && faded) || (isHighlighted && highlighted) || {}), + }; + const paddingAngle = Math.max( + 0, + (Math.PI * (attibuesOverride.paddingAngle ?? basePaddingAngle)) / 180, + ); + const innerRadius = Math.max(0, attibuesOverride.innerRadius ?? baseInnerRadius); + + const outerRadius = Math.max( + 0, + attibuesOverride.outerRadius ?? baseOuterRadius + attibuesOverride.additionalRadius, + ); + const cornerRadius = attibuesOverride.cornerRadius ?? baseCornerRadius; + + return { + ...item, + ...attibuesOverride, + isFaded, + isHighlighted, + paddingAngle, + innerRadius, + outerRadius, + cornerRadius, + }; + }), + [ + baseCornerRadius, + baseInnerRadius, + baseOuterRadius, + basePaddingAngle, + data, + faded, + getHighlightStatus, + highlighted, + ], + ); + + return dataWithHighlight; +} diff --git a/packages/x-charts/src/ResponsiveChartContainer/index.tsx b/packages/x-charts/src/ResponsiveChartContainer/index.tsx index f4a4c5472c633..7c3775d890bd7 100644 --- a/packages/x-charts/src/ResponsiveChartContainer/index.tsx +++ b/packages/x-charts/src/ResponsiveChartContainer/index.tsx @@ -3,7 +3,6 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; import ownerWindow from '@mui/utils/ownerWindow'; import { styled } from '@mui/material/styles'; import { ChartContainer, ChartContainerProps } from '../ChartContainer'; -import { MakeOptional } from '../models/helpers'; const useChartDimensions = ( inWidth?: number, @@ -90,7 +89,19 @@ const useChartDimensions = ( return [rootRef, inWidth ?? width, inHeight ?? height]; }; -export type ResponsiveChartContainerProps = MakeOptional; +export interface ResponsiveChartContainerProps + extends Omit { + /** + * The width of the chart in px. If not defined, it takes the width of the parent element. + * @default undefined + */ + width?: number; + /** + * The height of the chart in px. If not defined, it takes the height of the parent element. + * @default undefined + */ + height?: number; +} const ResizableContainer = styled('div', { name: 'MuiResponsiveChart', @@ -120,7 +131,9 @@ export const ResponsiveChartContainer = React.forwardRef(function ResponsiveChar return ( - + {width && height ? ( + + ) : null} ); }); diff --git a/packages/x-charts/src/ScatterChart/Scatter.tsx b/packages/x-charts/src/ScatterChart/Scatter.tsx index 829d67cf26ef7..fd3db8d2e8852 100644 --- a/packages/x-charts/src/ScatterChart/Scatter.tsx +++ b/packages/x-charts/src/ScatterChart/Scatter.tsx @@ -18,6 +18,16 @@ export interface ScatterProps { color: string; } +/** + * Demos: + * + * - [Scatter](https://mui.com/x/react-charts/scatter/) + * - [Scatter demonstration](https://mui.com/x/react-charts/scatter-demo/) + * + * API: + * + * - [Scatter API](https://mui.com/x/api/charts/scatter/) + */ function Scatter(props: ScatterProps) { const { series, xScale, yScale, color, markerSize } = props; diff --git a/packages/x-charts/src/ScatterChart/ScatterChart.tsx b/packages/x-charts/src/ScatterChart/ScatterChart.tsx index ad627a5f4a3b3..3299920fd05ed 100644 --- a/packages/x-charts/src/ScatterChart/ScatterChart.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterChart.tsx @@ -1,10 +1,6 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { - ScatterPlot, - ScatterPlotSlotComponentProps, - ScatterPlotSlotsComponent, -} from './ScatterPlot'; +import { ScatterPlot, ScatterPlotSlotProps, ScatterPlotSlots } from './ScatterPlot'; import { ResponsiveChartContainer, ResponsiveChartContainerProps, @@ -12,24 +8,31 @@ import { import { ChartsAxis, ChartsAxisProps } from '../ChartsAxis'; import { ScatterSeriesType } from '../models/seriesType/scatter'; import { MakeOptional } from '../models/helpers'; -import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip'; +import { + ChartsTooltip, + ChartsTooltipProps, + ChartsTooltipSlotProps, + ChartsTooltipSlots, +} from '../ChartsTooltip'; import { ChartsLegend, ChartsLegendProps, - ChartsLegendSlotComponentProps, - ChartsLegendSlotsComponent, + ChartsLegendSlotProps, + ChartsLegendSlots, } from '../ChartsLegend'; import { ChartsAxisHighlight, ChartsAxisHighlightProps } from '../ChartsAxisHighlight'; -import { ChartsAxisSlotsComponent, ChartsAxisSlotComponentProps } from '../models/axis'; +import { ChartsAxisSlots, ChartsAxisSlotProps } from '../models/axis'; -export interface ScatterChartSlotsComponent - extends ChartsAxisSlotsComponent, - ScatterPlotSlotsComponent, - ChartsLegendSlotsComponent {} -export interface ScatterChartSlotComponentProps - extends ChartsAxisSlotComponentProps, - ScatterPlotSlotComponentProps, - ChartsLegendSlotComponentProps {} +export interface ScatterChartSlots + extends ChartsAxisSlots, + ScatterPlotSlots, + ChartsLegendSlots, + ChartsTooltipSlots {} +export interface ScatterChartSlotProps + extends ChartsAxisSlotProps, + ScatterPlotSlotProps, + ChartsLegendSlotProps, + ChartsTooltipSlotProps {} export interface ScatterChartProps extends Omit, @@ -37,19 +40,32 @@ export interface ScatterChartProps series: MakeOptional[]; tooltip?: ChartsTooltipProps; axisHighlight?: ChartsAxisHighlightProps; + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend?: ChartsLegendProps; /** * Overridable component slots. * @default {} */ - slots?: ScatterChartSlotsComponent; + slots?: ScatterChartSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: ScatterChartSlotComponentProps; + slotProps?: ScatterChartSlotProps; } +/** + * Demos: + * + * - [Scatter](https://mui.com/x/react-charts/scatter/) + * - [Scatter demonstration](https://mui.com/x/react-charts/scatter-demo/) + * + * API: + * + * - [ScatterChart API](https://mui.com/x/api/charts/scatter-chart/) + */ const ScatterChart = React.forwardRef(function ScatterChart(props: ScatterChartProps, ref) { const { xAxis, @@ -116,18 +132,26 @@ ScatterChart.propTypes = { */ bottomAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -141,9 +165,21 @@ ScatterChart.propTypes = { * Color palette used to colorize multiple series. */ colors: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.string), PropTypes.func]), + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset: PropTypes.arrayOf(PropTypes.object), desc: PropTypes.string, + /** + * If `true`, the charts will not listen to the mouse move event. + * It might break interactive features, but will improve performance. + * @default false + */ disableAxisListener: PropTypes.bool, + /** + * The height of the chart in px. If not defined, it takes the height of the parent element. + * @default undefined + */ height: PropTypes.number, /** * Indicate which axis to display the left of the charts. @@ -152,18 +188,26 @@ ScatterChart.propTypes = { */ leftAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -171,24 +215,26 @@ ScatterChart.propTypes = { }), PropTypes.string, ]), + /** + * @deprecated Consider using `slotProps.legend` instead. + */ legend: PropTypes.shape({ classes: PropTypes.object, direction: PropTypes.oneOf(['column', 'row']), hidden: PropTypes.bool, - itemWidth: PropTypes.number, - markSize: PropTypes.number, - offset: PropTypes.shape({ - x: PropTypes.number, - y: PropTypes.number, - }), position: PropTypes.shape({ horizontal: PropTypes.oneOf(['left', 'middle', 'right']).isRequired, vertical: PropTypes.oneOf(['bottom', 'middle', 'top']).isRequired, }), slotProps: PropTypes.object, slots: PropTypes.object, - spacing: PropTypes.number, }), + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ margin: PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, @@ -202,18 +248,26 @@ ScatterChart.propTypes = { */ rightAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['left', 'right']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -264,6 +318,8 @@ ScatterChart.propTypes = { axisContent: PropTypes.elementType, classes: PropTypes.object, itemContent: PropTypes.elementType, + slotProps: PropTypes.object, + slots: PropTypes.object, trigger: PropTypes.oneOf(['axis', 'item', 'none']), }), /** @@ -273,18 +329,26 @@ ScatterChart.propTypes = { */ topAxis: PropTypes.oneOfType([ PropTypes.shape({ - axisId: PropTypes.string.isRequired, + axisId: PropTypes.string, classes: PropTypes.object, disableLine: PropTypes.bool, disableTicks: PropTypes.bool, fill: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, position: PropTypes.oneOf(['bottom', 'top']), slotProps: PropTypes.object, slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -298,7 +362,15 @@ ScatterChart.propTypes = { x: PropTypes.number, y: PropTypes.number, }), + /** + * The width of the chart in px. If not defined, it takes the width of the parent element. + * @default undefined + */ width: PropTypes.number, + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_X_AXIS_KEY`. + */ xAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -312,6 +384,7 @@ ScatterChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -320,6 +393,13 @@ ScatterChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -327,6 +407,10 @@ ScatterChart.propTypes = { valueFormatter: PropTypes.func, }), ), + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_Y_AXIS_KEY`. + */ yAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -340,6 +424,7 @@ ScatterChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -348,6 +433,13 @@ ScatterChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx index 4aa446251fa16..2c27f9d15b622 100644 --- a/packages/x-charts/src/ScatterChart/ScatterPlot.tsx +++ b/packages/x-charts/src/ScatterChart/ScatterPlot.tsx @@ -4,11 +4,11 @@ import { Scatter, ScatterProps } from './Scatter'; import { SeriesContext } from '../context/SeriesContextProvider'; import { CartesianContext } from '../context/CartesianContextProvider'; -export interface ScatterPlotSlotsComponent { +export interface ScatterPlotSlots { scatter?: React.JSXElementConstructor; } -export interface ScatterPlotSlotComponentProps { +export interface ScatterPlotSlotProps { scatter?: Partial; } @@ -17,14 +17,24 @@ export interface ScatterPlotProps { * Overridable component slots. * @default {} */ - slots?: ScatterPlotSlotsComponent; + slots?: ScatterPlotSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: ScatterPlotSlotComponentProps; + slotProps?: ScatterPlotSlotProps; } +/** + * Demos: + * + * - [Scatter](https://mui.com/x/react-charts/scatter/) + * - [Scatter demonstration](https://mui.com/x/react-charts/scatter-demo/) + * + * API: + * + * - [ScatterPlot API](https://mui.com/x/api/charts/scatter-plot/) + */ function ScatterPlot(props: ScatterPlotProps) { const { slots, slotProps } = props; const seriesData = React.useContext(SeriesContext).scatter; diff --git a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx index 3bf664374ab02..8531c6d2ef9d7 100644 --- a/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx +++ b/packages/x-charts/src/SparkLineChart/SparkLineChart.tsx @@ -7,32 +7,36 @@ import { ResponsiveChartContainerProps, } from '../ResponsiveChartContainer'; import { DEFAULT_X_AXIS_KEY } from '../constants'; -import { ChartsTooltip, ChartsTooltipProps } from '../ChartsTooltip'; +import { + ChartsTooltip, + ChartsTooltipProps, + ChartsTooltipSlotProps, + ChartsTooltipSlots, +} from '../ChartsTooltip'; import { ChartsAxisHighlight, ChartsAxisHighlightProps } from '../ChartsAxisHighlight'; import { AxisConfig } from '../models/axis'; import { MakeOptional } from '../models/helpers'; import { LineSeriesType } from '../models/seriesType/line'; -import { AreaPlotSlotsComponent, AreaPlotSlotComponentProps } from '../LineChart/AreaPlot'; -import { LinePlotSlotsComponent, LinePlotSlotComponentProps } from '../LineChart/LinePlot'; -import { MarkPlotSlotsComponent, MarkPlotSlotComponentProps } from '../LineChart/MarkPlot'; -import { - LineHighlightPlotSlotsComponent, - LineHighlightPlotSlotComponentProps, -} from '../LineChart/LineHighlightPlot'; -import { BarPlotSlotsComponent, BarPlotSlotComponentProps } from '../BarChart/BarPlot'; +import { AreaPlotSlots, AreaPlotSlotProps } from '../LineChart/AreaPlot'; +import { LinePlotSlots, LinePlotSlotProps } from '../LineChart/LinePlot'; +import { MarkPlotSlots, MarkPlotSlotProps } from '../LineChart/MarkPlot'; +import { LineHighlightPlotSlots, LineHighlightPlotSlotProps } from '../LineChart/LineHighlightPlot'; +import { BarPlotSlots, BarPlotSlotProps } from '../BarChart/BarPlot'; -export interface SparkLineChartSlotsComponent - extends AreaPlotSlotsComponent, - LinePlotSlotsComponent, - MarkPlotSlotsComponent, - LineHighlightPlotSlotsComponent, - BarPlotSlotsComponent {} -export interface SparkLineChartSlotComponentProps - extends AreaPlotSlotComponentProps, - LinePlotSlotComponentProps, - MarkPlotSlotComponentProps, - LineHighlightPlotSlotComponentProps, - BarPlotSlotComponentProps {} +export interface SparkLineChartSlots + extends AreaPlotSlots, + LinePlotSlots, + MarkPlotSlots, + LineHighlightPlotSlots, + BarPlotSlots, + ChartsTooltipSlots {} +export interface SparkLineChartSlotProps + extends AreaPlotSlotProps, + LinePlotSlotProps, + MarkPlotSlotProps, + LineHighlightPlotSlotProps, + BarPlotSlotProps, + ChartsTooltipSlotProps {} export interface SparkLineChartProps extends Omit { @@ -84,12 +88,12 @@ export interface SparkLineChartProps * Overridable component slots. * @default {} */ - slots?: SparkLineChartSlotsComponent; + slots?: SparkLineChartSlots; /** * The props used for each component slot. * @default {} */ - slotProps?: SparkLineChartSlotComponentProps; + slotProps?: SparkLineChartSlotProps; } const SPARKLINE_DEFAULT_MARGIN = { @@ -99,6 +103,15 @@ const SPARKLINE_DEFAULT_MARGIN = { right: 5, }; +/** + * Demos: + * + * - [SparkLine](https://mui.com/x/react-charts/sparkline/) + * + * API: + * + * - [SparkLineChart API](https://mui.com/x/api/charts/spark-line-chart/) + */ const SparkLineChart = React.forwardRef(function SparkLineChart(props: SparkLineChartProps, ref) { const { xAxis, @@ -172,7 +185,7 @@ const SparkLineChart = React.forwardRef(function SparkLineChart(props: SparkLine )} - {showTooltip && } + {showTooltip && } {children} @@ -217,10 +230,28 @@ SparkLineChart.propTypes = { * Data to plot. */ data: PropTypes.arrayOf(PropTypes.number).isRequired, + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset: PropTypes.arrayOf(PropTypes.object), desc: PropTypes.string, + /** + * If `true`, the charts will not listen to the mouse move event. + * It might break interactive features, but will improve performance. + * @default false + */ disableAxisListener: PropTypes.bool, + /** + * The height of the chart in px. If not defined, it takes the height of the parent element. + * @default undefined + */ height: PropTypes.number, + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ margin: PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, @@ -264,6 +295,8 @@ SparkLineChart.propTypes = { axisContent: PropTypes.elementType, classes: PropTypes.object, itemContent: PropTypes.elementType, + slotProps: PropTypes.object, + slots: PropTypes.object, trigger: PropTypes.oneOf(['axis', 'item', 'none']), }), /** @@ -278,6 +311,10 @@ SparkLineChart.propTypes = { x: PropTypes.number, y: PropTypes.number, }), + /** + * The width of the chart in px. If not defined, it takes the width of the parent element. + * @default undefined + */ width: PropTypes.number, /** * The xAxis configuration. @@ -295,6 +332,7 @@ SparkLineChart.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -303,6 +341,9 @@ SparkLineChart.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.array, PropTypes.func]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/constants.ts b/packages/x-charts/src/constants.ts index bb8cb0c694510..a198b2fa8f8ff 100644 --- a/packages/x-charts/src/constants.ts +++ b/packages/x-charts/src/constants.ts @@ -1,7 +1,7 @@ export const DEFAULT_X_AXIS_KEY = 'DEFAULT_X_AXIS_KEY'; export const DEFAULT_Y_AXIS_KEY = 'DEFAULT_Y_AXIS_KEY'; export const DEFAULT_MARGINS = { - top: 100, + top: 50, bottom: 50, left: 50, right: 50, diff --git a/packages/x-charts/src/context/CartesianContextProvider.tsx b/packages/x-charts/src/context/CartesianContextProvider.tsx index acbf9c9dc7c23..3d826e060ce2d 100644 --- a/packages/x-charts/src/context/CartesianContextProvider.tsx +++ b/packages/x-charts/src/context/CartesianContextProvider.tsx @@ -26,11 +26,22 @@ import { ExtremumGetterResult, } from '../models/seriesType/config'; import { MakeOptional } from '../models/helpers'; -import { getTicksNumber } from '../hooks/useTicks'; +import { getTickNumber } from '../hooks/useTicks'; export type CartesianContextProviderProps = { + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_X_AXIS_KEY`. + */ xAxis?: MakeOptional[]; + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_Y_AXIS_KEY`. + */ yAxis?: MakeOptional[]; + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset?: DatasetType; children: React.ReactNode; }; @@ -57,19 +68,33 @@ type DefaultizedAxisConfig = { export const CartesianContext = React.createContext<{ /** - * Mapping from axis key to scaling function + * Mapping from x-axis key to scaling configuration. */ xAxis: { DEFAULT_X_AXIS_KEY: AxisDefaultized; } & DefaultizedAxisConfig; + /** + * Mapping from y-axis key to scaling configuration. + */ yAxis: { DEFAULT_X_AXIS_KEY: AxisDefaultized; } & DefaultizedAxisConfig; + /** + * The x-axes IDs sorted by order they got provided. + */ xAxisIds: string[]; + /** + * The y-axes IDs sorted by order they got provided. + */ yAxisIds: string[]; // @ts-ignore }>({ xAxis: {}, yAxis: {}, xAxisIds: [], yAxisIds: [] }); +/** + * API: + * + * - [CartesianContextProvider API](https://mui.com/x/api/charts/cartesian-context-provider/) + */ function CartesianContextProvider({ xAxis: inXAxis, yAxis: inYAxis, @@ -183,14 +208,14 @@ function CartesianContextProvider({ scale: scaleBand(axis.data!, range) .paddingInner(categoryGapRatio) .paddingOuter(categoryGapRatio / 2), - ticksNumber: axis.data!.length, + tickNumber: axis.data!.length, }; } if (isPointScaleConfig(axis)) { completedXAxis[axis.id] = { ...axis, scale: scalePoint(axis.data!, range), - ticksNumber: axis.data!.length, + tickNumber: axis.data!.length, }; } if (axis.scaleType === 'band' || axis.scaleType === 'point') { @@ -201,9 +226,9 @@ function CartesianContextProvider({ const scaleType = axis.scaleType ?? 'linear'; const extremums = [axis.min ?? minData, axis.max ?? maxData]; - const ticksNumber = getTicksNumber({ ...axis, range, domain: extremums }); + const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const niceScale = getScale(scaleType, extremums, range).nice(ticksNumber); + const niceScale = getScale(scaleType, extremums, range).nice(tickNumber); const niceDomain = niceScale.domain(); const domain = [axis.min ?? niceDomain[0], axis.max ?? niceDomain[1]]; @@ -211,7 +236,7 @@ function CartesianContextProvider({ ...axis, scaleType, scale: niceScale.domain(domain), - ticksNumber, + tickNumber, } as AxisDefaultized; }); @@ -237,14 +262,14 @@ function CartesianContextProvider({ scale: scaleBand(axis.data!, [range[1], range[0]]) .paddingInner(categoryGapRatio) .paddingOuter(categoryGapRatio / 2), - ticksNumber: axis.data!.length, + tickNumber: axis.data!.length, }; } if (isPointScaleConfig(axis)) { completedYAxis[axis.id] = { ...axis, scale: scalePoint(axis.data!, [range[1], range[0]]), - ticksNumber: axis.data!.length, + tickNumber: axis.data!.length, }; } if (axis.scaleType === 'band' || axis.scaleType === 'point') { @@ -255,9 +280,9 @@ function CartesianContextProvider({ const scaleType = axis.scaleType ?? 'linear'; const extremums = [axis.min ?? minData, axis.max ?? maxData]; - const ticksNumber = getTicksNumber({ ...axis, range, domain: extremums }); + const tickNumber = getTickNumber({ ...axis, range, domain: extremums }); - const niceScale = getScale(scaleType, extremums, range).nice(ticksNumber); + const niceScale = getScale(scaleType, extremums, range).nice(tickNumber); const niceDomain = niceScale.domain(); const domain = [axis.min ?? niceDomain[0], axis.max ?? niceDomain[1]]; @@ -265,7 +290,7 @@ function CartesianContextProvider({ ...axis, scaleType, scale: niceScale.domain(domain), - ticksNumber, + tickNumber, } as AxisDefaultized; }); @@ -295,7 +320,14 @@ CartesianContextProvider.propTypes = { // | To update them edit the TypeScript types and run "yarn proptypes" | // ---------------------------------------------------------------------- children: PropTypes.node, + /** + * An array of objects that can be used to populate series and axes data using their `dataKey` property. + */ dataset: PropTypes.arrayOf(PropTypes.object), + /** + * The configuration of the x-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_X_AXIS_KEY`. + */ xAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -309,6 +341,7 @@ CartesianContextProvider.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -317,6 +350,13 @@ CartesianContextProvider.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, @@ -324,6 +364,10 @@ CartesianContextProvider.propTypes = { valueFormatter: PropTypes.func, }), ), + /** + * The configuration of the y-axes. + * If not provided, a default axis config is used with id set to `DEFAULT_Y_AXIS_KEY`. + */ yAxis: PropTypes.arrayOf( PropTypes.shape({ axisId: PropTypes.string, @@ -337,6 +381,7 @@ CartesianContextProvider.propTypes = { id: PropTypes.string, label: PropTypes.string, labelFontSize: PropTypes.number, + labelStyle: PropTypes.object, max: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), min: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.number]), position: PropTypes.oneOf(['bottom', 'left', 'right', 'top']), @@ -345,6 +390,13 @@ CartesianContextProvider.propTypes = { slots: PropTypes.object, stroke: PropTypes.string, tickFontSize: PropTypes.number, + tickInterval: PropTypes.oneOfType([ + PropTypes.oneOf(['auto']), + PropTypes.array, + PropTypes.func, + ]), + tickLabelInterval: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.func]), + tickLabelStyle: PropTypes.object, tickMaxStep: PropTypes.number, tickMinStep: PropTypes.number, tickNumber: PropTypes.number, diff --git a/packages/x-charts/src/context/DrawingProvider.tsx b/packages/x-charts/src/context/DrawingProvider.tsx index 161f32e283edc..4fd46723cbd09 100644 --- a/packages/x-charts/src/context/DrawingProvider.tsx +++ b/packages/x-charts/src/context/DrawingProvider.tsx @@ -12,20 +12,47 @@ export interface DrawingProviderProps extends LayoutConfig { * Defines the area in which it is possible to draw the charts. */ export type DrawingArea = { + /** + * The gap between the left border of the SVG and the drawing area. + */ left: number; + /** + * The gap between the top border of the SVG and the drawing area. + */ top: number; + /** + * The gap between the bottom border of the SVG and the drawing area. + */ + bottom: number; + /** + * The gap between the right border of the SVG and the drawing area. + */ + right: number; + /** + * The width of the drawing area. + */ width: number; + /** + * The height of the drawing area. + */ height: number; }; export const DrawingContext = React.createContext({ top: 0, left: 0, + bottom: 0, + right: 0, height: 300, width: 400, }); export const SVGContext = React.createContext>({ current: null }); +/** + * API: + * + * - [DrawingProvider API](https://mui.com/x/api/charts/drawing-provider/) + */ function DrawingProvider({ width, height, margin, svgRef, children }: DrawingProviderProps) { const drawingArea = useChartDimensions(width, height, margin); @@ -43,6 +70,12 @@ DrawingProvider.propTypes = { // ---------------------------------------------------------------------- children: PropTypes.node, height: PropTypes.number.isRequired, + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ margin: PropTypes.shape({ bottom: PropTypes.number, left: PropTypes.number, diff --git a/packages/x-charts/src/context/HighlightProvider.tsx b/packages/x-charts/src/context/HighlightProvider.tsx index f586da23334e7..346fe1b608f7a 100644 --- a/packages/x-charts/src/context/HighlightProvider.tsx +++ b/packages/x-charts/src/context/HighlightProvider.tsx @@ -11,7 +11,21 @@ export type HighlightOptions = 'none' | 'item' | 'series'; export type FadeOptions = 'none' | 'series' | 'global'; export type HighlightScope = { + /** + * The scope of highlighted elements. + * - 'none': no highlight. + * - 'item': only highlight the item. + * - 'series': highlight all elements of the same series. + * @default 'none' + */ highlighted: HighlightOptions; + /** + * The scope of faded elements. + * - 'none': no fading. + * - 'series': only fade element of the same series. + * - 'global': fade all elements that are not highlighted. + * @default 'none' + */ faded: FadeOptions; }; type HighlighActions = @@ -26,6 +40,9 @@ type HighlighActions = }; type HighlighState = { + /** + * The item that triggers the highlight state. + */ item: null | ItemHighlighData; scope: HighlightScope; dispatch: React.Dispatch; diff --git a/packages/x-charts/src/context/InteractionProvider.tsx b/packages/x-charts/src/context/InteractionProvider.tsx index ca638fb0695f0..dabe66abd9968 100644 --- a/packages/x-charts/src/context/InteractionProvider.tsx +++ b/packages/x-charts/src/context/InteractionProvider.tsx @@ -33,7 +33,13 @@ type InteractionActions = }; type InteractionState = { + /** + * The item currently interacting. + */ item: null | ItemInteractionData; + /** + * The x- and y-axes currently interacting. + */ axis: AxisInteractionData; dispatch: React.Dispatch; }; diff --git a/packages/x-charts/src/context/SeriesContextProvider.tsx b/packages/x-charts/src/context/SeriesContextProvider.tsx index 1833b4fd51ebe..b657e250cd00c 100644 --- a/packages/x-charts/src/context/SeriesContextProvider.tsx +++ b/packages/x-charts/src/context/SeriesContextProvider.tsx @@ -16,6 +16,11 @@ import { ChartsColorPalette, blueberryTwilightPalette } from '../colorPalettes'; export type SeriesContextProviderProps = { dataset?: DatasetType; + /** + * The array of series to display. + * Each type of series has its own specificity. + * Please refer to the appropriate docs page to learn more about it. + */ series: AllSeriesType[]; /** * Color palette used to colorize multiple series. diff --git a/packages/x-charts/src/hooks/useChartDimensions.ts b/packages/x-charts/src/hooks/useChartDimensions.ts index aa0d8a2754189..a8fe673fbf2c6 100644 --- a/packages/x-charts/src/hooks/useChartDimensions.ts +++ b/packages/x-charts/src/hooks/useChartDimensions.ts @@ -12,6 +12,8 @@ const useChartDimensions = (width: number, height: number, margin: LayoutConfig[ () => ({ left: defaultizedMargin.left, top: defaultizedMargin.top, + right: defaultizedMargin.right, + bottom: defaultizedMargin.bottom, width: Math.max(0, width - defaultizedMargin.left - defaultizedMargin.right), height: Math.max(0, height - defaultizedMargin.top - defaultizedMargin.bottom), }), diff --git a/packages/x-charts/src/hooks/useMounted.ts b/packages/x-charts/src/hooks/useMounted.ts new file mode 100644 index 0000000000000..298fc91f814ce --- /dev/null +++ b/packages/x-charts/src/hooks/useMounted.ts @@ -0,0 +1,20 @@ +import * as React from 'react'; +import useEnhancedEffect from '@mui/utils/useEnhancedEffect'; + +export function useMounted(defer = false) { + const [mountedState, setMountedState] = React.useState(false); + + useEnhancedEffect(() => { + if (!defer) { + setMountedState(true); + } + }, [defer]); + + React.useEffect(() => { + if (defer) { + setMountedState(true); + } + }, [defer]); + + return mountedState; +} diff --git a/packages/x-charts/src/hooks/useReducedMotion.ts b/packages/x-charts/src/hooks/useReducedMotion.ts new file mode 100644 index 0000000000000..f73b9b373b985 --- /dev/null +++ b/packages/x-charts/src/hooks/useReducedMotion.ts @@ -0,0 +1,31 @@ +import { useIsomorphicLayoutEffect, Globals } from '@react-spring/web'; + +/** + * Returns `boolean` or `null`, used to automatically + * set skipAnimations to the value of the user's + * `prefers-reduced-motion` query. + * + * The return value, post-effect, is the value of their prefered setting + */ +export const useReducedMotion = () => { + // Taken from: https://github.com/pmndrs/react-spring/blob/02ec877bbfab0df46da0e4a47d5f68d3e731206a/packages/shared/src/hooks/useReducedMotion.ts#L13 + + useIsomorphicLayoutEffect(() => { + const mql = window.matchMedia('(prefers-reduced-motion)'); + + const handleMediaChange = (e: MediaQueryListEvent | MediaQueryList) => { + Globals.assign({ + // Modification such the react-spring implementation such that this hook can remove animation but never activate animation. + skipAnimation: e.matches || undefined, + }); + }; + + handleMediaChange(mql); + + mql.addEventListener('change', handleMediaChange); + + return () => { + mql.removeEventListener('change', handleMediaChange); + }; + }, []); +}; diff --git a/packages/x-charts/src/hooks/useTicks.ts b/packages/x-charts/src/hooks/useTicks.ts index c4166ea91e4a0..70f094628199a 100644 --- a/packages/x-charts/src/hooks/useTicks.ts +++ b/packages/x-charts/src/hooks/useTicks.ts @@ -20,9 +20,17 @@ export interface TickParams { * Not supported by categorical axis (band, points). */ tickNumber?: number; + /** + * Defines which ticks are displayed. Its value can be: + * - 'auto' In such case the ticks are computed based on axis scale and other parameters. + * - a filtering function of the form `(value, index) => boolean` which is available only if the axis has a data property. + * - an array containing the values where ticks should be displayed. + * @default 'auto' + */ + tickInterval?: 'auto' | ((value: any, index: number) => boolean) | any[]; } -export function getTicksNumber( +export function getTickNumber( params: TickParams & { range: any[]; domain: any[]; @@ -40,12 +48,23 @@ export function getTicksNumber( return Math.min(maxTicks, Math.max(minTicks, defaultizedTickNumber)); } -function useTicks(options: { - scale: D3Scale; - ticksNumber?: number; - valueFormatter?: (value: any) => string; -}) { - const { scale, ticksNumber, valueFormatter } = options; +export type TickItemType = { + /** + * This property is undefined only if it's the tick closing the last band + */ + value?: any; + formattedValue?: string; + offset: number; + labelOffset: number; +}; + +function useTicks( + options: { + scale: D3Scale; + valueFormatter?: (value: any) => string; + } & Pick, +): TickItemType[] { + const { scale, tickNumber, valueFormatter, tickInterval } = options; return React.useMemo(() => { // band scale @@ -56,7 +75,8 @@ function useTicks(options: { // scale type = 'band' return [ ...domain.map((value) => ({ - formattedValue: valueFormatter?.(value) ?? value, + value, + formattedValue: valueFormatter?.(value) ?? `${value}`, offset: scale(value)! - (scale.step() - scale.bandwidth()) / 2, labelOffset: scale.step() / 2, })), @@ -70,19 +90,27 @@ function useTicks(options: { } // scale type = 'point' - return domain.map((value) => ({ - formattedValue: valueFormatter?.(value) ?? value, + const filteredDomain = + (typeof tickInterval === 'function' && domain.filter(tickInterval)) || + (typeof tickInterval === 'object' && tickInterval) || + domain; + + return filteredDomain.map((value) => ({ + value, + formattedValue: valueFormatter?.(value) ?? `${value}`, offset: scale(value)!, labelOffset: 0, })); } - return scale.ticks(ticksNumber).map((value: any) => ({ - formattedValue: valueFormatter?.(value) ?? scale.tickFormat(ticksNumber)(value), + const ticks = typeof tickInterval === 'object' ? tickInterval : scale.ticks(tickNumber); + return ticks.map((value: any) => ({ + value, + formattedValue: valueFormatter?.(value) ?? scale.tickFormat(tickNumber)(value), offset: scale(value), labelOffset: 0, })); - }, [ticksNumber, scale, valueFormatter]); + }, [tickNumber, scale, valueFormatter, tickInterval]); } export default useTicks; diff --git a/packages/x-charts/src/index.ts b/packages/x-charts/src/index.ts index df1245089c2fc..eca3260fcef2b 100644 --- a/packages/x-charts/src/index.ts +++ b/packages/x-charts/src/index.ts @@ -4,10 +4,12 @@ export * from './hooks'; export * from './colorPalettes'; export * from './models'; export * from './ChartsClipPath'; +export * from './ChartsReferenceLine'; export * from './ChartsAxis'; export * from './ChartsXAxis'; export * from './ChartsYAxis'; export * from './ChartsTooltip'; +export * from './ChartsLegend'; export * from './ChartsAxisHighlight'; export * from './BarChart'; export * from './LineChart'; diff --git a/packages/x-charts/src/internals/components/AxisSharedComponents.tsx b/packages/x-charts/src/internals/components/AxisSharedComponents.tsx index be5996eccddaa..ba6078ebe23b6 100644 --- a/packages/x-charts/src/internals/components/AxisSharedComponents.tsx +++ b/packages/x-charts/src/internals/components/AxisSharedComponents.tsx @@ -5,64 +5,23 @@ export const AxisRoot = styled('g', { name: 'MuiChartsAxis', slot: 'Root', overridesResolver: (props, styles) => styles.root, -})({ - [`&.${axisClasses.directionY}`]: { - [`.${axisClasses.tickLabel}`]: { dominantBaseline: 'middle' }, - [`.${axisClasses.label}`]: { dominantBaseline: 'auto', textAnchor: 'middle' }, - }, - [`&.${axisClasses.left}`]: { - [`.${axisClasses.tickLabel}`]: { dominantBaseline: 'central', textAnchor: 'end' }, +})(({ theme }) => ({ + [`& .${axisClasses.tickLabel}`]: { + ...theme.typography.caption, + fill: (theme.vars || theme).palette.text.primary, }, - [`&.${axisClasses.right}`]: { - [`.${axisClasses.tickLabel}`]: { dominantBaseline: 'central', textAnchor: 'start' }, + [`& .${axisClasses.label}`]: { + ...theme.typography.body1, + fill: (theme.vars || theme).palette.text.primary, }, - [`&.${axisClasses.bottom}`]: { - [`.${axisClasses.tickLabel}, .${axisClasses.label}`]: { - dominantBaseline: 'hanging', - textAnchor: 'middle', - }, + + [`& .${axisClasses.line}`]: { + stroke: (theme.vars || theme).palette.text.primary, + shapeRendering: 'crispEdges', + strokeWidth: 1, }, - [`&.${axisClasses.top}`]: { - [`.${axisClasses.tickLabel}, .${axisClasses.label}`]: { - dominantBaseline: 'baseline', - textAnchor: 'middle', - }, + [`& .${axisClasses.tick}`]: { + stroke: (theme.vars || theme).palette.text.primary, + shapeRendering: 'crispEdges', }, -}); - -export const ChartsLine = styled('line', { - name: 'MuiChartsAxis', - slot: 'Line', - overridesResolver: (props, styles) => styles.line, -})(({ theme }) => ({ - stroke: (theme.vars || theme).palette.text.primary, - shapeRendering: 'crispEdges', - strokeWidth: 1, -})); - -export const ChartsTick = styled('line', { - name: 'MuiChartsAxis', - slot: 'Tick', - overridesResolver: (props, styles) => styles.tick, -})(({ theme }) => ({ - stroke: (theme.vars || theme).palette.text.primary, - shapeRendering: 'crispEdges', -})); - -export const ChartsTickLabel = styled('text', { - name: 'MuiChartsAxis', - slot: 'TickLabel', - overridesResolver: (props, styles) => styles.tickLabel, -})(({ theme }) => ({ - ...theme.typography.caption, - fill: (theme.vars || theme).palette.text.primary, -})); - -export const ChartsLabel = styled('text', { - name: 'MuiChartsAxis', - slot: 'Label', - overridesResolver: (props, styles) => styles.label, -})(({ theme }) => ({ - ...theme.typography.body1, - fill: (theme.vars || theme).palette.text.primary, })); diff --git a/packages/x-charts/src/internals/components/ChartsText.tsx b/packages/x-charts/src/internals/components/ChartsText.tsx new file mode 100644 index 0000000000000..7f5db300c97b5 --- /dev/null +++ b/packages/x-charts/src/internals/components/ChartsText.tsx @@ -0,0 +1,107 @@ +import * as React from 'react'; +import { getStringSize } from '../domUtils'; + +export type ChartsTextBaseline = 'hanging' | 'central' | 'auto'; + +export interface ChartsTextStyle extends React.CSSProperties { + angle?: number; + /** + * The text baseline + * @default 'central' + */ + dominantBaseline?: ChartsTextBaseline; +} + +interface GetWordsByLinesParams { + /** + * Text displayed. + */ + text: string; + /** + * Style applied to text elements. + */ + style?: ChartsTextStyle; + /** + * If `true`, the line width is computed. + * @default false + */ + needsComputation?: boolean; +} + +export interface ChartsTextProps + extends Omit< + React.SVGTextElementAttributes, + 'width' | 'ref' | 'style' | 'dominantBaseline' + >, + GetWordsByLinesParams { + /** + * Height of a text line (in `em`). + */ + lineHeight?: number; + ownerState?: any; +} + +export function getWordsByLines({ style, needsComputation, text }: GetWordsByLinesParams) { + return text.split('\n').map((subText) => ({ + text: subText, + ...(needsComputation ? getStringSize(subText, style) : { width: 0, height: 0 }), + })); +} + +export function ChartsText(props: ChartsTextProps) { + const { x, y, style: styleProps, text, ownerState, ...textProps } = props; + + const { angle, textAnchor, dominantBaseline, ...style } = styleProps ?? {}; + + const wordsByLines = React.useMemo( + () => getWordsByLines({ style, needsComputation: text.includes('\n'), text }), + [style, text], + ); + + let startDy: number; + switch (dominantBaseline) { + case 'hanging': + startDy = 0; + break; + case 'central': + startDy = ((wordsByLines.length - 1) / 2) * -wordsByLines[0].height; + break; + default: + startDy = (wordsByLines.length - 1) * -wordsByLines[0].height; + break; + } + + const transforms = []; + // if (scaleToFit) { + // const lineWidth = wordsByLines[0].width; + // transforms.push(`scale(${(isNumber(width as number) ? (width as number) / lineWidth : 1) / lineWidth})`); + // } + if (angle) { + transforms.push(`rotate(${angle}, ${x}, ${y})`); + } + if (transforms.length) { + textProps.transform = transforms.join(' '); + } + + return ( + + {wordsByLines.map((line, index) => ( + + {line.text} + + ))} + + ); +} diff --git a/packages/x-charts/src/internals/domUtils.ts b/packages/x-charts/src/internals/domUtils.ts new file mode 100644 index 0000000000000..343e173dcef65 --- /dev/null +++ b/packages/x-charts/src/internals/domUtils.ts @@ -0,0 +1,153 @@ +// DOM utils taken from +// https://github.com/recharts/recharts/blob/master/src/util/DOMUtils.ts + +const isSsr = (): boolean => + !(typeof window !== 'undefined' && window.document && window.setTimeout); + +interface StringCache { + widthCache: Record; + cacheCount: number; +} + +const stringCache: StringCache = { + widthCache: {}, + cacheCount: 0, +}; +const MAX_CACHE_NUM = 2000; +const SPAN_STYLE = { + position: 'absolute', + top: '-20000px', + left: 0, + padding: 0, + margin: 0, + border: 'none', + whiteSpace: 'pre', +}; +const STYLE_LIST = [ + 'minWidth', + 'maxWidth', + 'width', + 'minHeight', + 'maxHeight', + 'height', + 'top', + 'left', + 'fontSize', + 'padding', + 'margin', + 'paddingLeft', + 'paddingRight', + 'paddingTop', + 'paddingBottom', + 'marginLeft', + 'marginRight', + 'marginTop', + 'marginBottom', +]; +const MEASUREMENT_SPAN_ID = 'mui_measurement_span'; + +/** + * + * @param name CSS property name + * @param value + * @returns add 'px' for distance properties + */ +function autoCompleteStyle(name: string, value: number) { + if (STYLE_LIST.indexOf(name) >= 0 && value === +value) { + return `${value}px`; + } + + return value; +} + +/** + * + * @param text camelcase css property + * @returns css property + */ +function camelToMiddleLine(text: string) { + const strs = text.split(''); + + const formatStrs = strs.reduce((result: string[], entry) => { + if (entry === entry.toUpperCase()) { + return [...result, '-', entry.toLowerCase()]; + } + + return [...result, entry]; + }, []); + + return formatStrs.join(''); +} + +/** + * + * @param style React style object + * @returns CSS styling string + */ +export const getStyleString = (style: React.CSSProperties) => + Object.keys(style) + .sort() + .reduce( + (result, s) => + `${result}${camelToMiddleLine(s)}:${autoCompleteStyle( + s, + (style as Record)[s], + )};`, + '', + ); + +/** + * + * @param text The string to estimate + * @param style The style applied + * @returns width and height of the text + */ +export const getStringSize = (text: string | number, style: React.CSSProperties = {}) => { + if (text === undefined || text === null || isSsr()) { + return { width: 0, height: 0 }; + } + + const str = `${text}`; + const styleString = getStyleString(style); + const cacheKey = `${str}-${styleString}`; + + if (stringCache.widthCache[cacheKey]) { + return stringCache.widthCache[cacheKey]; + } + + try { + let measurementSpan = document.getElementById(MEASUREMENT_SPAN_ID); + if (measurementSpan === null) { + measurementSpan = document.createElement('span'); + measurementSpan.setAttribute('id', MEASUREMENT_SPAN_ID); + measurementSpan.setAttribute('aria-hidden', 'true'); + document.body.appendChild(measurementSpan); + } + // Need to use CSS Object Model (CSSOM) to be able to comply with Content Security Policy (CSP) + // https://en.wikipedia.org/wiki/Content_Security_Policy + const measurementSpanStyle: Record = { ...SPAN_STYLE, ...style }; + + Object.keys(measurementSpanStyle).map((styleKey) => { + (measurementSpan!.style as Record)[camelToMiddleLine(styleKey)] = + autoCompleteStyle(styleKey, measurementSpanStyle[styleKey]); + return styleKey; + }); + measurementSpan.textContent = str; + + const rect = measurementSpan.getBoundingClientRect(); + const result = { width: rect.width, height: rect.height }; + + stringCache.widthCache[cacheKey] = result; + + if (stringCache.cacheCount + 1 > MAX_CACHE_NUM) { + stringCache.cacheCount = 0; + stringCache.widthCache = {}; + } else { + stringCache.cacheCount += 1; + } + + return result; + } catch (e) { + return { width: 0, height: 0 }; + } +}; diff --git a/packages/x-charts/src/internals/geometry.ts b/packages/x-charts/src/internals/geometry.ts new file mode 100644 index 0000000000000..43fd024cacc39 --- /dev/null +++ b/packages/x-charts/src/internals/geometry.ts @@ -0,0 +1,47 @@ +const ANGLE_APPROX = 5; // Angle (in deg) for which we approximate the rectangle as perfectly horizontal/vertical + +let warnedOnce = false; + +/** + * Return the minimal translation along the x-axis to avoid overflow of a rectangle of a given width, height, and rotation. + * This assumes that all rectangles have the same height and angle between -90 and 90. + * Otherwise it would be problematic because you need the height/width of the next rectangle to do the correct computation. + * @param width the side along the x-axis. + * @param height the side along the y-axis. + * @param angle the rotation in degrees. + */ +export function getMinXTranslation(width: number, height: number, angle: number = 0) { + if (process.env.NODE_ENV !== 'production') { + if (!warnedOnce && angle > 90 && angle < -90) { + warnedOnce = true; + console.warn( + [ + `MUI X: It seems you applied an angle larger than 90° or smaller than -90° to an axis text.`, + `This could cause some text overlapping.`, + `If you encounter a use case where it's needed, please open an issue.`, + ].join('\n'), + ); + } + } + const standardAngle = Math.min( + Math.abs(angle) % 180, + Math.abs((Math.abs(angle) % 180) - 180) % 180, + ); // Map from R to [0, 90] + + if (standardAngle < ANGLE_APPROX) { + // It's nearly horizontal + return width; + } + if (standardAngle > 90 - ANGLE_APPROX) { + // It's nearly vertical + return height; + } + + const radAngle = (standardAngle * Math.PI) / 180; + const angleSwich = Math.atan2(height, width); + + if (radAngle < angleSwich) { + return width / Math.cos(radAngle); + } + return height / Math.sin(radAngle); +} diff --git a/packages/x-charts/src/internals/isBandScale.ts b/packages/x-charts/src/internals/isBandScale.ts index 3a828d8b9eccb..0479662fd6079 100644 --- a/packages/x-charts/src/internals/isBandScale.ts +++ b/packages/x-charts/src/internals/isBandScale.ts @@ -1,6 +1,8 @@ import type { ScaleBand, ScalePoint } from 'd3-scale'; import { D3Scale } from '../models/axis'; -export function isBandScale(scale: D3Scale): scale is ScaleBand | ScalePoint { - return (scale as ScaleBand | ScalePoint).bandwidth !== undefined; +export function isBandScale( + scale: D3Scale, +): scale is ScaleBand | ScalePoint { + return (scale as ScaleBand | ScalePoint).bandwidth !== undefined; } diff --git a/packages/x-charts/src/internals/utils.ts b/packages/x-charts/src/internals/utils.ts index 72d216c2e02ab..6c1b3ced4f09d 100644 --- a/packages/x-charts/src/internals/utils.ts +++ b/packages/x-charts/src/internals/utils.ts @@ -5,3 +5,6 @@ export function getSymbol(shape: SymbolsTypes): number { return symbolNames.indexOf(shape) || 0; } + +type Without = { [P in Exclude]?: never }; +export type XOR = T | U extends object ? (Without & U) | (Without & T) : T | U; diff --git a/packages/x-charts/src/models/axis.ts b/packages/x-charts/src/models/axis.ts index fe961f887b791..da7a6ac59bdba 100644 --- a/packages/x-charts/src/models/axis.ts +++ b/packages/x-charts/src/models/axis.ts @@ -8,40 +8,46 @@ import type { } from 'd3-scale'; import { ChartsAxisClasses } from '../ChartsAxis/axisClasses'; import type { TickParams } from '../hooks/useTicks'; +import { ChartsTextProps } from '../internals/components/ChartsText'; -export type D3Scale = - | ScaleBand - | ScaleLogarithmic - | ScalePoint - | ScalePower - | ScaleTime - | ScaleLinear; +export type D3Scale< + Domain extends { toString(): string } = number | Date | string, + Range = number, + Output = number, +> = + | ScaleBand + | ScaleLogarithmic + | ScalePoint + | ScalePower + | ScaleTime + | ScaleLinear; -export type D3ContinuouseScale = - | ScaleLogarithmic - | ScalePower - | ScaleTime - | ScaleLinear; +export type D3ContinuouseScale = + | ScaleLogarithmic + | ScalePower + | ScaleTime + | ScaleLinear; -export interface ChartsAxisSlotsComponent { +export interface ChartsAxisSlots { axisLine?: React.JSXElementConstructor>; axisTick?: React.JSXElementConstructor>; - axisTickLabel?: React.JSXElementConstructor>; - axisLabel?: React.JSXElementConstructor>; + axisTickLabel?: React.JSXElementConstructor; + axisLabel?: React.JSXElementConstructor; } -export interface ChartsAxisSlotComponentProps { +export interface ChartsAxisSlotProps { axisLine?: Partial>; axisTick?: Partial>; - axisTickLabel?: Partial>; - axisLabel?: Partial>; + axisTickLabel?: Partial; + axisLabel?: Partial; } export interface ChartsAxisProps extends TickParams { /** - * Id of the axis to render. + * The id of the axis to render. + * If undefined, it will be the first defined axis. */ - axisId: string; + axisId?: string; /** * If true, the axis line is disabled. * @default false @@ -60,8 +66,24 @@ export interface ChartsAxisProps extends TickParams { /** * The font size of the axis ticks text. * @default 12 + * @deprecated Consider using `tickLabelStyle.fontSize` instead. */ tickFontSize?: number; + /** + * The style applied to ticks text. + */ + tickLabelStyle?: ChartsTextProps['style']; + /** + * The style applied to the axis label. + */ + labelStyle?: ChartsTextProps['style']; + /** + * Defines which ticks get its label displayed. Its value can be: + * - 'auto' In such case, labels are displayed if they do not overlap with the previous one. + * - a filtering function of the form (value, index) => boolean. Warning: the index is tick index, not data ones. + * @default 'auto' + */ + tickLabelInterval?: 'auto' | ((value: any, index: number) => boolean); /** * The label of the axis. */ @@ -69,6 +91,7 @@ export interface ChartsAxisProps extends TickParams { /** * The font size of the axis label. * @default 14 + * @deprecated Consider using `labelStyle.fontSize` instead. */ labelFontSize?: number; /** @@ -89,12 +112,12 @@ export interface ChartsAxisProps extends TickParams { * Overridable component slots. * @default {} */ - slots?: Partial; + slots?: Partial; /** * The props used for each component slot. * @default {} */ - slotProps?: Partial; + slotProps?: Partial; } export interface ChartsYAxisProps extends ChartsAxisProps { @@ -117,7 +140,7 @@ export type ContinuouseScaleName = 'linear' | 'log' | 'pow' | 'sqrt' | 'time' | interface AxisScaleConfig { band: { scaleType: 'band'; - scale: ScaleBand; + scale: ScaleBand; /** * The ratio between the space allocated for padding between two categories and the category width. * 0 means no gap, and 1 no data. @@ -133,41 +156,55 @@ interface AxisScaleConfig { }; point: { scaleType: 'point'; - scale: ScalePoint; + scale: ScalePoint; }; log: { scaleType: 'log'; - scale: ScaleLogarithmic; + scale: ScaleLogarithmic; }; pow: { scaleType: 'pow'; - scale: ScalePower; + scale: ScalePower; }; sqrt: { scaleType: 'sqrt'; - scale: ScalePower; + scale: ScalePower; }; time: { scaleType: 'time'; - scale: ScaleTime; + scale: ScaleTime; }; utc: { scaleType: 'utc'; - scale: ScaleTime; + scale: ScaleTime; }; linear: { scaleType: 'linear'; - scale: ScaleLinear; + scale: ScaleLinear; }; } export type AxisConfig = { + /** + * Id used to identify the axis. + */ id: string; + /** + * The minimal value of the domain. + * If not provided, it gets computed to display the entire chart data. + */ min?: number | Date; + /** + * The maximal value of the domain. + * If not provided, it gets computed to display the entire chart data. + */ max?: number | Date; + /** + * The data used by `'band'` and `'point'` scales. + */ data?: V[]; /** - * The key used to retrieve data from the dataset prop. + * The key used to retrieve `data` from the `dataset` prop. */ dataKey?: string; valueFormatter?: (value: V) => string; @@ -184,7 +221,10 @@ export type AxisDefaultized = Omit< 'scaleType' > & AxisScaleConfig[S] & { - ticksNumber: number; + /** + * An indication of the expected number of ticks. + */ + tickNumber: number; }; export function isBandScaleConfig( diff --git a/packages/x-charts/src/models/layout.ts b/packages/x-charts/src/models/layout.ts index ad76fea554f3c..5cf2926569e5a 100644 --- a/packages/x-charts/src/models/layout.ts +++ b/packages/x-charts/src/models/layout.ts @@ -1,10 +1,18 @@ +export interface CardinalDirections { + top?: T; + bottom?: T; + left?: T; + right?: T; +} + export type LayoutConfig = { width: number; height: number; - margin?: { - top?: number; - bottom?: number; - left?: number; - right?: number; - }; + /** + * The margin between the SVG and the drawing area. + * It's used for leaving some space for extra information such as the x- and y-axis or legend. + * Accepts an object with the optional properties: `top`, `bottom`, `left`, and `right`. + * @default object Depends on the charts type. + */ + margin?: Partial>; }; diff --git a/packages/x-charts/src/models/seriesType/common.ts b/packages/x-charts/src/models/seriesType/common.ts index 39817459d9e0a..03cad304dbed2 100644 --- a/packages/x-charts/src/models/seriesType/common.ts +++ b/packages/x-charts/src/models/seriesType/common.ts @@ -16,13 +16,29 @@ export type CommonSeriesType = { export type CommonDefaultizedProps = 'id' | 'valueFormatter' | 'data'; export type CartesianSeriesType = { + /** + * The id of the x-axis used to render the series. + */ xAxisKey?: string; + /** + * The id of the y-axis used to render the series. + */ yAxisKey?: string; }; export type StackableSeriesType = { + /** + * The key that identifies the stacking group. + * Series with the same `stack` property will be stacked together. + */ stack?: string; + /** + * Defines how stacked series handle negative values. + */ stackOffset?: StackOffsetType; + /** + * The order in which series' of the same group are stacked together. + */ stackOrder?: StackOrderType; }; diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index 24e99d4622db6..b3e04133314ca 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -16,7 +16,7 @@ export type CurveType = | 'stepBefore' | 'stepAfter'; -export interface ShowMarkParams { +export interface ShowMarkParams { /** * The item index. */ @@ -32,7 +32,7 @@ export interface ShowMarkParams { /** * The item position value. It likely comes from the axis `data` property. */ - position: number | Date; + position: AxisValue; /** * The item value. It comes from the series `data` property. */ @@ -47,7 +47,7 @@ export interface LineSeriesType /** * Data associated to the line. */ - data?: number[]; + data?: (number | null)[]; /** * The key used to retrive data from the dataset. */ @@ -67,6 +67,11 @@ export interface LineSeriesType * @default false */ disableHighlight?: boolean; + /** + * If `true`, line and area connect points separated by `null` values. + * @default false + */ + connectNulls?: boolean; } /** diff --git a/packages/x-charts/src/models/seriesType/pie.ts b/packages/x-charts/src/models/seriesType/pie.ts index cf1f4fc87c19a..346c53c8c59a3 100644 --- a/packages/x-charts/src/models/seriesType/pie.ts +++ b/packages/x-charts/src/models/seriesType/pie.ts @@ -2,10 +2,15 @@ import { PieArcDatum as D3PieArcDatum } from 'd3-shape'; import { DefaultizedProps } from '../helpers'; import { CommonDefaultizedProps, CommonSeriesType } from './common'; -export type PieValueType = { id: string | number; value: number; label?: string; color?: string }; +export type PieValueType = { + id: string | number; + value: number; + label?: string; + color?: string; +}; export type DefaultizedPieValueType = PieValueType & - D3PieArcDatum & { color: string; formattedValue: string }; + Omit, 'data'> & { color: string; formattedValue: string }; export type ChartsPieSorting = 'none' | 'asc' | 'desc' | ((a: number, b: number) => number); @@ -73,6 +78,8 @@ export interface PieSeriesType extends CommonSeriesType extends CommonSeriesType + + + { + alert('clicked'); + }, + }, + }} + /> +
                + ); +}; diff --git a/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/expected.spec.js b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/expected.spec.js new file mode 100644 index 0000000000000..d0b73a667a418 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/expected.spec.js @@ -0,0 +1,44 @@ +import { DataGrid, CustomToolbar } from '@mui/x-data-grid'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { DataGridPremium } from '@mui/x-data-grid-premium'; +import { Button, Checkbox, TextField } from '@mui/material'; + +export default function App() { + return ( +
                + + + { + alert('clicked'); + }, + }, + }} + /> +
                + ); +}; diff --git a/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/index.ts b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/index.ts new file mode 100644 index 0000000000000..377e76d01e490 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/index.ts @@ -0,0 +1,9 @@ +import transformRenameComponentsToSlots from '../../../v6.0.0/data-grid/rename-components-to-slots'; + +import { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; + +export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { + file.source = transformRenameComponentsToSlots(file, api, options); + + return file.source; +} diff --git a/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/preset-safe.test.ts b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/preset-safe.test.ts new file mode 100644 index 0000000000000..4dce7e1a6d4fa --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/data-grid/preset-safe/preset-safe.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/data-grid', () => { + describe('preset-safe', () => { + it('transforms code 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('./actual.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-codemod/src/v7.0.0/pickers/preset-safe/actual.spec.js b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/actual.spec.js new file mode 100644 index 0000000000000..096171f37cdaa --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/actual.spec.js @@ -0,0 +1,28 @@ +import { DatePicker } from '@mui/x-date-pickers'; +import { DateRangePicker } from '@mui/x-date-pickers-pro'; +import TextField from '@mui/material/TextField'; + +
                + + + +
                ; diff --git a/packages/x-codemod/src/v7.0.0/pickers/preset-safe/expected.spec.js b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/expected.spec.js new file mode 100644 index 0000000000000..7f0072e176083 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/expected.spec.js @@ -0,0 +1,28 @@ +import { DatePicker } from '@mui/x-date-pickers'; +import { DateRangePicker } from '@mui/x-date-pickers-pro'; +import TextField from '@mui/material/TextField'; + +
                + + + +
                ; diff --git a/packages/x-codemod/src/v7.0.0/pickers/preset-safe/index.ts b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/index.ts new file mode 100644 index 0000000000000..4d9fb737ea9c1 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/index.ts @@ -0,0 +1,9 @@ +import transformRenameComponentsToSlots from '../../../v6.0.0/pickers/rename-components-to-slots'; + +import { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../../types'; + +export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { + file.source = transformRenameComponentsToSlots(file, api, options); + + return file.source; +} diff --git a/packages/x-codemod/src/v7.0.0/pickers/preset-safe/preset-safe.test.ts b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/preset-safe.test.ts new file mode 100644 index 0000000000000..09d3f402b08ed --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/pickers/preset-safe/preset-safe.test.ts @@ -0,0 +1,41 @@ +import path from 'path'; +import { expect } from 'chai'; +import jscodeshift from 'jscodeshift'; +import transform from './index'; +import readFile from '../../../util/readFile'; + +function read(fileName) { + return readFile(path.join(__dirname, fileName)); +} + +describe('v7.0.0/pickers', () => { + describe('preset-safe', () => { + it('transforms code as needed', () => { + const actual = transform( + { + source: read('./actual.spec.js'), + path: require.resolve('./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 for expression', () => { + const actual = transform( + { + source: read('./expected.spec.js'), + path: require.resolve('./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-codemod/src/v7.0.0/preset-safe/actual.spec.js b/packages/x-codemod/src/v7.0.0/preset-safe/actual.spec.js new file mode 100644 index 0000000000000..41a0a692723b6 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/preset-safe/actual.spec.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { DataGrid } from '@mui/x-data-grid/DataGrid'; + +const theme = createTheme({}); + +function App() { + return ( + + + + + + + ); +} + +export default App; diff --git a/packages/x-codemod/src/v7.0.0/preset-safe/expected.spec.js b/packages/x-codemod/src/v7.0.0/preset-safe/expected.spec.js new file mode 100644 index 0000000000000..cb903bbf30635 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/preset-safe/expected.spec.js @@ -0,0 +1,29 @@ +import * as React from 'react'; +import { createTheme, ThemeProvider } from '@mui/material/styles'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; +import { DataGrid } from '@mui/x-data-grid/DataGrid'; + +const theme = createTheme({}); + +function App() { + return ( + + + + + + + ); +} + +export default App; diff --git a/packages/x-codemod/src/v7.0.0/preset-safe/index.ts b/packages/x-codemod/src/v7.0.0/preset-safe/index.ts new file mode 100644 index 0000000000000..51b584797e671 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/preset-safe/index.ts @@ -0,0 +1,10 @@ +import transformPickers from '../pickers/preset-safe'; +import transformDataGrid from '../data-grid/preset-safe'; +import { JsCodeShiftAPI, JsCodeShiftFileInfo } from '../../types'; + +export default function transformer(file: JsCodeShiftFileInfo, api: JsCodeShiftAPI, options: any) { + file.source = transformPickers(file, api, options); + file.source = transformDataGrid(file, api, options); + + return file.source; +} diff --git a/packages/x-codemod/src/v7.0.0/preset-safe/preset-safe.test.ts b/packages/x-codemod/src/v7.0.0/preset-safe/preset-safe.test.ts new file mode 100644 index 0000000000000..3db400316a841 --- /dev/null +++ b/packages/x-codemod/src/v7.0.0/preset-safe/preset-safe.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', () => { + describe('preset-safe', () => { + it('transforms code 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-codemod/tsconfig.json b/packages/x-codemod/tsconfig.json index 8f3781e0afa99..9a29c68504989 100644 --- a/packages/x-codemod/tsconfig.json +++ b/packages/x-codemod/tsconfig.json @@ -6,7 +6,7 @@ }, "include": [ "src/**/*", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../node_modules/@mui/material/themeCssVarsAugmentation", "../../node_modules/date-fns", "../../node_modules/dayjs/plugin/utc.d.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 022b217d3f588..539731747572b 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": "6.14.0", + "version": "6.18.0", "description": "The commercial edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -41,10 +41,10 @@ "directory": "packages/x-date-pickers-pro" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/base": "^5.0.0-beta.14", - "@mui/utils": "^5.14.8", - "@mui/x-date-pickers": "6.14.0", + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/utils": "^5.14.16", + "@mui/x-date-pickers": "6.18.0", "@mui/x-license-pro": "6.10.2", "clsx": "^2.0.0", "prop-types": "^15.8.1", @@ -95,9 +95,9 @@ } }, "devDependencies": { - "@types/luxon": "^3.3.2", + "@types/luxon": "^3.3.3", "date-fns": "^2.30.0", - "dayjs": "^1.11.9", + "dayjs": "^1.11.10", "luxon": "^3.4.3", "moment": "^2.29.4" }, diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx index 106790ab40ef4..d5c9a47e73659 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.test.tsx @@ -8,7 +8,7 @@ import { describeConformance, fireTouchChangedEvent, userEvent, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { adapterToUse, buildPickerDragInteractions, @@ -461,8 +461,8 @@ describe('', () => { it('should render custom day component', () => { render(
                , + slots={{ + day: (day) =>
                , }} />, ); diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx index 12aa265ce968d..b6acedd94bfb1 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.tsx @@ -25,8 +25,6 @@ import { useUtils, PickerSelectionState, useNow, - uncapitalizeObjectKeys, - UncapitalizeObjectKeys, DEFAULT_DESKTOP_MODE_MEDIA_QUERY, buildWarning, useControlledValueWithTimezone, @@ -155,6 +153,16 @@ type DateRangeCalendarComponent = (( props: DateRangeCalendarProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [DateRangeCalendar](https://mui.com/x/react-date-pickers/date-range-calendar/) + * + * API: + * + * - [DateRangeCalendar API](https://mui.com/x/api/date-pickers/date-range-calendar/) + */ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar( inProps: DateRangeCalendarProps, ref: React.Ref, @@ -183,10 +191,8 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar( onRangePositionChange: inOnRangePositionChange, calendars, currentMonthCalendarPosition = 1, - components, - componentsProps, - slots: innerSlots, - slotProps: innerSlotProps, + slots, + slotProps, loading, renderLoading, disableHighlightToday, @@ -216,9 +222,6 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar( const localeText = useLocaleText(); const now = useNow(timezone); - const slots = innerSlots ?? uncapitalizeObjectKeys(components); - const slotProps = innerSlotProps ?? componentsProps; - const { rangePosition, onRangePositionChange } = useRangePosition({ rangePosition: rangePositionProp, defaultRangePosition: inDefaultRangePosition, @@ -427,7 +430,7 @@ const DateRangeCalendar = React.forwardRef(function DateRangeCalendar( const slotsForDayCalendar = { day: DateRangePickerDay, ...slots, - } as UncapitalizeObjectKeys>; + } as DayCalendarSlotsComponent; const slotPropsForDayCalendar = { ...slotProps, @@ -609,18 +612,6 @@ DateRangeCalendar.propTypes = { calendars: PropTypes.oneOf([1, 2, 3]), classes: PropTypes.object, className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Position the current month is rendered in. * @default 1 @@ -628,9 +619,10 @@ DateRangeCalendar.propTypes = { currentMonthCalendarPosition: PropTypes.oneOf([1, 2, 3]), /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -748,6 +740,9 @@ DateRangeCalendar.propTypes = { renderLoading: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. 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 7ad16e2f9d1f0..dc0d182639626 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/DateRangeCalendar.types.ts @@ -18,7 +18,6 @@ import { PickerSelectionState, DayCalendarProps, ExportedUseViewsOptions, - UncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { DateRange, DayRangeValidationProps } from '../internals/models'; import { DateRangeCalendarClasses } from './dateRangeCalendarClasses'; @@ -29,14 +28,14 @@ export type DateRangePosition = 'start' | 'end'; export interface DateRangeCalendarSlotsComponent extends PickersArrowSwitcherSlotsComponent, - Omit, 'Day'>, + Omit, 'day'>, PickersCalendarHeaderSlotsComponent { /** * Custom component for day in range pickers. * Check the [DateRangePickersDay](https://mui.com/x/api/date-pickers/date-range-picker-day/) component. * @default DateRangePickersDay */ - Day?: React.ElementType>; + day?: React.ElementType>; } export interface DateRangeCalendarSlotsComponentsProps @@ -51,7 +50,7 @@ export interface DateRangeCalendarSlotsComponentsProps } export interface ExportedDateRangeCalendarProps - extends ExportedDayCalendarProps, + extends ExportedDayCalendarProps, BaseDateValidationProps, DayRangeValidationProps, TimezoneProps, @@ -135,23 +134,11 @@ export interface DateRangeCalendarProps * The system prop that allows defining system overrides as well as additional CSS styles. */ sx?: SxProps; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DateRangeCalendarSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DateRangeCalendarSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DateRangeCalendarSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/DateRangeCalendar/useDragRange.ts b/packages/x-date-pickers-pro/src/DateRangeCalendar/useDragRange.ts index b8b3046111414..6283ae2e247f4 100644 --- a/packages/x-date-pickers-pro/src/DateRangeCalendar/useDragRange.ts +++ b/packages/x-date-pickers-pro/src/DateRangeCalendar/useDragRange.ts @@ -152,12 +152,6 @@ const useDragRangeEvents = ({ } setRangeDragDay(newDate); - setIsDragging(true); - const button = event.target as HTMLButtonElement; - const buttonDataset = button.dataset; - if (buttonDataset.position) { - onDatePositionChange(buttonDataset.position as DateRangePosition); - } }); const handleDragEnter = useEventCallback((event: React.DragEvent) => { @@ -173,7 +167,7 @@ const useDragRangeEvents = ({ const handleTouchMove = useEventCallback((event: React.TouchEvent) => { const target = resolveElementFromTouch(event); - if (!isDragging || !target) { + if (!target) { return; } @@ -181,6 +175,21 @@ const useDragRangeEvents = ({ if (newDate) { setRangeDragDay(newDate); } + + // this prevents initiating drag when user starts touchmove outside and then moves over a draggable element + const targetsAreIdentical = target === event.changedTouches[0].target; + if (!targetsAreIdentical || !isElementDraggable(newDate)) { + return; + } + + // on mobile we should only initialize dragging state after move is detected + setIsDragging(true); + + const button = event.target as HTMLButtonElement; + const buttonDataset = button.dataset; + if (buttonDataset.position) { + onDatePositionChange(buttonDataset.position as DateRangePosition); + } }); const handleDragLeave = useEventCallback((event: React.DragEvent) => { diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.test.tsx index de63cb3d2ef43..35d62b9a5d613 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; -import { fireEvent, screen } from '@mui/monorepo/test/utils/createRenderer'; +import { fireEvent, screen } from '@mui-internal/test-utils/createRenderer'; import { expect } from 'chai'; import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx index 66665551f39ff..e0c5e59247d6b 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.tsx @@ -11,6 +11,16 @@ type DatePickerComponent = (( props: DateRangePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DateRangePicker API](https://mui.com/x/api/date-pickers/date-range-picker/) + */ const DateRangePicker = React.forwardRef(function DateRangePicker( inProps: DateRangePickerProps, ref: React.Ref, @@ -55,18 +65,6 @@ DateRangePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Position the current month is rendered in. * @default 1 @@ -74,9 +72,10 @@ DateRangePicker.propTypes = { currentMonthCalendarPosition: PropTypes.oneOf([1, 2, 3]), /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -291,6 +290,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.types.ts b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.types.ts index f6da88ae6970c..23e944bcfc5fc 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePicker.types.ts @@ -1,4 +1,3 @@ -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; import { DesktopDateRangePickerProps, DesktopDateRangePickerSlotsComponent, @@ -27,23 +26,11 @@ export interface DateRangePickerProps * @example '@media (min-width: 720px)' or theme.breakpoints.up("sm") */ desktopModeMediaQuery?: string; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DateRangePickerSlotsComponents; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DateRangePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DateRangePickerSlotsComponents; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerToolbar.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerToolbar.tsx index 77ba0bcbaafd3..03eabe0b01660 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerToolbar.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/DateRangePickerToolbar.tsx @@ -56,6 +56,16 @@ const DateRangePickerToolbarContainer = styled('div', { display: 'flex', }); +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [Custom components](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [DateRangePickerToolbar API](https://mui.com/x/api/date-pickers/date-range-picker-toolbar/) + */ const DateRangePickerToolbar = React.forwardRef(function DateRangePickerToolbar< TDate extends unknown, >(inProps: DateRangePickerToolbarProps, ref: React.Ref) { @@ -131,6 +141,11 @@ DateRangePickerToolbar.propTypes = { onRangePositionChange: PropTypes.func.isRequired, rangePosition: PropTypes.oneOf(['end', 'start']).isRequired, readOnly: PropTypes.bool, + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), titleId: PropTypes.string, /** * Toolbar date format. diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/describes.DateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/describes.DateRangePicker.test.tsx index 75f4c240c23a9..a73fac7a776ae 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/describes.DateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/describes.DateRangePicker.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker'; import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers-pro/src/DateRangePicker/shared.tsx b/packages/x-date-pickers-pro/src/DateRangePicker/shared.tsx index 08714709bdb45..e6b558657986c 100644 --- a/packages/x-date-pickers-pro/src/DateRangePicker/shared.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePicker/shared.tsx @@ -9,8 +9,6 @@ import { BaseDateValidationProps, BasePickerInputProps, PickerViewRendererLookup, - UncapitalizeObjectKeys, - uncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { DateRangeValidationError } from '../models'; import { DateRange } from '../internals/models'; @@ -32,7 +30,7 @@ export interface BaseDateRangePickerSlotsComponent * Custom component for the toolbar rendered above the views. * @default DateTimePickerToolbar */ - Toolbar?: React.JSXElementConstructor>; + toolbar?: React.JSXElementConstructor>; } export interface BaseDateRangePickerSlotsComponentsProps @@ -47,23 +45,11 @@ export interface BaseDateRangePickerProps >, ExportedDateRangeCalendarProps, BaseDateValidationProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: BaseDateRangePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: BaseDateRangePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: BaseDateRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} @@ -93,7 +79,7 @@ export function useDateRangePickerDefaultizedProps< ): UseDateRangePickerDefaultizedProps> { const utils = useUtils(); const defaultDates = useDefaultDates(); - const { components, componentsProps, ...themeProps } = useThemeProps({ + const themeProps = useThemeProps({ props, name, }); @@ -118,8 +104,7 @@ export function useDateRangePickerDefaultizedProps< maxDate: applyDefaultDate(utils, themeProps.maxDate, defaultDates.maxDate), slots: { toolbar: DateRangePickerToolbar, - ...(themeProps.slots ?? uncapitalizeObjectKeys(components)), + ...themeProps.slots, }, - slotProps: themeProps.slotProps ?? componentsProps, }; } diff --git a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.test.tsx b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.test.tsx index de40a4b2c83fa..9d339f0a5eca9 100644 --- a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.test.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { DateRangePickerDay, dateRangePickerDayClasses as classes, diff --git a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx index 364809934b96f..9647c304b1628 100644 --- a/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx +++ b/packages/x-date-pickers-pro/src/DateRangePickerDay/DateRangePickerDay.tsx @@ -523,10 +523,9 @@ DateRangePickerDayRaw.propTypes = { } as any; /** - * * Demos: * - * - [Date Range Picker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) * * API: * diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx index a079f1edb8ec6..f99339db81184 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.tsx @@ -16,6 +16,16 @@ type DesktopDateRangePickerComponent = (( props: DesktopDateRangePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DesktopDateRangePicker API](https://mui.com/x/api/date-pickers/desktop-date-range-picker/) + */ const DesktopDateRangePicker = React.forwardRef(function DesktopDateRangePicker( inProps: DesktopDateRangePickerProps, ref: React.Ref, @@ -91,18 +101,6 @@ DesktopDateRangePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Position the current month is rendered in. * @default 1 @@ -110,9 +108,10 @@ DesktopDateRangePicker.propTypes = { currentMonthCalendarPosition: PropTypes.oneOf([1, 2, 3]), /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -321,6 +320,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.types.ts b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.types.ts index c07667ac4efa8..6c0d4bc6afe79 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/DesktopDateRangePicker.types.ts @@ -1,4 +1,4 @@ -import { MakeOptional, UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { MakeOptional } from '@mui/x-date-pickers/internals'; import { UseDesktopRangePickerSlotsComponent, UseDesktopRangePickerSlotsComponentsProps, @@ -12,7 +12,7 @@ import { export interface DesktopDateRangePickerSlotsComponent extends BaseDateRangePickerSlotsComponent, - MakeOptional, 'Field'> {} + MakeOptional, 'field'> {} export interface DesktopDateRangePickerSlotsComponentsProps extends BaseDateRangePickerSlotsComponentsProps, @@ -26,23 +26,11 @@ export interface DesktopDateRangePickerProps * @default 2 */ calendars?: 1 | 2 | 3; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DesktopDateRangePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DesktopDateRangePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DesktopDateRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx index 0f56616d9aad9..1ca0d5c508e57 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, fireEvent, userEvent, act, getByRole } from '@mui/monorepo/test/utils'; +import { screen, fireEvent, userEvent, act, getByRole } from '@mui-internal/test-utils'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { DesktopDateRangePicker } from '@mui/x-date-pickers-pro/DesktopDateRangePicker'; import { DateRange, LocalizationProvider } from '@mui/x-date-pickers-pro'; diff --git a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx index a1795406ea0bc..6284ed1070f09 100644 --- a/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/describes.DesktopDateRangePicker.test.tsx @@ -1,8 +1,5 @@ import * as React from 'react'; -import { describeConformance, screen, userEvent } from '@mui/monorepo/test/utils'; -import { DesktopDateRangePicker } from '@mui/x-date-pickers-pro/DesktopDateRangePicker'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { describeConformance, screen, userEvent } from '@mui-internal/test-utils'; import { adapterToUse, createPickerRenderer, @@ -10,9 +7,12 @@ import { getTextbox, expectInputPlaceholder, expectInputValue, + describePicker, + describeValue, + describeRangeValidation, } from 'test/utils/pickers'; +import { DesktopDateRangePicker } from '@mui/x-date-pickers-pro/DesktopDateRangePicker'; import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx index aa9c7698ed50b..28b399a86f35a 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.tsx @@ -16,6 +16,16 @@ type MobileDateRangePickerComponent = (( props: MobileDateRangePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [MobileDateRangePicker API](https://mui.com/x/api/date-pickers/mobile-date-range-picker/) + */ const MobileDateRangePicker = React.forwardRef(function MobileDateRangePicker( inProps: MobileDateRangePickerProps, ref: React.Ref, @@ -91,18 +101,6 @@ MobileDateRangePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Position the current month is rendered in. * @default 1 @@ -110,9 +108,10 @@ MobileDateRangePicker.propTypes = { currentMonthCalendarPosition: PropTypes.oneOf([1, 2, 3]), /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -321,6 +320,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.types.ts b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.types.ts index 9eebc5356ad6e..ac8fbeff6b6c6 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/MobileDateRangePicker.types.ts @@ -1,4 +1,4 @@ -import { MakeOptional, UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { MakeOptional } from '@mui/x-date-pickers/internals'; import { UseMobileRangePickerSlotsComponent, UseMobileRangePickerSlotsComponentsProps, @@ -12,7 +12,7 @@ import { export interface MobileDateRangePickerSlotsComponent extends BaseDateRangePickerSlotsComponent, - MakeOptional, 'Field'> {} + MakeOptional, 'field'> {} export interface MobileDateRangePickerSlotsComponentsProps extends BaseDateRangePickerSlotsComponentsProps, @@ -26,23 +26,11 @@ export interface MobileDateRangePickerProps * @default 2 */ calendars?: 1 | 2 | 3; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MobileDateRangePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MobileDateRangePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: MobileDateRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx index 4228c4d358356..6eaf629f3f2e7 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/MobileDateRangePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { screen, userEvent, fireEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent, fireEvent } from '@mui-internal/test-utils'; import { MobileDateRangePicker } from '@mui/x-date-pickers-pro/MobileDateRangePicker'; import { createPickerRenderer, adapterToUse, openPicker } from 'test/utils/pickers'; import { DateRange } from '@mui/x-date-pickers-pro'; diff --git a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx index 3b7a7bc5f36f4..622e735e77299 100644 --- a/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/MobileDateRangePicker/tests/describes.MobileDateRangePicker.test.tsx @@ -4,11 +4,8 @@ import { screen, userEvent, fireDiscreteEvent, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { MobileDateRangePicker } from '@mui/x-date-pickers-pro/MobileDateRangePicker'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; import { adapterToUse, createPickerRenderer, @@ -16,6 +13,9 @@ import { openPicker, expectInputPlaceholder, expectInputValue, + describeRangeValidation, + describeValue, + describePicker, } from 'test/utils/pickers'; describe(' - Describes', () => { diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx index 2f7cea805d30a..a15d45baaf591 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.tsx @@ -14,7 +14,6 @@ import { import { splitFieldInternalAndForwardedProps, FieldsTextFieldProps, - uncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { MultiInputDateRangeFieldProps } from './MultiInputDateRangeField.types'; import { useMultiInputDateRangeField } from '../internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField'; @@ -63,6 +62,16 @@ type MultiInputDateRangeFieldComponent = (( props: MultiInputDateRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangeField](http://mui.com/x/react-date-pickers/date-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [MultiInputDateRangeField API](https://mui.com/x/api/multi-input-date-range-field/) + */ const MultiInputDateRangeField = React.forwardRef(function MultiInputDateRangeField( inProps: MultiInputDateRangeFieldProps, ref: React.Ref, @@ -75,14 +84,15 @@ const MultiInputDateRangeField = React.forwardRef(function MultiInputDateRangeFi const { internalProps: dateFieldInternalProps, forwardedProps } = splitFieldInternalAndForwardedProps< typeof themeProps, - keyof Omit, 'unstableFieldRef' | 'disabled'> + keyof Omit< + UseDateRangeFieldProps, + 'unstableFieldRef' | 'disabled' | 'clearable' | 'onClear' + > >(themeProps, 'date'); const { - slots: innerSlots, - slotProps: innerSlotProps, - components, - componentsProps, + slots, + slotProps, disabled, autoFocus, unstableStartFieldRef, @@ -91,9 +101,6 @@ const MultiInputDateRangeField = React.forwardRef(function MultiInputDateRangeFi ...otherForwardedProps } = forwardedProps; - const slots = innerSlots ?? uncapitalizeObjectKeys(components); - const slotProps = innerSlotProps ?? componentsProps; - const ownerState = themeProps; const classes = useUtilityClasses(ownerState); @@ -125,7 +132,7 @@ const MultiInputDateRangeField = React.forwardRef(function MultiInputDateRangeFi const Separator = slots?.separator ?? MultiInputDateRangeFieldSeparator; const separatorProps = useSlotProps({ elementType: Separator, - externalSlotProps: slotProps?.separator ?? componentsProps?.separator, + externalSlotProps: slotProps?.separator, ownerState, className: classes.separator, }); @@ -203,18 +210,6 @@ MultiInputDateRangeField.propTypes = { classes: PropTypes.object, className: PropTypes.string, component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -328,6 +323,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.types.ts b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.types.ts index b8c559a15def0..35290f151d646 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/MultiInputDateRangeField.types.ts @@ -4,7 +4,6 @@ import Typography from '@mui/material/Typography'; import Stack, { StackProps } from '@mui/material/Stack'; import TextField from '@mui/material/TextField'; import { FieldRef } from '@mui/x-date-pickers/models'; -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; import { UseDateRangeFieldProps } from '../internals/models/dateRange'; import { RangePosition } from '../internals/models/range'; import { UseMultiInputRangeFieldParams } from '../internals/hooks/useMultiInputRangeField/useMultiInputRangeField.types'; @@ -17,7 +16,7 @@ export type UseMultiInputDateRangeFieldParams< > = UseMultiInputRangeFieldParams, TTextFieldSlotProps>; export interface UseMultiInputDateRangeFieldProps - extends Omit, 'unstableFieldRef'> { + extends Omit, 'unstableFieldRef' | 'clearable' | 'onClear'> { unstableStartFieldRef?: React.Ref>; unstableEndFieldRef?: React.Ref>; } @@ -35,23 +34,11 @@ export interface MultiInputDateRangeFieldProps * Override or extend the styles applied to the component. */ classes?: Partial; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MultiInputDateRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MultiInputDateRangeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: MultiInputDateRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -66,19 +53,19 @@ export interface MultiInputDateRangeFieldSlotsComponent { * Element rendered at the root. * @default MultiInputDateRangeFieldRoot */ - Root?: React.ElementType; + root?: React.ElementType; /** * Form control with an input to render a date. * It is rendered twice: once for the start date and once for the end date. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; /** * Element rendered between the two inputs. * @default MultiInputDateRangeFieldSeparator */ - Separator?: React.ElementType; + separator?: React.ElementType; } export interface MultiInputDateRangeFieldSlotsComponentsProps { diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.conformance.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.conformance.test.tsx index 1fd6874768b68..939bf28a56a3f 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.conformance.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.conformance.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { MultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; @@ -14,6 +14,6 @@ describe('', () => { wrapMount: wrapPickerMount, refInstanceof: window.HTMLDivElement, // cannot test reactTestRenderer because of required context - skip: ['reactTestRenderer', 'themeVariants'], + skip: ['reactTestRenderer', 'themeVariants', 'componentProp', 'componentsProp'], })); }); diff --git a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.validation.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.validation.test.tsx index d06b6008c5c6f..56348ce63bb1a 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.validation.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateRangeField/tests/MultiInputDateRangeField.validation.test.tsx @@ -1,8 +1,7 @@ -import { screen } from '@mui/monorepo/test/utils'; -import { fireEvent } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils'; +import { fireEvent } from '@mui-internal/test-utils/createRenderer'; import { MultiInputDateRangeField } from '@mui/x-date-pickers-pro/MultiInputDateRangeField'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; +import { createPickerRenderer, adapterToUse, describeRangeValidation } from 'test/utils/pickers'; describe('', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx index a270d81260412..89dbf674403f7 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.tsx @@ -14,7 +14,6 @@ import { import { splitFieldInternalAndForwardedProps, FieldsTextFieldProps, - uncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { MultiInputDateTimeRangeFieldProps } from './MultiInputDateTimeRangeField.types'; import { useMultiInputDateTimeRangeField } from '../internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField'; @@ -61,6 +60,16 @@ type MultiInputDateTimeRangeFieldComponent = (( props: MultiInputDateTimeRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimeRangeField](http://mui.com/x/react-date-pickers/date-time-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [MultiInputDateTimeRangeField API](https://mui.com/x/api/multi-input-date-time-range-field/) + */ const MultiInputDateTimeRangeField = React.forwardRef(function MultiInputDateTimeRangeField( inProps: MultiInputDateTimeRangeFieldProps, ref: React.Ref, @@ -73,14 +82,15 @@ const MultiInputDateTimeRangeField = React.forwardRef(function MultiInputDateTim const { internalProps: dateTimeFieldInternalProps, forwardedProps } = splitFieldInternalAndForwardedProps< typeof themeProps, - keyof Omit, 'unstableFieldRef' | 'disabled'> + keyof Omit< + UseDateTimeRangeFieldProps, + 'unstableFieldRef' | 'disabled' | 'clearable' | 'onClear' + > >(themeProps, 'date-time'); const { - slots: innerSlots, - slotProps: innerSlotProps, - components, - componentsProps, + slots, + slotProps, disabled, autoFocus, unstableStartFieldRef, @@ -89,9 +99,6 @@ const MultiInputDateTimeRangeField = React.forwardRef(function MultiInputDateTim ...otherForwardedProps } = forwardedProps; - const slots = innerSlots ?? uncapitalizeObjectKeys(components); - const slotProps = innerSlotProps ?? componentsProps; - const ownerState = themeProps; const classes = useUtilityClasses(ownerState); @@ -206,18 +213,6 @@ MultiInputDateTimeRangeField.propTypes = { classes: PropTypes.object, className: PropTypes.string, component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -367,6 +362,9 @@ MultiInputDateTimeRangeField.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.types.ts index b2e96bd6e6a73..1eb9ab21bca7e 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/MultiInputDateTimeRangeField.types.ts @@ -4,7 +4,6 @@ import { SlotComponentProps } from '@mui/base/utils'; import Typography from '@mui/material/Typography'; import Stack, { StackProps } from '@mui/material/Stack'; import TextField from '@mui/material/TextField'; -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; import { UseDateTimeRangeFieldDefaultizedProps, UseDateTimeRangeFieldProps, @@ -20,7 +19,7 @@ export type UseMultiInputDateTimeRangeFieldParams< > = UseMultiInputRangeFieldParams, TTextFieldSlotProps>; export interface UseMultiInputDateTimeRangeFieldProps - extends Omit, 'unstableFieldRef'> { + extends Omit, 'unstableFieldRef' | 'clearable' | 'onClear'> { unstableStartFieldRef?: React.Ref>; unstableEndFieldRef?: React.Ref>; } @@ -38,23 +37,11 @@ export interface MultiInputDateTimeRangeFieldProps * Override or extend the styles applied to the component. */ classes?: Partial; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MultiInputDateTimeRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MultiInputDateTimeRangeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: MultiInputDateTimeRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -70,19 +57,19 @@ export interface MultiInputDateTimeRangeFieldSlotsComponent { * Element rendered at the root. * @default MultiInputDateTimeRangeFieldRoot */ - Root?: React.ElementType; + root?: React.ElementType; /** * Form control with an input to render a date and time. * It is rendered twice: once for the start date time and once for the end date time. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; /** * Element rendered between the two inputs. * @default MultiInputDateTimeRangeFieldSeparator */ - Separator?: React.ElementType; + separator?: React.ElementType; } export interface MultiInputDateTimeRangeFieldSlotsComponentsProps { diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.conformance.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.conformance.test.tsx index 42f3a8147ac96..81c4865ce0c1a 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.conformance.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.conformance.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputDateTimeRangeField'; import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; @@ -14,6 +14,6 @@ describe('', () => { wrapMount: wrapPickerMount, refInstanceof: window.HTMLDivElement, // cannot test reactTestRenderer because of required context - skip: ['reactTestRenderer', 'themeVariants'], + skip: ['componentProp', 'componentsProp', 'reactTestRenderer', 'themeVariants'], })); }); diff --git a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx index 668f0da15caee..d31a5dbcedc31 100644 --- a/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputDateTimeRangeField/tests/MultiInputDateTimeRangeField.validation.test.tsx @@ -1,8 +1,7 @@ -import { screen } from '@mui/monorepo/test/utils'; -import { fireEvent } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils'; +import { fireEvent } from '@mui-internal/test-utils/createRenderer'; import { MultiInputDateTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputDateTimeRangeField'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; +import { createPickerRenderer, adapterToUse, describeRangeValidation } from 'test/utils/pickers'; describe('', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx index 350be349f524d..0fc88a4f63e1f 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.tsx @@ -14,7 +14,6 @@ import { import { splitFieldInternalAndForwardedProps, FieldsTextFieldProps, - uncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { MultiInputTimeRangeFieldProps } from './MultiInputTimeRangeField.types'; import { useMultiInputTimeRangeField } from '../internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField'; @@ -63,6 +62,16 @@ type MultiInputTimeRangeFieldComponent = (( props: MultiInputTimeRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimeRangeField](http://mui.com/x/react-date-pickers/time-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [MultiInputTimeRangeField API](https://mui.com/x/api/multi-input-time-range-field/) + */ const MultiInputTimeRangeField = React.forwardRef(function MultiInputTimeRangeField( inProps: MultiInputTimeRangeFieldProps, ref: React.Ref, @@ -75,14 +84,15 @@ const MultiInputTimeRangeField = React.forwardRef(function MultiInputTimeRangeFi const { internalProps: timeFieldInternalProps, forwardedProps } = splitFieldInternalAndForwardedProps< typeof themeProps, - keyof Omit, 'unstableFieldRef' | 'disabled'> + keyof Omit< + UseTimeRangeFieldProps, + 'unstableFieldRef' | 'disabled' | 'clearable' | 'onClear' + > >(themeProps, 'time'); const { - slots: innerSlots, - slotProps: innerSlotProps, - components, - componentsProps, + slots, + slotProps, disabled, autoFocus, unstableStartFieldRef, @@ -91,9 +101,6 @@ const MultiInputTimeRangeField = React.forwardRef(function MultiInputTimeRangeFi ...otherForwardedProps } = forwardedProps; - const slots = innerSlots ?? uncapitalizeObjectKeys(components); - const slotProps = innerSlotProps ?? componentsProps; - const ownerState = themeProps; const classes = useUtilityClasses(ownerState); @@ -209,18 +216,6 @@ MultiInputTimeRangeField.propTypes = { classes: PropTypes.object, className: PropTypes.string, component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.types.ts index 16530f7b418cd..fae4dc6f5dc22 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/MultiInputTimeRangeField.types.ts @@ -4,7 +4,6 @@ import Typography from '@mui/material/Typography'; import Stack, { StackProps } from '@mui/material/Stack'; import TextField from '@mui/material/TextField'; import { FieldRef } from '@mui/x-date-pickers/models'; -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; import { UseTimeRangeFieldDefaultizedProps, UseTimeRangeFieldProps, @@ -20,7 +19,7 @@ export type UseMultiInputTimeRangeFieldParams< > = UseMultiInputRangeFieldParams, TTextFieldSlotProps>; export interface UseMultiInputTimeRangeFieldProps - extends Omit, 'unstableFieldRef'> { + extends Omit, 'unstableFieldRef' | 'clearable' | 'onClear'> { unstableStartFieldRef?: React.Ref>; unstableEndFieldRef?: React.Ref>; } @@ -38,23 +37,11 @@ export interface MultiInputTimeRangeFieldProps * Override or extend the styles applied to the component. */ classes?: Partial; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MultiInputTimeRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MultiInputTimeRangeFieldSlotsComponentsProps; /** * Overridable slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: MultiInputTimeRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -69,19 +56,19 @@ export interface MultiInputTimeRangeFieldSlotsComponent { * Element rendered at the root. * @default MultiInputTimeRangeFieldRoot */ - Root?: React.ElementType; + root?: React.ElementType; /** * Form control with an input to render a time. * It is rendered twice: once for the start time and once for the end time. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; /** * Element rendered between the two inputs. * @default MultiInputTimeRangeFieldSeparator */ - Separator?: React.ElementType; + separator?: React.ElementType; } export interface MultiInputTimeRangeFieldSlotsComponentsProps { diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.conformance.test.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.conformance.test.tsx index 7d2eadc7584eb..218ae21f60292 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.conformance.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.conformance.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { MultiInputTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputTimeRangeField'; import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; @@ -14,6 +14,6 @@ describe('', () => { wrapMount: wrapPickerMount, refInstanceof: window.HTMLDivElement, // cannot test reactTestRenderer because of required context - skip: ['reactTestRenderer', 'themeVariants'], + skip: ['reactTestRenderer', 'themeVariants', 'componentProp', 'componentsProp'], })); }); diff --git a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx index 66e84d419bc69..18980367909e3 100644 --- a/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx +++ b/packages/x-date-pickers-pro/src/MultiInputTimeRangeField/tests/MultiInputTimeRangeField.validation.test.tsx @@ -1,8 +1,7 @@ -import { screen } from '@mui/monorepo/test/utils'; -import { fireEvent } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils'; +import { fireEvent } from '@mui-internal/test-utils/createRenderer'; import { MultiInputTimeRangeField } from '@mui/x-date-pickers-pro/MultiInputTimeRangeField'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; +import { createPickerRenderer, adapterToUse, describeRangeValidation } from 'test/utils/pickers'; describe('', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx index fea8b89623953..642835ac3ba07 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.tsx @@ -3,14 +3,29 @@ import PropTypes from 'prop-types'; import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; import { refType } from '@mui/utils'; -import { SingleInputDateRangeFieldProps } from './SingleInputDateRangeField.types'; +import { + SingleInputDateRangeFieldProps, + SingleInputDateRangeFieldSlotsComponentsProps, + SingleInputDateRangeFieldSlotsComponent, +} from './SingleInputDateRangeField.types'; import { useSingleInputDateRangeField } from './useSingleInputDateRangeField'; type DateRangeFieldComponent = (( props: SingleInputDateRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any; fieldType?: string }; +/** + * Demos: + * + * - [DateRangeField](http://mui.com/x/react-date-pickers/date-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [SingleInputDateRangeField API](https://mui.com/x/api/single-input-date-range-field/) + */ const SingleInputDateRangeField = React.forwardRef(function SingleInputDateRangeField( inProps: SingleInputDateRangeFieldProps, ref: React.Ref, @@ -20,16 +35,15 @@ const SingleInputDateRangeField = React.forwardRef(function SingleInputDateRange name: 'MuiSingleInputDateRangeField', }); - const { slots, slotProps, components, componentsProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: SingleInputDateRangeFieldProps = useSlotProps({ elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }); @@ -44,17 +58,33 @@ const SingleInputDateRangeField = React.forwardRef(function SingleInputDateRange onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useSingleInputDateRangeField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + SingleInputDateRangeFieldSlotsComponent, + SingleInputDateRangeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); @@ -73,26 +103,19 @@ SingleInputDateRangeField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -201,6 +224,10 @@ SingleInputDateRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. @@ -261,6 +288,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts index 4ae01f6458062..96acceeadeb1e 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/SingleInputDateRangeField.types.ts @@ -1,7 +1,11 @@ import * as React from 'react'; import { SlotComponentProps } from '@mui/base/utils'; import TextField from '@mui/material/TextField'; -import { FieldsTextFieldProps, UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { + FieldsTextFieldProps, + FieldSlotsComponents, + FieldSlotsComponentsProps, +} from '@mui/x-date-pickers/internals'; import { UseDateRangeFieldDefaultizedProps, UseDateRangeFieldProps } from '../internals/models'; export interface UseSingleInputDateRangeFieldParams { @@ -27,23 +31,11 @@ export type SingleInputDateRangeFieldProps< TDate, TChildProps extends {} = FieldsTextFieldProps, > = UseSingleInputDateRangeFieldComponentProps & { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: SingleInputDateRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: SingleInputDateRangeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: SingleInputDateRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -53,15 +45,16 @@ export type SingleInputDateRangeFieldProps< export type SingleInputDateRangeFieldOwnerState = SingleInputDateRangeFieldProps; -export interface SingleInputDateRangeFieldSlotsComponent { +export interface SingleInputDateRangeFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface SingleInputDateRangeFieldSlotsComponentsProps { +export interface SingleInputDateRangeFieldSlotsComponentsProps + extends FieldSlotsComponentsProps { textField?: SlotComponentProps>; } diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/describes.SingleInputDateRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/describes.SingleInputDateRangeField.test.tsx index 60cbf6e1613f2..0a79c9e39c543 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/describes.SingleInputDateRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/describes.SingleInputDateRangeField.test.tsx @@ -1,9 +1,8 @@ import * as React from 'react'; import TextField from '@mui/material/TextField'; -import { describeConformance } from '@mui/monorepo/test/utils'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; +import { describeConformance } from '@mui-internal/test-utils'; import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; +import { createPickerRenderer, wrapPickerMount, describeRangeValidation } from 'test/utils/pickers'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx index 606217e07faca..f24be9b99be94 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/editing.SingleInputDateRangeField.test.tsx @@ -1,119 +1,194 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { userEvent, fireEvent } from '@mui/monorepo/test/utils'; -import { expectInputValue } from 'test/utils/pickers'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; +import { userEvent, fireEvent } from '@mui-internal/test-utils'; +import { expectInputValue, describeAdapters } from 'test/utils/pickers'; describe(' - Editing', () => { - ['Backspace', 'Delete'].forEach((keyToClearValue) => { - describeAdapters( - `key: ${keyToClearValue}`, - SingleInputDateRangeField, - ({ adapter, renderWithProps }) => { - it('should clear all the sections when all sections are selected and all sections are completed', () => { - const { input, selectSection } = renderWithProps({ - defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], - format: `${adapter.formats.month} ${adapter.formats.year}`, - }); - - selectSection('month'); - - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'MMMM YYYY – MMMM YYYY'); - }); - - it('should clear all the sections when all sections are selected and not all sections are completed', () => { - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - }); - - selectSection('month'); - - // Set a value for the "month" section - fireEvent.change(input, { - target: { value: 'j YYYY – MMMM YYYY' }, - }); // Press "j" - expectInputValue(input, 'January YYYY – MMMM YYYY'); - - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + describeAdapters(`key: Delete`, SingleInputDateRangeField, ({ adapter, renderWithProps }) => { + it('should clear all the sections when all sections are selected and all sections are completed', () => { + const { input, selectSection } = renderWithProps({ + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + format: `${adapter.formats.month} ${adapter.formats.year}`, + }); + + selectSection('month'); + + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'MMMM YYYY – MMMM YYYY'); + }); + + it('should clear all the sections when all sections are selected and not all sections are completed', () => { + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + }); + + selectSection('month'); + + // Set a value for the "month" section + fireEvent.change(input, { + target: { value: 'j YYYY – MMMM YYYY' }, + }); // Press "j" + expectInputValue(input, 'January YYYY – MMMM YYYY'); + + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'MMMM YYYY – MMMM YYYY'); + }); + + it('should not call `onChange` when clearing all sections and both dates are already empty', () => { + const onChange = spy(); + + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: [null, null], + onChange, + }); + + selectSection('month'); + + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + + userEvent.keyPress(input, { key: 'Delete' }); + expect(onChange.callCount).to.equal(0); + }); + + it('should call `onChange` when clearing the each section of each date', () => { + const handleChange = spy(); + + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + onChange: handleChange, + }); + + selectSection('month'); + + // Start date + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); + userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(2); + expect(handleChange.lastCall.firstArg[0]).to.equal(null); + expect(handleChange.lastCall.firstArg[1]).toEqualDateTime( + adapter.addYears(adapter.date(), 1), + ); + + // End date + userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(3); + userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(4); + expect(handleChange.lastCall.firstArg[0]).to.equal(null); + expect(handleChange.lastCall.firstArg[1]).to.equal(null); + }); + + it('should not call `onChange` if the section is already empty', () => { + const handleChange = spy(); + + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + onChange: handleChange, + }); + + selectSection('month'); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); + + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); + }); + }); - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'MMMM YYYY – MMMM YYYY'); + describeAdapters( + `Backspace editing`, + SingleInputDateRangeField, + ({ adapter, renderWithProps, testFieldChange }) => { + it('should clear all the sections when all sections are selected and all sections are completed (Backspace)', () => { + testFieldChange({ + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + format: `${adapter.formats.month} ${adapter.formats.year}`, + keyStrokes: [{ value: '', expected: 'MMMM YYYY – MMMM YYYY' }], + }); + }); + + it('should clear all the sections when all sections are selected and not all sections are completed (Backspace)', () => { + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + keyStrokes: [ + { value: 'j YYYY – MMMM YYYY', expected: 'January YYYY – MMMM YYYY' }, + { value: '', expected: 'MMMM YYYY – MMMM YYYY' }, + ], }); + }); - it('should not call `onChange` when clearing all sections and both dates are already empty', () => { - const onChange = spy(); + it('should not call `onChange` when clearing all sections and both dates are already empty (Backspace)', () => { + const onChange = spy(); - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: [null, null], - onChange, - }); + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + keyStrokes: [{ value: '', expected: 'MMMM YYYY – MMMM YYYY' }], + }); - selectSection('month'); + expect(onChange.callCount).to.equal(0); + }); - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + it('should call `onChange` when clearing the each section of each date (Backspace)', () => { + const onChange = spy(); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(onChange.callCount).to.equal(0); + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + onChange, }); - it('should call `onChange` when clearing the each section of each date', () => { - const handleChange = spy(); - - const { selectSection, input } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], - onChange: handleChange, - }); - - selectSection('month'); - - // Start date - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); - userEvent.keyPress(input, { key: 'ArrowRight' }); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(2); - expect(handleChange.lastCall.firstArg[0]).to.equal(null); - expect(handleChange.lastCall.firstArg[1]).toEqualDateTime( - adapter.addYears(adapter.date(), 1), - ); - - // End date - userEvent.keyPress(input, { key: 'ArrowRight' }); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(3); - userEvent.keyPress(input, { key: 'ArrowRight' }); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(4); - expect(handleChange.lastCall.firstArg[0]).to.equal(null); - expect(handleChange.lastCall.firstArg[1]).to.equal(null); + selectSection('month'); + + // Start date + fireEvent.change(input, { target: { value: ' 2022 – June 2023' } }); + expect(onChange.callCount).to.equal(1); + userEvent.keyPress(input, { key: 'ArrowRight' }); + fireEvent.change(input, { target: { value: 'MMMM – June 2023' } }); + expect(onChange.callCount).to.equal(2); + expect(onChange.lastCall.firstArg[0]).to.equal(null); + expect(onChange.lastCall.firstArg[1]).toEqualDateTime(adapter.addYears(adapter.date(), 1)); + + // End date + userEvent.keyPress(input, { key: 'ArrowRight' }); + fireEvent.change(input, { target: { value: 'MMMM YYYY – 2023' } }); + expect(onChange.callCount).to.equal(3); + userEvent.keyPress(input, { key: 'ArrowRight' }); + fireEvent.change(input, { target: { value: 'MMMM YYYY – MMMM ' } }); + expect(onChange.callCount).to.equal(4); + expect(onChange.lastCall.firstArg[0]).to.equal(null); + expect(onChange.lastCall.firstArg[1]).to.equal(null); + }); + + it('should not call `onChange` if the section is already empty (Backspace)', () => { + const onChange = spy(); + + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], + onChange, + keyStrokes: [ + { value: ' 2022 – June 2023', expected: 'MMMM 2022 – June 2023' }, + { value: ' 2022 – June 2023', expected: 'MMMM 2022 – June 2023' }, + ], }); - it('should not call `onChange` if the section is already empty', () => { - const handleChange = spy(); - - const { selectSection, input } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: [adapter.date(), adapter.addYears(adapter.date(), 1)], - onChange: handleChange, - }); - - selectSection('month'); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); - - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); - }); - }, - ); - }); + expect(onChange.callCount).to.equal(1); + }); + }, + ); }); diff --git a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/selection.SingleInputDateRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/selection.SingleInputDateRangeField.test.tsx index c5fd65018455e..063554cf19cc1 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/selection.SingleInputDateRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateRangeField/tests/selection.SingleInputDateRangeField.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { SingleInputDateRangeField } from '@mui/x-date-pickers-pro/SingleInputDateRangeField'; -import { act, userEvent } from '@mui/monorepo/test/utils'; +import { act, userEvent } from '@mui-internal/test-utils'; import { adapterToUse, buildFieldInteractions, diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx index bf7ef82c2b1b9..1f7a4e58dbaec 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.tsx @@ -3,14 +3,29 @@ import PropTypes from 'prop-types'; import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; import { refType } from '@mui/utils'; -import { SingleInputDateTimeRangeFieldProps } from './SingleInputDateTimeRangeField.types'; +import { + SingleInputDateTimeRangeFieldProps, + SingleInputDateTimeRangeFieldSlotsComponent, + SingleInputDateTimeRangeFieldSlotsComponentsProps, +} from './SingleInputDateTimeRangeField.types'; import { useSingleInputDateTimeRangeField } from './useSingleInputDateTimeRangeField'; type DateRangeFieldComponent = (( props: SingleInputDateTimeRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any; fieldType?: string }; +/** + * Demos: + * + * - [DateTimeRangeField](http://mui.com/x/react-date-pickers/date-time-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [SingleInputDateTimeRangeField API](https://mui.com/x/api/single-input-date-time-range-field/) + */ const SingleInputDateTimeRangeField = React.forwardRef(function SingleInputDateTimeRangeField< TDate, >(inProps: SingleInputDateTimeRangeFieldProps, ref: React.Ref) { @@ -19,18 +34,17 @@ const SingleInputDateTimeRangeField = React.forwardRef(function SingleInputDateT name: 'MuiSingleInputDateTimeRangeField', }); - const { slots, slotProps, components, componentsProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: SingleInputDateTimeRangeFieldProps = useSlotProps({ elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }); @@ -45,17 +59,33 @@ const SingleInputDateTimeRangeField = React.forwardRef(function SingleInputDateT onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useSingleInputDateTimeRangeField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + SingleInputDateTimeRangeFieldSlotsComponent, + SingleInputDateTimeRangeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); @@ -79,26 +109,19 @@ SingleInputDateTimeRangeField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -235,6 +258,10 @@ SingleInputDateTimeRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. @@ -303,6 +330,9 @@ SingleInputDateTimeRangeField.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts index 0b72006f6fe93..d459fcf2dc914 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/SingleInputDateTimeRangeField.types.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { SlotComponentProps } from '@mui/base/utils'; import TextField from '@mui/material/TextField'; import { FieldsTextFieldProps } from '@mui/x-date-pickers/internals/models/fields'; -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '@mui/x-date-pickers/internals'; import { UseDateTimeRangeFieldDefaultizedProps, UseDateTimeRangeFieldProps, @@ -29,23 +29,11 @@ export type UseSingleInputDateTimeRangeFieldComponentProps extends UseSingleInputDateTimeRangeFieldComponentProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: SingleInputDateTimeRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: SingleInputDateTimeRangeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: SingleInputDateTimeRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -56,16 +44,17 @@ export interface SingleInputDateTimeRangeFieldProps export type SingleInputDateTimeRangeFieldOwnerState = SingleInputDateTimeRangeFieldProps; -export interface SingleInputDateTimeRangeFieldSlotsComponent { +export interface SingleInputDateTimeRangeFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface SingleInputDateTimeRangeFieldSlotsComponentsProps { +export interface SingleInputDateTimeRangeFieldSlotsComponentsProps + extends FieldSlotsComponentsProps { textField?: SlotComponentProps< typeof TextField, {}, diff --git a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/tests/describes.SingleInputDateTimeRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/tests/describes.SingleInputDateTimeRangeField.test.tsx index f21a198634449..6535d00aba2d2 100644 --- a/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/tests/describes.SingleInputDateTimeRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputDateTimeRangeField/tests/describes.SingleInputDateTimeRangeField.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { SingleInputDateTimeRangeField } from '@mui/x-date-pickers-pro/SingleInputDateTimeRangeField'; -import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; +import { createPickerRenderer, wrapPickerMount, describeRangeValidation } from 'test/utils/pickers'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx index d4d28798cc9dd..2ee3ab055bb8c 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.tsx @@ -1,16 +1,31 @@ import * as React from 'react'; import PropTypes from 'prop-types'; +import { useClearableField } from '@mui/x-date-pickers/hooks'; import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; import { refType } from '@mui/utils'; -import { SingleInputTimeRangeFieldProps } from './SingleInputTimeRangeField.types'; +import { + SingleInputTimeRangeFieldProps, + SingleInputTimeRangeFieldSlotsComponent, + SingleInputTimeRangeFieldSlotsComponentsProps, +} from './SingleInputTimeRangeField.types'; import { useSingleInputTimeRangeField } from './useSingleInputTimeRangeField'; type DateRangeFieldComponent = (( props: SingleInputTimeRangeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any; fieldType?: string }; +/** + * Demos: + * + * - [TimeRangeField](http://mui.com/x/react-date-pickers/time-range-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [SingleInputTimeRangeField API](https://mui.com/x/api/single-input-time-range-field/) + */ const SingleInputTimeRangeField = React.forwardRef(function SingleInputTimeRangeField( inProps: SingleInputTimeRangeFieldProps, ref: React.Ref, @@ -20,16 +35,15 @@ const SingleInputTimeRangeField = React.forwardRef(function SingleInputTimeRange name: 'MuiSingleInputTimeRangeField', }); - const { slots, slotProps, components, componentsProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: SingleInputTimeRangeFieldProps = useSlotProps({ elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }); @@ -44,17 +58,33 @@ const SingleInputTimeRangeField = React.forwardRef(function SingleInputTimeRange onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useSingleInputTimeRangeField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + SingleInputTimeRangeFieldSlotsComponent, + SingleInputTimeRangeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); @@ -78,26 +108,19 @@ SingleInputTimeRangeField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -218,6 +241,10 @@ SingleInputTimeRangeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts index 6ce1602f23a32..60f049de08eb2 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/SingleInputTimeRangeField.types.ts @@ -2,7 +2,7 @@ import * as React from 'react'; import { SlotComponentProps } from '@mui/base/utils'; import TextField from '@mui/material/TextField'; import { FieldsTextFieldProps } from '@mui/x-date-pickers/internals/models/fields'; -import { UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '@mui/x-date-pickers/internals'; import { UseTimeRangeFieldDefaultizedProps, UseTimeRangeFieldProps } from '../internals/models'; export interface UseSingleInputTimeRangeFieldParams { @@ -25,23 +25,11 @@ export type UseSingleInputTimeRangeFieldComponentProps extends UseSingleInputTimeRangeFieldComponentProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: SingleInputTimeRangeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: SingleInputTimeRangeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: SingleInputTimeRangeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -51,15 +39,16 @@ export interface SingleInputTimeRangeFieldProps export type SingleInputTimeRangeFieldOwnerState = SingleInputTimeRangeFieldProps; -export interface SingleInputTimeRangeFieldSlotsComponent { +export interface SingleInputTimeRangeFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface SingleInputTimeRangeFieldSlotsComponentsProps { +export interface SingleInputTimeRangeFieldSlotsComponentsProps + extends FieldSlotsComponentsProps { textField?: SlotComponentProps>; } diff --git a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/tests/describes.SingleInputTimeRangeField.test.tsx b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/tests/describes.SingleInputTimeRangeField.test.tsx index d7371a8e5c45b..01d212338c9e6 100644 --- a/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/tests/describes.SingleInputTimeRangeField.test.tsx +++ b/packages/x-date-pickers-pro/src/SingleInputTimeRangeField/tests/describes.SingleInputTimeRangeField.test.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; +import { describeConformance } from '@mui-internal/test-utils'; import { SingleInputTimeRangeField } from '@mui/x-date-pickers-pro/SingleInputTimeRangeField'; -import { createPickerRenderer, wrapPickerMount } from 'test/utils/pickers'; +import { createPickerRenderer, wrapPickerMount, describeRangeValidation } from 'test/utils/pickers'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx index 863633dec6de5..b3402d6664b95 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.test.tsx @@ -2,9 +2,13 @@ import * as React from 'react'; import { expect } from 'chai'; import { isWeekend } from 'date-fns'; import { StaticDateRangePicker } from '@mui/x-date-pickers-pro/StaticDateRangePicker'; -import { describeConformance, screen } from '@mui/monorepo/test/utils'; -import { wrapPickerMount, createPickerRenderer, adapterToUse } from 'test/utils/pickers'; -import { describeRangeValidation } from '@mui/x-date-pickers-pro/tests/describeRangeValidation'; +import { describeConformance, screen } from '@mui-internal/test-utils'; +import { + wrapPickerMount, + createPickerRenderer, + adapterToUse, + describeRangeValidation, +} from 'test/utils/pickers'; describe('', () => { const { render, clock } = createPickerRenderer({ diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx index 0e50baea9fa6e..f717ce61f30d8 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.tsx @@ -13,6 +13,16 @@ type StaticDateRangePickerComponent = (( props: StaticDateRangePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateRangePicker](https://mui.com/x/react-date-pickers/date-range-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [StaticDateRangePicker API](https://mui.com/x/api/date-pickers/static-date-range-picker/) + */ const StaticDateRangePicker = React.forwardRef(function StaticDateRangePicker( inProps: StaticDateRangePickerProps, ref: React.Ref, @@ -78,18 +88,6 @@ StaticDateRangePicker.propTypes = { * Class name applied to the root element. */ className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Position the current month is rendered in. * @default 1 @@ -97,9 +95,10 @@ StaticDateRangePicker.propTypes = { currentMonthCalendarPosition: PropTypes.oneOf([1, 2, 3]), /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -246,6 +245,9 @@ StaticDateRangePicker.propTypes = { renderLoading: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.types.ts b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.types.ts index c2e4022929805..0a7d9429f2a6a 100644 --- a/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/StaticDateRangePicker/StaticDateRangePicker.types.ts @@ -1,4 +1,4 @@ -import { MakeOptional, UncapitalizeObjectKeys } from '@mui/x-date-pickers/internals'; +import { MakeOptional } from '@mui/x-date-pickers/internals'; import { StaticRangeOnlyPickerProps, UseStaticRangePickerSlotsComponent, @@ -21,23 +21,11 @@ export interface StaticDateRangePickerSlotsComponentsProps export interface StaticDateRangePickerProps extends BaseDateRangePickerProps, MakeOptional { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: StaticDateRangePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: StaticDateRangePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: StaticDateRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx b/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx index 8722b3cf2fdce..8ed9b3cc52459 100644 --- a/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx +++ b/packages/x-date-pickers-pro/src/dateRangeViewRenderers/dateRangeViewRenderers.tsx @@ -33,8 +33,6 @@ export const renderDateRangeViewCalendar = ({ onRangePositionChange, calendars, currentMonthCalendarPosition, - components, - componentsProps, slots, slotProps, loading, @@ -72,8 +70,6 @@ export const renderDateRangeViewCalendar = ({ onRangePositionChange={onRangePositionChange} calendars={calendars} currentMonthCalendarPosition={currentMonthCalendarPosition} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} loading={loading} diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts index cf012f9e28174..26887c2ff42f2 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useDesktopRangePicker/useDesktopRangePicker.types.ts @@ -5,7 +5,6 @@ import { PickersPopperSlotsComponentsProps, ExportedBaseToolbarProps, UsePickerViewsProps, - UncapitalizeObjectKeys, BaseNonStaticPickerProps, UsePickerValueNonStaticProps, UsePickerViewsNonStaticProps, @@ -68,7 +67,7 @@ export interface UseDesktopRangePickerProps< * Overridable component slots. * @default {} */ - slots: UncapitalizeObjectKeys>; + slots: UseDesktopRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts index 0bb6dccd50e2b..95dae2061cfc6 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useEnrichedRangePickerFieldProps.ts @@ -14,9 +14,10 @@ import { useLocaleText, UsePickerResponse, WrapperVariant, - UncapitalizeObjectKeys, UsePickerProps, getActiveElement, + FieldSlotsComponents, + FieldSlotsComponentsProps, } from '@mui/x-date-pickers/internals'; import { BaseMultiInputFieldProps, @@ -29,28 +30,28 @@ import { } from '../models'; import { UseRangePositionResponse } from './useRangePosition'; -export interface RangePickerFieldSlotsComponent { - Field: React.ElementType; +export interface RangePickerFieldSlotsComponent extends FieldSlotsComponents { + field: React.ElementType; /** * Element rendered at the root. * Ignored if the field has only one input. */ - FieldRoot?: React.ElementType; + fieldRoot?: React.ElementType; /** * Element rendered between the two inputs. * Ignored if the field has only one input. */ - FieldSeparator?: React.ElementType; + fieldSeparator?: React.ElementType; /** * Form control with an input to render a date or time inside the default field. * It is rendered twice: once for the start element and once for the end element. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface RangePickerFieldSlotsComponentsProps { +export interface RangePickerFieldSlotsComponentsProps extends FieldSlotsComponentsProps { field?: SlotComponentProps< React.ElementType< BaseMultiInputFieldProps, TDate, RangeFieldSection, unknown> @@ -93,7 +94,7 @@ export interface UseEnrichedRangePickerFieldPropsParams< label?: React.ReactNode; localeText: PickersInputLocaleText | undefined; pickerSlotProps: RangePickerFieldSlotsComponentsProps | undefined; - pickerSlots: UncapitalizeObjectKeys | undefined; + pickerSlots: RangePickerFieldSlotsComponent | undefined; fieldProps: FieldProps; anchorRef?: React.Ref; } @@ -214,6 +215,7 @@ const useMultiInputFieldSlotProps = >; + slots: UseMobileRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts new file mode 100644 index 0000000000000..f3568f99ad13d --- /dev/null +++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/shared.ts @@ -0,0 +1,12 @@ +/* TODO: remove this when a clearable behavior for multiple input range fields is implemented */ +export const excludeProps = ( + props: TProps, + excludedProps: Array, +): TProps => { + return (Object.keys(props) as Array).reduce((acc, key) => { + if (!excludedProps.includes(key)) { + acc[key] = props[key]; + } + return acc; + }, {} as TProps); +}; diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts index a6c72af7cdb9c..1f9dc7c1e5211 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateRangeField.ts @@ -24,6 +24,7 @@ import { import { rangeValueManager } from '../../utils/valueManagers'; import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types'; import { DateRangeValidationError } from '../../../models'; +import { excludeProps } from './shared'; export const useMultiInputDateRangeField = ({ sharedProps: inSharedProps, @@ -151,5 +152,9 @@ export const useMultiInputDateRangeField = ; - return { startDate: startDateResponse, endDate: endDateResponse }; + /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */ + return { + startDate: excludeProps(startDateResponse, ['clearable', 'onClear']), + endDate: excludeProps(endDateResponse, ['clearable', 'onClear']), + }; }; diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts index 5d9a6770c7767..54bedc812e8fa 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputDateTimeRangeField.ts @@ -30,6 +30,7 @@ import { } from '../../utils/validation/validateDateTimeRange'; import { rangeValueManager } from '../../utils/valueManagers'; import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types'; +import { excludeProps } from './shared'; export const useDefaultizedDateTimeRangeFieldProps = ( props: UseMultiInputDateTimeRangeFieldProps, @@ -81,6 +82,8 @@ export const useMultiInputDateTimeRangeField = ; - return { startDate: startDateResponse, endDate: endDateResponse }; + /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */ + return { + startDate: excludeProps(startDateResponse, ['clearable', 'onClear']), + endDate: excludeProps(endDateResponse, ['clearable', 'onClear']), + }; }; diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts index 092b9e8b121aa..37e409f12fed4 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useMultiInputRangeField/useMultiInputTimeRangeField.ts @@ -28,6 +28,7 @@ import type { } from '../../../MultiInputTimeRangeField/MultiInputTimeRangeField.types'; import { rangeValueManager } from '../../utils/valueManagers'; import type { UseMultiInputRangeFieldResponse } from './useMultiInputRangeField.types'; +import { excludeProps } from './shared'; export const useDefaultizedTimeRangeFieldProps = ( props: UseMultiInputTimeRangeFieldProps, @@ -71,6 +72,8 @@ export const useMultiInputTimeRangeField = ; - return { startDate: startDateResponse, endDate: endDateResponse }; + /* TODO: Undo this change when a clearable behavior for multiple input range fields is implemented */ + return { + startDate: excludeProps(startDateResponse, ['clearable', 'onClear']), + endDate: excludeProps(endDateResponse, ['clearable', 'onClear']), + }; }; diff --git a/packages/x-date-pickers-pro/src/internals/hooks/useStaticRangePicker/useStaticRangePicker.types.ts b/packages/x-date-pickers-pro/src/internals/hooks/useStaticRangePicker/useStaticRangePicker.types.ts index c68398447ffda..40d4f1e11926e 100644 --- a/packages/x-date-pickers-pro/src/internals/hooks/useStaticRangePicker/useStaticRangePicker.types.ts +++ b/packages/x-date-pickers-pro/src/internals/hooks/useStaticRangePicker/useStaticRangePicker.types.ts @@ -4,7 +4,6 @@ import { UsePickerParams, ExportedBaseToolbarProps, StaticOnlyPickerProps, - UncapitalizeObjectKeys, } from '@mui/x-date-pickers/internals'; import { ExportedPickersLayoutSlotsComponent, @@ -38,7 +37,7 @@ export interface UseStaticRangePickerProps< * Overridable components. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: UseStaticRangePickerSlotsComponent; /** * The props used for each component slot. * @default {} 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 e509679ab14d0..1be8b780ea7b2 100644 --- a/packages/x-date-pickers-pro/src/internals/models/dateRange.ts +++ b/packages/x-date-pickers-pro/src/internals/models/dateRange.ts @@ -14,6 +14,9 @@ import { RangeFieldSection } from './fields'; 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. + * * @template TDate * @param {TDate} day The date to test. * @param {string} position The date to test, 'start' or 'end'. diff --git a/packages/x-date-pickers-pro/src/internals/models/fields.ts b/packages/x-date-pickers-pro/src/internals/models/fields.ts index c4d8a103eb8c8..77e5050fe5d56 100644 --- a/packages/x-date-pickers-pro/src/internals/models/fields.ts +++ b/packages/x-date-pickers-pro/src/internals/models/fields.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import { SlotComponentProps } from '@mui/base/utils'; -import { BaseFieldProps } from '@mui/x-date-pickers/internals'; +import { BaseFieldProps, FieldsTextFieldProps } from '@mui/x-date-pickers/internals'; import { FieldSection } from '@mui/x-date-pickers/models'; export interface RangeFieldSection extends FieldSection { @@ -19,9 +19,7 @@ export interface MultiInputFieldSlotTextFieldProps { onKeyDown?: React.KeyboardEventHandler; onFocus?: React.FocusEventHandler; focused?: boolean; - InputProps?: { - ref?: React.Ref; - }; + InputProps?: Partial; } /** diff --git a/packages/x-date-pickers-pro/tsconfig.build.json b/packages/x-date-pickers-pro/tsconfig.build.json index eda8907eb27bc..0f80aaf556fe2 100644 --- a/packages/x-date-pickers-pro/tsconfig.build.json +++ b/packages/x-date-pickers-pro/tsconfig.build.json @@ -15,7 +15,7 @@ { "path": "../x-license-pro/tsconfig.build.json" } ], "include": [ - "src/**/*.ts*", + "./src/**/*.ts*", "../../node_modules/@mui/material/themeCssVarsAugmentation", "../../node_modules/dayjs/plugin/utc.d.ts", "../../node_modules/dayjs/plugin/timezone.d.ts", diff --git a/packages/x-date-pickers-pro/tsconfig.json b/packages/x-date-pickers-pro/tsconfig.json index 75f0e23cefbee..9db9967fdf72a 100644 --- a/packages/x-date-pickers-pro/tsconfig.json +++ b/packages/x-date-pickers-pro/tsconfig.json @@ -7,7 +7,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../node_modules/@mui/material/themeCssVarsAugmentation", "../../node_modules/dayjs/plugin/utc.d.ts", "../../node_modules/dayjs/plugin/timezone.d.ts", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index bd0ba5187f08f..aeb2ce0fe8da3 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": "6.14.0", + "version": "6.18.0", "description": "The community edition of the date picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -44,10 +44,10 @@ "directory": "packages/x-date-pickers" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/base": "^5.0.0-beta.14", - "@mui/utils": "^5.14.8", - "@types/react-transition-group": "^4.4.6", + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/utils": "^5.14.16", + "@types/react-transition-group": "^4.4.8", "clsx": "^2.0.0", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" @@ -97,11 +97,11 @@ } }, "devDependencies": { - "@types/luxon": "^3.3.2", - "@types/moment-jalaali": "^0.7.6", + "@types/luxon": "^3.3.3", + "@types/moment-jalaali": "^0.7.8", "date-fns": "^2.30.0", "date-fns-jalali": "^2.13.0-0", - "dayjs": "^1.11.9", + "dayjs": "^1.11.10", "luxon": "^3.4.3", "moment": "^2.29.4", "moment-hijri": "^2.1.2", diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx index 1f0f1ee282182..ae997c105d71a 100644 --- a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.test.tsx @@ -2,17 +2,19 @@ import * as React from 'react'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils/createRenderer'; import { expect } from 'chai'; -import { createPickerRenderer, expectInputPlaceholder, expectInputValue } from 'test/utils/pickers'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, + describeGregorianAdapter, + TEST_DATE_ISO_STRING, +} from 'test/utils/pickers'; import enUS from 'date-fns/locale/en-US'; import fr from 'date-fns/locale/fr'; import de from 'date-fns/locale/de'; import ru from 'date-fns/locale/ru'; -import { - describeGregorianAdapter, - TEST_DATE_ISO_STRING, -} from '@mui/x-date-pickers/tests/describeGregorianAdapter'; describe('', () => { describeGregorianAdapter(AdapterDateFns, { @@ -26,6 +28,7 @@ describe('', () => { const adapter = new AdapterDateFns({ locale: enUS }); const date = adapter.date(TEST_DATE_ISO_STRING)!; + // TODO v7: can be removed after v7 release it('getWeekdays: should start on Sunday', () => { const result = adapter.getWeekdays(); expect(result).to.deep.equal(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); diff --git a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts index 7b1f5b0533822..53575f892a59e 100644 --- a/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts +++ b/packages/x-date-pickers/src/AdapterDateFns/AdapterDateFns.ts @@ -138,7 +138,7 @@ const defaultFormats: AdapterFormats = { monthShort: 'MMM', dayOfMonth: 'd', weekday: 'EEEE', - weekdayShort: 'EEE', + weekdayShort: 'EEEEEE', hours24h: 'HH', hours12h: 'hh', meridiem: 'aa', diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx index da40df3d3afac..659b57d696726 100644 --- a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.test.tsx @@ -2,12 +2,16 @@ import * as React from 'react'; import { expect } from 'chai'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterDateFnsJalali } from '@mui/x-date-pickers/AdapterDateFnsJalali'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { createPickerRenderer, expectInputPlaceholder, expectInputValue } from 'test/utils/pickers'; +import { screen } from '@mui-internal/test-utils/createRenderer'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, + describeJalaliAdapter, +} from 'test/utils/pickers'; import enUS from 'date-fns/locale/en-US'; import faIR from 'date-fns-jalali/locale/fa-IR'; import faJalaliIR from 'date-fns-jalali/locale/fa-jalali-IR'; -import { describeJalaliAdapter } from '@mui/x-date-pickers/tests/describeJalaliAdapter'; import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; import { AdapterFormats } from '@mui/x-date-pickers'; diff --git a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts index 542a322b5dd6f..f114a55155b4d 100644 --- a/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts +++ b/packages/x-date-pickers/src/AdapterDateFnsJalali/AdapterDateFnsJalali.ts @@ -138,7 +138,7 @@ const defaultFormats: AdapterFormats = { monthShort: 'MMM', dayOfMonth: 'd', weekday: 'EEEE', - weekdayShort: 'EEE', + weekdayShort: 'EEEEEE', hours24h: 'HH', hours12h: 'hh', meridiem: 'aa', diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx index e8e158ef379cd..bf10b9c81c512 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.test.tsx @@ -5,7 +5,7 @@ import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { expect } from 'chai'; import { buildPickerDragInteractions, @@ -13,14 +13,11 @@ import { expectInputPlaceholder, expectInputValue, createPickerRenderer, + describeGregorianAdapter, + TEST_DATE_ISO_STRING, } from 'test/utils/pickers'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { DateRangeCalendar } from '@mui/x-date-pickers-pro/DateRangeCalendar'; -import { - describeGregorianAdapter, - TEST_DATE_ISO_STRING, -} from '@mui/x-date-pickers/tests/describeGregorianAdapter'; - import 'dayjs/locale/fr'; import 'dayjs/locale/de'; // We import the plugins here just to have the typing @@ -55,6 +52,7 @@ describe('', () => { const adapter = new AdapterDayjs({ locale: 'en' }); const date = adapter.date(TEST_DATE_ISO_STRING)!; + // TODO v7: can be removed after v7 release it('getWeekdays: should start on Sunday', () => { const result = adapter.getWeekdays(); expect(result).to.deep.equal(['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa']); diff --git a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts index 34ba8fbc44b65..85bf90432ae7e 100644 --- a/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts +++ b/packages/x-date-pickers/src/AdapterDayjs/AdapterDayjs.ts @@ -75,7 +75,7 @@ const defaultFormats: AdapterFormats = { monthShort: 'MMM', dayOfMonth: 'D', weekday: 'dddd', - weekdayShort: 'ddd', + weekdayShort: 'dd', hours24h: 'HH', hours12h: 'hh', meridiem: 'A', @@ -326,15 +326,17 @@ export class AdapterDayjs implements MuiPickersAdapter { }; public getTimezone = (value: Dayjs): string => { - if (this.hasUTCPlugin() && value.isUTC()) { - return 'UTC'; - } - if (this.hasTimezonePlugin()) { // @ts-ignore const zone = value.$x?.$timezone; - return zone ?? 'system'; + if (zone) { + return zone; + } + } + + if (this.hasUTCPlugin() && value.isUTC()) { + return 'UTC'; } return 'system'; diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx index 3c9859bb10e15..c4caf38497374 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.test.tsx @@ -4,17 +4,15 @@ import { DateTime, Settings } from 'luxon'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils/createRenderer'; import { cleanText, createPickerRenderer, expectInputPlaceholder, expectInputValue, -} from 'test/utils/pickers'; -import { describeGregorianAdapter, TEST_DATE_ISO_STRING, -} from '@mui/x-date-pickers/tests/describeGregorianAdapter'; +} from 'test/utils/pickers'; describe('', () => { describeGregorianAdapter(AdapterLuxon, { @@ -38,6 +36,7 @@ describe('', () => { describe('Russian', () => { const adapter = new AdapterLuxon({ locale: 'ru' }); + // TODO v7: can be removed after v7 release it('getWeekDays: should start on Monday', () => { const result = adapter.getWeekdays(); expect(result).to.deep.equal(['П', 'В', 'С', 'Ч', 'П', 'С', 'В']); diff --git a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts index 8e817cd740ecb..60cdfa0341844 100644 --- a/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts +++ b/packages/x-date-pickers/src/AdapterLuxon/AdapterLuxon.ts @@ -62,7 +62,7 @@ const defaultFormats: AdapterFormats = { monthShort: 'MMM', dayOfMonth: 'd', weekday: 'cccc', - weekdayShort: 'ccc', + weekdayShort: 'ccccc', hours24h: 'HH', hours12h: 'hh', meridiem: 'a', diff --git a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx index f2d0e9f56506e..c592f25d1cb4b 100644 --- a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx +++ b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.test.tsx @@ -4,17 +4,19 @@ import momentTZ from 'moment-timezone'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterMoment } from '@mui/x-date-pickers/AdapterMoment'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils/createRenderer'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createPickerRenderer, expectInputPlaceholder, expectInputValue } from 'test/utils/pickers'; -import 'moment/locale/de'; -import 'moment/locale/fr'; -import 'moment/locale/ko'; import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, describeGregorianAdapter, TEST_DATE_ISO_STRING, -} from '@mui/x-date-pickers/tests/describeGregorianAdapter'; +} from 'test/utils/pickers'; +import 'moment/locale/de'; +import 'moment/locale/fr'; +import 'moment/locale/ko'; describe('', () => { const commonParams = { @@ -43,6 +45,7 @@ describe('', () => { const adapter = new AdapterMoment({ locale: 'en' }); const date = adapter.date(TEST_DATE_ISO_STRING)!; + // TODO v7: can be removed after v7 release it('getWeekdays: should start on Monday', () => { const result = adapter.getWeekdays(); expect(result).to.deep.equal(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']); diff --git a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts index e6c314c9932e5..084b65ea6caef 100644 --- a/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts +++ b/packages/x-date-pickers/src/AdapterMoment/AdapterMoment.ts @@ -239,16 +239,13 @@ export class AdapterMoment implements MuiPickersAdapter { }; public getTimezone = (value: Moment): string => { - if (value.isUTC()) { - return 'UTC'; - } - // @ts-ignore // eslint-disable-next-line no-underscore-dangle const zone = value._z?.name; + const defaultZone = value.isUTC() ? 'UTC' : 'system'; // @ts-ignore - return zone ?? this.moment.defaultZone?.name ?? 'system'; + return zone ?? this.moment.defaultZone?.name ?? defaultZone; }; public setTimezone = (value: Moment, timezone: PickersTimezone): Moment => { diff --git a/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx index 19aaea6e40418..9dd52c850bcf7 100644 --- a/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx +++ b/packages/x-date-pickers/src/AdapterMomentHijri/AdapterMomentHijri.test.tsx @@ -4,9 +4,13 @@ import { expect } from 'chai'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterMomentHijri } from '@mui/x-date-pickers/AdapterMomentHijri'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { createPickerRenderer, expectInputPlaceholder, expectInputValue } from 'test/utils/pickers'; -import { describeHijriAdapter } from '@mui/x-date-pickers/tests/describeHijriAdapter'; +import { screen } from '@mui-internal/test-utils/createRenderer'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, + describeHijriAdapter, +} from 'test/utils/pickers'; import 'moment/locale/ar'; describe('', () => { diff --git a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx index 4b09c35aa7cdf..6f66267176c42 100644 --- a/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx +++ b/packages/x-date-pickers/src/AdapterMomentJalaali/AdapterMomentJalaali.test.tsx @@ -4,10 +4,14 @@ import moment from 'moment'; import jMoment from 'moment-jalaali'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; import { AdapterMomentJalaali } from '@mui/x-date-pickers/AdapterMomentJalaali'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; -import { createPickerRenderer, expectInputPlaceholder, expectInputValue } from 'test/utils/pickers'; +import { screen } from '@mui-internal/test-utils/createRenderer'; +import { + createPickerRenderer, + expectInputPlaceholder, + expectInputValue, + describeJalaliAdapter, +} from 'test/utils/pickers'; import { AdapterFormats } from '@mui/x-date-pickers/models'; -import { describeJalaliAdapter } from '@mui/x-date-pickers/tests/describeJalaliAdapter'; import 'moment/locale/fa'; describe('', () => { diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx index 5f9293bd0f97f..00632350e648d 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.tsx @@ -28,6 +28,7 @@ import { getDateCalendarUtilityClass } from './dateCalendarClasses'; import { BaseDateValidationProps } from '../internals/models/validation'; import { useControlledValueWithTimezone } from '../internals/hooks/useValueWithTimezone'; import { singleItemValueManager } from '../internals/utils/valueManagers'; +import { VIEW_HEIGHT } from '../internals/constants/dimensions'; const useUtilityClasses = (ownerState: DateCalendarProps) => { const { classes } = ownerState; @@ -73,6 +74,7 @@ const DateCalendarRoot = styled(PickerViewRoot, { })<{ ownerState: DateCalendarProps }>({ display: 'flex', flexDirection: 'column', + height: VIEW_HEIGHT, }); const DateCalendarViewTransitionContainer = styled(PickersFadeTransitionGroup, { @@ -86,10 +88,11 @@ type DateCalendarComponent = (( ) => React.JSX.Element) & { propTypes?: any }; /** - * * Demos: * - * - [Date Picker](https://mui.com/x/react-date-pickers/date-picker/) + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) * * API: * @@ -133,8 +136,6 @@ export const DateCalendar = React.forwardRef(function DateCalendar( showDaysOutsideCurrentMonth, fixedWeekNumber, dayOfWeekFormatter, - components, - componentsProps, slots, slotProps, loading, @@ -196,11 +197,10 @@ export const DateCalendar = React.forwardRef(function DateCalendar( const gridLabelId = `${id}-grid-label`; const hasFocus = focusedView !== null; - const CalendarHeader = - slots?.calendarHeader ?? components?.CalendarHeader ?? PickersCalendarHeader; + const CalendarHeader = slots?.calendarHeader ?? PickersCalendarHeader; const calendarHeaderProps: PickersCalendarHeaderProps = useSlotProps({ elementType: CalendarHeader, - externalSlotProps: slotProps?.calendarHeader ?? componentsProps?.calendarHeader, + externalSlotProps: slotProps?.calendarHeader, additionalProps: { views, view, @@ -389,8 +389,6 @@ export const DateCalendar = React.forwardRef(function DateCalendar( fixedWeekNumber={fixedWeekNumber} dayOfWeekFormatter={dayOfWeekFormatter} displayWeekNumber={displayWeekNumber} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} loading={loading} @@ -417,23 +415,12 @@ DateCalendar.propTypes = { autoFocus: PropTypes.bool, classes: PropTypes.object, className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -559,6 +546,9 @@ DateCalendar.propTypes = { renderLoading: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/DateCalendar/DateCalendar.types.ts b/packages/x-date-pickers/src/DateCalendar/DateCalendar.types.ts index 789fafaf7c864..21a4d6b7847a6 100644 --- a/packages/x-date-pickers/src/DateCalendar/DateCalendar.types.ts +++ b/packages/x-date-pickers/src/DateCalendar/DateCalendar.types.ts @@ -24,7 +24,6 @@ import { PickerSelectionState } from '../internals/hooks/usePicker/usePickerValu import { ExportedUseViewsOptions } from '../internals/hooks/useViews'; import { DateView, TimezoneProps } from '../models'; import { DefaultizedProps } from '../internals/models/helpers'; -import { SlotsAndSlotProps } from '../internals/utils/slots-migration'; import { ExportedYearCalendarProps } from '../YearCalendar/YearCalendar.types'; import { ExportedMonthCalendarProps } from '../MonthCalendar/MonthCalendar.types'; @@ -36,7 +35,7 @@ export interface DateCalendarSlotsComponent * Check the [PickersCalendarHeader](https://mui.com/x/api/date-pickers/pickers-calendar-header/) component. * @default PickersCalendarHeader */ - CalendarHeader?: React.ElementType>; + calendarHeader?: React.ElementType>; } export interface DateCalendarSlotsComponentsProps @@ -46,7 +45,7 @@ export interface DateCalendarSlotsComponentsProps } export interface ExportedDateCalendarProps - extends ExportedDayCalendarProps, + extends ExportedDayCalendarProps, ExportedMonthCalendarProps, ExportedYearCalendarProps, BaseDateValidationProps, @@ -95,8 +94,7 @@ export interface ExportedDateCalendarProps export interface DateCalendarProps extends ExportedDateCalendarProps, - ExportedUseViewsOptions, - SlotsAndSlotProps, DateCalendarSlotsComponentsProps> { + ExportedUseViewsOptions { /** * The selected value. * Used when the component is controlled. @@ -125,6 +123,16 @@ export interface DateCalendarProps * The system prop that allows defining system overrides as well as additional CSS styles. */ sx?: SxProps; + /** + * Overridable component slots. + * @default {} + */ + slots?: DateCalendarSlotsComponent; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: DateCalendarSlotsComponentsProps; } export type DateCalendarDefaultizedProps = DefaultizedProps< diff --git a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx index a256dff17dcc3..dc0b7bb31ae09 100644 --- a/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx +++ b/packages/x-date-pickers/src/DateCalendar/DayCalendar.tsx @@ -24,9 +24,8 @@ import { YearValidationProps, } from '../internals/models/validation'; import { useIsDateDisabled } from './useIsDateDisabled'; -import { findClosestEnabledDate } from '../internals/utils/date-utils'; +import { findClosestEnabledDate, getWeekdays } from '../internals/utils/date-utils'; import { DayCalendarClasses, getDayCalendarUtilityClass } from './dayCalendarClasses'; -import { SlotsAndSlotProps } from '../internals/utils/slots-migration'; import { TimezoneProps } from '../models'; import { DefaultizedProps } from '../internals/models/helpers'; @@ -36,7 +35,7 @@ export interface DayCalendarSlotsComponent { * Check the [PickersDay](https://mui.com/x/api/date-pickers/pickers-day/) component. * @default PickersDay */ - Day?: React.ElementType>; + day?: React.ElementType>; } export interface DayCalendarSlotsComponentsProps { @@ -47,7 +46,7 @@ export interface DayCalendarSlotsComponentsProps { >; } -export interface ExportedDayCalendarProps extends ExportedPickersDayProps { +export interface ExportedDayCalendarProps extends ExportedPickersDayProps { /** * If `true`, calls `renderLoading` instead of rendering the day calendar. * Can be used to preload information and show it in calendar. @@ -62,11 +61,12 @@ export interface ExportedDayCalendarProps extends ExportedPickersDayProps { renderLoading?: () => React.ReactNode; /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ - dayOfWeekFormatter?: (day: string) => string; + dayOfWeekFormatter?: (day: string, date: TDate) => string; /** * If `true`, the week number will be display in the calendar. */ @@ -80,13 +80,12 @@ export interface ExportedDayCalendarProps extends ExportedPickersDayProps { } export interface DayCalendarProps - extends ExportedDayCalendarProps, + extends ExportedDayCalendarProps, DayValidationProps, MonthValidationProps, YearValidationProps, Required>, - DefaultizedProps, - SlotsAndSlotProps, DayCalendarSlotsComponentsProps> { + DefaultizedProps { autoFocus?: boolean; className?: string; currentMonth: TDate; @@ -105,11 +104,22 @@ export interface DayCalendarProps onFocusedViewChange?: (newHasFocus: boolean) => void; gridLabelId?: string; classes?: Partial; + /** + * Overridable component slots. + * @default {} + */ + slots?: DayCalendarSlotsComponent; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: DayCalendarSlotsComponentsProps; } const useUtilityClasses = (ownerState: DayCalendarProps) => { const { classes } = ownerState; const slots = { + root: ['root'], header: ['header'], weekDayLabel: ['weekDayLabel'], loadingContainer: ['loadingContainer'], @@ -123,10 +133,14 @@ const useUtilityClasses = (ownerState: DayCalendarProps) => { return composeClasses(slots, getDayCalendarUtilityClass, classes); }; -const defaultDayOfWeekFormatter = (day: string) => day.charAt(0).toUpperCase(); - const weeksContainerHeight = (DAY_SIZE + DAY_MARGIN * 2) * 6; +const PickersCalendarDayRoot = styled('div', { + name: 'MuiDayCalendar', + slot: 'Root', + overridesResolver: (_, styles) => styles.root, +})({}); + const PickersCalendarDayHeader = styled('div', { name: 'MuiDayCalendar', slot: 'Header', @@ -153,7 +167,7 @@ const PickersCalendarWeekDayLabel = styled(Typography, { })); const PickersCalendarWeekNumberLabel = styled(Typography, { - name: 'MuiDayPicker', + name: 'MuiDayCalendar', slot: 'WeekNumberLabel', overridesResolver: (_, styles) => styles.weekNumberLabel, })(({ theme }) => ({ @@ -168,7 +182,7 @@ const PickersCalendarWeekNumberLabel = styled(Typography, { })); const PickersCalendarWeekNumber = styled(Typography, { - name: 'MuiDayPicker', + name: 'MuiDayCalendar', slot: 'WeekNumber', overridesResolver: (_, styles) => styles.weekNumber, })(({ theme }) => ({ @@ -242,8 +256,6 @@ function WrappedDay({ disableHighlightToday, isMonthSwitchingAnimating, showDaysOutsideCurrentMonth, - components, - componentsProps, slots, slotProps, timezone, @@ -256,11 +268,11 @@ function WrappedDay({ const isSelected = selectedDays.some((selectedDay) => utils.isSameDay(selectedDay, day)); const isToday = utils.isSameDay(day, now); - const Day = slots?.day ?? components?.Day ?? PickersDay; + const Day = slots?.day ?? PickersDay; // We don't want to pass to ownerState down, to avoid re-rendering all the day whenever a prop changes. const { ownerState: dayOwnerState, ...dayProps } = useSlotProps({ elementType: Day, - externalSlotProps: slotProps?.day ?? componentsProps?.day, + externalSlotProps: slotProps?.day, additionalProps: { disableHighlightToday, showDaysOutsideCurrentMonth, @@ -344,7 +356,7 @@ export function DayCalendar(inProps: DayCalendarProps) { shouldDisableDate, shouldDisableMonth, shouldDisableYear, - dayOfWeekFormatter = defaultDayOfWeekFormatter, + dayOfWeekFormatter: dayOfWeekFormatterFromProps, hasFocus, onFocusedViewChange, gridLabelId, @@ -360,6 +372,11 @@ export function DayCalendar(inProps: DayCalendarProps) { const theme = useTheme(); const isRTL = theme.direction === 'rtl'; + // before we could define this outside of the component scope, but now we need utils, which is only defined here + const dayOfWeekFormatter = + dayOfWeekFormatterFromProps || + ((_day: string, date: TDate) => utils.format(date, 'weekdayShort').charAt(0).toUpperCase()); + const isDateDisabled = useIsDateDisabled({ shouldDisableDate, shouldDisableMonth, @@ -541,7 +558,7 @@ export function DayCalendar(inProps: DayCalendarProps) { }, [currentMonth, fixedWeekNumber, utils, timezone]); return ( -
                + {displayWeekNumber && ( (inProps: DayCalendarProps) { {localeText.calendarWeekNumberHeaderText} )} - {utils.getWeekdays().map((day, i) => ( - - {dayOfWeekFormatter?.(day) ?? day} - - ))} + {getWeekdays(utils, now).map((weekday, i) => { + const day = utils.format(weekday, 'weekdayShort'); + return ( + + {dayOfWeekFormatter?.(day, weekday) ?? day} + + ); + })} {loading ? ( @@ -629,6 +649,6 @@ export function DayCalendar(inProps: DayCalendarProps) { )} -
                + ); } diff --git a/packages/x-date-pickers/src/DateCalendar/PickersFadeTransitionGroup.tsx b/packages/x-date-pickers/src/DateCalendar/PickersFadeTransitionGroup.tsx index 4eb328dd27a35..f82497ffc0199 100644 --- a/packages/x-date-pickers/src/DateCalendar/PickersFadeTransitionGroup.tsx +++ b/packages/x-date-pickers/src/DateCalendar/PickersFadeTransitionGroup.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import clsx from 'clsx'; -import Fade from '@mui/material/Fade'; -import { styled, useThemeProps } from '@mui/material/styles'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; import { TransitionGroup } from 'react-transition-group'; +import Fade from '@mui/material/Fade'; +import { styled, useTheme, useThemeProps } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; import { getPickersFadeTransitionGroupUtilityClass, PickersFadeTransitionGroupClasses, @@ -26,8 +26,6 @@ const useUtilityClasses = (ownerState: PickersFadeTransitionGroupProps) => { return composeClasses(slots, getPickersFadeTransitionGroupUtilityClass, classes); }; -const animationDuration = 500; - const PickersFadeTransitionGroupRoot = styled(TransitionGroup, { name: 'MuiPickersFadeTransitionGroup', slot: 'Root', @@ -44,6 +42,7 @@ export function PickersFadeTransitionGroup(inProps: PickersFadeTransitionGroupPr const props = useThemeProps({ props: inProps, name: 'MuiPickersFadeTransitionGroup' }); const { children, className, reduceAnimations, transKey } = props; const classes = useUtilityClasses(props); + const theme = useTheme(); if (reduceAnimations) { return children; } @@ -55,7 +54,11 @@ export function PickersFadeTransitionGroup(inProps: PickersFadeTransitionGroupPr mountOnEnter unmountOnExit key={transKey} - timeout={{ appear: animationDuration, enter: animationDuration / 2, exit: 0 }} + timeout={{ + appear: theme.transitions.duration.enteringScreen, + enter: theme.transitions.duration.enteringScreen, + exit: 0, + }} > {children} diff --git a/packages/x-date-pickers/src/DateCalendar/PickersSlideTransition.tsx b/packages/x-date-pickers/src/DateCalendar/PickersSlideTransition.tsx index c1e65b71d3ea3..0e724659752c4 100644 --- a/packages/x-date-pickers/src/DateCalendar/PickersSlideTransition.tsx +++ b/packages/x-date-pickers/src/DateCalendar/PickersSlideTransition.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import clsx from 'clsx'; -import { styled, useThemeProps } from '@mui/material/styles'; -import { unstable_composeClasses as composeClasses } from '@mui/utils'; +import { styled, useTheme, useThemeProps } from '@mui/material/styles'; +import composeClasses from '@mui/utils/composeClasses'; import { CSSTransition, TransitionGroup } from 'react-transition-group'; import { CSSTransitionProps } from 'react-transition-group/CSSTransition'; import { TransitionGroupProps } from 'react-transition-group/TransitionGroup'; @@ -38,8 +38,6 @@ const useUtilityClasses = (ownerState: SlideTransitionProps) => { return composeClasses(slots, getPickersSlideTransitionUtilityClass, classes); }; -export const slideAnimationDuration = 350; - const PickersSlideTransitionRoot = styled(TransitionGroup, { name: 'MuiPickersSlideTransition', slot: 'Root', @@ -60,7 +58,7 @@ const PickersSlideTransitionRoot = styled(TransitionGroup, { ], })(({ theme }) => { const slideTransition = theme.transitions.create('transform', { - duration: slideAnimationDuration, + duration: theme.transitions.duration.complex, easing: 'cubic-bezier(0.35, 0.8, 0.4, 1)', }); return { @@ -121,6 +119,7 @@ export function PickersSlideTransition(inProps: SlideTransitionProps) { ...other } = props; const classes = useUtilityClasses(props); + const theme = useTheme(); if (reduceAnimations) { return
                {children}
                ; } @@ -146,7 +145,7 @@ export function PickersSlideTransition(inProps: SlideTransitionProps) { mountOnEnter unmountOnExit key={transKey} - timeout={slideAnimationDuration} + timeout={theme.transitions.duration.complex} classNames={transitionClasses} {...other} > diff --git a/packages/x-date-pickers/src/DateCalendar/dayCalendarClasses.ts b/packages/x-date-pickers/src/DateCalendar/dayCalendarClasses.ts index f78b05f4aa230..ece2f85e98c32 100644 --- a/packages/x-date-pickers/src/DateCalendar/dayCalendarClasses.ts +++ b/packages/x-date-pickers/src/DateCalendar/dayCalendarClasses.ts @@ -4,6 +4,8 @@ import { } from '@mui/utils'; export interface DayCalendarClasses { + /** Styles applied to the root element. */ + root: string; /** Styles applied to the header element. */ header: string; /** Styles applied to the week day label element. */ @@ -28,6 +30,7 @@ export const getDayCalendarUtilityClass = (slot: string) => generateUtilityClass('MuiDayCalendar', slot); export const dayPickerClasses: DayCalendarClasses = generateUtilityClasses('MuiDayCalendar', [ + 'root', 'header', 'weekDayLabel', 'loadingContainer', 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 ab5acc697f961..41042aaf6049f 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/DateCalendar.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { fireEvent, userEvent, screen } from '@mui/monorepo/test/utils'; +import { fireEvent, userEvent, screen } from '@mui-internal/test-utils'; import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; import { PickersDay } from '@mui/x-date-pickers/PickersDay'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; @@ -183,14 +183,17 @@ describe('', () => { render( , ); + // should make the reference day firstly focusable + expect(screen.getByRole('gridcell', { name: '17' })).to.have.attribute('tabindex', '0'); + userEvent.mousePress(screen.getByRole('gridcell', { name: '2' })); expect(onChange.callCount).to.equal(1); - expect(onChange.lastCall.firstArg).toEqualDateTime(new Date(2018, 0, 2, 12, 30)); + expect(onChange.lastCall.firstArg).toEqualDateTime(new Date(2022, 3, 2, 12, 30)); }); it('should not use `referenceDate` when a value is defined', () => { diff --git a/packages/x-date-pickers/src/DateCalendar/tests/describes.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/describes.DateCalendar.test.tsx index 7c38ba7e57d11..41135ff7ba10f 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/describes.DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/describes.DateCalendar.test.tsx @@ -1,11 +1,15 @@ import * as React from 'react'; import { expect } from 'chai'; -import { describeConformance, screen, userEvent } from '@mui/monorepo/test/utils'; +import { describeConformance, screen, userEvent } from '@mui-internal/test-utils'; import { DateCalendar, dateCalendarClasses as classes } from '@mui/x-date-pickers/DateCalendar'; import { pickersDayClasses } from '@mui/x-date-pickers/PickersDay'; -import { adapterToUse, wrapPickerMount, createPickerRenderer } from 'test/utils/pickers'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { + adapterToUse, + wrapPickerMount, + createPickerRenderer, + describeValidation, + describeValue, +} from 'test/utils/pickers'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/DateCalendar/tests/keyboard.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/keyboard.DateCalendar.test.tsx index ff3afd7411208..f562523a5b709 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/keyboard.DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/keyboard.DateCalendar.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { act, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { act, fireEvent, screen } from '@mui-internal/test-utils'; import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; import { adapterToUse, createPickerRenderer } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/DateCalendar/tests/localization.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/localization.DateCalendar.test.tsx new file mode 100644 index 0000000000000..ac7f013442b4d --- /dev/null +++ b/packages/x-date-pickers/src/DateCalendar/tests/localization.DateCalendar.test.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import { screen } from '@mui-internal/test-utils'; +import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; +import { createPickerRenderer, AdapterName } from 'test/utils/pickers'; +import { he } from 'date-fns/locale'; +import 'dayjs/locale/he'; +import 'moment/locale/he'; + +const ADAPTERS_TO_USE: AdapterName[] = ['date-fns', 'dayjs', 'luxon', 'moment']; + +describe(' - localization', () => { + ADAPTERS_TO_USE.forEach((adapterName) => { + describe(`with '${adapterName}'`, () => { + const { render } = createPickerRenderer({ + locale: adapterName === 'date-fns' ? he : { code: 'he' }, + adapterName, + }); + + it('should display correct week day labels in Hebrew locale ', () => { + render(); + + expect(screen.getByText('א')).toBeVisible(); + }); + }); + }); +}); diff --git a/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx index 6db0a47d8b60c..261af345fe218 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/timezone.DateCalendar.test.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { userEvent, screen } from '@mui/monorepo/test/utils'; +import { userEvent, screen } from '@mui-internal/test-utils'; +import { describeAdapters } from 'test/utils/pickers'; import { DateCalendar } from '@mui/x-date-pickers/DateCalendar'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; const TIMEZONE_TO_TEST = ['UTC', 'system', 'America/New_York']; diff --git a/packages/x-date-pickers/src/DateCalendar/tests/validation.DateCalendar.test.tsx b/packages/x-date-pickers/src/DateCalendar/tests/validation.DateCalendar.test.tsx index 85cc4d44e7d09..293c07986ba6c 100644 --- a/packages/x-date-pickers/src/DateCalendar/tests/validation.DateCalendar.test.tsx +++ b/packages/x-date-pickers/src/DateCalendar/tests/validation.DateCalendar.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen, fireEvent } from '@mui/monorepo/test/utils'; +import { screen, fireEvent } from '@mui-internal/test-utils'; import { DateCalendar, DateCalendarProps } from '@mui/x-date-pickers/DateCalendar'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx b/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx index 1754dc47f8ac6..440fa628f4dd6 100644 --- a/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx +++ b/packages/x-date-pickers/src/DateCalendar/useCalendarState.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import useEventCallback from '@mui/utils/useEventCallback'; import { SlideDirection } from './PickersSlideTransition'; import { useIsDateDisabled } from './useIsDateDisabled'; -import { useUtils, useNow } from '../internals/hooks/useUtils'; +import { useUtils } from '../internals/hooks/useUtils'; import { MuiPickersAdapter, PickersTimezone } from '../models'; import { DateCalendarDefaultizedProps } from './DateCalendar.types'; import { singleItemValueManager } from '../internals/utils/valueManagers'; @@ -127,7 +127,6 @@ export const useCalendarState = (params: UseCalendarState timezone, } = params; - const now = useNow(timezone); const utils = useUtils(); const reducerFn = React.useRef( @@ -162,7 +161,7 @@ export const useCalendarState = (params: UseCalendarState const [calendarState, dispatch] = React.useReducer(reducerFn, { isMonthSwitchingAnimating: false, - focusedDay: value || now, + focusedDay: referenceDate, currentMonth: utils.startOfMonth(referenceDate), slideDirection: 'left', }); diff --git a/packages/x-date-pickers/src/DateField/DateField.tsx b/packages/x-date-pickers/src/DateField/DateField.tsx index 9ca2e103dc45f..5144dd840083c 100644 --- a/packages/x-date-pickers/src/DateField/DateField.tsx +++ b/packages/x-date-pickers/src/DateField/DateField.tsx @@ -4,13 +4,28 @@ import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; import { refType } from '@mui/utils'; -import { DateFieldProps } from './DateField.types'; +import { + DateFieldProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps, +} from './DateField.types'; import { useDateField } from './useDateField'; +import { useClearableField } from '../hooks'; type DateFieldComponent = (( props: DateFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateField](http://mui.com/x/react-date-pickers/date-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [DateField API](https://mui.com/x/api/date-pickers/date-field/) + */ const DateField = React.forwardRef(function DateField( inProps: DateFieldProps, ref: React.Ref, @@ -20,15 +35,14 @@ const DateField = React.forwardRef(function DateField( name: 'MuiDateField', }); - const { components, componentsProps, slots, slotProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: DateFieldProps = useSlotProps({ elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }); @@ -43,17 +57,36 @@ const DateField = React.forwardRef(function DateField( onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useDateField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + DateFieldSlotsComponent, + DateFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); @@ -70,26 +103,19 @@ DateField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -198,6 +224,10 @@ DateField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. @@ -258,6 +288,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/DateField/DateField.types.ts b/packages/x-date-pickers/src/DateField/DateField.types.ts index 2664c54f87690..9fde1f631104c 100644 --- a/packages/x-date-pickers/src/DateField/DateField.types.ts +++ b/packages/x-date-pickers/src/DateField/DateField.types.ts @@ -1,6 +1,7 @@ import * as React from 'react'; import { SlotComponentProps } from '@mui/base/utils'; import TextField from '@mui/material/TextField'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '../internals'; import { DateValidationError, FieldSection } from '../models'; import { UseFieldInternalProps } from '../internals/hooks/useField'; import { DefaultizedProps, MakeOptional } from '../internals/models/helpers'; @@ -11,7 +12,6 @@ import { YearValidationProps, } from '../internals/models/validation'; import { FieldsTextFieldProps } from '../internals/models/fields'; -import { SlotsAndSlotProps } from '../internals/utils/slots-migration'; export interface UseDateFieldParams { props: UseDateFieldComponentProps; @@ -40,20 +40,30 @@ export type UseDateFieldComponentProps = Omit< UseDateFieldProps; export interface DateFieldProps - extends UseDateFieldComponentProps, - SlotsAndSlotProps> {} + extends UseDateFieldComponentProps { + /** + * Overridable component slots. + * @default {} + */ + slots?: DateFieldSlotsComponent; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: DateFieldSlotsComponentsProps; +} export type DateFieldOwnerState = DateFieldProps; -export interface DateFieldSlotsComponent { +export interface DateFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface DateFieldSlotsComponentsProps { +export interface DateFieldSlotsComponentsProps extends FieldSlotsComponentsProps { textField?: SlotComponentProps>; } diff --git a/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx index 27cd6f7c3c82a..4bbb2faf8a1cf 100644 --- a/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/describes.DateField.test.tsx @@ -1,8 +1,6 @@ import * as React from 'react'; -import { describeConformance, userEvent } from '@mui/monorepo/test/utils'; +import { describeConformance, userEvent } from '@mui-internal/test-utils'; import TextField from '@mui/material/TextField'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; import { DateField } from '@mui/x-date-pickers/DateField'; import { createPickerRenderer, @@ -11,6 +9,8 @@ import { expectInputPlaceholder, adapterToUse, getTextbox, + describeValidation, + describeValue, } from 'test/utils/pickers'; describe(' - Describes', () => { diff --git a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx index 6f1eb1b125bac..7ec6224393480 100644 --- a/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/editing.DateField.test.tsx @@ -2,9 +2,8 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { DateField } from '@mui/x-date-pickers/DateField'; -import { act, userEvent, fireEvent } from '@mui/monorepo/test/utils'; -import { expectInputValue, getTextbox } from 'test/utils/pickers'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; +import { act, userEvent, fireEvent } from '@mui-internal/test-utils'; +import { expectInputValue, getTextbox, describeAdapters } from 'test/utils/pickers'; describe(' - Editing', () => { describeAdapters('key: ArrowDown', DateField, ({ adapter, testFieldKeyPress }) => { @@ -207,155 +206,149 @@ describe(' - Editing', () => { }); }); - ['Backspace', 'Delete'].forEach((keyToClearValue) => { - describeAdapters( - `key: ${keyToClearValue}`, - DateField, - ({ adapter, testFieldKeyPress, renderWithProps }) => { - it('should clear the selected section when only this section is completed', () => { - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - }); - selectSection('month'); - - // Set a value for the "month" section - fireEvent.change(input, { - target: { value: 'j YYYY' }, - }); // press "j" - expectInputValue(input, 'January YYYY'); - - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'MMMM YYYY'); - }); + describeAdapters(`key: Delete`, DateField, ({ adapter, testFieldKeyPress, renderWithProps }) => { + it('should clear the selected section when only this section is completed', () => { + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + }); + selectSection('month'); - it('should clear the selected section when all sections are completed', () => { - testFieldKeyPress({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: adapter.date(), - key: keyToClearValue, - expectedValue: 'MMMM 2022', - }); - }); + // Set a value for the "month" section + fireEvent.change(input, { + target: { value: 'j YYYY' }, + }); // press "j" + expectInputValue(input, 'January YYYY'); - it('should clear all the sections when all sections are selected and all sections are completed', () => { - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: adapter.date(), - }); + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'MMMM YYYY'); + }); - selectSection('month'); + it('should clear the selected section when all sections are completed', () => { + testFieldKeyPress({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + key: 'Delete', + expectedValue: 'MMMM 2022', + }); + }); - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + it('should clear all the sections when all sections are selected and all sections are completed', () => { + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + }); - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'MMMM YYYY'); - }); + selectSection('month'); - it('should clear all the sections when all sections are selected and not all sections are completed', () => { - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - }); + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - selectSection('month'); + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'MMMM YYYY'); + }); - // Set a value for the "month" section - fireEvent.change(input, { - target: { value: 'j YYYY' }, - }); // Press "j" - expectInputValue(input, 'January YYYY'); + it('should clear all the sections when all sections are selected and not all sections are completed', () => { + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + }); - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + selectSection('month'); - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'MMMM YYYY'); - }); + // Set a value for the "month" section + fireEvent.change(input, { + target: { value: 'j YYYY' }, + }); // Press "j" + expectInputValue(input, 'January YYYY'); - it('should not keep query after typing again on a cleared section', () => { - const { input, selectSection } = renderWithProps({ - format: adapter.formats.year, - }); + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - selectSection('year'); + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'MMMM YYYY'); + }); - fireEvent.change(input, { target: { value: '2' } }); // press "2" - expectInputValue(input, '0002'); + it('should not keep query after typing again on a cleared section', () => { + const { input, selectSection } = renderWithProps({ + format: adapter.formats.year, + }); - userEvent.keyPress(input, { key: keyToClearValue }); - expectInputValue(input, 'YYYY'); + selectSection('year'); - fireEvent.change(input, { target: { value: '2' } }); // press "2" - expectInputValue(input, '0002'); - }); + fireEvent.change(input, { target: { value: '2' } }); // press "2" + expectInputValue(input, '0002'); - it('should not clear the sections when props.readOnly = true', () => { - testFieldKeyPress({ - format: adapter.formats.year, - defaultValue: adapter.date(), - readOnly: true, - key: keyToClearValue, - expectedValue: '2022', - }); - }); + userEvent.keyPress(input, { key: 'Delete' }); + expectInputValue(input, 'YYYY'); - it('should not call `onChange` when clearing all sections and both dates are already empty', () => { - const onChange = spy(); + fireEvent.change(input, { target: { value: '2' } }); // press "2" + expectInputValue(input, '0002'); + }); - const { input, selectSection } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - onChange, - }); + it('should not clear the sections when props.readOnly = true', () => { + testFieldKeyPress({ + format: adapter.formats.year, + defaultValue: adapter.date(), + readOnly: true, + key: 'Delete', + expectedValue: '2022', + }); + }); + + it('should not call `onChange` when clearing all sections and both dates are already empty', () => { + const onChange = spy(); + + const { input, selectSection } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + onChange, + }); - selectSection('month'); + selectSection('month'); - // Select all sections - userEvent.keyPress(input, { key: 'a', ctrlKey: true }); + // Select all sections + userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(onChange.callCount).to.equal(0); - }); + userEvent.keyPress(input, { key: 'Delete' }); + expect(onChange.callCount).to.equal(0); + }); - it('should call `onChange` when clearing the first and last section', () => { - const handleChange = spy(); + it('should call `onChange` when clearing the first and last section', () => { + const handleChange = spy(); - const { selectSection, input } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: adapter.date(), - onChange: handleChange, - }); + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + onChange: handleChange, + }); - selectSection('month'); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); - expect(handleChange.lastCall.args[1].validationError).to.equal('invalidDate'); + selectSection('month'); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); + expect(handleChange.lastCall.args[1].validationError).to.equal('invalidDate'); - userEvent.keyPress(input, { key: 'ArrowRight' }); + userEvent.keyPress(input, { key: 'ArrowRight' }); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(2); - expect(handleChange.lastCall.firstArg).to.equal(null); - expect(handleChange.lastCall.args[1].validationError).to.equal(null); - }); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(2); + expect(handleChange.lastCall.firstArg).to.equal(null); + expect(handleChange.lastCall.args[1].validationError).to.equal(null); + }); - it('should not call `onChange` if the section is already empty', () => { - const handleChange = spy(); + it('should not call `onChange` if the section is already empty', () => { + const handleChange = spy(); - const { selectSection, input } = renderWithProps({ - format: `${adapter.formats.month} ${adapter.formats.year}`, - defaultValue: adapter.date(), - onChange: handleChange, - }); + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + onChange: handleChange, + }); - selectSection('month'); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); + selectSection('month'); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); - userEvent.keyPress(input, { key: keyToClearValue }); - expect(handleChange.callCount).to.equal(1); - }); - }, - ); + userEvent.keyPress(input, { key: 'Delete' }); + expect(handleChange.callCount).to.equal(1); + }); }); describeAdapters('Digit editing', DateField, ({ adapter, testFieldChange }) => { @@ -639,6 +632,118 @@ describe(' - Editing', () => { }, ); + describeAdapters( + `Backspace editing`, + DateField, + ({ adapter, renderWithProps, testFieldChange }) => { + it('should clear the selected section when only this section is completed (Backspace)', () => { + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + keyStrokes: [ + { value: 'j YYYY', expected: 'January YYYY' }, + { value: ' YYYY', expected: 'MMMM YYYY' }, + ], + }); + }); + + it('should clear the selected section when all sections are completed (Backspace)', () => { + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + keyStrokes: [{ value: ' 2022', expected: 'MMMM 2022' }], + }); + }); + + it('should clear all the sections when all sections are selected and all sections are completed (Backspace)', () => { + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + keyStrokes: [{ value: '', expected: 'MMMM YYYY' }], + }); + }); + + it('should clear all the sections when all sections are selected and not all sections are completed (Backspace)', () => { + testFieldChange({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + keyStrokes: [ + { value: 'j YYYY', expected: 'January YYYY' }, + { value: '', expected: 'MMMM YYYY' }, + ], + }); + }); + + it('should not keep query after typing again on a cleared section (Backspace)', () => { + testFieldChange({ + format: adapter.formats.year, + keyStrokes: [ + { value: '2', expected: '0002' }, + { value: '', expected: 'YYYY' }, + { value: '2', expected: '0002' }, + ], + }); + }); + + it('should not clear the sections when props.readOnly = true (Backspace)', () => { + testFieldChange({ + format: adapter.formats.year, + defaultValue: adapter.date(), + readOnly: true, + keyStrokes: [{ value: '2', expected: '2022' }], + }); + }); + + it('should not call `onChange` when clearing all sections and both dates are already empty (Backspace)', () => { + const onChange = spy(); + + testFieldChange({ + format: adapter.formats.year, + onChange, + keyStrokes: [{ value: '', expected: 'YYYY' }], + }); + + expect(onChange.callCount).to.equal(0); + }); + + it('should call `onChange` when clearing the first and last section (Backspace)', () => { + const onChange = spy(); + + const { selectSection, input } = renderWithProps({ + format: `${adapter.formats.month} ${adapter.formats.year}`, + defaultValue: adapter.date(), + onChange, + }); + + selectSection('month'); + fireEvent.change(input, { target: { value: ' 2022' } }); + expect(onChange.callCount).to.equal(1); + expect(onChange.lastCall.args[1].validationError).to.equal('invalidDate'); + + userEvent.keyPress(input, { key: 'ArrowRight' }); + + fireEvent.change(input, { target: { value: 'MMMM ' } }); + expect(onChange.callCount).to.equal(2); + expect(onChange.lastCall.firstArg).to.equal(null); + expect(onChange.lastCall.args[1].validationError).to.equal(null); + }); + + it('should not call `onChange` if the section is already empty (Backspace)', () => { + const onChange = spy(); + + testFieldChange({ + format: adapter.formats.year, + defaultValue: adapter.date(), + keyStrokes: [ + { value: '', expected: 'YYYY' }, + { value: '', expected: 'YYYY' }, + ], + onChange, + }); + + expect(onChange.callCount).to.equal(1); + }); + }, + ); + describeAdapters('Pasting', DateField, ({ adapter, render, renderWithProps, clickOnInput }) => { const firePasteEvent = (input: HTMLInputElement, pastedValue: string) => { act(() => { @@ -852,7 +957,7 @@ describe(' - Editing', () => { selectSection('month'); userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - userEvent.keyPress(input, { key: 'Backspace' }); + fireEvent.change(input, { target: { value: '' } }); userEvent.keyPress(input, { key: 'ArrowLeft' }); fireEvent.change(input, { target: { value: '1/DD/YYYY' } }); // Press "1" diff --git a/packages/x-date-pickers/src/DateField/tests/format.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/format.DateField.test.tsx index 07a178ba50e63..2e96282647877 100644 --- a/packages/x-date-pickers/src/DateField/tests/format.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/format.DateField.test.tsx @@ -1,7 +1,11 @@ import * as React from 'react'; -import { expectInputPlaceholder, expectInputValue, getTextbox } from 'test/utils/pickers'; +import { + expectInputPlaceholder, + expectInputValue, + getTextbox, + describeAdapters, +} from 'test/utils/pickers'; import { DateField } from '@mui/x-date-pickers/DateField'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; describeAdapters(' - Format', DateField, ({ render, adapter }) => { it('should support escaped characters in start separator', () => { diff --git a/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx b/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx index da57ee886422a..935fb69cce371 100644 --- a/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx +++ b/packages/x-date-pickers/src/DateField/tests/selection.DateField.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { DateField } from '@mui/x-date-pickers/DateField'; -import { act, userEvent } from '@mui/monorepo/test/utils'; +import { act, userEvent } from '@mui-internal/test-utils'; import { createPickerRenderer, expectInputValue, diff --git a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx index e7093838003fc..589f3cf9a2006 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePicker.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePicker.tsx @@ -12,6 +12,16 @@ type DatePickerComponent = (( props: DatePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DatePicker API](https://mui.com/x/api/date-pickers/date-picker/) + */ const DatePicker = React.forwardRef(function DatePicker( inProps: DatePickerProps, ref: React.Ref, @@ -51,23 +61,12 @@ DatePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -281,6 +280,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/DatePicker/DatePicker.types.ts b/packages/x-date-pickers/src/DatePicker/DatePicker.types.ts index 7be6c27eafafe..1727654401d56 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePicker.types.ts +++ b/packages/x-date-pickers/src/DatePicker/DatePicker.types.ts @@ -3,7 +3,6 @@ import { DesktopDatePickerSlotsComponent, DesktopDatePickerSlotsComponentsProps, } from '../DesktopDatePicker'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { MobileDatePickerProps, MobileDatePickerSlotsComponent, @@ -32,23 +31,11 @@ export interface DatePickerProps * @default 4 on desktop, 3 on mobile */ yearsPerRow?: 3 | 4; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DatePickerSlotsComponents; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DatePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DatePickerSlotsComponents; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx index 58f02f4c42b76..b650b1bff51fb 100644 --- a/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DatePicker/DatePickerToolbar.tsx @@ -53,6 +53,16 @@ type DatePickerToolbarComponent = (( props: DatePickerToolbarProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [Custom components](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [DatePickerToolbar API](https://mui.com/x/api/date-pickers/date-picker-toolbar/) + */ const DatePickerToolbar = React.forwardRef(function DatePickerToolbar( inProps: DatePickerToolbarProps, ref: React.Ref, diff --git a/packages/x-date-pickers/src/DatePicker/shared.tsx b/packages/x-date-pickers/src/DatePicker/shared.tsx index a376480dc3717..8dc20da2b54b9 100644 --- a/packages/x-date-pickers/src/DatePicker/shared.tsx +++ b/packages/x-date-pickers/src/DatePicker/shared.tsx @@ -11,7 +11,7 @@ import { applyDefaultViewProps } from '../internals/utils/views'; import { DateValidationError, DateView } from '../models'; import { BasePickerInputProps } from '../internals/models/props/basePickerProps'; import { applyDefaultDate } from '../internals/utils/date-utils'; -import { BaseDateValidationProps, UncapitalizeObjectKeys } from '../internals'; +import { BaseDateValidationProps } from '../internals'; import { LocalizedComponent, PickersInputLocaleText } from '../locales/utils/pickersLocaleTextApi'; import { DatePickerToolbar, @@ -20,14 +20,13 @@ import { } from './DatePickerToolbar'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; import { DateViewRendererProps } from '../dateViewRenderers'; -import { uncapitalizeObjectKeys } from '../internals/utils/slots-migration'; export interface BaseDatePickerSlotsComponent extends DateCalendarSlotsComponent { /** * Custom component for the toolbar rendered above the views. * @default DatePickerToolbar */ - Toolbar?: React.JSXElementConstructor>; + toolbar?: React.JSXElementConstructor>; } export interface BaseDatePickerSlotsComponentsProps @@ -38,23 +37,11 @@ export interface BaseDatePickerSlotsComponentsProps export interface BaseDatePickerProps extends BasePickerInputProps, ExportedDateCalendarProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: BaseDatePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: BaseDatePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: BaseDatePickerSlotsComponent; /** * The props used for each component slot. * @default {} @@ -103,7 +90,6 @@ export function useDatePickerDefaultizedProps', () => { diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx index c36fd6ce82dc6..4d7256e7b999c 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.tsx @@ -4,13 +4,28 @@ import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; import { refType } from '@mui/utils'; -import { DateTimeFieldProps } from './DateTimeField.types'; +import { + DateTimeFieldProps, + DateTimeFieldSlotsComponent, + DateTimeFieldSlotsComponentsProps, +} from './DateTimeField.types'; import { useDateTimeField } from './useDateTimeField'; +import { useClearableField } from '../hooks'; type DateTimeFieldComponent = (( props: DateTimeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimeField](http://mui.com/x/react-date-pickers/date-time-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [DateTimeField API](https://mui.com/x/api/date-pickers/date-time-field/) + */ const DateTimeField = React.forwardRef(function DateTimeField( inProps: DateTimeFieldProps, ref: React.Ref, @@ -20,16 +35,15 @@ const DateTimeField = React.forwardRef(function DateTimeField( name: 'MuiDateTimeField', }); - const { components, componentsProps, slots, slotProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: DateTimeFieldProps = useSlotProps( { elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }, @@ -45,17 +59,32 @@ const DateTimeField = React.forwardRef(function DateTimeField( onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useDateTimeField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + DateTimeFieldSlotsComponent, + DateTimeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); return ( ); @@ -77,26 +106,19 @@ DateTimeField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -233,6 +255,10 @@ DateTimeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. @@ -301,6 +327,9 @@ DateTimeField.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts index 5b860c84e24be..4ffafb771386f 100644 --- a/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts +++ b/packages/x-date-pickers/src/DateTimeField/DateTimeField.types.ts @@ -14,7 +14,7 @@ import { YearValidationProps, } from '../internals/models/validation'; import { FieldsTextFieldProps } from '../internals/models/fields'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '../internals'; export interface UseDateTimeFieldParams { props: UseDateTimeFieldComponentProps; @@ -53,23 +53,11 @@ export type UseDateTimeFieldComponentProps = Omit export interface DateTimeFieldProps extends UseDateTimeFieldComponentProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DateTimeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DateTimeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: DateTimeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -79,15 +67,15 @@ export interface DateTimeFieldProps export type DateTimeFieldOwnerState = DateTimeFieldProps; -export interface DateTimeFieldSlotsComponent { +export interface DateTimeFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface DateTimeFieldSlotsComponentsProps { +export interface DateTimeFieldSlotsComponentsProps extends FieldSlotsComponentsProps { textField?: SlotComponentProps>; } diff --git a/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx b/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx index 067654d440537..21dd0dfbdf9ce 100644 --- a/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx +++ b/packages/x-date-pickers/src/DateTimeField/tests/describes.DateTimeField.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; import TextField from '@mui/material/TextField'; -import { describeConformance, userEvent } from '@mui/monorepo/test/utils'; +import { describeConformance, userEvent } from '@mui-internal/test-utils'; import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'; import { adapterToUse, @@ -10,8 +9,9 @@ import { expectInputValue, expectInputPlaceholder, getTextbox, + describeValidation, + describeValue, } from 'test/utils/pickers'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/DateTimeField/tests/editing.DateTimeField.test.tsx b/packages/x-date-pickers/src/DateTimeField/tests/editing.DateTimeField.test.tsx index 070c0254962d9..99e81989203f4 100644 --- a/packages/x-date-pickers/src/DateTimeField/tests/editing.DateTimeField.test.tsx +++ b/packages/x-date-pickers/src/DateTimeField/tests/editing.DateTimeField.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { userEvent, screen } from '@mui/monorepo/test/utils'; +import { userEvent, screen } from '@mui-internal/test-utils'; import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'; import { adapterToUse, buildFieldInteractions, createPickerRenderer } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx b/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx index e47d66c3997a4..330d2b0d73295 100644 --- a/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx +++ b/packages/x-date-pickers/src/DateTimeField/tests/timezone.DateTimeField.test.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { userEvent } from '@mui/monorepo/test/utils'; +import { userEvent } from '@mui-internal/test-utils'; import { DateTimeField } from '@mui/x-date-pickers/DateTimeField'; -import { createPickerRenderer, expectInputValue, getTextbox } from 'test/utils/pickers'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; +import { + createPickerRenderer, + expectInputValue, + getTextbox, + describeAdapters, +} from 'test/utils/pickers'; const TIMEZONE_TO_TEST = ['UTC', 'system', 'America/New_York']; diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx index b00519225beb1..59101d62d135a 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.tsx @@ -12,6 +12,16 @@ type DateTimePickerComponent = (( props: DateTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DateTimePicker API](https://mui.com/x/api/date-pickers/date-time-picker/) + */ const DateTimePicker = React.forwardRef(function DateTimePicker( inProps: DateTimePickerProps, ref: React.Ref, @@ -61,23 +71,12 @@ DateTimePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -327,6 +326,9 @@ DateTimePicker.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. @@ -388,6 +390,11 @@ DateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, /** * The time steps between two time unit options. * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts index 3ed5b62e344dd..e3fe39bb2f826 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePicker.types.ts @@ -4,7 +4,6 @@ import { DesktopDateTimePickerSlotsComponentsProps, } from '../DesktopDateTimePicker'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { MobileDateTimePickerProps, MobileDateTimePickerSlotsComponent, @@ -33,23 +32,11 @@ export interface DateTimePickerProps * @default 4 on desktop, 3 on mobile */ yearsPerRow?: 3 | 4; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DateTimePickerSlotsComponents; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DateTimePickerSlotsComponents; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx index 380a6a2ba05f2..6de4781ee8a01 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerTabs.tsx @@ -83,6 +83,16 @@ const DateTimePickerTabsRoot = styled(Tabs, { }, })); +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Custom slots and subcomponents](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [DateTimePickerTabs API](https://mui.com/x/api/date-pickers/date-time-picker-tabs/) + */ const DateTimePickerTabs = function DateTimePickerTabs(inProps: DateTimePickerTabsProps) { const props = useThemeProps({ props: inProps, name: 'MuiDateTimePickerTabs' }); const { diff --git a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx index 41fb25d978a40..33e9119f53110 100644 --- a/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/DateTimePickerToolbar.tsx @@ -185,6 +185,16 @@ const DateTimePickerToolbarAmPmSelection = styled('div', { }, })); +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Custom components](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [DateTimePickerToolbar API](https://mui.com/x/api/date-pickers/date-time-picker-toolbar/) + */ function DateTimePickerToolbar(inProps: DateTimePickerToolbarProps) { const props = useThemeProps({ props: inProps, name: 'MuiDateTimePickerToolbar' }); const { @@ -383,6 +393,11 @@ DateTimePickerToolbar.propTypes = { */ onViewChange: PropTypes.func.isRequired, readOnly: PropTypes.bool, + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), titleId: PropTypes.string, /** * Toolbar date format. diff --git a/packages/x-date-pickers/src/DateTimePicker/shared.tsx b/packages/x-date-pickers/src/DateTimePicker/shared.tsx index 729cf69aba5f4..d57aa85222c2b 100644 --- a/packages/x-date-pickers/src/DateTimePicker/shared.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/shared.tsx @@ -34,7 +34,6 @@ import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePicker import { DateViewRendererProps } from '../dateViewRenderers'; import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; -import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; import { DateOrTimeViewWithMeridiem, TimeViewWithMeridiem } from '../internals/models'; @@ -45,12 +44,12 @@ export interface BaseDateTimePickerSlotsComponent * Tabs enabling toggling between date and time pickers. * @default DateTimePickerTabs */ - Tabs?: React.ElementType; + tabs?: React.ElementType; /** * Custom component for the toolbar rendered above the views. * @default DateTimePickerToolbar */ - Toolbar?: React.JSXElementConstructor>; + toolbar?: React.JSXElementConstructor>; } export interface BaseDateTimePickerSlotsComponentsProps @@ -76,23 +75,11 @@ export interface BaseDateTimePickerProps; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: BaseDateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: BaseDateTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} @@ -159,8 +146,6 @@ export function useDateTimePickerDefaultizedProps< }; }, [themeProps.localeText]); - const slots = themeProps.slots ?? uncapitalizeObjectKeys(themeProps.components); - const slotProps = themeProps.slotProps ?? themeProps.componentsProps; return { ...themeProps, ...applyDefaultViewProps({ @@ -199,13 +184,13 @@ export function useDateTimePickerDefaultizedProps< slots: { toolbar: DateTimePickerToolbar, tabs: DateTimePickerTabs, - ...slots, + ...themeProps.slots, }, slotProps: { - ...slotProps, + ...themeProps.slotProps, toolbar: { ampm, - ...slotProps?.toolbar, + ...themeProps.slotProps?.toolbar, }, }, }; diff --git a/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx index 63ba1c1099590..cac1fdb4074df 100644 --- a/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DateTimePicker/tests/DateTimePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils/createRenderer'; import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers'; describe('', () => { diff --git a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.test.tsx b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.test.tsx index 048fe9cad8072..9555f053f31eb 100644 --- a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.test.tsx +++ b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { describeConformance } from '@mui/monorepo/test/utils'; +import { describeConformance } from '@mui-internal/test-utils'; import { DayCalendarSkeleton, dayCalendarSkeletonClasses as classes, diff --git a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx index b9f751f493a32..cca99858f8aea 100644 --- a/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx +++ b/packages/x-date-pickers/src/DayCalendarSkeleton/DayCalendarSkeleton.tsx @@ -84,10 +84,9 @@ const monthMap = [ ]; /** - * * Demos: * - * - [Date Picker](https://mui.com/x/react-date-pickers/date-picker/) + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) * * API: * diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx index e2513fd0f8967..288916bac5505 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.tsx @@ -19,6 +19,16 @@ type DesktopDatePickerComponent = (( props: DesktopDatePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DesktopDatePicker API](https://mui.com/x/api/date-pickers/desktop-date-picker/) + */ const DesktopDatePicker = React.forwardRef(function DesktopDatePicker( inProps: DesktopDatePickerProps, ref: React.Ref, @@ -97,23 +107,12 @@ DesktopDatePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -321,6 +320,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.types.ts b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.types.ts index 433e4887dc25c..19e5ea1221592 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopDatePicker/DesktopDatePicker.types.ts @@ -10,11 +10,10 @@ import { } from '../DatePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { DateView } from '../models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; export interface DesktopDatePickerSlotsComponent extends BaseDatePickerSlotsComponent, - MakeOptional, 'Field' | 'OpenPickerIcon'> {} + MakeOptional, 'field' | 'openPickerIcon'> {} export interface DesktopDatePickerSlotsComponentsProps extends BaseDatePickerSlotsComponentsProps, @@ -28,23 +27,11 @@ export interface DesktopDatePickerProps * @default 4 */ yearsPerRow?: 3 | 4; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DesktopDatePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DesktopDatePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DesktopDatePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx index fc8df6b936811..9fbd1e015b8cc 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/DesktopDatePicker.test.tsx @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { TransitionProps } from '@mui/material/transitions'; import { inputBaseClasses } from '@mui/material/InputBase'; -import { fireEvent, screen, userEvent } from '@mui/monorepo/test/utils'; +import { fireEvent, screen, userEvent } from '@mui-internal/test-utils'; import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; import { createPickerRenderer, @@ -16,7 +16,7 @@ import { const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe('', () => { - const { render } = createPickerRenderer({ clock: 'fake' }); + const { render, clock } = createPickerRenderer({ clock: 'fake' }); it('allows to change selected date from the field according to `format`', () => { const handleChange = spy(); @@ -40,7 +40,7 @@ describe('', () => { render( , @@ -100,6 +100,29 @@ describe('', () => { expect(handleViewChange.lastCall.firstArg).to.equal('month'); }); + it('should go to the relevant `view` when `views` prop changes', () => { + const { setProps } = render( + , + ); + + openPicker({ type: 'date', variant: 'desktop' }); + + expect(screen.getByRole('radio', { checked: true, name: '2018' })).to.not.equal(null); + + // Dismiss the picker + userEvent.keyPress(document.activeElement!, { key: 'Escape' }); + setProps({ views: ['month', 'year'] }); + openPicker({ type: 'date', variant: 'desktop' }); + // wait for all pending changes to be flushed + clock.runToLast(); + + // should have changed the open view + expect(screen.getByRole('radio', { checked: true, name: 'January' })).to.not.equal(null); + }); + it('should move the focus to the newly opened views', function test() { if (isJSDOM) { this.skip(); diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx index ab1587134f9af..37656e7a8647f 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/describes.DesktopDatePicker.test.tsx @@ -1,15 +1,15 @@ -import { screen, userEvent } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { createPickerRenderer, adapterToUse, expectInputValue, expectInputPlaceholder, getTextbox, + describeValidation, + describeValue, + describePicker, } from 'test/utils/pickers'; import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/DesktopDatePicker/tests/field.DesktopDatePicker.test.tsx b/packages/x-date-pickers/src/DesktopDatePicker/tests/field.DesktopDatePicker.test.tsx index e24d0da24b5cc..81dd41756569f 100644 --- a/packages/x-date-pickers/src/DesktopDatePicker/tests/field.DesktopDatePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDatePicker/tests/field.DesktopDatePicker.test.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { fireEvent, userEvent } from '@mui/monorepo/test/utils'; +import { fireEvent } from '@mui-internal/test-utils'; import { DesktopDatePicker, DesktopDatePickerProps } from '@mui/x-date-pickers/DesktopDatePicker'; import { createPickerRenderer, @@ -8,8 +8,8 @@ import { expectInputValue, expectInputPlaceholder, adapterToUse, + describeAdapters, } from 'test/utils/pickers'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; describe(' - Field', () => { describe('Basic behaviors', () => { @@ -40,7 +40,7 @@ describe(' - Field', () => { fireEvent.change(input, { target: { value: 'November 4' } }); // Press "1" expectInputValue(input, 'November 04'); - userEvent.keyPress(input, { key: 'Backspace' }); + fireEvent.change(input, { target: { value: 'November ' } }); expectInputValue(input, 'November DD'); }); @@ -81,7 +81,7 @@ describe(' - Field', () => { expectInputValue(input, 'June 2022'); clickOnInput(input, 0); - userEvent.keyPress(input, { key: 'Backspace' }); + fireEvent.change(input, { target: { value: ' 2022' } }); expectInputValue(input, 'MMMM 2022'); }); }); diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx index 03dbd89ff05c9..c306141e9865b 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.tsx @@ -15,13 +15,26 @@ import { CalendarIcon } from '../icons'; import { useDesktopPicker } from '../internals/hooks/useDesktopPicker'; import { extractValidationProps } from '../internals/utils/validation/extractValidationProps'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; -import { resolveDateTimeFormat } from '../internals/utils/date-time-utils'; +import { + resolveDateTimeFormat, + resolveTimeViewsResponse, +} from '../internals/utils/date-time-utils'; import { PickersActionBarAction } from '../PickersActionBar'; type DesktopDateTimePickerComponent = (( props: DesktopDateTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DesktopDateTimePicker API](https://mui.com/x/api/date-pickers/desktop-date-time-picker/) + */ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker( inProps: DesktopDateTimePickerProps, ref: React.Ref, @@ -36,7 +49,12 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker >(inProps, 'MuiDesktopDateTimePicker'); - const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps }; + const { + shouldRenderTimeInASingleColumn, + thresholdToRenderTimeInASingleColumn, + views, + timeSteps, + } = resolveTimeViewsResponse(defaultizedProps); const shouldUseNewRenderer = !defaultizedProps.viewRenderers || Object.keys(defaultizedProps.viewRenderers).length === 0; @@ -71,12 +89,12 @@ const DesktopDateTimePicker = React.forwardRef(function DesktopDateTimePicker day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -409,6 +416,9 @@ DesktopDateTimePicker.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. @@ -470,6 +480,11 @@ DesktopDateTimePicker.propTypes = { PropTypes.func, PropTypes.object, ]), + /** + * Amount of time options below or at which the single column time renderer is used. + * @default 24 + */ + thresholdToRenderTimeInASingleColumn: PropTypes.number, /** * The time steps between two time unit options. * For example, if `timeStep.minutes = 8`, then the available minute options will be `[0, 8, 16, 24, 32, 40, 48, 56]`. diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts index 04795749a5f70..12d2cdf07db5c 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/DesktopDateTimePicker.types.ts @@ -10,31 +10,33 @@ import { } from '../DateTimePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { DateOrTimeView } from '../models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { MultiSectionDigitalClockSlotsComponent, MultiSectionDigitalClockSlotsComponentsProps, } from '../MultiSectionDigitalClock'; +import { DigitalClockSlotsComponent, DigitalClockSlotsComponentsProps } from '../DigitalClock'; export interface DesktopDateTimePickerSlotsComponent extends BaseDateTimePickerSlotsComponent, MakeOptional< UseDesktopPickerSlotsComponent, - 'Field' | 'OpenPickerIcon' + 'field' | 'openPickerIcon' >, + DigitalClockSlotsComponent, MultiSectionDigitalClockSlotsComponent {} export interface DesktopDateTimePickerSlotsComponentsProps extends BaseDateTimePickerSlotsComponentsProps, ExportedUseDesktopPickerSlotsComponentsProps, + DigitalClockSlotsComponentsProps, MultiSectionDigitalClockSlotsComponentsProps {} export interface DesktopDateTimePickerProps extends BaseDateTimePickerProps, DesktopOnlyPickerProps, - Omit, 'thresholdToRenderTimeInASingleColumn'> { + DesktopOnlyTimePickerProps { /** * Available views. */ @@ -44,23 +46,11 @@ export interface DesktopDateTimePickerProps * @default 4 */ yearsPerRow?: 3 | 4; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DesktopDateTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DesktopDateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DesktopDateTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx index 1e1a87665f10c..b26c8bda0530f 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/DesktopDateTimePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { DesktopDateTimePicker } from '@mui/x-date-pickers/DesktopDateTimePicker'; import { adapterToUse, createPickerRenderer, openPicker } from 'test/utils/pickers'; @@ -63,4 +63,47 @@ describe('', () => { expect(onClose.callCount).to.equal(1); }); }); + + describe('prop: timeSteps', () => { + it('should use "DigitalClock" view renderer, when "timeSteps.minutes" = 60', () => { + const onChange = spy(); + const onAccept = spy(); + render( + , + ); + + userEvent.mousePress(screen.getByLabelText(/Choose date/)); + + userEvent.mousePress(screen.getByRole('gridcell', { name: '2' })); + userEvent.mousePress(screen.getByRole('option', { name: '03:00 AM' })); + + expect(onChange.callCount).to.equal(2); + expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 2, 3, 0, 0)); + expect(onAccept.callCount).to.equal(1); + }); + + it('should accept value and close picker when selecting time on "DigitalClock" view renderer', () => { + const onChange = spy(); + const onAccept = spy(); + render( + , + ); + + userEvent.mousePress(screen.getByLabelText(/Choose date/)); + + userEvent.mousePress(screen.getByRole('option', { name: '03:00 AM' })); + + expect(onChange.callCount).to.equal(1); + expect(onChange.lastCall.args[0]).toEqualDateTime(new Date(2018, 0, 1, 3, 0, 0)); + expect(onAccept.callCount).to.equal(1); + }); + }); }); diff --git a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx index 9113feb103c0b..25b94f71c33c9 100644 --- a/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopDateTimePicker/tests/describes.DesktopDateTimePicker.test.tsx @@ -1,15 +1,15 @@ -import { screen, userEvent } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { createPickerRenderer, adapterToUse, expectInputValue, expectInputPlaceholder, getTextbox, + describeValidation, + describeValue, + describePicker, } from 'test/utils/pickers'; import { DesktopDateTimePicker } from '@mui/x-date-pickers/DesktopDateTimePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx index 5857c3f2242d8..e5bff3caf28b3 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.tsx @@ -19,11 +19,23 @@ import { import { PickersActionBarAction } from '../PickersActionBar'; import { TimeViewWithMeridiem } from '../internals/models'; import { resolveTimeFormat } from '../internals/utils/time-utils'; +import { resolveTimeViewsResponse } from '../internals/utils/date-time-utils'; +import { TimeView } from '../models/views'; type DesktopTimePickerComponent = (( props: DesktopTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [DesktopTimePicker API](https://mui.com/x/api/date-pickers/desktop-time-picker/) + */ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( inProps: DesktopTimePickerProps, ref: React.Ref, @@ -38,11 +50,11 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( DesktopTimePickerProps >(inProps, 'MuiDesktopTimePicker'); - const thresholdToRenderTimeInASingleColumn = - defaultizedProps.thresholdToRenderTimeInASingleColumn ?? 24; - const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...defaultizedProps.timeSteps }; - const shouldRenderTimeInASingleColumn = - (24 * 60) / (timeSteps.hours * timeSteps.minutes) <= thresholdToRenderTimeInASingleColumn; + const { + shouldRenderTimeInASingleColumn, + views: resolvedViews, + timeSteps, + } = resolveTimeViewsResponse(defaultizedProps); const renderTimeView = shouldRenderTimeInASingleColumn ? renderDigitalClockTimeView @@ -63,10 +75,9 @@ const DesktopTimePicker = React.forwardRef(function DesktopTimePicker( // Need to avoid adding the `meridiem` view when unexpected renderer is specified const shouldHoursRendererContainMeridiemView = viewRenderers.hours?.name === renderMultiSectionDigitalClockTimeView.name; - const views: readonly TimeViewWithMeridiem[] = - defaultizedProps.ampm && shouldHoursRendererContainMeridiemView - ? [...defaultizedProps.views, 'meridiem'] - : defaultizedProps.views; + const views = !shouldHoursRendererContainMeridiemView + ? resolvedViews.filter((view) => view !== 'meridiem') + : resolvedViews; // Props with the default values specific to the desktop variant const props = { @@ -145,18 +156,6 @@ DesktopTimePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts index 38e9d03a7d404..56b9af0f02eb2 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts +++ b/packages/x-date-pickers/src/DesktopTimePicker/DesktopTimePicker.types.ts @@ -10,7 +10,6 @@ import { } from '../TimePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { TimeViewWithMeridiem } from '../internals/models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DesktopOnlyTimePickerProps } from '../internals/models/props/clock'; import { DigitalClockSlotsComponent, DigitalClockSlotsComponentsProps } from '../DigitalClock'; import { @@ -23,7 +22,7 @@ export interface DesktopTimePickerSlotsComponent extends BaseTimePickerSlotsComponent, MakeOptional< UseDesktopPickerSlotsComponent, - 'Field' | 'OpenPickerIcon' + 'field' | 'openPickerIcon' >, DigitalClockSlotsComponent, MultiSectionDigitalClockSlotsComponent {} @@ -42,23 +41,11 @@ export interface DesktopTimePickerProps * Available views. */ views?: readonly TimeView[]; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DesktopTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DesktopTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: DesktopTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx index 3ec04eafc7abc..a98180cffe748 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/DesktopTimePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { DesktopTimePicker } from '@mui/x-date-pickers/DesktopTimePicker'; import { createPickerRenderer, openPicker } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx index f6e6d69fa3104..7259992a7c8dd 100644 --- a/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx +++ b/packages/x-date-pickers/src/DesktopTimePicker/tests/describes.DesktopTimePicker.test.tsx @@ -1,7 +1,5 @@ import * as React from 'react'; -import { screen, userEvent, describeConformance } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, userEvent, describeConformance } from '@mui-internal/test-utils'; import { createPickerRenderer, wrapPickerMount, @@ -9,9 +7,12 @@ import { expectInputValue, expectInputPlaceholder, getTextbox, + describeValidation, + describeValue, + describePicker, + formatFullTimeValue, } from 'test/utils/pickers'; import { DesktopTimePicker } from '@mui/x-date-pickers/DesktopTimePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); @@ -68,9 +69,7 @@ describe(' - Describes', () => { } expectInputValue( input, - expectedValue - ? adapterToUse.format(expectedValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h') - : '', + expectedValue ? formatFullTimeValue(adapterToUse, expectedValue) : '', ); }, setNewValue: (value, { isOpened, applySameValue, selectSection }) => { diff --git a/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx index 8a4e79e09d90d..720f52747c141 100644 --- a/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx +++ b/packages/x-date-pickers/src/DigitalClock/DigitalClock.tsx @@ -85,6 +85,16 @@ type DigitalClockComponent = (( props: DigitalClockProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [DigitalClock](https://mui.com/x/react-date-pickers/digital-clock/) + * + * API: + * + * - [DigitalClock API](https://mui.com/x/api/date-pickers/digital-clock/) + */ export const DigitalClock = React.forwardRef(function DigitalClock( inProps: DigitalClockProps, ref: React.Ref, @@ -103,8 +113,6 @@ export const DigitalClock = React.forwardRef(function DigitalClock - handleRawValueChange(newValue, 'finish'), + handleRawValueChange(newValue, 'finish', 'hours'), ); const { setValueAndGoToNextView } = useViews>({ @@ -194,14 +202,17 @@ export const DigitalClock = React.forwardRef(function DigitalClock( - '[role="listbox"] [role="option"][aria-selected="true"]', + const activeItem = containerRef.current.querySelector( + '[role="listbox"] [role="option"][tabindex="0"], [role="listbox"] [role="option"][aria-selected="true"]', ); - if (!selectedItem) { + if (!activeItem) { return; } - const offsetTop = selectedItem.offsetTop; + const offsetTop = activeItem.offsetTop; + if (autoFocus || !!focusedView) { + activeItem.focus(); + } // Subtracting the 4px of extra margin intended for the first visible section item containerRef.current.scrollTop = offsetTop - 4; @@ -273,6 +284,10 @@ export const DigitalClock = React.forwardRef(function DigitalClock + utils.isEqual(option, valueOrReferenceDate), + ); + return ( - {timeOptions.map((option) => { + {timeOptions.map((option, index) => { if (skipDisabled && isTimeDisabled(option)) { return null; } const isSelected = utils.isEqual(option, value); + const tabIndex = + focusedOptionIndex === index || (focusedOptionIndex === -1 && index === 0) ? 0 : -1; return ( {utils.format(option, ampm ? 'fullTime12h' : 'fullTime24h')} @@ -335,18 +352,6 @@ DigitalClock.propTypes = { */ classes: PropTypes.object, className: PropTypes.string, - /** - * Overrideable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default selected value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts b/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts index 3d17c19a5cab3..90d82750389ff 100644 --- a/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts +++ b/packages/x-date-pickers/src/DigitalClock/DigitalClock.types.ts @@ -1,7 +1,6 @@ import { SlotComponentProps } from '@mui/base/utils'; import MenuItem from '@mui/material/MenuItem'; import { DigitalClockClasses } from './digitalClockClasses'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, DigitalClockOnlyProps, @@ -18,7 +17,7 @@ export interface DigitalClockSlotsComponent { * Component responsible for rendering a single digital clock item. * @default MenuItem from '@mui/material' */ - DigitalClockItem?: React.ElementType; + digitalClockItem?: React.ElementType; } export interface DigitalClockSlotsComponentsProps { @@ -32,23 +31,11 @@ export interface DigitalClockProps * Override or extend the styles applied to the component. */ classes?: Partial; - /** - * Overrideable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: DigitalClockSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: DigitalClockSlotsComponentsProps; /** * Overrideable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: DigitalClockSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/DigitalClock/tests/DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/DigitalClock.test.tsx index 0df3c3fee7472..6002e825f08ef 100644 --- a/packages/x-date-pickers/src/DigitalClock/tests/DigitalClock.test.tsx +++ b/packages/x-date-pickers/src/DigitalClock/tests/DigitalClock.test.tsx @@ -2,7 +2,13 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; -import { adapterToUse, createPickerRenderer, digitalClockHandler } from 'test/utils/pickers'; +import { + adapterToUse, + createPickerRenderer, + digitalClockHandler, + formatFullTimeValue, +} from 'test/utils/pickers'; +import { screen } from '@mui-internal/test-utils'; describe('', () => { const { render } = createPickerRenderer(); @@ -10,13 +16,22 @@ describe('', () => { describe('Reference date', () => { it('should use `referenceDate` when no value defined', () => { const onChange = spy(); + const referenceDate = new Date(2018, 0, 1, 12, 30); - render( - , - ); + render(); + + // the first item should not be initially focusable when `referenceDate` is defined + expect( + screen.getByRole('option', { + name: formatFullTimeValue(adapterToUse, new Date(2018, 0, 1, 0, 0, 0)), + }), + ).to.have.attribute('tabindex', '-1'); + // check that the relevant time based on the `referenceDate` is focusable + expect( + screen.getByRole('option', { + name: formatFullTimeValue(adapterToUse, referenceDate), + }), + ).to.have.attribute('tabindex', '0'); digitalClockHandler.setViewValue( adapterToUse, @@ -26,6 +41,18 @@ describe('', () => { expect(onChange.lastCall.firstArg).toEqualDateTime(new Date(2018, 0, 1, 15, 30)); }); + it('should fallback to making the first entry focusable when `referenceDate` does not map to any option', () => { + const referenceDate = new Date(2018, 0, 1, 12, 33); + + render(); + + expect( + screen.getByRole('option', { + name: formatFullTimeValue(adapterToUse, new Date(2018, 0, 1, 0, 0, 0)), + }), + ).to.have.attribute('tabindex', '0'); + }); + it('should not use `referenceDate` when a value is defined', () => { const onChange = spy(); diff --git a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx index 242d5b1b9e6e4..45050c55ae149 100644 --- a/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx +++ b/packages/x-date-pickers/src/DigitalClock/tests/describes.DigitalClock.test.tsx @@ -1,13 +1,14 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen, describeConformance } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, describeConformance } from '@mui-internal/test-utils'; import { createPickerRenderer, wrapPickerMount, adapterToUse, digitalClockHandler, + describeValidation, + describeValue, + formatFullTimeValue, } from 'test/utils/pickers'; import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; @@ -56,14 +57,11 @@ describe(' - Describes', () => { emptyValue: null, clock, assertRenderedValue: (expectedValue: any) => { - const hasMeridiem = adapterToUse.is12HourCycleInCurrentLocale(); const selectedItem = screen.queryByRole('option', { selected: true }); if (!expectedValue) { expect(selectedItem).to.equal(null); } else { - expect(selectedItem).to.have.text( - adapterToUse.format(expectedValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h'), - ); + expect(selectedItem).to.have.text(formatFullTimeValue(adapterToUse, expectedValue)); } }, setNewValue: (value) => { diff --git a/packages/x-date-pickers/src/DigitalClock/tests/timezone.DigitalClock.test.tsx b/packages/x-date-pickers/src/DigitalClock/tests/timezone.DigitalClock.test.tsx index c03d8e30aa299..50d692bfe6792 100644 --- a/packages/x-date-pickers/src/DigitalClock/tests/timezone.DigitalClock.test.tsx +++ b/packages/x-date-pickers/src/DigitalClock/tests/timezone.DigitalClock.test.tsx @@ -1,10 +1,9 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { DigitalClock } from '@mui/x-date-pickers/DigitalClock'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; -import { getDateOffset } from 'test/utils/pickers'; +import { getDateOffset, describeAdapters } from 'test/utils/pickers'; const TIMEZONE_TO_TEST = ['UTC', 'system', 'America/New_York']; diff --git a/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.test.tsx b/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.test.tsx index 550589dc372d8..b7120b6f6c0ca 100644 --- a/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.test.tsx +++ b/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { createRenderer } from '@mui/monorepo/test/utils'; +import { createRenderer } from '@mui-internal/test-utils'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { useLocalizationContext } from '@mui/x-date-pickers/internals'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; diff --git a/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.tsx b/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.tsx index 935cfbd52e2cc..f2784c8c3e10c 100644 --- a/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.tsx +++ b/packages/x-date-pickers/src/LocalizationProvider/LocalizationProvider.tsx @@ -55,6 +55,18 @@ type LocalizationProviderComponent = (( props: LocalizationProviderProps, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [Date format and localization](https://mui.com/x/react-date-pickers/adapters-locale/) + * - [Calendar systems](https://mui.com/x/react-date-pickers/calendar-systems/) + * - [Translated components](https://mui.com/x/react-date-pickers/localization/) + * - [UTC and timezones](https://mui.com/x/react-date-pickers/timezone/) + * + * API: + * + * - [LocalizationProvider API](https://mui.com/x/api/date-pickers/localization-provider/) + */ export const LocalizationProvider = function LocalizationProvider( inProps: LocalizationProviderProps, ) { diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx index ab414d3d2371b..691de0d6b41fd 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.tsx @@ -17,6 +17,16 @@ type MobileDatePickerComponent = (( props: MobileDatePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [MobileDatePicker API](https://mui.com/x/api/date-pickers/mobile-date-picker/) + */ const MobileDatePicker = React.forwardRef(function MobileDatePicker( inProps: MobileDatePickerProps, ref: React.Ref, @@ -93,23 +103,12 @@ MobileDatePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -317,6 +316,9 @@ 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.types.ts b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.types.ts index fbce20c3e4ec5..cffb71c814786 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.types.ts +++ b/packages/x-date-pickers/src/MobileDatePicker/MobileDatePicker.types.ts @@ -10,11 +10,10 @@ import { } from '../DatePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { DateView } from '../models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; export interface MobileDatePickerSlotsComponent extends BaseDatePickerSlotsComponent, - MakeOptional, 'Field'> {} + MakeOptional, 'field'> {} export interface MobileDatePickerSlotsComponentsProps extends BaseDatePickerSlotsComponentsProps, @@ -23,23 +22,11 @@ export interface MobileDatePickerSlotsComponentsProps export interface MobileDatePickerProps extends BaseDatePickerProps, MobileOnlyPickerProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MobileDatePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MobileDatePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: MobileDatePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx index 618299f987e8e..a567dc886d9d5 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/tests/MobileDatePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { fireEvent, screen, userEvent } from '@mui/monorepo/test/utils'; +import { fireEvent, screen, userEvent } from '@mui-internal/test-utils'; import { PickersDay } from '@mui/x-date-pickers/PickersDay'; import { DayCalendarSkeleton } from '@mui/x-date-pickers/DayCalendarSkeleton'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; @@ -106,7 +106,7 @@ describe('', () => { }); it('should render the toolbar when `hidden` is `false`', () => { - render(); + render(); expect(screen.getByMuiTest('picker-toolbar')).toBeVisible(); }); diff --git a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx index 5942efb54a65b..68411cd23471c 100644 --- a/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDatePicker/tests/describes.MobileDatePicker.test.tsx @@ -1,6 +1,4 @@ -import { screen, userEvent } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { createPickerRenderer, adapterToUse, @@ -8,9 +6,11 @@ import { expectInputPlaceholder, openPicker, getTextbox, + describeValidation, + describeValue, + describePicker, } from 'test/utils/pickers'; import { MobileDatePicker } from '@mui/x-date-pickers/MobileDatePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx index 9311762827604..d5ab36148eefd 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.tsx @@ -20,6 +20,16 @@ type MobileDateTimePickerComponent = (( props: MobileDateTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [MobileDateTimePicker API](https://mui.com/x/api/date-pickers/mobile-date-time-picker/) + */ const MobileDateTimePicker = React.forwardRef(function MobileDateTimePicker( inProps: MobileDateTimePickerProps, ref: React.Ref, @@ -117,23 +127,12 @@ MobileDateTimePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -377,6 +376,9 @@ MobileDateTimePicker.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts index 61be2967f7a74..c3f45859e4775 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/MobileDateTimePicker/MobileDateTimePicker.types.ts @@ -10,14 +10,13 @@ import { } from '../DateTimePicker/shared'; import { MakeOptional } from '../internals/models/helpers'; import { DateOrTimeView } from '../models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; export interface MobileDateTimePickerSlotsComponent< TDate, TView extends DateOrTimeViewWithMeridiem = DateOrTimeView, > extends BaseDateTimePickerSlotsComponent, - MakeOptional, 'Field'> {} + MakeOptional, 'field'> {} export interface MobileDateTimePickerSlotsComponentsProps< TDate, @@ -30,23 +29,11 @@ export interface MobileDateTimePickerProps< TView extends DateOrTimeViewWithMeridiem = DateOrTimeView, > extends BaseDateTimePickerProps, MobileOnlyPickerProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MobileDateTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MobileDateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: MobileDateTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} 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 98116ef31ad30..b9eed3880cb2b 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/MobileDateTimePicker.test.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import TextField from '@mui/material/TextField'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { fireTouchChangedEvent, screen, userEvent } from '@mui/monorepo/test/utils'; +import { fireTouchChangedEvent, screen, userEvent } from '@mui-internal/test-utils'; import { MobileDateTimePicker } from '@mui/x-date-pickers/MobileDateTimePicker'; import { adapterToUse, @@ -18,7 +18,7 @@ describe('', () => { render( , ); @@ -78,7 +78,7 @@ describe('', () => { render( , ); diff --git a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx index d8c8143c43cc3..21a60e4d696ac 100644 --- a/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileDateTimePicker/tests/describes.MobileDateTimePicker.test.tsx @@ -1,6 +1,4 @@ -import { screen, userEvent, fireTouchChangedEvent } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, userEvent, fireTouchChangedEvent } from '@mui-internal/test-utils'; import { createPickerRenderer, adapterToUse, @@ -9,9 +7,11 @@ import { openPicker, getClockTouchEvent, getTextbox, + describeValidation, + describeValue, + describePicker, } from 'test/utils/pickers'; import { MobileDateTimePicker } from '@mui/x-date-pickers/MobileDateTimePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx index 2310f8dc69698..75960ca72487f 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.tsx @@ -19,6 +19,16 @@ type MobileTimePickerComponent = (( props: MobileTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [MobileTimePicker API](https://mui.com/x/api/date-pickers/mobile-time-picker/) + */ const MobileTimePicker = React.forwardRef(function MobileTimePicker( inProps: MobileTimePickerProps, ref: React.Ref, @@ -109,18 +119,6 @@ MobileTimePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts index 98412691d2f4f..31ba372115e5c 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts +++ b/packages/x-date-pickers/src/MobileTimePicker/MobileTimePicker.types.ts @@ -11,13 +11,12 @@ import { import { MakeOptional } from '../internals/models/helpers'; import { TimeView } from '../models'; import { TimeViewWithMeridiem } from '../internals/models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; export interface MobileTimePickerSlotsComponent< TDate, TView extends TimeViewWithMeridiem = TimeView, > extends BaseTimePickerSlotsComponent, - MakeOptional, 'Field'> {} + MakeOptional, 'field'> {} export interface MobileTimePickerSlotsComponentsProps< TDate, @@ -28,23 +27,11 @@ export interface MobileTimePickerSlotsComponentsProps< export interface MobileTimePickerProps extends BaseTimePickerProps, MobileOnlyPickerProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MobileTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MobileTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: MobileTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx index f278b9276bcf0..7db671ad7b38d 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/tests/MobileTimePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { fireTouchChangedEvent, screen, userEvent, act } from '@mui/monorepo/test/utils'; +import { fireTouchChangedEvent, screen, userEvent, act } from '@mui-internal/test-utils'; import { MobileTimePicker } from '@mui/x-date-pickers/MobileTimePicker'; import { createPickerRenderer, @@ -32,7 +32,7 @@ describe('', () => { ampm onChange={handleChange} open - componentsProps={{ toolbar: { hidden: false } }} + slotProps={{ toolbar: { hidden: false } }} value={adapterToUse.date(new Date(2019, 0, 1, 4, 20))} />, ); diff --git a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx index 6708375a68c61..7d06143444916 100644 --- a/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx +++ b/packages/x-date-pickers/src/MobileTimePicker/tests/describes.MobileTimePicker.test.tsx @@ -4,9 +4,7 @@ import { screen, userEvent, fireTouchChangedEvent, -} from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +} from '@mui-internal/test-utils'; import { createPickerRenderer, wrapPickerMount, @@ -16,9 +14,12 @@ import { openPicker, getClockTouchEvent, getTextbox, + describeValidation, + describeValue, + describePicker, + formatFullTimeValue, } from 'test/utils/pickers'; import { MobileTimePicker } from '@mui/x-date-pickers/MobileTimePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -73,7 +74,7 @@ describe(' - Describes', () => { expectInputPlaceholder(input, hasMeridiem ? 'hh:mm aa' : 'hh:mm'); } const expectedValueStr = expectedValue - ? adapterToUse.format(expectedValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h') + ? formatFullTimeValue(adapterToUse, expectedValue) : ''; expectInputValue(input, expectedValueStr); diff --git a/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx b/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx index 9ae4ca908b297..2ee5130f4226b 100644 --- a/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/MonthCalendar.tsx @@ -17,6 +17,7 @@ import { MonthCalendarProps } from './MonthCalendar.types'; import { singleItemValueManager } from '../internals/utils/valueManagers'; import { SECTION_TYPE_GRANULARITY } from '../internals/utils/getDefaultReferenceDate'; import { useControlledValueWithTimezone } from '../internals/hooks/useValueWithTimezone'; +import { DIALOG_WIDTH } from '../internals/constants/dimensions'; const useUtilityClasses = (ownerState: MonthCalendarProps) => { const { classes } = ownerState; @@ -60,13 +61,24 @@ const MonthCalendarRoot = styled('div', { flexWrap: 'wrap', alignContent: 'stretch', padding: '0 4px', - width: 320, + width: DIALOG_WIDTH, + // avoid padding increasing width over defined + boxSizing: 'border-box', }); type MonthCalendarComponent = (( props: MonthCalendarProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) + * + * API: + * + * - [MonthCalendar API](https://mui.com/x/api/date-pickers/month-calendar/) + */ export const MonthCalendar = React.forwardRef(function MonthCalendar( inProps: MonthCalendarProps, ref: React.Ref, @@ -132,13 +144,11 @@ export const MonthCalendar = React.forwardRef(function MonthCalendar( return utils.getMonth(value); } - if (disableHighlightToday) { - return null; - } - - return utils.getMonth(referenceDate); - }, [value, utils, disableHighlightToday, referenceDate]); - const [focusedMonth, setFocusedMonth] = React.useState(() => selectedMonth || todayMonth); + return null; + }, [value, utils]); + const [focusedMonth, setFocusedMonth] = React.useState( + () => selectedMonth || utils.getMonth(referenceDate), + ); const [internalHasFocus, setInternalHasFocus] = useControlled({ name: 'MonthCalendar', diff --git a/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx b/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx index a7ad330898331..4bcf96e6fbe47 100644 --- a/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/tests/MonthCalendar.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { fireEvent, screen } from '@mui/monorepo/test/utils'; +import { fireEvent, screen } from '@mui-internal/test-utils'; import { MonthCalendar } from '@mui/x-date-pickers/MonthCalendar'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; @@ -169,5 +169,11 @@ describe('', () => { expect(january).not.to.have.attribute('disabled'); expect(february).to.have.attribute('disabled'); }); + + it('should not mark the `referenceDate` month as selected', () => { + render(); + + expect(screen.getByRole('radio', { name: 'February', checked: false })).to.not.equal(null); + }); }); }); diff --git a/packages/x-date-pickers/src/MonthCalendar/tests/describes.MonthCalendar.test.tsx b/packages/x-date-pickers/src/MonthCalendar/tests/describes.MonthCalendar.test.tsx index b9efcbc18975e..8cd311f7422b9 100644 --- a/packages/x-date-pickers/src/MonthCalendar/tests/describes.MonthCalendar.test.tsx +++ b/packages/x-date-pickers/src/MonthCalendar/tests/describes.MonthCalendar.test.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; import { expect } from 'chai'; -import { describeConformance, userEvent, screen } from '@mui/monorepo/test/utils'; -import { wrapPickerMount, createPickerRenderer, adapterToUse } from 'test/utils/pickers'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { describeConformance, userEvent, screen } from '@mui-internal/test-utils'; import { - MonthCalendar, - monthCalendarClasses as classes, - pickersMonthClasses, -} from '@mui/x-date-pickers/MonthCalendar'; + wrapPickerMount, + createPickerRenderer, + adapterToUse, + describeValidation, + describeValue, +} from 'test/utils/pickers'; +import { MonthCalendar, monthCalendarClasses as classes } from '@mui/x-date-pickers/MonthCalendar'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); @@ -38,17 +38,19 @@ describe(' - Describes', () => { emptyValue: null, clock, assertRenderedValue: (expectedValue: any) => { - const selectedCells = document.querySelectorAll(`.${pickersMonthClasses.selected}`); + const activeMonth = screen + .queryAllByRole('radio') + .find((cell) => cell.getAttribute('tabindex') === '0'); + expect(activeMonth).not.to.equal(null); if (expectedValue == null) { - expect(selectedCells).to.have.length(1); - expect(selectedCells[0]).to.have.text( + expect(activeMonth).to.have.text( adapterToUse.format(adapterToUse.date(), 'monthShort').toString(), ); } else { - expect(selectedCells).to.have.length(1); - expect(selectedCells[0]).to.have.text( + expect(activeMonth).to.have.text( adapterToUse.format(expectedValue, 'monthShort').toString(), ); + expect(activeMonth).to.have.attribute('aria-checked', 'true'); } }, setNewValue: (value) => { diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx index 0917ad29c1b08..9f083eac67783 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.tsx @@ -48,6 +48,16 @@ type MultiSectionDigitalClockComponent = (( props: MultiSectionDigitalClockProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [DigitalClock](https://mui.com/x/react-date-pickers/digital-clock/) + * + * API: + * + * - [MultiSectionDigitalClock API](https://mui.com/x/api/date-pickers/multi-section-digital-clock/) + */ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDigitalClock< TDate extends unknown, >(inProps: MultiSectionDigitalClockProps, ref: React.Ref) { @@ -62,8 +72,6 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi ampm = utils.is12HourCycleInCurrentLocale(), timeSteps: inTimeSteps, autoFocus, - components, - componentsProps, slots, slotProps, value: valueProp, @@ -294,6 +302,7 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi isDisabled: (hours) => disabled || isTimeDisabled(hours, 'hours'), timeStep: timeSteps.hours, resolveAriaLabel: localeText.hoursClockNumberText, + valueOrReferenceDate, }), }; } @@ -340,12 +349,14 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi value: 'am', label: amLabel, isSelected: () => !!value && meridiemMode === 'am', + isFocused: () => !!valueOrReferenceDate && meridiemMode === 'am', ariaLabel: amLabel, }, { value: 'pm', label: pmLabel, isSelected: () => !!value && meridiemMode === 'pm', + isFocused: () => !!valueOrReferenceDate && meridiemMode === 'pm', ariaLabel: pmLabel, }, ], @@ -402,8 +413,8 @@ export const MultiSectionDigitalClock = React.forwardRef(function MultiSectionDi autoFocus={autoFocus ?? focusedView === timeView} disabled={disabled} readOnly={readOnly} - slots={slots ?? components} - slotProps={slotProps ?? componentsProps} + slots={slots} + slotProps={slotProps} skipDisabled={skipDisabled} aria-label={localeText.selectViewText(timeView as TimeViewWithMeridiem)} /> @@ -434,18 +445,6 @@ MultiSectionDigitalClock.propTypes = { */ classes: PropTypes.object, className: PropTypes.string, - /** - * Overrideable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default selected value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts index bc51ef50d49f7..ac0b3e6aa4f2b 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.types.ts @@ -1,7 +1,6 @@ import { SlotComponentProps } from '@mui/base/utils'; import MenuItem from '@mui/material/MenuItem'; import { MultiSectionDigitalClockClasses } from './multiSectionDigitalClockClasses'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps, @@ -13,6 +12,7 @@ import { TimeViewWithMeridiem } from '../internals/models'; export interface MultiSectionDigitalClockOption { isDisabled?: (value: TValue) => boolean; isSelected: (value: TValue) => boolean; + isFocused: (value: TValue) => boolean; label: string; value: TValue; ariaLabel: string; @@ -30,7 +30,7 @@ export interface MultiSectionDigitalClockSlotsComponent { * Component responsible for rendering a single multi section digital clock section item. * @default MenuItem from '@mui/material' */ - DigitalClockSectionItem?: React.ElementType; + digitalClockSectionItem?: React.ElementType; } export interface MultiSectionDigitalClockSlotsComponentsProps { @@ -44,23 +44,11 @@ export interface MultiSectionDigitalClockProps * Override or extend the styles applied to the component. */ classes?: Partial; - /** - * Overrideable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: MultiSectionDigitalClockSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: MultiSectionDigitalClockSlotsComponentsProps; /** * Overrideable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: MultiSectionDigitalClockSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts index da44fac419543..25ed52dfd2046 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClock.utils.ts @@ -9,6 +9,7 @@ interface IGetHoursSectionOptions { isDisabled: (value: number) => boolean; timeStep: number; resolveAriaLabel: (value: string) => string; + valueOrReferenceDate: TDate; } export const getHourSectionOptions = ({ @@ -19,25 +20,31 @@ export const getHourSectionOptions = ({ isDisabled, resolveAriaLabel, timeStep, + valueOrReferenceDate, }: IGetHoursSectionOptions): MultiSectionDigitalClockOption[] => { const currentHours = value ? utils.getHours(value) : null; const result: MultiSectionDigitalClockOption[] = []; - const isSelected = (hour: number) => { - if (currentHours === null) { + const isSelected = (hour: number, overriddenCurrentHours?: number) => { + const resolvedCurrentHours = overriddenCurrentHours ?? currentHours; + if (resolvedCurrentHours === null) { return false; } if (ampm) { if (hour === 12) { - return currentHours === 12 || currentHours === 0; + return resolvedCurrentHours === 12 || resolvedCurrentHours === 0; } - return currentHours === hour || currentHours - 12 === hour; + return resolvedCurrentHours === hour || resolvedCurrentHours - 12 === hour; } - return currentHours === hour; + return resolvedCurrentHours === hour; + }; + + const isFocused = (hour: number) => { + return isSelected(hour, utils.getHours(valueOrReferenceDate)); }; const endHour = ampm ? 11 : 23; @@ -52,6 +59,7 @@ export const getHourSectionOptions = ({ label, isSelected, isDisabled, + isFocused, ariaLabel, }); } @@ -83,6 +91,10 @@ export const getTimeSectionOptions = ({ return hasValue && value === timeValue; }; + const isFocused = (timeValue: number) => { + return value === timeValue; + }; + return [ ...Array.from({ length: Math.ceil(60 / timeStep) }, (_, index) => { const timeValue = timeStep * index; @@ -91,6 +103,7 @@ export const getTimeSectionOptions = ({ label: resolveLabel(timeValue), isDisabled, isSelected, + isFocused, ariaLabel: resolveAriaLabel(timeValue.toString()), }; }), diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx index c0dfd8b959c6a..becbdcf81de64 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/MultiSectionDigitalClockSection.tsx @@ -14,7 +14,6 @@ import type { MultiSectionDigitalClockSlotsComponent, MultiSectionDigitalClockSlotsComponentsProps, } from './MultiSectionDigitalClock.types'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DIGITAL_CLOCK_VIEW_HEIGHT, MULTI_SECTION_CLOCK_SECTION_WIDTH, @@ -23,7 +22,7 @@ import { export interface ExportedMultiSectionDigitalClockSectionProps { className?: string; classes?: Partial; - slots?: UncapitalizeObjectKeys; + slots?: MultiSectionDigitalClockSlotsComponent; slotProps?: MultiSectionDigitalClockSlotsComponentsProps; } @@ -122,7 +121,7 @@ export const MultiSectionDigitalClockSection = React.forwardRef( ) { const containerRef = React.useRef(null); const handleRef = useForkRef(ref, containerRef); - const previousSelected = React.useRef(null); + const previousActive = React.useRef(null); const props = useThemeProps({ props: inProps, @@ -155,26 +154,28 @@ export const MultiSectionDigitalClockSection = React.forwardRef( if (containerRef.current === null) { return; } - const selectedItem = containerRef.current.querySelector( - '[role="option"][aria-selected="true"]', + const activeItem = containerRef.current.querySelector( + '[role="option"][tabindex="0"], [role="option"][aria-selected="true"]', ); - if (!selectedItem || previousSelected.current === selectedItem) { + if (!activeItem || previousActive.current === activeItem) { // Handle setting the ref to null if the selected item is ever reset via UI - if (previousSelected.current !== selectedItem) { - previousSelected.current = selectedItem; + if (previousActive.current !== activeItem) { + previousActive.current = activeItem; } return; } - previousSelected.current = selectedItem; + previousActive.current = activeItem; if (active && autoFocus) { - selectedItem.focus(); + activeItem.focus(); } - const offsetTop = selectedItem.offsetTop; + const offsetTop = activeItem.offsetTop; // Subtracting the 4px of extra margin intended for the first visible section item containerRef.current.scrollTop = offsetTop - 4; }); + const focusedOptionIndex = items.findIndex((item) => item.isFocused(item.value)); + return ( - {items.map((option) => { + {items.map((option, index) => { if (skipDisabled && option.isDisabled?.(option.value)) { return null; } const isSelected = option.isSelected(option.value); + const tabIndex = + focusedOptionIndex === index || (focusedOptionIndex === -1 && index === 0) ? 0 : -1; return ( {option.label} diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/MultiSectionDigitalClock.test.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/MultiSectionDigitalClock.test.tsx index ec9da34386c2a..1b5fea603f38d 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/MultiSectionDigitalClock.test.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/MultiSectionDigitalClock.test.tsx @@ -10,6 +10,7 @@ import { adapterToUse, multiSectionDigitalClockHandler, } from 'test/utils/pickers'; +import { screen } from '@mui-internal/test-utils'; describe('', () => { const { render } = createPickerRenderer(); @@ -17,14 +18,24 @@ describe('', () => { describe('Reference date', () => { it('should use `referenceDate` when no value defined', () => { const onChange = spy(); + const referenceDate = new Date(2018, 0, 1, 13, 30); render( , ); + // the first section items should not be initially focusable when `referenceDate` is defined + expect(screen.getByRole('option', { name: '12 hours' })).to.have.attribute('tabindex', '-1'); + expect(screen.getByRole('option', { name: '0 minutes' })).to.have.attribute('tabindex', '-1'); + expect(screen.getByRole('option', { name: 'AM' })).to.have.attribute('tabindex', '-1'); + // check that the relevant time based on the `referenceDate` is focusable + expect(screen.getByRole('option', { name: '1 hours' })).to.have.attribute('tabindex', '0'); + expect(screen.getByRole('option', { name: '30 minutes' })).to.have.attribute('tabindex', '0'); + expect(screen.getByRole('option', { name: 'PM' })).to.have.attribute('tabindex', '0'); + multiSectionDigitalClockHandler.setViewValue( adapterToUse, adapterToUse.setMinutes(adapterToUse.setHours(adapterToUse.date(), 15), 30), @@ -33,6 +44,14 @@ describe('', () => { expect(onChange.lastCall.firstArg).toEqualDateTime(new Date(2018, 0, 1, 15, 30)); }); + it('should fallback to making the first entry focusable when `referenceDate` does not map to an option', () => { + const referenceDate = new Date(2018, 0, 1, 13, 33); + + render(); + + expect(screen.getByRole('option', { name: '0 minutes' })).to.have.attribute('tabindex', '0'); + }); + it('should not use `referenceDate` when a value is defined', () => { const onChange = spy(); diff --git a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx index 65a51f6c0f0b2..2cfd9786188ff 100644 --- a/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx +++ b/packages/x-date-pickers/src/MultiSectionDigitalClock/tests/describes.MultiSectionDigitalClock.test.tsx @@ -1,13 +1,13 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen, describeConformance } from '@mui/monorepo/test/utils'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { screen, describeConformance } from '@mui-internal/test-utils'; import { createPickerRenderer, wrapPickerMount, adapterToUse, multiSectionDigitalClockHandler, + describeValidation, + describeValue, } from 'test/utils/pickers'; import { MultiSectionDigitalClock } from '@mui/x-date-pickers/MultiSectionDigitalClock'; import { formatMeridiem } from '@mui/x-date-pickers/internals/utils/date-utils'; diff --git a/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.test.tsx b/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.test.tsx index 60b1fb41f9d79..066f60e07b489 100644 --- a/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.test.tsx +++ b/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { PickersActionBar } from '@mui/x-date-pickers/PickersActionBar'; import { createPickerRenderer } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.tsx b/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.tsx index f34e1fb1145d6..78dc13fee9e07 100644 --- a/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.tsx +++ b/packages/x-date-pickers/src/PickersActionBar/PickersActionBar.tsx @@ -19,6 +19,16 @@ export interface PickersActionBarProps extends DialogActionsProps { onSetToday: () => void; } +/** + * Demos: + * + * - [Custom slots and subcomponents](https://mui.com/x/react-date-pickers/custom-components/) + * - [Custom layout](https://mui.com/x/react-date-pickers/custom-layout/) + * + * API: + * + * - [PickersActionBar API](https://mui.com/x/api/date-pickers/pickers-action-bar/) + */ function PickersActionBar(props: PickersActionBarProps) { const { onAccept, onClear, onCancel, onSetToday, actions, ...other } = props; diff --git a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx index fd36b819bb74f..f82978de8bf93 100644 --- a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx +++ b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.tsx @@ -2,106 +2,26 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import Fade from '@mui/material/Fade'; -import { styled, SxProps, Theme, useThemeProps } from '@mui/material/styles'; -import { SlotComponentProps, useSlotProps } from '@mui/base/utils'; +import { styled, useThemeProps } from '@mui/material/styles'; +import { useSlotProps } from '@mui/base/utils'; import { unstable_composeClasses as composeClasses } from '@mui/utils'; import IconButton from '@mui/material/IconButton'; -import SvgIcon from '@mui/material/SvgIcon'; -import { SlideDirection } from '../DateCalendar/PickersSlideTransition'; import { useLocaleText, useUtils } from '../internals/hooks/useUtils'; import { PickersFadeTransitionGroup } from '../DateCalendar/PickersFadeTransitionGroup'; import { ArrowDropDownIcon } from '../icons'; -import { - PickersArrowSwitcher, - ExportedPickersArrowSwitcherProps, - PickersArrowSwitcherSlotsComponent, - PickersArrowSwitcherSlotsComponentsProps, -} from '../internals/components/PickersArrowSwitcher'; +import { PickersArrowSwitcher } from '../internals/components/PickersArrowSwitcher'; import { usePreviousMonthDisabled, useNextMonthDisabled, - MonthValidationOptions, } from '../internals/hooks/date-helpers-hooks'; -import { DateView } from '../models'; import { getPickersCalendarHeaderUtilityClass, pickersCalendarHeaderClasses, - PickersCalendarHeaderClasses, } from './pickersCalendarHeaderClasses'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; - -export type ExportedPickersCalendarHeaderProps = Pick< - PickersCalendarHeaderProps, - 'classes' | 'slots' | 'slotProps' ->; - -export interface PickersCalendarHeaderSlotsComponent extends PickersArrowSwitcherSlotsComponent { - /** - * Button displayed to switch between different calendar views. - * @default IconButton - */ - SwitchViewButton?: React.ElementType; - /** - * Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is 'year'. - * @default ArrowDropDown - */ - SwitchViewIcon?: React.ElementType; -} - -// We keep the interface to allow module augmentation -export interface PickersCalendarHeaderComponentsPropsOverrides {} - -type PickersCalendarHeaderOwnerState = PickersCalendarHeaderProps; - -export interface PickersCalendarHeaderSlotsComponentsProps - extends PickersArrowSwitcherSlotsComponentsProps { - switchViewButton?: SlotComponentProps< - typeof IconButton, - PickersCalendarHeaderComponentsPropsOverrides, - PickersCalendarHeaderOwnerState - >; - - switchViewIcon?: SlotComponentProps< - typeof SvgIcon, - PickersCalendarHeaderComponentsPropsOverrides, - undefined - >; -} - -export interface PickersCalendarHeaderProps - extends ExportedPickersArrowSwitcherProps, - MonthValidationOptions { - /** - * Overridable component slots. - * @default {} - */ - slots?: UncapitalizeObjectKeys; - /** - * The props used for each component slot. - * @default {} - */ - slotProps?: PickersCalendarHeaderSlotsComponentsProps; - currentMonth: TDate; - disabled?: boolean; - views: readonly DateView[]; - onMonthChange: (date: TDate, slideDirection: SlideDirection) => void; - view: DateView; - reduceAnimations: boolean; - onViewChange?: (view: DateView) => void; - labelId?: string; - /** - * Override or extend the styles applied to the component. - */ - classes?: Partial; - /** - * className applied to the root element. - */ - className?: string; - /** - * The system prop that allows defining system overrides as well as additional CSS styles. - */ - sx?: SxProps; -} +import { + PickersCalendarHeaderOwnerState, + PickersCalendarHeaderProps, +} from './PickersCalendarHeader.types'; const useUtilityClasses = (ownerState: PickersCalendarHeaderOwnerState) => { const { classes } = ownerState; @@ -189,6 +109,17 @@ type PickersCalendarHeaderComponent = (( props: PickersCalendarHeaderProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) + * - [DateRangeCalendar](https://mui.com/x/react-date-pickers/date-range-calendar/) + * - [Custom slots and subcomponents](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [PickersCalendarHeader API](https://mui.com/x/api/date-pickers/pickers-calendar-header/) + */ const PickersCalendarHeader = React.forwardRef(function PickersCalendarHeader( inProps: PickersCalendarHeaderProps, ref: React.Ref, diff --git a/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.types.ts b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.types.ts new file mode 100644 index 0000000000000..91ffbe1322043 --- /dev/null +++ b/packages/x-date-pickers/src/PickersCalendarHeader/PickersCalendarHeader.types.ts @@ -0,0 +1,86 @@ +import { SlotComponentProps } from '@mui/base/utils'; +import IconButton from '@mui/material/IconButton'; +import SvgIcon from '@mui/material/SvgIcon'; +import { SxProps, Theme } from '@mui/material/styles'; +import { + ExportedPickersArrowSwitcherProps, + PickersArrowSwitcherSlotsComponent, + PickersArrowSwitcherSlotsComponentsProps, +} from '../internals/components/PickersArrowSwitcher'; +import { MonthValidationOptions } from '../internals/hooks/date-helpers-hooks'; +import { DateView } from '../models/views'; +import { SlideDirection } from '../DateCalendar/PickersSlideTransition'; +import { PickersCalendarHeaderClasses } from './pickersCalendarHeaderClasses'; + +export interface PickersCalendarHeaderSlotsComponent extends PickersArrowSwitcherSlotsComponent { + /** + * Button displayed to switch between different calendar views. + * @default IconButton + */ + switchViewButton?: React.ElementType; + /** + * Icon displayed in the SwitchViewButton. Rotated by 180° when the open view is 'year'. + * @default ArrowDropDown + */ + switchViewIcon?: React.ElementType; +} + +// We keep the interface to allow module augmentation +export interface PickersCalendarHeaderComponentsPropsOverrides {} + +export type PickersCalendarHeaderOwnerState = PickersCalendarHeaderProps; + +export interface PickersCalendarHeaderSlotsComponentsProps + extends PickersArrowSwitcherSlotsComponentsProps { + switchViewButton?: SlotComponentProps< + typeof IconButton, + PickersCalendarHeaderComponentsPropsOverrides, + PickersCalendarHeaderOwnerState + >; + + switchViewIcon?: SlotComponentProps< + typeof SvgIcon, + PickersCalendarHeaderComponentsPropsOverrides, + undefined + >; +} + +export interface PickersCalendarHeaderProps + extends ExportedPickersArrowSwitcherProps, + MonthValidationOptions { + /** + * Overridable component slots. + * @default {} + */ + slots?: PickersCalendarHeaderSlotsComponent; + /** + * The props used for each component slot. + * @default {} + */ + slotProps?: PickersCalendarHeaderSlotsComponentsProps; + currentMonth: TDate; + disabled?: boolean; + views: readonly DateView[]; + onMonthChange: (date: TDate, slideDirection: SlideDirection) => void; + view: DateView; + reduceAnimations: boolean; + onViewChange?: (view: DateView) => void; + labelId?: string; + /** + * Override or extend the styles applied to the component. + */ + classes?: Partial; + /** + * className applied to the root element. + */ + className?: string; + /** + * The system prop that allows defining system overrides as well as additional CSS styles. + */ + sx?: SxProps; +} + +export type ExportedPickersCalendarHeaderProps = Pick< + PickersCalendarHeaderProps, + 'classes' | 'slots' | 'slotProps' +>; diff --git a/packages/x-date-pickers/src/PickersCalendarHeader/index.ts b/packages/x-date-pickers/src/PickersCalendarHeader/index.ts index 60ec6cc838a15..54a863b264a57 100644 --- a/packages/x-date-pickers/src/PickersCalendarHeader/index.ts +++ b/packages/x-date-pickers/src/PickersCalendarHeader/index.ts @@ -9,4 +9,4 @@ export type { PickersCalendarHeaderSlotsComponent, PickersCalendarHeaderSlotsComponentsProps, ExportedPickersCalendarHeaderProps, -} from './PickersCalendarHeader'; +} from './PickersCalendarHeader.types'; diff --git a/packages/x-date-pickers/src/PickersDay/PickersDay.test.tsx b/packages/x-date-pickers/src/PickersDay/PickersDay.test.tsx index aa01c26312a65..c7d516e730921 100644 --- a/packages/x-date-pickers/src/PickersDay/PickersDay.test.tsx +++ b/packages/x-date-pickers/src/PickersDay/PickersDay.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { describeConformance, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { describeConformance, fireEvent, screen } from '@mui-internal/test-utils'; import ButtonBase from '@mui/material/ButtonBase'; import { PickersDay, pickersDayClasses as classes } from '@mui/x-date-pickers/PickersDay'; import { adapterToUse, wrapPickerMount, createPickerRenderer } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx index 8f009f85b4d57..4f33f91c31b59 100644 --- a/packages/x-date-pickers/src/PickersDay/PickersDay.tsx +++ b/packages/x-date-pickers/src/PickersDay/PickersDay.tsx @@ -495,11 +495,9 @@ PickersDayRaw.propTypes = { } as any; /** - * * Demos: * - * - [Date Picker](https://mui.com/x/react-date-pickers/date-picker/) - * + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) * API: * * - [PickersDay API](https://mui.com/x/api/date-pickers/pickers-day/) diff --git a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx index d27bf3fde8730..4247d58b2f8ab 100644 --- a/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx +++ b/packages/x-date-pickers/src/PickersLayout/PickersLayout.tsx @@ -70,6 +70,15 @@ export const PickersLayoutContentWrapper = styled('div', { flexDirection: 'column', }); +/** + * Demos: + * + * - [Custom layout](https://mui.com/x/react-date-pickers/custom-layout/) + * + * API: + * + * - [PickersLayout API](https://mui.com/x/api/date-pickers/pickers-layout/) + */ const PickersLayout = function PickersLayout< TValue, TDate, @@ -118,18 +127,6 @@ PickersLayout.propTypes = { children: PropTypes.node, classes: PropTypes.object, className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, disabled: PropTypes.bool, isLandscape: PropTypes.bool.isRequired, isValid: PropTypes.func.isRequired, diff --git a/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts b/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts index 1b9a223a5c54a..f4202700621e5 100644 --- a/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts +++ b/packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts @@ -5,7 +5,6 @@ import { PickersActionBarProps } from '../PickersActionBar'; import { BaseToolbarProps, ExportedBaseToolbarProps } from '../internals/models/props/toolbar'; import { BaseTabsProps, ExportedBaseTabsProps } from '../internals/models/props/tabs'; import { UsePickerLayoutPropsResponseLayoutProps } from '../internals/hooks/usePicker/usePickerLayoutProps'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { PickersLayoutClasses } from './pickersLayoutClasses'; import { DateOrTimeViewWithMeridiem, WrapperVariant } from '../internals/models/common'; import { PickersShortcutsProps } from '../PickersShortcuts'; @@ -19,17 +18,17 @@ export interface ExportedPickersLayoutSlotsComponent< * Custom component for the action bar, it is placed below the picker views. * @default PickersActionBar */ - ActionBar?: React.ElementType; + actionBar?: React.ElementType; /** * Custom component for the shortcuts. * @default PickersShortcuts */ - Shortcuts?: React.JSXElementConstructor>; + shortcuts?: React.JSXElementConstructor>; /** * Custom component for wrapping the layout. * It wraps the toolbar, views, action bar, and shortcuts. */ - Layout?: React.JSXElementConstructor< + layout?: React.JSXElementConstructor< PickersLayoutProps & React.RefAttributes >; } @@ -80,12 +79,12 @@ export interface PickersLayoutSlotsComponent< /** * Tabs enabling toggling between views. */ - Tabs?: React.ElementType>; + tabs?: React.ElementType>; /** * Custom component for the toolbar. * It is placed above the picker views. */ - Toolbar?: React.JSXElementConstructor>; + toolbar?: React.JSXElementConstructor>; } export interface PickersLayoutSlotsComponentsProps< @@ -114,23 +113,11 @@ export interface PickersLayoutProps; classes?: Partial; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: PickersLayoutSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: PickersLayoutSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: PickersLayoutSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx b/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx index 386130ec0e685..6b410dfb30421 100644 --- a/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx +++ b/packages/x-date-pickers/src/PickersLayout/usePickerLayout.tsx @@ -6,7 +6,6 @@ import { PickersLayoutProps, SubComponents } from './PickersLayout.types'; import { getPickersLayoutUtilityClass } from './pickersLayoutClasses'; import { PickersShortcuts } from '../PickersShortcuts'; import { BaseToolbarProps } from '../internals/models/props/toolbar'; -import { uncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; function toolbarHasView( @@ -59,19 +58,14 @@ const usePickerLayout = ; - const slots = innerSlots ?? uncapitalizeObjectKeys(components); - const slotProps = innerSlotProps ?? componentsProps; - const classes = useUtilityClasses(props); // Action bar diff --git a/packages/x-date-pickers/src/PickersShortcuts/PickersShortcuts.tsx b/packages/x-date-pickers/src/PickersShortcuts/PickersShortcuts.tsx index 88bc458a759e4..e58fbf8f93b67 100644 --- a/packages/x-date-pickers/src/PickersShortcuts/PickersShortcuts.tsx +++ b/packages/x-date-pickers/src/PickersShortcuts/PickersShortcuts.tsx @@ -45,6 +45,15 @@ export interface PickersShortcutsProps extends ExportedPickersShortcutPr isValid: (value: TValue) => boolean; } +/** + * Demos: + * + * - [Shortcuts](https://mui.com/x/react-date-pickers/shortcuts/) + * + * API: + * + * - [PickersShortcuts API](https://mui.com/x/api/date-pickers/pickers-shortcuts/) + */ function PickersShortcuts(props: PickersShortcutsProps) { const { items, changeImportance, isLandscape, onChange, isValid, ...other } = props; diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx index a8671f97512f0..2a2cc1056cc93 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.tsx @@ -13,6 +13,16 @@ type StaticDatePickerComponent = (( props: StaticDatePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DatePicker](https://mui.com/x/react-date-pickers/date-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [StaticDatePicker API](https://mui.com/x/api/date-pickers/static-date-picker/) + */ const StaticDatePicker = React.forwardRef(function StaticDatePicker( inProps: StaticDatePickerProps, ref: React.Ref, @@ -73,23 +83,12 @@ StaticDatePicker.propTypes = { * Class name applied to the root element. */ className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -237,6 +236,9 @@ StaticDatePicker.propTypes = { renderLoading: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.types.ts b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.types.ts index ef324866eb0cd..6c8ef45257fc4 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.types.ts +++ b/packages/x-date-pickers/src/StaticDatePicker/StaticDatePicker.types.ts @@ -8,7 +8,7 @@ import { UseStaticPickerSlotsComponent, UseStaticPickerSlotsComponentsProps, } from '../internals/hooks/useStaticPicker'; -import { MakeOptional, UncapitalizeObjectKeys } from '../internals'; +import { MakeOptional } from '../internals'; import { DateView } from '../models'; export interface StaticDatePickerSlotsComponent @@ -22,23 +22,11 @@ export interface StaticDatePickerSlotsComponentsProps export interface StaticDatePickerProps extends BaseDatePickerProps, MakeOptional { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: StaticDatePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: StaticDatePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: StaticDatePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx index a4162257d2db9..307da572d4e08 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePicker.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { fireEvent, screen } from '@mui/monorepo/test/utils'; +import { fireEvent, screen } from '@mui-internal/test-utils'; import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx index 10600e40e0941..779db5f95e102 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/tests/StaticDatePickerKeyboard.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { act, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { act, fireEvent, screen } from '@mui-internal/test-utils'; import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'; import { DateView } from '@mui/x-date-pickers/models'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx index d1623cd99580b..aebe8aef0c5ba 100644 --- a/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticDatePicker/tests/describes.StaticDatePicker.test.tsx @@ -1,7 +1,5 @@ -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { createPickerRenderer } from 'test/utils/pickers'; +import { createPickerRenderer, describeValidation, describePicker } from 'test/utils/pickers'; import { StaticDatePicker } from '@mui/x-date-pickers/StaticDatePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx index f0862ea721f74..fd6d12e5cb2a0 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx +++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.tsx @@ -14,6 +14,16 @@ type StaticDateTimePickerComponent = (( props: StaticDateTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [DateTimePicker](https://mui.com/x/react-date-pickers/date-time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [StaticDateTimePicker API](https://mui.com/x/api/date-pickers/static-date-time-picker/) + */ const StaticDateTimePicker = React.forwardRef(function StaticDateTimePicker( inProps: StaticDateTimePickerProps, ref: React.Ref, @@ -95,23 +105,12 @@ StaticDateTimePicker.propTypes = { * Class name applied to the root element. */ className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * Formats the day of week displayed in the calendar header. - * @param {string} day The day of week provided by the adapter's method `getWeekdays`. + * @param {string} day The day of week provided by the adapter. Deprecated, will be removed in v7: Use `date` instead. + * @param {TDate} date The date of the day of week provided by the adapter. * @returns {string} The name to display. - * @default (day) => day.charAt(0).toUpperCase() + * @default (_day: string, date: TDate) => adapter.format(date, 'weekdayShort').charAt(0).toUpperCase() */ dayOfWeekFormatter: PropTypes.func, /** @@ -295,6 +294,9 @@ StaticDateTimePicker.propTypes = { shouldDisableClock: PropTypes.func, /** * 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts index 8ac7948fd2e49..ad995c1e9b066 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts +++ b/packages/x-date-pickers/src/StaticDateTimePicker/StaticDateTimePicker.types.ts @@ -8,7 +8,7 @@ import { UseStaticPickerSlotsComponent, UseStaticPickerSlotsComponentsProps, } from '../internals/hooks/useStaticPicker'; -import { MakeOptional, UncapitalizeObjectKeys } from '../internals'; +import { MakeOptional } from '../internals'; import { DateOrTimeView } from '../models'; export interface StaticDateTimePickerSlotsComponent @@ -22,23 +22,11 @@ export interface StaticDateTimePickerSlotsComponentsProps export interface StaticDateTimePickerProps extends BaseDateTimePickerProps, MakeOptional { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: StaticDateTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: StaticDateTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: StaticDateTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx index d88b9df429f29..2a78e406fb593 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticDateTimePicker/tests/StaticDateTimePicker.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { fireEvent, screen } from '@mui/monorepo/test/utils'; +import { fireEvent, screen } from '@mui-internal/test-utils'; import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; import { DateTimePickerTabs, DateTimePickerTabsProps } from '../../DateTimePicker'; diff --git a/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx b/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx index 2b2c1e9693d73..b36e149ad6268 100644 --- a/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticDateTimePicker/tests/describes.StaticDatePicker.test.tsx @@ -1,7 +1,5 @@ -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { createPickerRenderer } from 'test/utils/pickers'; +import { createPickerRenderer, describeValidation, describePicker } from 'test/utils/pickers'; import { StaticDateTimePicker } from '@mui/x-date-pickers/StaticDateTimePicker'; -import { describePicker } from '@mui/x-date-pickers/tests/describePicker'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ clock: 'fake' }); diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx index 6f0d2649671bd..28d9f536650d4 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.test.tsx @@ -7,10 +7,14 @@ import { screen, getAllByRole, fireEvent, -} from '@mui/monorepo/test/utils'; -import { adapterToUse, wrapPickerMount, createPickerRenderer } from 'test/utils/pickers'; +} from '@mui-internal/test-utils'; +import { + adapterToUse, + wrapPickerMount, + createPickerRenderer, + describeValidation, +} from 'test/utils/pickers'; import { StaticTimePicker } from '@mui/x-date-pickers/StaticTimePicker'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; describe('', () => { const { render, clock } = createPickerRenderer({ diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx index eed677b37c9e5..d753093dced67 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.tsx @@ -13,6 +13,16 @@ type StaticTimePickerComponent = (( props: StaticTimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [StaticTimePicker API](https://mui.com/x/api/date-pickers/static-time-picker/) + */ const StaticTimePicker = React.forwardRef(function StaticTimePicker( inProps: StaticTimePickerProps, ref: React.Ref, @@ -86,18 +96,6 @@ StaticTimePicker.propTypes = { * Class name applied to the root element. */ className: PropTypes.string, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts index 28c08d303b017..8fac415cafbf4 100644 --- a/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts +++ b/packages/x-date-pickers/src/StaticTimePicker/StaticTimePicker.types.ts @@ -8,7 +8,7 @@ import { UseStaticPickerSlotsComponent, UseStaticPickerSlotsComponentsProps, } from '../internals/hooks/useStaticPicker'; -import { MakeOptional, UncapitalizeObjectKeys } from '../internals'; +import { MakeOptional } from '../internals'; import { TimeView } from '../models'; export interface StaticTimePickerSlotsComponent @@ -22,23 +22,11 @@ export interface StaticTimePickerSlotsComponentsProps export interface StaticTimePickerProps extends BaseTimePickerProps, MakeOptional { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: StaticTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: StaticTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: StaticTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/TimeClock/TimeClock.tsx b/packages/x-date-pickers/src/TimeClock/TimeClock.tsx index d333f51404395..9bc885b9a99ab 100644 --- a/packages/x-date-pickers/src/TimeClock/TimeClock.tsx +++ b/packages/x-date-pickers/src/TimeClock/TimeClock.tsx @@ -17,7 +17,6 @@ import { TimeClockProps } from './TimeClock.types'; import { getHourNumbers, getMinutesNumbers } from './ClockNumbers'; import { useControlledValueWithTimezone } from '../internals/hooks/useValueWithTimezone'; import { singleItemValueManager } from '../internals/utils/valueManagers'; -import { uncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { useClockReferenceDate } from '../internals/hooks/useClockReferenceDate'; const useUtilityClasses = (ownerState: TimeClockProps) => { @@ -57,6 +56,10 @@ type TimeClockComponent = (( const TIME_CLOCK_DEFAULT_VIEWS: TimeView[] = ['hours', 'minutes']; /** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [TimeClock](https://mui.com/x/react-date-pickers/time-clock/) * * API: * @@ -77,10 +80,8 @@ export const TimeClock = React.forwardRef(function TimeClock; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: TimeClockSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: TimeClockSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: TimeClockSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/TimeClock/tests/TimeClock.test.tsx b/packages/x-date-pickers/src/TimeClock/tests/TimeClock.test.tsx index e2221b91cbe04..aeb95cd302a91 100644 --- a/packages/x-date-pickers/src/TimeClock/tests/TimeClock.test.tsx +++ b/packages/x-date-pickers/src/TimeClock/tests/TimeClock.test.tsx @@ -7,7 +7,7 @@ import { screen, within, getAllByRole, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { TimeClock } from '@mui/x-date-pickers/TimeClock'; import { createPickerRenderer, adapterToUse, timeClockHandler } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx index 8b1a929cb900c..52a53102831aa 100644 --- a/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx +++ b/packages/x-date-pickers/src/TimeClock/tests/describes.TimeClock.test.tsx @@ -1,7 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { describeConformance, screen } from '@mui/monorepo/test/utils'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; +import { describeConformance, screen } from '@mui-internal/test-utils'; import { clockPointerClasses, TimeClock, @@ -12,6 +11,7 @@ import { createPickerRenderer, adapterToUse, timeClockHandler, + describeValue, } from 'test/utils/pickers'; describe(' - Describes', () => { diff --git a/packages/x-date-pickers/src/TimeClock/tests/timezone.TimeClock.test.tsx b/packages/x-date-pickers/src/TimeClock/tests/timezone.TimeClock.test.tsx index 22dcf63e1ca54..0ef7ac322471a 100644 --- a/packages/x-date-pickers/src/TimeClock/tests/timezone.TimeClock.test.tsx +++ b/packages/x-date-pickers/src/TimeClock/tests/timezone.TimeClock.test.tsx @@ -1,10 +1,14 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { screen, fireTouchChangedEvent } from '@mui/monorepo/test/utils'; +import { screen, fireTouchChangedEvent } from '@mui-internal/test-utils'; import { TimeClock } from '@mui/x-date-pickers/TimeClock'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; -import { getClockTouchEvent, getTimeClockValue, getDateOffset } from 'test/utils/pickers'; +import { + getClockTouchEvent, + getTimeClockValue, + getDateOffset, + describeAdapters, +} from 'test/utils/pickers'; const TIMEZONE_TO_TEST = ['UTC', 'system', 'America/New_York']; diff --git a/packages/x-date-pickers/src/TimeField/TimeField.tsx b/packages/x-date-pickers/src/TimeField/TimeField.tsx index 0c0b602fe1287..ecf404f99e87d 100644 --- a/packages/x-date-pickers/src/TimeField/TimeField.tsx +++ b/packages/x-date-pickers/src/TimeField/TimeField.tsx @@ -4,13 +4,28 @@ import MuiTextField from '@mui/material/TextField'; import { useThemeProps } from '@mui/material/styles'; import { useSlotProps } from '@mui/base/utils'; import { refType } from '@mui/utils'; -import { TimeFieldProps } from './TimeField.types'; +import { + TimeFieldProps, + TimeFieldSlotsComponent, + TimeFieldSlotsComponentsProps, +} from './TimeField.types'; import { useTimeField } from './useTimeField'; +import { useClearableField } from '../hooks'; type TimeFieldComponent = (( props: TimeFieldProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimeField](http://mui.com/x/react-date-pickers/time-field/) + * - [Fields](https://mui.com/x/react-date-pickers/fields/) + * + * API: + * + * - [TimeField API](https://mui.com/x/api/date-pickers/time-field/) + */ const TimeField = React.forwardRef(function TimeField( inProps: TimeFieldProps, ref: React.Ref, @@ -20,15 +35,14 @@ const TimeField = React.forwardRef(function TimeField( name: 'MuiTimeField', }); - const { slots, slotProps, components, componentsProps, InputProps, inputProps, ...other } = - themeProps; + const { slots, slotProps, InputProps, inputProps, ...other } = themeProps; const ownerState = themeProps; - const TextField = slots?.textField ?? components?.TextField ?? MuiTextField; + const TextField = slots?.textField ?? MuiTextField; const { inputRef: externalInputRef, ...textFieldProps }: TimeFieldProps = useSlotProps({ elementType: TextField, - externalSlotProps: slotProps?.textField ?? componentsProps?.textField, + externalSlotProps: slotProps?.textField, externalForwardedProps: other, ownerState, }); @@ -43,17 +57,33 @@ const TimeField = React.forwardRef(function TimeField( onKeyDown, inputMode, readOnly, + clearable, + onClear, ...fieldProps } = useTimeField({ props: textFieldProps, inputRef: externalInputRef, }); + const { InputProps: ProcessedInputProps, fieldProps: processedFieldProps } = useClearableField< + typeof fieldProps, + typeof fieldProps.InputProps, + TimeFieldSlotsComponent, + TimeFieldSlotsComponentsProps + >({ + onClear, + clearable, + fieldProps, + InputProps: fieldProps.InputProps, + slots, + slotProps, + }); + return ( ); @@ -75,26 +105,19 @@ TimeField.propTypes = { */ autoFocus: PropTypes.bool, className: PropTypes.string, + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable: PropTypes.bool, /** * The color of the component. * It supports both default and custom theme colors, which can be added as shown in the - * [palette customization guide](https://mui.com/material-ui/customization/palette/#adding-new-colors). + * [palette customization guide](https://mui.com/material-ui/customization/palette/#custom-colors). * @default 'primary' */ color: PropTypes.oneOf(['error', 'info', 'primary', 'secondary', 'success', 'warning']), component: PropTypes.elementType, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. Use when the component is not controlled. */ @@ -215,6 +238,10 @@ TimeField.propTypes = { * @param {FieldChangeHandlerContext} context The context containing the validation result of the current value. */ onChange: PropTypes.func, + /** + * Callback fired when the clear button is clicked. + */ + onClear: PropTypes.func, /** * Callback fired when the error associated to the current value changes. * @template TValue The value type. Will be either the same type as `value` or `null`. Can be in `[start, end]` format in case of range value. diff --git a/packages/x-date-pickers/src/TimeField/TimeField.types.ts b/packages/x-date-pickers/src/TimeField/TimeField.types.ts index de566f65bd91c..3064c563fdeb3 100644 --- a/packages/x-date-pickers/src/TimeField/TimeField.types.ts +++ b/packages/x-date-pickers/src/TimeField/TimeField.types.ts @@ -4,9 +4,9 @@ import TextField from '@mui/material/TextField'; import { UseFieldInternalProps } from '../internals/hooks/useField'; import { DefaultizedProps, MakeOptional } from '../internals/models/helpers'; import { BaseTimeValidationProps, TimeValidationProps } from '../internals/models/validation'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { FieldsTextFieldProps } from '../internals/models/fields'; import { FieldSection, TimeValidationError } from '../models'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '../internals'; export interface UseTimeFieldParams { props: UseTimeFieldComponentProps; @@ -40,23 +40,11 @@ export type UseTimeFieldComponentProps = Omit< export interface TimeFieldProps extends UseTimeFieldComponentProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: TimeFieldSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: TimeFieldSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: TimeFieldSlotsComponent; /** * The props used for each component slot. * @default {} @@ -66,15 +54,15 @@ export interface TimeFieldProps export type TimeFieldOwnerState = TimeFieldProps; -export interface TimeFieldSlotsComponent { +export interface TimeFieldSlotsComponent extends FieldSlotsComponents { /** * Form control with an input to render the value. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } -export interface TimeFieldSlotsComponentsProps { +export interface TimeFieldSlotsComponentsProps extends FieldSlotsComponentsProps { textField?: SlotComponentProps>; } diff --git a/packages/x-date-pickers/src/TimeField/tests/describes.TimeField.test.tsx b/packages/x-date-pickers/src/TimeField/tests/describes.TimeField.test.tsx index 5eb3dae28b36e..04b67aaa2975c 100644 --- a/packages/x-date-pickers/src/TimeField/tests/describes.TimeField.test.tsx +++ b/packages/x-date-pickers/src/TimeField/tests/describes.TimeField.test.tsx @@ -1,13 +1,14 @@ -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { userEvent } from '@mui/monorepo/test/utils'; +import { userEvent } from '@mui-internal/test-utils'; import { adapterToUse, createPickerRenderer, expectInputPlaceholder, expectInputValue, getTextbox, + describeValidation, + describeValue, + formatFullTimeValue, } from 'test/utils/pickers'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; import { TimeField } from '@mui/x-date-pickers/TimeField'; describe(' - Describes', () => { @@ -33,7 +34,7 @@ describe(' - Describes', () => { expectInputPlaceholder(input, hasMeridiem ? 'hh:mm aa' : 'hh:mm'); } const expectedValueStr = expectedValue - ? adapterToUse.format(expectedValue, hasMeridiem ? 'fullTime12h' : 'fullTime24h') + ? formatFullTimeValue(adapterToUse, expectedValue) : ''; expectInputValue(input, expectedValueStr); }, diff --git a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx index 9d933288dcbe6..3b389d300ae09 100644 --- a/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx +++ b/packages/x-date-pickers/src/TimeField/tests/editing.TimeField.test.tsx @@ -1,9 +1,8 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { TimeField } from '@mui/x-date-pickers/TimeField'; -import { userEvent, fireEvent } from '@mui/monorepo/test/utils'; -import { expectInputValue, getCleanedSelectedContent } from 'test/utils/pickers'; -import { describeAdapters } from '@mui/x-date-pickers/tests/describeAdapters'; +import { userEvent, fireEvent } from '@mui-internal/test-utils'; +import { expectInputValue, getCleanedSelectedContent, describeAdapters } from 'test/utils/pickers'; describe(' - Editing', () => { describeAdapters('key: ArrowDown', TimeField, ({ adapter, testFieldKeyPress }) => { @@ -366,7 +365,7 @@ describe(' - Editing', () => { selectSection('hours'); userEvent.keyPress(input, { key: 'a', ctrlKey: true }); - userEvent.keyPress(input, { key: 'Backspace' }); + fireEvent.change(input, { target: { value: '' } }); userEvent.keyPress(input, { key: 'ArrowLeft' }); fireEvent.change(input, { target: { value: '3:mm' } }); // Press "3" diff --git a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx index fe1203855a44b..af900f1a186fb 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePicker.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePicker.tsx @@ -12,6 +12,16 @@ type TimePickerComponent = (( props: TimePickerProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [Validation](https://mui.com/x/react-date-pickers/validation/) + * + * API: + * + * - [TimePicker API](https://mui.com/x/api/date-pickers/time-picker/) + */ const TimePicker = React.forwardRef(function TimePicker( inProps: TimePickerProps, ref: React.Ref, @@ -61,18 +71,6 @@ TimePicker.propTypes = { * @default `true` for desktop, `false` for mobile (based on the chosen wrapper and `desktopModeMediaQuery` prop). */ closeOnSelect: PropTypes.bool, - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components: PropTypes.object, - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps: PropTypes.object, /** * The default value. * Used when the component is not controlled. diff --git a/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts b/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts index 6116ab13f0b1a..93232adcd4e4d 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts +++ b/packages/x-date-pickers/src/TimePicker/TimePicker.types.ts @@ -4,7 +4,6 @@ import { DesktopTimePickerSlotsComponentsProps, } from '../DesktopTimePicker'; import { TimeViewWithMeridiem } from '../internals/models'; -import { UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { MobileTimePickerProps, MobileTimePickerSlotsComponent, @@ -28,23 +27,11 @@ export interface TimePickerProps * @example '@media (min-width: 720px)' or theme.breakpoints.up("sm") */ desktopModeMediaQuery?: string; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: TimePickerSlotsComponents; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: TimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: TimePickerSlotsComponents; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx index 4940b1d4f519a..bfb788628d546 100644 --- a/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx +++ b/packages/x-date-pickers/src/TimePicker/TimePickerToolbar.tsx @@ -143,6 +143,16 @@ TimePickerToolbarAmPmSelection.propTypes = { ]), } as any; +/** + * Demos: + * + * - [TimePicker](https://mui.com/x/react-date-pickers/time-picker/) + * - [Custom components](https://mui.com/x/react-date-pickers/custom-components/) + * + * API: + * + * - [TimePickerToolbar API](https://mui.com/x/api/date-pickers/time-picker-toolbar/) + */ function TimePickerToolbar(inProps: TimePickerToolbarProps) { const props = useThemeProps({ props: inProps, name: 'MuiTimePickerToolbar' }); const { @@ -280,6 +290,11 @@ TimePickerToolbar.propTypes = { */ onViewChange: PropTypes.func.isRequired, readOnly: PropTypes.bool, + sx: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), + PropTypes.func, + PropTypes.object, + ]), titleId: PropTypes.string, /** * Toolbar date format. diff --git a/packages/x-date-pickers/src/TimePicker/shared.tsx b/packages/x-date-pickers/src/TimePicker/shared.tsx index 116f4a3e42172..3febf0b7900da 100644 --- a/packages/x-date-pickers/src/TimePicker/shared.tsx +++ b/packages/x-date-pickers/src/TimePicker/shared.tsx @@ -18,7 +18,6 @@ import { TimeValidationError } from '../models'; import { PickerViewRendererLookup } from '../internals/hooks/usePicker/usePickerViews'; import { TimeViewRendererProps } from '../timeViewRenderers'; import { applyDefaultViewProps } from '../internals/utils/views'; -import { uncapitalizeObjectKeys, UncapitalizeObjectKeys } from '../internals/utils/slots-migration'; import { BaseClockProps, ExportedBaseClockProps } from '../internals/models/props/clock'; import { TimeViewWithMeridiem } from '../internals/models'; @@ -27,7 +26,7 @@ export interface BaseTimePickerSlotsComponent extends TimeClockSlotsCompo * Custom component for the toolbar rendered above the views. * @default TimePickerToolbar */ - Toolbar?: React.JSXElementConstructor>; + toolbar?: React.JSXElementConstructor>; } export interface BaseTimePickerSlotsComponentsProps extends TimeClockSlotsComponentsProps { @@ -42,23 +41,11 @@ export interface BaseTimePickerProps * @default true on desktop, false on mobile */ ampmInClock?: boolean; - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: BaseTimePickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: BaseTimePickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: BaseTimePickerSlotsComponent; /** * The props used for each component slot. * @default {} @@ -115,8 +102,6 @@ export function useTimePickerDefaultizedProps< }; }, [themeProps.localeText]); - const slots = themeProps.slots ?? uncapitalizeObjectKeys(themeProps.components); - const slotProps = themeProps.slotProps ?? themeProps.componentsProps; return { ...themeProps, ampm, @@ -131,14 +116,14 @@ export function useTimePickerDefaultizedProps< disablePast: themeProps.disablePast ?? false, slots: { toolbar: TimePickerToolbar, - ...slots, + ...themeProps.slots, }, slotProps: { - ...slotProps, + ...themeProps.slotProps, toolbar: { ampm, ampmInClock: themeProps.ampmInClock, - ...slotProps?.toolbar, + ...themeProps.slotProps?.toolbar, }, }, }; diff --git a/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx index be3374130f24d..902c910ddcf55 100644 --- a/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx +++ b/packages/x-date-pickers/src/TimePicker/tests/TimePicker.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { TimePicker } from '@mui/x-date-pickers/TimePicker'; -import { screen } from '@mui/monorepo/test/utils/createRenderer'; +import { screen } from '@mui-internal/test-utils/createRenderer'; import { expect } from 'chai'; import { createPickerRenderer, stubMatchMedia } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx b/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx index ef0dee84d8ea5..8dce10be09ffb 100644 --- a/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx +++ b/packages/x-date-pickers/src/YearCalendar/PickersYear.tsx @@ -64,7 +64,7 @@ const PickersYearButton = styled('button', { border: 0, outline: 0, ...theme.typography.subtitle1, - margin: '8px 0', + margin: '6px 0', height: 36, width: 72, borderRadius: 18, diff --git a/packages/x-date-pickers/src/YearCalendar/YearCalendar.tsx b/packages/x-date-pickers/src/YearCalendar/YearCalendar.tsx index b489b47f2ab85..38752aba87560 100644 --- a/packages/x-date-pickers/src/YearCalendar/YearCalendar.tsx +++ b/packages/x-date-pickers/src/YearCalendar/YearCalendar.tsx @@ -18,6 +18,7 @@ import { YearCalendarProps } from './YearCalendar.types'; import { singleItemValueManager } from '../internals/utils/valueManagers'; import { SECTION_TYPE_GRANULARITY } from '../internals/utils/getDefaultReferenceDate'; import { useControlledValueWithTimezone } from '../internals/hooks/useValueWithTimezone'; +import { DIALOG_WIDTH, MAX_CALENDAR_HEIGHT } from '../internals/constants/dimensions'; const useUtilityClasses = (ownerState: YearCalendarProps) => { const { classes } = ownerState; @@ -34,7 +35,7 @@ function useYearCalendarDefaultizedProps( name: string, ): DefaultizedProps< YearCalendarProps, - 'minDate' | 'maxDate' | 'disableFuture' | 'disablePast' + 'minDate' | 'maxDate' | 'disableFuture' | 'disablePast' | 'yearsPerRow' > { const utils = useUtils(); const defaultDates = useDefaultDates(); @@ -47,6 +48,7 @@ function useYearCalendarDefaultizedProps( disablePast: false, disableFuture: false, ...themeProps, + yearsPerRow: themeProps.yearsPerRow ?? 3, minDate: applyDefaultDate(utils, themeProps.minDate, defaultDates.minDate), maxDate: applyDefaultDate(utils, themeProps.maxDate, defaultDates.maxDate), }; @@ -63,8 +65,8 @@ const YearCalendarRoot = styled('div', { overflowY: 'auto', height: '100%', padding: '0 4px', - width: 320, - maxHeight: 304, + width: DIALOG_WIDTH, + maxHeight: MAX_CALENDAR_HEIGHT, // avoid padding increasing width over defined boxSizing: 'border-box', position: 'relative', @@ -74,6 +76,15 @@ type YearCalendarComponent = ((props: YearCalendarProps) => React. propTypes?: any; }; +/** + * Demos: + * + * - [DateCalendar](https://mui.com/x/react-date-pickers/date-calendar/) + * + * API: + * + * - [YearCalendar API](https://mui.com/x/api/date-pickers/year-calendar/) + */ export const YearCalendar = React.forwardRef(function YearCalendar( inProps: YearCalendarProps, ref: React.Ref, @@ -97,7 +108,7 @@ export const YearCalendar = React.forwardRef(function YearCalendar( onYearFocus, hasFocus, onFocusedViewChange, - yearsPerRow = 3, + yearsPerRow, timezone: timezoneProp, gridLabelId, ...other @@ -137,15 +148,12 @@ export const YearCalendar = React.forwardRef(function YearCalendar( if (value != null) { return utils.getYear(value); } + return null; + }, [value, utils]); - if (disableHighlightToday) { - return null; - } - - return utils.getYear(referenceDate); - }, [value, utils, disableHighlightToday, referenceDate]); - - const [focusedYear, setFocusedYear] = React.useState(() => selectedYear || todayYear); + const [focusedYear, setFocusedYear] = React.useState( + () => selectedYear || utils.getYear(referenceDate), + ); const [internalHasFocus, setInternalHasFocus] = useControlled({ name: 'YearCalendar', diff --git a/packages/x-date-pickers/src/YearCalendar/tests/YearCalendar.test.tsx b/packages/x-date-pickers/src/YearCalendar/tests/YearCalendar.test.tsx index d9269ddb04842..17528fb8c8bba 100644 --- a/packages/x-date-pickers/src/YearCalendar/tests/YearCalendar.test.tsx +++ b/packages/x-date-pickers/src/YearCalendar/tests/YearCalendar.test.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { spy } from 'sinon'; import { expect } from 'chai'; -import { act, fireEvent, screen } from '@mui/monorepo/test/utils'; +import { act, fireEvent, screen } from '@mui-internal/test-utils'; import { YearCalendar } from '@mui/x-date-pickers/YearCalendar'; import { createPickerRenderer, adapterToUse } from 'test/utils/pickers'; @@ -169,4 +169,10 @@ describe('', () => { expect(year2019).not.to.have.attribute('disabled'); expect(year2020).to.have.attribute('disabled'); }); + + it('should not mark the `referenceDate` year as selected', () => { + render(); + + expect(screen.getByRole('radio', { name: '2018', checked: false })).to.not.equal(null); + }); }); diff --git a/packages/x-date-pickers/src/YearCalendar/tests/describes.YearCalendar.test.tsx b/packages/x-date-pickers/src/YearCalendar/tests/describes.YearCalendar.test.tsx index e4127bc1c8e29..a46fee1e629b0 100644 --- a/packages/x-date-pickers/src/YearCalendar/tests/describes.YearCalendar.test.tsx +++ b/packages/x-date-pickers/src/YearCalendar/tests/describes.YearCalendar.test.tsx @@ -1,14 +1,14 @@ import * as React from 'react'; import { expect } from 'chai'; -import { userEvent, screen, describeConformance } from '@mui/monorepo/test/utils'; +import { userEvent, screen, describeConformance } from '@mui-internal/test-utils'; +import { YearCalendar, yearCalendarClasses as classes } from '@mui/x-date-pickers/YearCalendar'; import { - pickersYearClasses, - YearCalendar, - yearCalendarClasses as classes, -} from '@mui/x-date-pickers/YearCalendar'; -import { wrapPickerMount, createPickerRenderer, adapterToUse } from 'test/utils/pickers'; -import { describeValidation } from '@mui/x-date-pickers/tests/describeValidation'; -import { describeValue } from '@mui/x-date-pickers/tests/describeValue'; + wrapPickerMount, + createPickerRenderer, + adapterToUse, + describeValidation, + describeValue, +} from 'test/utils/pickers'; describe(' - Describes', () => { const { render, clock } = createPickerRenderer({ @@ -40,13 +40,15 @@ describe(' - Describes', () => { emptyValue: null, clock, assertRenderedValue: (expectedValue: any) => { - const selectedCells = document.querySelectorAll(`.${pickersYearClasses.selected}`); + const activeYear = screen + .queryAllByRole('radio') + .find((cell) => cell.getAttribute('tabindex') === '0'); + expect(activeYear).not.to.equal(null); if (expectedValue == null) { - expect(selectedCells).to.have.length(1); - expect(selectedCells[0]).to.have.text(adapterToUse.getYear(adapterToUse.date()).toString()); + expect(activeYear).to.have.text(adapterToUse.getYear(adapterToUse.date()).toString()); } else { - expect(selectedCells).to.have.length(1); - expect(selectedCells[0]).to.have.text(adapterToUse.getYear(expectedValue).toString()); + expect(activeYear).to.have.text(adapterToUse.getYear(expectedValue).toString()); + expect(activeYear).to.have.attribute('aria-checked', 'true'); } }, setNewValue: (value) => { diff --git a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx index 563d5bc45d263..21feacab38f52 100644 --- a/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateTimeViewRenderers/dateTimeViewRenderers.tsx @@ -4,7 +4,6 @@ import { resolveComponentProps } from '@mui/base/utils'; import { DateCalendar, DateCalendarProps } from '../DateCalendar'; import { DateOrTimeViewWithMeridiem } from '../internals/models'; import { - MultiSectionDigitalClock, MultiSectionDigitalClockProps, multiSectionDigitalClockSectionClasses, } from '../MultiSectionDigitalClock'; @@ -12,6 +11,12 @@ import { DateTimeViewWrapper } from '../internals/components/DateTimeViewWrapper import { isInternalTimeView } from '../internals/utils/time-utils'; import { isDatePickerView } from '../internals/utils/date-utils'; import type { DateTimePickerProps } from '../DateTimePicker/DateTimePicker.types'; +import { + renderDigitalClockTimeView, + renderMultiSectionDigitalClockTimeView, +} from '../timeViewRenderers'; +import { digitalClockClasses } from '../DigitalClock'; +import { VIEW_HEIGHT } from '../internals/constants/dimensions'; export interface DateTimeViewRendererProps extends Omit< @@ -26,12 +31,13 @@ export interface DateTimeViewRendererProps | 'slots' | 'slotProps' >, - Pick, 'components' | 'componentsProps' | 'slots' | 'slotProps'> { + Pick, 'slots' | 'slotProps'> { view: DateOrTimeViewWithMeridiem; onViewChange?: (view: DateOrTimeViewWithMeridiem) => void; views: readonly DateOrTimeViewWithMeridiem[]; focusedView: DateOrTimeViewWithMeridiem | null; timeViewsCount: number; + shouldRenderTimeInASingleColumn: boolean; } export const renderDesktopDateTimeView = ({ @@ -65,8 +71,6 @@ export const renderDesktopDateTimeView = ({ onYearChange, yearsPerRow, defaultCalendarMonth, - components, - componentsProps, slots, slotProps, loading, @@ -85,11 +89,40 @@ export const renderDesktopDateTimeView = ({ timeSteps, skipDisabled, timeViewsCount, + shouldRenderTimeInASingleColumn, }: DateTimeViewRendererProps) => { - const isActionBarVisible = !!resolveComponentProps( - slotProps?.actionBar ?? componentsProps?.actionBar, - {} as any, - )?.actions?.length; + const isActionBarVisible = !!resolveComponentProps(slotProps?.actionBar, {} as any)?.actions + ?.length; + const commonTimeProps = { + view: isInternalTimeView(view) ? view : 'hours', + onViewChange, + focusedView: focusedView && isInternalTimeView(focusedView) ? focusedView : null, + onFocusedViewChange, + views: views.filter(isInternalTimeView), + value, + defaultValue, + referenceDate, + onChange, + className, + classes, + disableFuture, + disablePast, + minTime, + maxTime, + shouldDisableTime, + shouldDisableClock, + minutesStep, + ampm, + slots, + slotProps, + readOnly, + disabled, + autoFocus, + disableIgnoringDatePartForTimeValidation, + timeSteps, + skipDisabled, + timezone, + }; return ( @@ -118,8 +151,6 @@ export const renderDesktopDateTimeView = ({ onYearChange={onYearChange} yearsPerRow={yearsPerRow} defaultCalendarMonth={defaultCalendarMonth} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} loading={loading} @@ -138,46 +169,34 @@ export const renderDesktopDateTimeView = ({ {timeViewsCount > 0 && ( - + {shouldRenderTimeInASingleColumn + ? renderDigitalClockTimeView({ + ...commonTimeProps, + view: 'hours', + views: ['hours'], + focusedView: focusedView && isInternalTimeView(focusedView) ? 'hours' : null, + sx: { + width: 'auto', + [`&.${digitalClockClasses.root}`]: { + maxHeight: VIEW_HEIGHT, + }, + ...(Array.isArray(sx) ? sx : [sx]), + }, + }) + : renderMultiSectionDigitalClockTimeView({ + ...commonTimeProps, + view: isInternalTimeView(view) ? view : 'hours', + views: views.filter(isInternalTimeView), + focusedView: focusedView && isInternalTimeView(focusedView) ? focusedView : null, + sx: { + borderBottom: 0, + width: 'auto', + [`.${multiSectionDigitalClockSectionClasses.root}`]: { + maxHeight: '100%', + }, + ...(Array.isArray(sx) ? sx : [sx]), + }, + })} )} diff --git a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx index a4cb9e7cb9ce1..9a5b0588a1085 100644 --- a/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx +++ b/packages/x-date-pickers/src/dateViewRenderers/dateViewRenderers.tsx @@ -40,8 +40,6 @@ export const renderDateViewCalendar = ({ onYearChange, yearsPerRow, defaultCalendarMonth, - components, - componentsProps, slots, slotProps, loading, @@ -82,8 +80,6 @@ export const renderDateViewCalendar = ({ onYearChange={onYearChange} yearsPerRow={yearsPerRow} defaultCalendarMonth={defaultCalendarMonth} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} loading={loading} diff --git a/packages/x-date-pickers/src/hooks/index.tsx b/packages/x-date-pickers/src/hooks/index.tsx new file mode 100644 index 0000000000000..aa5038bf75703 --- /dev/null +++ b/packages/x-date-pickers/src/hooks/index.tsx @@ -0,0 +1 @@ +export { useClearableField } from './useClearableField'; diff --git a/packages/x-date-pickers/src/hooks/useClearableField.tsx b/packages/x-date-pickers/src/hooks/useClearableField.tsx new file mode 100644 index 0000000000000..795fecbd8c4e9 --- /dev/null +++ b/packages/x-date-pickers/src/hooks/useClearableField.tsx @@ -0,0 +1,109 @@ +import * as React from 'react'; +import { useSlotProps } from '@mui/base/utils'; +import MuiIconButton from '@mui/material/IconButton'; +import InputAdornment from '@mui/material/InputAdornment'; +import { ClearIcon } from '../icons'; +import { + FieldSlotsComponents, + FieldSlotsComponentsProps, + FieldsTextFieldProps, + useLocaleText, +} from '../internals'; + +type UseClearableFieldProps< + TFieldProps extends FieldsTextFieldProps, + TInputProps extends { endAdornment?: React.ReactNode } | undefined, + TFieldSlots extends FieldSlotsComponents, + TFieldSlotsComponentsProps extends FieldSlotsComponentsProps, +> = { + clearable: boolean; + fieldProps: TFieldProps; + InputProps: TInputProps; + onClear: React.MouseEventHandler; + slots?: { [K in keyof TFieldSlots as Uncapitalize]: TFieldSlots[K] }; + slotProps?: TFieldSlotsComponentsProps; +}; + +export const useClearableField = < + TFieldProps extends FieldsTextFieldProps, + TInputProps extends { endAdornment?: React.ReactNode } | undefined, + TFieldSlotsComponents extends FieldSlotsComponents, + TFieldSlotsComponentsProps extends FieldSlotsComponentsProps, +>({ + clearable, + fieldProps: forwardedFieldProps, + InputProps: ForwardedInputProps, + onClear, + slots, + slotProps, +}: UseClearableFieldProps< + TFieldProps, + TInputProps, + TFieldSlotsComponents, + TFieldSlotsComponentsProps +>) => { + const localeText = useLocaleText(); + + const IconButton = slots?.clearButton ?? MuiIconButton; + // The spread is here to avoid this bug mui/material-ui#34056 + const { ownerState, ...iconButtonProps } = useSlotProps({ + elementType: IconButton, + externalSlotProps: slotProps?.clearButton, + ownerState: {}, + className: 'clearButton', + additionalProps: { + title: localeText.fieldClearLabel, + }, + }); + const EndClearIcon = slots?.clearIcon ?? ClearIcon; + const endClearIconProps = useSlotProps({ + elementType: EndClearIcon, + externalSlotProps: slotProps?.clearIcon, + ownerState: {}, + }); + + const InputProps = { + ...ForwardedInputProps, + endAdornment: clearable ? ( + + + + + + + {ForwardedInputProps?.endAdornment} + + ) : ( + ForwardedInputProps?.endAdornment + ), + }; + + const fieldProps: TFieldProps = { + ...forwardedFieldProps, + sx: [ + { + '& .clearButton': { + opacity: 1, + }, + '@media (pointer: fine)': { + '& .clearButton': { + opacity: 0, + }, + '&:hover, &:focus-within': { + '.clearButton': { + opacity: 1, + }, + }, + }, + }, + ...(Array.isArray(forwardedFieldProps.sx) + ? forwardedFieldProps.sx + : [forwardedFieldProps.sx]), + ], + }; + + return { InputProps, fieldProps }; +}; diff --git a/packages/x-date-pickers/src/icons/index.tsx b/packages/x-date-pickers/src/icons/index.tsx index 74cf00414bb43..6ce0eb2461904 100644 --- a/packages/x-date-pickers/src/icons/index.tsx +++ b/packages/x-date-pickers/src/icons/index.tsx @@ -59,3 +59,11 @@ export const TimeIcon = createSvgIcon( , 'Time', ); + +/** + * @ignore - internal component. + */ +export const ClearIcon = createSvgIcon( + , + 'Clear', +); diff --git a/packages/x-date-pickers/src/index.ts b/packages/x-date-pickers/src/index.ts index c7e082779e503..680bcd4736dbf 100644 --- a/packages/x-date-pickers/src/index.ts +++ b/packages/x-date-pickers/src/index.ts @@ -48,3 +48,5 @@ export { DEFAULT_DESKTOP_MODE_MEDIA_QUERY } from './internals/utils/utils'; export * from './models'; export * from './icons'; + +export * from './hooks'; diff --git a/packages/x-date-pickers/src/internals/components/FakeTextField/FakeTextField.tsx b/packages/x-date-pickers/src/internals/components/FakeTextField/FakeTextField.tsx new file mode 100644 index 0000000000000..8cf10f6f25fdf --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/FakeTextField/FakeTextField.tsx @@ -0,0 +1,68 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; + +export interface FakeTextFieldElement { + container: React.HTMLAttributes; + content: React.HTMLAttributes; + before: React.HTMLAttributes; + after: React.HTMLAttributes; +} + +interface FakeTextFieldProps { + elements: FakeTextFieldElement[]; + valueStr: string; + onValueStrChange: React.ChangeEventHandler; + error: boolean; + id?: string; + InputProps: any; + inputProps: any; + disabled?: boolean; + autoFocus?: boolean; + ownerState?: any; + valueType: 'value' | 'placeholder'; +} + +export const FakeTextField = React.forwardRef(function FakeTextField( + props: FakeTextFieldProps, + ref: React.Ref, +) { + const { + elements, + valueStr, + onValueStrChange, + id, + error, + InputProps, + inputProps, + autoFocus, + disabled, + valueType, + ownerState, + ...other + } = props; + + return ( + + + {elements.map(({ container, content, before, after }, elementIndex) => ( + + + + + + ))} + + + + ); +}); diff --git a/packages/x-date-pickers/src/internals/components/FakeTextField/index.ts b/packages/x-date-pickers/src/internals/components/FakeTextField/index.ts new file mode 100644 index 0000000000000..17b2be0c0a9e6 --- /dev/null +++ b/packages/x-date-pickers/src/internals/components/FakeTextField/index.ts @@ -0,0 +1 @@ +export { FakeTextField } from './FakeTextField'; diff --git a/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.types.tsx b/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.types.tsx index 93c88ce658a72..8a8fd49dfb030 100644 --- a/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.types.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersArrowSwitcher/PickersArrowSwitcher.types.tsx @@ -3,14 +3,13 @@ import { SlotComponentProps } from '@mui/base/utils'; import IconButton from '@mui/material/IconButton'; import SvgIcon from '@mui/material/SvgIcon'; import { PickersArrowSwitcherClasses } from './pickersArrowSwitcherClasses'; -import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; export interface ExportedPickersArrowSwitcherProps { /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: PickersArrowSwitcherSlotsComponent; /** * The props used for each component slot. * @default {} @@ -42,22 +41,22 @@ export interface PickersArrowSwitcherSlotsComponent { * Button allowing to switch to the left view. * @default IconButton */ - PreviousIconButton?: React.ElementType; + previousIconButton?: React.ElementType; /** * Button allowing to switch to the right view. * @default IconButton */ - NextIconButton?: React.ElementType; + nextIconButton?: React.ElementType; /** * Icon displayed in the left view switch button. * @default ArrowLeft */ - LeftArrowIcon?: React.ElementType; + leftArrowIcon?: React.ElementType; /** * Icon displayed in the right view switch button. * @default ArrowRight */ - RightArrowIcon?: React.ElementType; + rightArrowIcon?: React.ElementType; } export interface PickersArrowSwitcherButtonSlotOwnerState extends PickersArrowSwitcherOwnerState { diff --git a/packages/x-date-pickers/src/internals/components/PickersModalDialog.tsx b/packages/x-date-pickers/src/internals/components/PickersModalDialog.tsx index eba9fae19fafd..fa58334d9913e 100644 --- a/packages/x-date-pickers/src/internals/components/PickersModalDialog.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersModalDialog.tsx @@ -6,7 +6,6 @@ import { PaperProps as MuiPaperProps } from '@mui/material/Paper/Paper'; import { TransitionProps as MuiTransitionProps } from '@mui/material/transitions/transition'; import { styled } from '@mui/material/styles'; import { DIALOG_WIDTH } from '../constants/dimensions'; -import { UncapitalizeObjectKeys } from '../utils/slots-migration'; import { UsePickerValueActions } from '../hooks/usePicker/usePickerValue.types'; export interface PickersModalDialogSlotsComponent { @@ -14,17 +13,17 @@ export interface PickersModalDialogSlotsComponent { * Custom component for the dialog inside which the views are rendered on mobile. * @default PickersModalDialogRoot */ - Dialog?: React.ElementType; + dialog?: React.ElementType; /** * Custom component for the paper rendered inside the mobile picker's Dialog. * @default Paper from '@mui/material'. */ - MobilePaper?: React.JSXElementConstructor; + mobilePaper?: React.JSXElementConstructor; /** * Custom component for the mobile dialog [Transition](https://mui.com/material-ui/transitions/). * @default Fade from '@mui/material'. */ - MobileTransition?: React.JSXElementConstructor; + mobileTransition?: React.JSXElementConstructor; } export interface PickersModalDialogSlotsComponentsProps { @@ -47,7 +46,7 @@ export interface PickersModalDialogProps extends UsePickerValueActions { * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys; + slots?: PickersModalDialogSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx index 6152aa89ce132..9d5afc0c398ab 100644 --- a/packages/x-date-pickers/src/internals/components/PickersPopper.tsx +++ b/packages/x-date-pickers/src/internals/components/PickersPopper.tsx @@ -20,7 +20,6 @@ import { styled, useThemeProps } from '@mui/material/styles'; import { TransitionProps as MuiTransitionProps } from '@mui/material/transitions'; import { getPickersPopperUtilityClass, PickersPopperClasses } from './pickersPopperClasses'; import { getActiveElement } from '../utils/utils'; -import { UncapitalizeObjectKeys } from '../utils/slots-migration'; import { UsePickerValueActions } from '../hooks/usePicker/usePickerValue.types'; import { useDefaultReduceAnimations } from '../hooks/useDefaultReduceAnimations'; @@ -33,22 +32,22 @@ export interface PickersPopperSlotsComponent { * Custom component for the paper rendered inside the desktop picker's Popper. * @default PickersPopperPaper */ - DesktopPaper?: React.JSXElementConstructor; + desktopPaper?: React.JSXElementConstructor; /** * Custom component for the desktop popper [Transition](https://mui.com/material-ui/transitions/). * @default Grow or Fade from '@mui/material' when `reduceAnimations` is `true`. */ - DesktopTransition?: React.JSXElementConstructor; + desktopTransition?: React.JSXElementConstructor; /** * Custom component for trapping the focus inside the views on desktop. * @default FocusTrap from '@mui/base'. */ - DesktopTrapFocus?: React.JSXElementConstructor; + desktopTrapFocus?: React.JSXElementConstructor; /** * Custom component for the popper inside which the views are rendered on desktop. * @default Popper from '@mui/material'. */ - Popper?: React.ElementType; + popper?: React.ElementType; } export interface PickersPopperSlotsComponentsProps { @@ -78,7 +77,7 @@ export interface PickerPopperProps extends UsePickerValueActions { containerRef?: React.Ref; children?: React.ReactNode; onBlur?: () => void; - slots?: UncapitalizeObjectKeys; + slots?: PickersPopperSlotsComponent; slotProps?: PickersPopperSlotsComponentsProps; classes?: Partial; shouldRestoreFocus?: () => boolean; diff --git a/packages/x-date-pickers/src/internals/constants/dimensions.ts b/packages/x-date-pickers/src/internals/constants/dimensions.ts index 6a97a3b59695a..79278d058b135 100644 --- a/packages/x-date-pickers/src/internals/constants/dimensions.ts +++ b/packages/x-date-pickers/src/internals/constants/dimensions.ts @@ -1,6 +1,7 @@ export const DAY_SIZE = 36; export const DAY_MARGIN = 2; export const DIALOG_WIDTH = 320; -export const VIEW_HEIGHT = 358; +export const MAX_CALENDAR_HEIGHT = 280; +export const VIEW_HEIGHT = 334; export const DIGITAL_CLOCK_VIEW_HEIGHT = 232; export const MULTI_SECTION_CLOCK_SECTION_WIDTH = 48; diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx index 65f3168f21c12..738976af622f7 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.tsx @@ -142,6 +142,8 @@ export const useDesktopPicker = < unknown >['slots'] = { textField: slots.textField, + clearIcon: slots.clearIcon, + clearButton: slots.clearButton, ...fieldProps.slots, }; diff --git a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts index c6410882e8208..4ec22910d7124 100644 --- a/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useDesktopPicker/useDesktopPicker.types.ts @@ -21,39 +21,40 @@ import { } from '../../../PickersLayout/PickersLayout.types'; import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types'; import { UsePickerViewsNonStaticProps, UsePickerViewsProps } from '../usePicker/usePickerViews'; -import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; import { DateOrTimeViewWithMeridiem } from '../../models'; +import { FieldSlotsComponents, FieldSlotsComponentsProps } from '../useField'; export interface UseDesktopPickerSlotsComponent extends Pick< PickersPopperSlotsComponent, - 'DesktopPaper' | 'DesktopTransition' | 'DesktopTrapFocus' | 'Popper' + 'desktopPaper' | 'desktopTransition' | 'desktopTrapFocus' | 'popper' >, - ExportedPickersLayoutSlotsComponent { + ExportedPickersLayoutSlotsComponent, + FieldSlotsComponents { /** * Component used to enter the date with the keyboard. */ - Field: React.ElementType>; + field: React.ElementType>; /** * Form control with an input to render the value inside the default field. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; /** * Component displayed on the start or end input adornment used to open the picker on desktop. * @default InputAdornment */ - InputAdornment?: React.ElementType; + inputAdornment?: React.ElementType; /** * Button to open the picker on desktop. * @default IconButton */ - OpenPickerButton?: React.ElementType; + openPickerButton?: React.ElementType; /** * Icon displayed in the open picker button on desktop. */ - OpenPickerIcon: React.ElementType; + openPickerIcon: React.ElementType; } export interface UseDesktopPickerSlotsComponentsProps< @@ -66,7 +67,8 @@ export interface ExportedUseDesktopPickerSlotsComponentsProps< TDate, TView extends DateOrTimeViewWithMeridiem, > extends PickersPopperSlotsComponentsProps, - ExportedPickersLayoutSlotsComponentsProps { + ExportedPickersLayoutSlotsComponentsProps, + FieldSlotsComponentsProps { field?: SlotComponentProps< React.ElementType>, {}, @@ -104,7 +106,7 @@ export interface UseDesktopPickerProps< * Overridable component slots. * @default {} */ - slots: UncapitalizeObjectKeys>; + slots: UseDesktopPickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/internals/hooks/useField/index.ts b/packages/x-date-pickers/src/internals/hooks/useField/index.ts index 955beab828fea..a4fc8fdccdbdf 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/index.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/index.ts @@ -8,6 +8,8 @@ export type { FieldChangeHandler, FieldChangeHandlerContext, FieldRef, + FieldSlotsComponents, + FieldSlotsComponentsProps, } from './useField.types'; export { splitFormatIntoSections, diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts index f94b18ce9f970..8a103450acf64 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.ts @@ -55,6 +55,9 @@ export const useField = < onMouseUp, onPaste, error, + clearable, + onClear, + disabled, ...otherForwardedProps }, fieldValueManager, @@ -100,12 +103,17 @@ export const useField = < ); } const sectionIndex = nextSectionIndex === -1 ? state.sections.length - 1 : nextSectionIndex - 1; - setSelectedSections(sectionIndex); }; - const handleInputClick = useEventCallback((...args) => { - onClick?.(...(args as [])); + const handleInputClick = useEventCallback((event: React.MouseEvent, ...args) => { + // The click event on the clear button would propagate to the input, trigger this handler and result in a wrong section selection. + // We avoid this by checking if the call of `handleInputClick` is actually intended, or a side effect. + if (event.isDefaultPrevented()) { + return; + } + + onClick?.(event, ...(args as [])); syncSelectionFromDOM(); }); @@ -194,6 +202,12 @@ export const useField = < } const targetValue = event.target.value; + if (targetValue === '') { + resetCharacterQuery(); + clearValue(); + return; + } + const eventData = (event.nativeEvent as InputEvent).data; // Calling `.fill(04/11/2022)` in playwright will trigger a change event with the requested content to insert in `event.nativeEvent.data` // usual changes have only the currently typed character in the `event.nativeEvent.data` @@ -259,8 +273,14 @@ export const useField = < ); } - if (isAndroid() && keyPressed.length === 0) { - setTempAndroidValueStr(valueStr); + if (keyPressed.length === 0) { + if (isAndroid()) { + setTempAndroidValueStr(valueStr); + } else { + resetCharacterQuery(); + clearActiveSection(); + } + return; } @@ -318,7 +338,7 @@ export const useField = < } // Reset the value of the selected section - case ['Backspace', 'Delete'].includes(event.key): { + case event.key === 'Delete': { event.preventDefault(); if (readOnly) { @@ -404,7 +424,7 @@ export const useField = < // Fix scroll jumping on iOS browser: https://github.com/mui/mui-x/issues/8321 const currentScrollTop = inputRef.current.scrollTop; // On multi input range pickers we want to update selection range only for the active input - // This helps avoiding the focus jumping on Safari https://github.com/mui/mui-x/issues/9003 + // This helps to avoid the focus jumping on Safari https://github.com/mui/mui-x/issues/9003 // because WebKit implements the `setSelectionRange` based on the spec: https://bugs.webkit.org/show_bug.cgi?id=224425 if (inputRef.current === getActiveElement(document)) { inputRef.current.setSelectionRange(selectionStart, selectionEnd); @@ -476,8 +496,12 @@ export const useField = < }, [selectedSectionIndexes, state.sections]); const inputHasFocus = inputRef.current && inputRef.current === getActiveElement(document); - const shouldShowPlaceholder = - !inputHasFocus && valueManager.areValuesEqual(utils, state.value, valueManager.emptyValue); + const areAllSectionsEmpty = valueManager.areValuesEqual( + utils, + state.value, + valueManager.emptyValue, + ); + const shouldShowPlaceholder = !inputHasFocus && areAllSectionsEmpty; React.useImperativeHandle(unstableFieldRef, () => ({ getSections: () => state.sections, @@ -499,9 +523,18 @@ export const useField = < setSelectedSections: (activeSectionIndex) => setSelectedSections(activeSectionIndex), })); + const handleClearValue = useEventCallback((event: React.MouseEvent, ...args) => { + event.preventDefault(); + onClear?.(event, ...(args as [])); + clearValue(); + inputRef?.current?.focus(); + setSelectedSections(0); + }); + return { placeholder, autoComplete: 'off', + disabled: Boolean(disabled), ...otherForwardedProps, value: shouldShowPlaceholder ? '' : valueStr, inputMode, @@ -513,7 +546,9 @@ export const useField = < onChange: handleInputChange, onKeyDown: handleInputKeyDown, onMouseUp: handleInputMouseUp, + onClear: handleClearValue, error: inputError, ref: handleRef, + clearable: Boolean(clearable && !areAllSectionsEmpty && !readOnly && !disabled), }; }; diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts index c8eb121a0c51b..20cb04cc061a3 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.types.ts @@ -1,4 +1,7 @@ import * as React from 'react'; +import { SlotComponentProps } from '@mui/base/utils'; +import IconButton from '@mui/material/IconButton'; +import { ClearIcon } from '../../../icons'; import { FieldSectionType, FieldSection, @@ -116,6 +119,20 @@ export interface UseFieldInternalProps>; + /** + * Callback fired when the clear button is clicked. + */ + onClear?: React.MouseEventHandler; + /** + * If `true`, a clear button will be shown in the field allowing value clearing. + * @default false + */ + clearable?: boolean; + /** + * If `true`, the component is disabled. + * @default false + */ + disabled?: boolean; } export interface FieldRef { @@ -141,10 +158,13 @@ export interface UseFieldForwardedProps { onKeyDown?: React.KeyboardEventHandler; onMouseUp?: React.MouseEventHandler; onPaste?: React.ClipboardEventHandler; - onClick?: () => void; + onClick?: React.MouseEventHandler; onFocus?: () => void; onBlur?: () => void; error?: boolean; + onClear?: React.MouseEventHandler; + clearable?: boolean; + disabled?: boolean; } export type UseFieldResponse = Omit< @@ -371,3 +391,21 @@ export type SectionOrdering = { */ endIndex: number; }; + +export interface FieldSlotsComponents { + /** + * Icon to display inside the clear button. + * @default ClearIcon + */ + clearIcon?: React.ElementType; + /** + * Button to clear the value. + * @default IconButton + */ + clearButton?: React.ElementType; +} + +export interface FieldSlotsComponentsProps { + clearIcon?: SlotComponentProps; + clearButton?: SlotComponentProps; +} diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts index 1f50d8e275fde..10e7196c5978a 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useField.utils.ts @@ -473,7 +473,7 @@ export const doesSectionFormatHaveLeadingZeros = ( } case 'seconds': { - return utils.formatByString(utils.setMinutes(now, 1), format).length > 1; + return utils.formatByString(utils.setSeconds(now, 1), format).length > 1; } default: { diff --git a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts index 660ead62ac7dc..62dcccb0696bb 100644 --- a/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts +++ b/packages/x-date-pickers/src/internals/hooks/useField/useFieldState.ts @@ -213,6 +213,10 @@ export const useFieldState = < tempValueStrAndroid: null, })); + if (valueManager.areValuesEqual(utils, state.value, value)) { + return; + } + const context: FieldChangeHandlerContext> = { validationError: validator({ adapter, @@ -237,10 +241,6 @@ export const useFieldState = < }; const clearValue = () => { - if (valueManager.areValuesEqual(utils, state.value, valueManager.emptyValue)) { - return; - } - publishValue({ value: valueManager.emptyValue, referenceValue: state.referenceValue, @@ -254,20 +254,16 @@ export const useFieldState = < } const activeSection = state.sections[selectedSectionIndexes.startIndex]; - - if (activeSection.value === '') { - return; - } - const activeDateManager = fieldValueManager.getActiveDateManager(utils, state, activeSection); const nonEmptySectionCountBefore = activeDateManager .getSections(state.sections) .filter((section) => section.value !== '').length; - const isTheOnlyNonEmptySection = nonEmptySectionCountBefore === 1; + const hasNoOtherNonEmptySections = + nonEmptySectionCountBefore === (activeSection.value === '' ? 0 : 1); const newSections = setSectionValue(selectedSectionIndexes.startIndex, ''); - const newActiveDate = isTheOnlyNonEmptySection ? null : utils.date(new Date('')); + const newActiveDate = hasNoOtherNonEmptySections ? null : utils.date(new Date('')); const newValues = activeDateManager.getNewValuesFromNewActiveDate(newActiveDate); if ( diff --git a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts index 21a858ce3b1f9..de945d4caae6e 100644 --- a/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useMobilePicker/useMobilePicker.types.ts @@ -19,7 +19,6 @@ import { } from '../../../PickersLayout/PickersLayout.types'; import { UsePickerValueNonStaticProps } from '../usePicker/usePickerValue.types'; import { UsePickerViewsNonStaticProps, UsePickerViewsProps } from '../usePicker/usePickerViews'; -import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; import { DateOrTimeViewWithMeridiem } from '../../models'; export interface UseMobilePickerSlotsComponent @@ -28,13 +27,13 @@ export interface UseMobilePickerSlotsComponent>; + field: React.ElementType>; /** * Form control with an input to render the value inside the default field. * Receives the same props as `@mui/material/TextField`. * @default TextField from '@mui/material' */ - TextField?: React.ElementType; + textField?: React.ElementType; } export interface ExportedUseMobilePickerSlotsComponentsProps< @@ -73,7 +72,7 @@ export interface UseMobilePickerProps< * Overridable component slots. * @default {} */ - slots: UncapitalizeObjectKeys>; + slots: UseMobilePickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts index 9663c2261749e..2a5a56b01ec6b 100644 --- a/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts +++ b/packages/x-date-pickers/src/internals/hooks/useStaticPicker/useStaticPicker.types.ts @@ -4,7 +4,6 @@ import { ExportedPickersLayoutSlotsComponentsProps, } from '../../../PickersLayout/PickersLayout.types'; import { BasePickerProps } from '../../models/props/basePickerProps'; -import { UncapitalizeObjectKeys } from '../../utils/slots-migration'; import { UsePickerParams } from '../usePicker'; import { UsePickerViewsProps } from '../usePicker/usePickerViews'; import { FieldSection } from '../../../models'; @@ -43,23 +42,11 @@ export interface UseStaticPickerProps< TExternalProps extends UsePickerViewsProps, > extends BasePickerProps, StaticOnlyPickerProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: UseStaticPickerSlotsComponent; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: UseStaticPickerSlotsComponentsProps; /** * Overridable component slots. * @default {} */ - slots?: UncapitalizeObjectKeys>; + slots?: UseStaticPickerSlotsComponent; /** * The props used for each component slot. * @default {} diff --git a/packages/x-date-pickers/src/internals/hooks/useViews.tsx b/packages/x-date-pickers/src/internals/hooks/useViews.tsx index 1555a56a7e965..6292375a348de 100644 --- a/packages/x-date-pickers/src/internals/hooks/useViews.tsx +++ b/packages/x-date-pickers/src/internals/hooks/useViews.tsx @@ -214,7 +214,8 @@ export function useViews({ setFocusedView: handleFocusedViewChange, nextView, previousView, - defaultView: defaultView.current, + // Always return up to date default view instead of the initial one (i.e. defaultView.current) + defaultView: views.includes(openTo!) ? openTo! : views[0], goToNextView, setValueAndGoToNextView, setValueAndGoToView, diff --git a/packages/x-date-pickers/src/internals/index.ts b/packages/x-date-pickers/src/internals/index.ts index be9a99358b3f9..1b2b432023ef2 100644 --- a/packages/x-date-pickers/src/internals/index.ts +++ b/packages/x-date-pickers/src/internals/index.ts @@ -66,6 +66,8 @@ export type { FieldValueManager, FieldChangeHandler, FieldChangeHandlerContext, + FieldSlotsComponents, + FieldSlotsComponentsProps, } from './hooks/useField'; export type { MobileOnlyPickerProps } from './hooks/useMobilePicker'; export { usePicker } from './hooks/usePicker'; @@ -137,8 +139,6 @@ export { validateDate } from './utils/validation/validateDate'; export { validateDateTime } from './utils/validation/validateDateTime'; export { validateTime } from './utils/validation/validateTime'; export { buildDeprecatedPropsWarning, buildWarning } from './utils/warning'; -export { uncapitalizeObjectKeys } from './utils/slots-migration'; -export type { UncapitalizeObjectKeys, SlotsAndSlotProps } from './utils/slots-migration'; export { DayCalendar } from '../DateCalendar/DayCalendar'; export type { diff --git a/packages/x-date-pickers/src/internals/models/props/toolbar.ts b/packages/x-date-pickers/src/internals/models/props/toolbar.ts index 36275b5103a4a..0b8ad1c99ff47 100644 --- a/packages/x-date-pickers/src/internals/models/props/toolbar.ts +++ b/packages/x-date-pickers/src/internals/models/props/toolbar.ts @@ -1,4 +1,6 @@ import * as React from 'react'; +import { SxProps } from '@mui/system'; +import { Theme } from '@mui/material/styles'; import { DateOrTimeViewWithMeridiem } from '../common'; export interface BaseToolbarProps @@ -41,4 +43,6 @@ export interface ExportedBaseToolbarProps { * @default `true` for Desktop, `false` for Mobile. */ hidden?: boolean; + + sx?: SxProps; } diff --git a/packages/x-date-pickers/src/internals/models/validation.ts b/packages/x-date-pickers/src/internals/models/validation.ts index e33e00ebf5b7e..891f7b73ff1ce 100644 --- a/packages/x-date-pickers/src/internals/models/validation.ts +++ b/packages/x-date-pickers/src/internals/models/validation.ts @@ -82,6 +82,9 @@ export interface BaseDateValidationProps extends FutureAndPastValidationP 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. + * * @template TDate * @param {TDate} day The date to test. * @returns {boolean} If `true` the date will be disabled. diff --git a/packages/x-date-pickers/src/internals/utils/date-time-utils.ts b/packages/x-date-pickers/src/internals/utils/date-time-utils.ts index 0f7cb2d93cf85..61f4daa693c15 100644 --- a/packages/x-date-pickers/src/internals/utils/date-time-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/date-time-utils.ts @@ -1,6 +1,15 @@ -import { DateOrTimeView, DateView, MuiPickersAdapter, TimeView } from '../../models'; -import { resolveTimeFormat, isTimeView } from './time-utils'; +import { + DateOrTimeView, + DateView, + MuiPickersAdapter, + TimeStepOptions, + TimeView, +} from '../../models'; +import { resolveTimeFormat, isTimeView, isInternalTimeView } from './time-utils'; import { resolveDateFormat } from './date-utils'; +import { DateOrTimeViewWithMeridiem } from '../models'; +import { DesktopOnlyTimePickerProps } from '../models/props/clock'; +import { DefaultizedProps } from '../models/helpers'; export const resolveDateTimeFormat = ( utils: MuiPickersAdapter, @@ -34,3 +43,56 @@ export const resolveDateTimeFormat = ( return `${dateFormat} ${timeFormat}`; }; + +const resolveViews = ( + ampm: boolean, + views: readonly DateOrTimeView[], + shouldUseSingleColumn: boolean, +): TView[] => { + if (shouldUseSingleColumn) { + return views.filter((view) => !isInternalTimeView(view) || view === 'hours') as TView[]; + } + return (ampm ? [...views, 'meridiem'] : views) as TView[]; +}; + +const resolveShouldRenderTimeInASingleColumn = (timeSteps: TimeStepOptions, threshold: number) => + (24 * 60) / ((timeSteps.hours ?? 1) * (timeSteps.minutes ?? 5)) <= threshold; + +interface DefaultizedTimeViewsProps + extends DefaultizedProps, 'ampm'> { + views: readonly TView[]; +} + +interface DefaultizedTimeViewsResponse + extends Required< + Pick< + DefaultizedTimeViewsProps, + 'thresholdToRenderTimeInASingleColumn' | 'timeSteps' | 'views' + > + > { + shouldRenderTimeInASingleColumn: boolean; +} + +export function resolveTimeViewsResponse< + TDate, + InTView extends DateOrTimeView = DateOrTimeView, + OutTView extends DateOrTimeViewWithMeridiem = DateOrTimeViewWithMeridiem, +>({ + thresholdToRenderTimeInASingleColumn: inThreshold, + ampm, + timeSteps: inTimeSteps, + views, +}: DefaultizedTimeViewsProps): DefaultizedTimeViewsResponse { + const thresholdToRenderTimeInASingleColumn = inThreshold ?? 24; + const timeSteps = { hours: 1, minutes: 5, seconds: 5, ...inTimeSteps }; + const shouldRenderTimeInASingleColumn = resolveShouldRenderTimeInASingleColumn( + timeSteps, + thresholdToRenderTimeInASingleColumn, + ); + return { + thresholdToRenderTimeInASingleColumn, + timeSteps, + shouldRenderTimeInASingleColumn, + views: resolveViews(ampm, views, shouldRenderTimeInASingleColumn), + }; +} 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 be46884130608..6227bf2cc6945 100644 --- a/packages/x-date-pickers/src/internals/utils/date-utils.ts +++ b/packages/x-date-pickers/src/internals/utils/date-utils.ts @@ -183,3 +183,8 @@ export const resolveDateFormat = ( return formats.keyboardDate; }; + +export const getWeekdays = (utils: MuiPickersAdapter, date: TDate) => { + const start = utils.startOfWeek(date); + return [0, 1, 2, 3, 4, 5, 6].map((diff) => utils.addDays(start, diff)); +}; diff --git a/packages/x-date-pickers/src/internals/utils/slots-migration.ts b/packages/x-date-pickers/src/internals/utils/slots-migration.ts deleted file mode 100644 index 01878c7d704f6..0000000000000 --- a/packages/x-date-pickers/src/internals/utils/slots-migration.ts +++ /dev/null @@ -1,65 +0,0 @@ -// TODO v7: This file exist only to simplify typing between -// components/componentsProps and slots/slotProps -// Should be deleted when components/componentsProps are removed - -type OptionalKeys = Exclude< - { [K in keyof T]: {} extends Pick ? K : never }[keyof T], - undefined ->; - -type UncapitalizeKeys = Uncapitalize; - -export type UncapitalizeObjectKeys = { - [key in UncapitalizeKeys>>]?: Capitalize extends keyof T - ? T[Capitalize] - : never; -} & { - [key in UncapitalizeKeys>>]: Capitalize extends keyof T - ? T[Capitalize] - : never; -}; - -export interface SlotsAndSlotProps { - /** - * Overridable components. - * @default {} - * @deprecated Please use `slots`. - */ - components?: TSlots; - /** - * The props used for each component slot. - * @default {} - * @deprecated Please use `slotProps`. - */ - componentsProps?: TSlotProps; - /** - * Overridable component slots. - * @default {} - */ - slots?: UncapitalizeObjectKeys; - /** - * The props used for each component slot. - * @default {} - */ - slotProps?: TSlotProps; -} - -type ObjectWithUnCapitalizedKeys = TInputType extends object - ? UncapitalizeObjectKeys - : undefined; - -export const uncapitalizeObjectKeys = ( - capitalizedObject: TInputType | undefined, -): ObjectWithUnCapitalizedKeys => { - if (capitalizedObject === undefined) { - return undefined as ObjectWithUnCapitalizedKeys; - } - return Object.keys(capitalizedObject).reduce( - (acc, key) => ({ - ...acc, - [`${key.slice(0, 1).toLowerCase()}${key.slice(1)}`]: - capitalizedObject[key as keyof TInputType], - }), - {} as ObjectWithUnCapitalizedKeys, - ); -}; diff --git a/packages/x-date-pickers/src/locales/beBY.ts b/packages/x-date-pickers/src/locales/beBY.ts index 76004e920f7ad..ff290e09184b1 100644 --- a/packages/x-date-pickers/src/locales/beBY.ts +++ b/packages/x-date-pickers/src/locales/beBY.ts @@ -66,6 +66,7 @@ const beBYPickers: Partial> = { value !== null && utils.isValid(value) ? `Абраць час, абрыны час ${utils.format(value, 'fullTime')}` : 'Абраць час', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'абраць час', diff --git a/packages/x-date-pickers/src/locales/caES.ts b/packages/x-date-pickers/src/locales/caES.ts index 4253843313b34..ad82c3f7c3ec8 100644 --- a/packages/x-date-pickers/src/locales/caES.ts +++ b/packages/x-date-pickers/src/locales/caES.ts @@ -67,6 +67,7 @@ const caESPickers: Partial> = { value !== null && utils.isValid(value) ? `Tria l'hora, l'hora triada és ${utils.format(value, 'fullTime')}` : "Tria l'hora", + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'tria la data', diff --git a/packages/x-date-pickers/src/locales/csCZ.ts b/packages/x-date-pickers/src/locales/csCZ.ts index 9630ed8fd5be3..baab53b389af8 100644 --- a/packages/x-date-pickers/src/locales/csCZ.ts +++ b/packages/x-date-pickers/src/locales/csCZ.ts @@ -66,6 +66,7 @@ const csCZPickers: Partial> = { value !== null && utils.isValid(value) ? `Vyberte čas, vybraný čas je ${utils.format(value, 'fullTime')}` : 'Vyberte čas', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'vyberte čas', diff --git a/packages/x-date-pickers/src/locales/daDK.ts b/packages/x-date-pickers/src/locales/daDK.ts index f46f0b28a7972..45be74c3579f0 100644 --- a/packages/x-date-pickers/src/locales/daDK.ts +++ b/packages/x-date-pickers/src/locales/daDK.ts @@ -68,6 +68,7 @@ const daDKPickers: Partial> = { value !== null && utils.isValid(value) ? `Vælg tidspunkt, valgte tidspunkt er ${utils.format(value, 'fullTime')}` : 'Vælg tidspunkt', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'vælg tidspunkt', diff --git a/packages/x-date-pickers/src/locales/deDE.ts b/packages/x-date-pickers/src/locales/deDE.ts index 8319f063b1b88..01bee187797d9 100644 --- a/packages/x-date-pickers/src/locales/deDE.ts +++ b/packages/x-date-pickers/src/locales/deDE.ts @@ -68,6 +68,7 @@ const deDEPickers: Partial> = { value !== null && utils.isValid(value) ? `Uhrzeit auswählen, gewählte Uhrzeit ist ${utils.format(value, 'fullTime')}` : 'Uhrzeit auswählen', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'Uhrzeit auswählen', diff --git a/packages/x-date-pickers/src/locales/elGR.ts b/packages/x-date-pickers/src/locales/elGR.ts index 7a15dc49987d3..2c4ae628dc829 100644 --- a/packages/x-date-pickers/src/locales/elGR.ts +++ b/packages/x-date-pickers/src/locales/elGR.ts @@ -67,6 +67,7 @@ const elGRPickers: Partial> = { value !== null && utils.isValid(value) ? `Επιλέξτε ώρα, η επιλεγμένη ώρα είναι ${utils.format(value, 'fullTime')}` : 'Επιλέξτε ώρα', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'επιλέξτε ώρα', diff --git a/packages/x-date-pickers/src/locales/enUS.ts b/packages/x-date-pickers/src/locales/enUS.ts index 53fce05b2081b..f46dde84fe5b1 100644 --- a/packages/x-date-pickers/src/locales/enUS.ts +++ b/packages/x-date-pickers/src/locales/enUS.ts @@ -60,6 +60,8 @@ const enUSPickers: PickersLocaleText = { ? `Choose time, selected time is ${utils.format(value, 'fullTime')}` : 'Choose time', + fieldClearLabel: 'Clear value', + // Table labels timeTableLabel: 'pick time', dateTableLabel: 'pick date', diff --git a/packages/x-date-pickers/src/locales/esES.ts b/packages/x-date-pickers/src/locales/esES.ts index b5fec0ff00d6e..c355724c62c79 100644 --- a/packages/x-date-pickers/src/locales/esES.ts +++ b/packages/x-date-pickers/src/locales/esES.ts @@ -42,8 +42,8 @@ const esESPickers: Partial> = { clockLabelText: (view, time, adapter) => `Seleccione ${views[view]}. ${ time === null - ? 'Sin tiempo seleccionado' - : `El tiempo seleccionado es ${adapter.format(time, 'fullTime')}` + ? 'No hay hora seleccionada' + : `La hora seleccionada es ${adapter.format(time, 'fullTime')}` }`, hoursClockNumberText: (hours) => `${hours} horas`, minutesClockNumberText: (minutes) => `${minutes} minutos`, @@ -61,19 +61,20 @@ const esESPickers: Partial> = { // Open picker labels openDatePickerDialogue: (value, utils) => value !== null && utils.isValid(value) - ? `Elige la fecha, la fecha elegida es ${utils.format(value, 'fullDate')}` - : 'Elige la fecha', + ? `Elige fecha, la fecha elegida es ${utils.format(value, 'fullDate')}` + : 'Elige fecha', openTimePickerDialogue: (value, utils) => value !== null && utils.isValid(value) - ? `Elige la hora, la hora elegido es ${utils.format(value, 'fullTime')}` - : 'Elige la hora', + ? `Elige hora, la hora elegida es ${utils.format(value, 'fullTime')}` + : 'Elige hora', + fieldClearLabel: 'Limpiar valor', // Table labels - timeTableLabel: 'elige la fecha', - dateTableLabel: 'elige la hora', + timeTableLabel: 'elige hora', + dateTableLabel: 'elige fecha', // Field section placeholders - fieldYearPlaceholder: (params) => 'Y'.repeat(params.digitAmount), + fieldYearPlaceholder: (params) => 'A'.repeat(params.digitAmount), fieldMonthPlaceholder: (params) => (params.contentType === 'letter' ? 'MMMM' : 'MM'), fieldDayPlaceholder: () => 'DD', fieldWeekDayPlaceholder: (params) => (params.contentType === 'letter' ? 'EEEE' : 'EE'), diff --git a/packages/x-date-pickers/src/locales/eu.ts b/packages/x-date-pickers/src/locales/eu.ts new file mode 100644 index 0000000000000..d99e2febb7a18 --- /dev/null +++ b/packages/x-date-pickers/src/locales/eu.ts @@ -0,0 +1,87 @@ +import { PickersLocaleText } from './utils/pickersLocaleTextApi'; +import { getPickersLocalization } from './utils/getPickersLocalization'; +import { TimeViewWithMeridiem } from '../internals/models'; + +const views: Record = { + hours: 'orduak', + minutes: 'minutuak', + seconds: 'segunduak', + meridiem: 'meridianoa', +}; + +const euPickers: Partial> = { + // Calendar navigation + previousMonth: 'Azken hilabetea', + nextMonth: 'Hurrengo hilabetea', + + // View navigation + openPreviousView: 'azken bista ireki', + openNextView: 'hurrengo bista ireki', + calendarViewSwitchingButtonAriaLabel: (view) => + view === 'year' + ? 'urteko bista irekita dago, aldatu egutegi bistara' + : 'egutegi bista irekita dago, aldatu urteko bistara', + + // DateRange placeholders + start: 'Hasi', + end: 'Bukatu', + + // Action bar + cancelButtonLabel: 'Utxi', + clearButtonLabel: 'Garbitu', + okButtonLabel: 'OK', + todayButtonLabel: 'Gaur', + + // Toolbar titles + datePickerToolbarTitle: 'Data aukeratu', + dateTimePickerToolbarTitle: 'Data eta ordua aukeratu', + timePickerToolbarTitle: 'Ordua aukeratu', + dateRangePickerToolbarTitle: 'Data tartea aukeratu', + + // Clock labels + clockLabelText: (view, time, adapter) => + `Aukeratu ${views[view]}. ${ + time === null + ? 'Ez da ordurik aukertau' + : `Aukeratutako ordua ${adapter.format(time, 'fullTime')} da` + }`, + hoursClockNumberText: (hours) => `${hours} ordu`, + minutesClockNumberText: (minutes) => `${minutes} minutu`, + secondsClockNumberText: (seconds) => `${seconds} segundu`, + + // Digital clock labels + selectViewText: (view) => `Aukeratu ${views[view]}`, + + // Calendar labels + calendarWeekNumberHeaderLabel: 'Astea zenbakia', + calendarWeekNumberHeaderText: '#', + calendarWeekNumberAriaLabelText: (weekNumber) => `${weekNumber} astea`, + calendarWeekNumberText: (weekNumber) => `${weekNumber}`, + + // Open picker labels + openDatePickerDialogue: (value, utils) => + value !== null && utils.isValid(value) + ? `Data aukeratu, aukeratutako data ${utils.format(value, 'fullDate')} da` + : 'Data aukeratu', + openTimePickerDialogue: (value, utils) => + value !== null && utils.isValid(value) + ? `Ordua aukeratu, aukeratutako ordua ${utils.format(value, 'fullTime')} da` + : 'Ordua aukeratu', + fieldClearLabel: 'Balioa garbitu', + + // Table labels + timeTableLabel: 'ordua aukeratu', + dateTableLabel: 'data aukeratu', + + // Field section placeholders + fieldYearPlaceholder: (params) => 'Y'.repeat(params.digitAmount), + fieldMonthPlaceholder: (params) => (params.contentType === 'letter' ? 'MMMM' : 'MM'), + fieldDayPlaceholder: () => 'DD', + fieldWeekDayPlaceholder: (params) => (params.contentType === 'letter' ? 'EEEE' : 'EE'), + fieldHoursPlaceholder: () => 'hh', + fieldMinutesPlaceholder: () => 'mm', + fieldSecondsPlaceholder: () => 'ss', + fieldMeridiemPlaceholder: () => 'aa', +}; + +export const eu = getPickersLocalization(euPickers); diff --git a/packages/x-date-pickers/src/locales/faIR.ts b/packages/x-date-pickers/src/locales/faIR.ts index 5a748c92810dc..64545ebd2a264 100644 --- a/packages/x-date-pickers/src/locales/faIR.ts +++ b/packages/x-date-pickers/src/locales/faIR.ts @@ -67,6 +67,7 @@ const faIRPickers: Partial> = { value !== null && utils.isValid(value) ? `ساعت را انتخاب کنید، ساعت انتخاب شده ${utils.format(value, 'fullTime')} می باشد` : 'ساعت را انتخاب کنید', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'انتخاب تاریخ', diff --git a/packages/x-date-pickers/src/locales/fiFI.ts b/packages/x-date-pickers/src/locales/fiFI.ts index cef50d5f4072d..9bcca993f08bf 100644 --- a/packages/x-date-pickers/src/locales/fiFI.ts +++ b/packages/x-date-pickers/src/locales/fiFI.ts @@ -65,6 +65,7 @@ const fiFIPickers: Partial> = { value !== null && utils.isValid(value) ? `Valitse aika, valittu aika on ${utils.format(value, 'fullTime')}` : 'Valitse aika', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'valitse aika', diff --git a/packages/x-date-pickers/src/locales/frFR.ts b/packages/x-date-pickers/src/locales/frFR.ts index 14ac7e7d7dbc4..6fdb39d1e9ba1 100644 --- a/packages/x-date-pickers/src/locales/frFR.ts +++ b/packages/x-date-pickers/src/locales/frFR.ts @@ -67,6 +67,7 @@ const frFRPickers: Partial> = { value !== null && utils.isValid(value) ? `Choisir l'heure, l'heure sélectionnée est ${utils.format(value, 'fullTime')}` : "Choisir l'heure", + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: "choix de l'heure", diff --git a/packages/x-date-pickers/src/locales/heIL.ts b/packages/x-date-pickers/src/locales/heIL.ts index dceedbbff2b61..98f438249a0cb 100644 --- a/packages/x-date-pickers/src/locales/heIL.ts +++ b/packages/x-date-pickers/src/locales/heIL.ts @@ -65,6 +65,7 @@ const heILPickers: Partial> = { value !== null && utils.isValid(value) ? `בחירת שעה, השעה שנבחרה היא ${utils.format(value, 'fullTime')}` : 'בחירת שעה', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'בחירת שעה', diff --git a/packages/x-date-pickers/src/locales/huHU.ts b/packages/x-date-pickers/src/locales/huHU.ts index 86fc0a23469c8..3738aa46e1af9 100644 --- a/packages/x-date-pickers/src/locales/huHU.ts +++ b/packages/x-date-pickers/src/locales/huHU.ts @@ -68,6 +68,7 @@ const huHUPickers: Partial> = { value !== null && utils.isValid(value) ? `Válasszon időt, a kiválasztott idő: ${utils.format(value, 'fullTime')}` : 'Válasszon időt', + fieldClearLabel: 'Tartalom ürítése', // Table labels timeTableLabel: 'válasszon időt', diff --git a/packages/x-date-pickers/src/locales/index.ts b/packages/x-date-pickers/src/locales/index.ts index 610e3d41a476a..410f06361fae0 100644 --- a/packages/x-date-pickers/src/locales/index.ts +++ b/packages/x-date-pickers/src/locales/index.ts @@ -5,6 +5,7 @@ export * from './deDE'; export * from './elGR'; export * from './enUS'; export * from './esES'; +export * from './eu'; export * from './faIR'; export * from './fiFI'; export * from './frFR'; diff --git a/packages/x-date-pickers/src/locales/isIS.ts b/packages/x-date-pickers/src/locales/isIS.ts index cfdfa2124e29a..e478eefdd3f3f 100644 --- a/packages/x-date-pickers/src/locales/isIS.ts +++ b/packages/x-date-pickers/src/locales/isIS.ts @@ -65,6 +65,7 @@ const isISPickers: Partial> = { value !== null && utils.isValid(value) ? `Velja tíma, valinn tími er ${utils.format(value, 'fullTime')}` : 'Velja tíma', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'velja tíma', diff --git a/packages/x-date-pickers/src/locales/itIT.ts b/packages/x-date-pickers/src/locales/itIT.ts index feb675cc5fcce..e7ac7b62703b5 100644 --- a/packages/x-date-pickers/src/locales/itIT.ts +++ b/packages/x-date-pickers/src/locales/itIT.ts @@ -67,6 +67,7 @@ const itITPickers: Partial> = { value !== null && utils.isValid(value) ? `Scegli l'ora, l'ora selezionata è ${utils.format(value, 'fullTime')}` : "Scegli l'ora", + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: "scegli un'ora", diff --git a/packages/x-date-pickers/src/locales/jaJP.ts b/packages/x-date-pickers/src/locales/jaJP.ts index 74aa23834109a..0d822813e62de 100644 --- a/packages/x-date-pickers/src/locales/jaJP.ts +++ b/packages/x-date-pickers/src/locales/jaJP.ts @@ -68,6 +68,7 @@ const jaJPPickers: Partial> = { value !== null && utils.isValid(value) ? `時間を選択してください。選択した時間は ${utils.format(value, 'fullTime')} です` : '時間を選択してください', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: '時間を選択', diff --git a/packages/x-date-pickers/src/locales/koKR.ts b/packages/x-date-pickers/src/locales/koKR.ts index a44d3c2182918..51c64f0c4bb49 100644 --- a/packages/x-date-pickers/src/locales/koKR.ts +++ b/packages/x-date-pickers/src/locales/koKR.ts @@ -67,6 +67,7 @@ const koKRPickers: Partial> = { value !== null && utils.isValid(value) ? `시간을 선택하세요. 현재 선택된 시간은 ${utils.format(value, 'fullTime')}입니다.` : '시간을 선택하세요', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: '선택한 시간', diff --git a/packages/x-date-pickers/src/locales/kzKZ.ts b/packages/x-date-pickers/src/locales/kzKZ.ts index bb43a39e2ae7d..c27450d3df48a 100644 --- a/packages/x-date-pickers/src/locales/kzKZ.ts +++ b/packages/x-date-pickers/src/locales/kzKZ.ts @@ -66,6 +66,7 @@ const kzKZPickers: Partial> = { value !== null && utils.isValid(value) ? `Уақытты таңдаңыз, таңдалған уақыт ${utils.format(value, 'fullTime')}` : 'Уақытты таңдаңыз', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'уақытты таңдау', diff --git a/packages/x-date-pickers/src/locales/nbNO.ts b/packages/x-date-pickers/src/locales/nbNO.ts index 8679592b861e9..68b44e72e9c2b 100644 --- a/packages/x-date-pickers/src/locales/nbNO.ts +++ b/packages/x-date-pickers/src/locales/nbNO.ts @@ -65,6 +65,7 @@ const nbNOPickers: Partial> = { value !== null && utils.isValid(value) ? `Velg tid, valgt tid er ${utils.format(value, 'fullTime')}` : 'Velg tid', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'velg tid', diff --git a/packages/x-date-pickers/src/locales/nlNL.ts b/packages/x-date-pickers/src/locales/nlNL.ts index 3d73af4c05952..56477a3234d5c 100644 --- a/packages/x-date-pickers/src/locales/nlNL.ts +++ b/packages/x-date-pickers/src/locales/nlNL.ts @@ -67,6 +67,7 @@ const nlNLPickers: Partial> = { value !== null && utils.isValid(value) ? `Kies tijd, geselecteerde tijd is ${utils.format(value, 'fullTime')}` : 'Kies tijd', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'kies tijd', diff --git a/packages/x-date-pickers/src/locales/plPL.ts b/packages/x-date-pickers/src/locales/plPL.ts index 6b7720e969c32..dc2133f322d66 100644 --- a/packages/x-date-pickers/src/locales/plPL.ts +++ b/packages/x-date-pickers/src/locales/plPL.ts @@ -65,6 +65,7 @@ const plPLPickers: Partial> = { value !== null && utils.isValid(value) ? `Wybierz czas, obecnie wybrany czas to ${utils.format(value, 'fullTime')}` : 'Wybierz czas', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'wybierz czas', diff --git a/packages/x-date-pickers/src/locales/ptBR.ts b/packages/x-date-pickers/src/locales/ptBR.ts index 032c8ccfccf0d..4b0d4ccfa96ce 100644 --- a/packages/x-date-pickers/src/locales/ptBR.ts +++ b/packages/x-date-pickers/src/locales/ptBR.ts @@ -67,6 +67,7 @@ const ptBRPickers: Partial> = { value !== null && utils.isValid(value) ? `Escolha uma hora, hora selecionada ${utils.format(value, 'fullTime')}` : 'Escolha uma hora', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'escolha uma hora', diff --git a/packages/x-date-pickers/src/locales/roRO.ts b/packages/x-date-pickers/src/locales/roRO.ts index 7c24c4a6b6806..2334197303aed 100644 --- a/packages/x-date-pickers/src/locales/roRO.ts +++ b/packages/x-date-pickers/src/locales/roRO.ts @@ -68,6 +68,7 @@ const roROPickers: Partial> = { value !== null && utils.isValid(value) ? `Selectați ora, ora selectată este ${utils.format(value, 'fullTime')}` : 'Selectați ora', + fieldClearLabel: 'Golire conținut', // Table labels timeTableLabel: 'Selectați ora', diff --git a/packages/x-date-pickers/src/locales/ruRU.ts b/packages/x-date-pickers/src/locales/ruRU.ts index d947bcd5b2538..8f98d866c4c6f 100644 --- a/packages/x-date-pickers/src/locales/ruRU.ts +++ b/packages/x-date-pickers/src/locales/ruRU.ts @@ -66,6 +66,7 @@ const ruRUPickers: Partial> = { value !== null && utils.isValid(value) ? `Выберите время, выбрано время ${utils.format(value, 'fullTime')}` : 'Выберите время', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'выбрать время', diff --git a/packages/x-date-pickers/src/locales/skSK.ts b/packages/x-date-pickers/src/locales/skSK.ts index 22ab6650219ec..96cdfd13bde1b 100644 --- a/packages/x-date-pickers/src/locales/skSK.ts +++ b/packages/x-date-pickers/src/locales/skSK.ts @@ -66,6 +66,7 @@ const skSKPickers: Partial> = { value !== null && utils.isValid(value) ? `Vyberte čas, vybraný čas je ${utils.format(value, 'fullTime')}` : 'Vyberte čas', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'vyberte čas', diff --git a/packages/x-date-pickers/src/locales/svSE.ts b/packages/x-date-pickers/src/locales/svSE.ts index b8296507ce945..498c02df0c2b9 100644 --- a/packages/x-date-pickers/src/locales/svSE.ts +++ b/packages/x-date-pickers/src/locales/svSE.ts @@ -65,6 +65,7 @@ const svSEPickers: Partial> = { value !== null && utils.isValid(value) ? `Välj tid, vald tid är ${utils.format(value, 'fullTime')}` : 'Välj tid', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'välj tid', diff --git a/packages/x-date-pickers/src/locales/trTR.ts b/packages/x-date-pickers/src/locales/trTR.ts index a640746cadf61..948c0cc1e2b0b 100644 --- a/packages/x-date-pickers/src/locales/trTR.ts +++ b/packages/x-date-pickers/src/locales/trTR.ts @@ -65,6 +65,7 @@ const trTRPickers: Partial> = { value !== null && utils.isValid(value) ? `Saat seçin, seçilen saat: ${utils.format(value, 'fullTime')}` : 'Saat seç', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'saat seç', diff --git a/packages/x-date-pickers/src/locales/ukUA.ts b/packages/x-date-pickers/src/locales/ukUA.ts index 45242cabee79f..d9895177ac12c 100644 --- a/packages/x-date-pickers/src/locales/ukUA.ts +++ b/packages/x-date-pickers/src/locales/ukUA.ts @@ -65,6 +65,7 @@ const ukUAPickers: Partial> = { value !== null && utils.isValid(value) ? `Оберіть час, обраний час ${utils.format(value, 'fullTime')}` : 'Оберіть час', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'оберіть час', diff --git a/packages/x-date-pickers/src/locales/urPK.ts b/packages/x-date-pickers/src/locales/urPK.ts index 1c73d41e999b2..ab0043a0aef98 100644 --- a/packages/x-date-pickers/src/locales/urPK.ts +++ b/packages/x-date-pickers/src/locales/urPK.ts @@ -65,6 +65,7 @@ const urPKPickers: Partial> = { value !== null && utils.isValid(value) ? `وقت منتخب کریں، منتخب شدہ وقت ہے ${utils.format(value, 'fullTime')}` : 'وقت منتخب کریں', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'وقت منتخب کریں', diff --git a/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts b/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts index a2acb4fa27bf7..e7a6e8e6e0e1d 100644 --- a/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts +++ b/packages/x-date-pickers/src/locales/utils/pickersLocaleTextApi.ts @@ -63,6 +63,9 @@ export interface PickersComponentAgnosticLocaleText { openDatePickerDialogue: (date: TDate | null, utils: MuiPickersAdapter) => string; openTimePickerDialogue: (date: TDate | null, utils: MuiPickersAdapter) => string; + // Clear button label + fieldClearLabel: string; + // Table labels timeTableLabel: string; dateTableLabel: string; diff --git a/packages/x-date-pickers/src/locales/viVN.ts b/packages/x-date-pickers/src/locales/viVN.ts index f238b31ea5f54..638f4c58fdf6b 100644 --- a/packages/x-date-pickers/src/locales/viVN.ts +++ b/packages/x-date-pickers/src/locales/viVN.ts @@ -67,6 +67,7 @@ const viVNPickers: Partial> = { value !== null && utils.isValid(value) ? `Chọn giờ, giờ đã chọn là ${utils.format(value, 'fullTime')}` : 'Chọn giờ', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: 'chọn giờ', diff --git a/packages/x-date-pickers/src/locales/zhCN.ts b/packages/x-date-pickers/src/locales/zhCN.ts index 544b6d23c1bd0..ec77887c5bed7 100644 --- a/packages/x-date-pickers/src/locales/zhCN.ts +++ b/packages/x-date-pickers/src/locales/zhCN.ts @@ -63,6 +63,7 @@ const zhCNPickers: Partial> = { value !== null && utils.isValid(value) ? `选择时间,已选择${utils.format(value, 'fullTime')}` : '选择时间', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: '选择时间', diff --git a/packages/x-date-pickers/src/locales/zhHK.ts b/packages/x-date-pickers/src/locales/zhHK.ts index 8c454b92a8e31..c06008ad29164 100644 --- a/packages/x-date-pickers/src/locales/zhHK.ts +++ b/packages/x-date-pickers/src/locales/zhHK.ts @@ -63,6 +63,7 @@ const zhHKPickers: Partial> = { value !== null && utils.isValid(value) ? `選擇時間,已選擇${utils.format(value, 'fullTime')}` : '選擇時間', + // fieldClearLabel: 'Clear value', // Table labels timeTableLabel: '選擇時間', diff --git a/packages/x-date-pickers/src/models/adapters.ts b/packages/x-date-pickers/src/models/adapters.ts index b07832fcd2629..c43bcac5ac5d8 100644 --- a/packages/x-date-pickers/src/models/adapters.ts +++ b/packages/x-date-pickers/src/models/adapters.ts @@ -721,6 +721,7 @@ export interface MuiPickersAdapter { mergeDateAndTime(dateParam: TDate, timeParam: TDate): TDate; /** * Get the label of each day of a week. + * @deprecated Will be removed in v7. Use `getWeekdays` from date-utils and format the dates. * @returns {string[]} The label of each day of a week. */ getWeekdays(): string[]; diff --git a/packages/x-date-pickers/src/models/fields.ts b/packages/x-date-pickers/src/models/fields.ts index 06b589a3a9d30..30e3410328e74 100644 --- a/packages/x-date-pickers/src/models/fields.ts +++ b/packages/x-date-pickers/src/models/fields.ts @@ -150,4 +150,6 @@ export interface BaseSingleInputFieldProps({ minutesStep, ampm, ampmInClock, - components, - componentsProps, slots, slotProps, readOnly, @@ -74,8 +72,6 @@ export const renderTimeViewClock = ({ minutesStep={minutesStep} ampm={ampm} ampmInClock={ampmInClock} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} readOnly={readOnly} @@ -108,8 +104,6 @@ export const renderDigitalClockTimeView = ({ shouldDisableClock, minutesStep, ampm, - components, - componentsProps, slots, slotProps, readOnly, @@ -144,8 +138,6 @@ export const renderDigitalClockTimeView = ({ shouldDisableClock={shouldDisableClock} minutesStep={minutesStep} ampm={ampm} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} readOnly={readOnly} @@ -179,8 +171,6 @@ export const renderMultiSectionDigitalClockTimeView = ({ shouldDisableClock, minutesStep, ampm, - components, - componentsProps, slots, slotProps, readOnly, @@ -212,8 +202,6 @@ export const renderMultiSectionDigitalClockTimeView = ({ shouldDisableClock={shouldDisableClock} minutesStep={minutesStep} ampm={ampm} - components={components} - componentsProps={componentsProps} slots={slots} slotProps={slotProps} readOnly={readOnly} diff --git a/packages/x-date-pickers/tsconfig.json b/packages/x-date-pickers/tsconfig.json index 75f0e23cefbee..9db9967fdf72a 100644 --- a/packages/x-date-pickers/tsconfig.json +++ b/packages/x-date-pickers/tsconfig.json @@ -7,7 +7,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../node_modules/@mui/material/themeCssVarsAugmentation", "../../node_modules/dayjs/plugin/utc.d.ts", "../../node_modules/dayjs/plugin/timezone.d.ts", diff --git a/packages/x-license-pro/package.json b/packages/x-license-pro/package.json index ccb156a8d3911..c76b6fbb6687c 100644 --- a/packages/x-license-pro/package.json +++ b/packages/x-license-pro/package.json @@ -35,8 +35,8 @@ "directory": "packages/x-license-pro" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/utils": "^5.14.8" + "@babel/runtime": "^7.23.2", + "@mui/utils": "^5.14.16" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0" diff --git a/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.test.tsx b/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.test.tsx index 9a84aecc97315..67d3846d4e39a 100644 --- a/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.test.tsx +++ b/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.test.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { createRenderer, screen } from '@mui/monorepo/test/utils'; +import { createRenderer, screen } from '@mui-internal/test-utils'; import { useLicenseVerifier, LicenseInfo, @@ -10,17 +10,37 @@ import { import { sharedLicenseStatuses } from './useLicenseVerifier'; import { generateReleaseInfo } from '../verifyLicense'; +const oneDayInMS = 1000 * 60 * 60 * 24; const releaseDate = new Date(3000, 0, 0, 0, 0, 0, 0); -const releaseInfo = generateReleaseInfo(releaseDate); +const RELEASE_INFO = generateReleaseInfo(releaseDate); function TestComponent() { - const licesenStatus = useLicenseVerifier('x-date-pickers-pro', releaseInfo); + const licesenStatus = useLicenseVerifier('x-date-pickers-pro', RELEASE_INFO); return
                Status: {licesenStatus.status}
                ; } -describe('useLicenseVerifier', () => { +describe('useLicenseVerifier', function test() { + // Can't change the process.env.NODE_ENV in Karma + if (!/jsdom/.test(window.navigator.userAgent)) { + return; + } + const { render } = createRenderer(); + let env: any; + beforeEach(() => { + env = process.env.NODE_ENV; + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = 'test'; + }); + + afterEach(() => { + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = env; + }); + describe('error', () => { beforeEach(() => { Object.keys(sharedLicenseStatuses).forEach((key) => { @@ -56,5 +76,33 @@ describe('useLicenseVerifier', () => { expect(screen.getByTestId('status')).to.have.text('Status: Valid'); }); + + it('should throw if the license is expired by more than a 30 days', () => { + // Avoid Karma "Invalid left-hand side in assignment" SyntaxError + // eslint-disable-next-line no-useless-concat + process.env['NODE_' + 'ENV'] = 'development'; + + const expiredLicenseKey = generateLicense({ + expiryDate: new Date(new Date().getTime() - oneDayInMS * 30), + orderNumber: 'MUI-123', + scope: 'pro', + licensingModel: 'subscription', + }); + LicenseInfo.setLicenseKey(expiredLicenseKey); + + let actualErrorMsg; + expect(() => { + try { + render(); + } catch (error: any) { + actualErrorMsg = error.message; + } + }).to.toErrorDev([ + 'MUI: Expired license key', + 'MUI: Expired license key', + 'The above error occurred in the component', + ]); + expect(actualErrorMsg).to.match(/MUI: Expired license key/); + }); }); }); diff --git a/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.ts b/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.ts index 9cdcf4717e697..45cb84f4d6127 100644 --- a/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.ts +++ b/packages/x-license-pro/src/useLicenseVerifier/useLicenseVerifier.ts @@ -56,7 +56,6 @@ export function useLicenseVerifier( acceptedScopes, }); - sharedLicenseStatuses[packageName] = { key: licenseKey, licenseVerifier: licenseStatus }; const fullPackageName = `@mui/${packageName}`; if (licenseStatus.status === LICENSE_STATUS.Valid) { @@ -77,6 +76,7 @@ export function useLicenseVerifier( throw new Error('missing status handler'); } + sharedLicenseStatuses[packageName] = { key: licenseKey, licenseVerifier: licenseStatus }; return licenseStatus; }, [packageName, releaseInfo, contextKey]); } diff --git a/packages/x-license-pro/tsconfig.json b/packages/x-license-pro/tsconfig.json index 16bd9c3be1b6a..6a8e1ed5b14fc 100644 --- a/packages/x-license-pro/tsconfig.json +++ b/packages/x-license-pro/tsconfig.json @@ -6,7 +6,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../node_modules/@mui/material/themeCssVarsAugmentation" ] } diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index 139678cc680a3..2a995868c3022 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": "6.0.0-alpha.3", + "version": "6.17.0", "description": "The community edition of the tree view components (MUI X).", "author": "MUI Team", "main": "src/index.ts", @@ -42,10 +42,10 @@ "directory": "packages/x-tree-view" }, "dependencies": { - "@babel/runtime": "^7.22.15", - "@mui/base": "^5.0.0-beta.14", - "@mui/utils": "^5.14.8", - "@types/react-transition-group": "^4.4.6", + "@babel/runtime": "^7.23.2", + "@mui/base": "^5.0.0-beta.22", + "@mui/utils": "^5.14.16", + "@types/react-transition-group": "^4.4.8", "clsx": "^2.0.0", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index 26a5749d71bee..da7a2a2f26462 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -9,7 +9,7 @@ import { createRenderer, fireEvent, screen, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import { TreeView } from '@mui/x-tree-view/TreeView'; import { TreeItem, treeItemClasses as classes } from '@mui/x-tree-view/TreeItem'; diff --git a/packages/x-tree-view/src/TreeView/TreeView.test.tsx b/packages/x-tree-view/src/TreeView/TreeView.test.tsx index 01d9aa84b2a8c..2ada3feaa24e4 100644 --- a/packages/x-tree-view/src/TreeView/TreeView.test.tsx +++ b/packages/x-tree-view/src/TreeView/TreeView.test.tsx @@ -8,7 +8,7 @@ import { fireEvent, screen, describeConformance, -} from '@mui/monorepo/test/utils'; +} from '@mui-internal/test-utils'; import Portal from '@mui/material/Portal'; import { TreeView, treeViewClasses as classes } from '@mui/x-tree-view/TreeView'; import { TreeItem } from '@mui/x-tree-view/TreeItem'; @@ -42,7 +42,7 @@ describe('', () => { it('should warn when switching from controlled to uncontrolled of the selected prop', () => { const { setProps } = render( - + , ); @@ -442,6 +442,99 @@ describe('', () => { }); }); + describe('useTreeViewFocus', () => { + it('should focus the selected item when the tree is focused', () => { + const onNodeFocus = spy(); + + const { getByRole } = render( + + + + , + ); + + act(() => { + getByRole('tree').focus(); + }); + + expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + }); + + it('should focus the selected item when the tree is focused (multi select)', () => { + const onNodeFocus = spy(); + + const { getByRole } = render( + + + + , + ); + + act(() => { + getByRole('tree').focus(); + }); + + expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + }); + + it('should focus the first visible selected item when the tree is focused (multi select)', () => { + const onNodeFocus = spy(); + + const { getByRole } = render( + + + + + + , + ); + + act(() => { + getByRole('tree').focus(); + }); + + expect(onNodeFocus.lastCall.lastArg).to.equal('2'); + }); + + it('should focus the first item if the selected item is not visible', () => { + const onNodeFocus = spy(); + + const { getByRole } = render( + + + + + + , + ); + + act(() => { + getByRole('tree').focus(); + }); + + expect(onNodeFocus.lastCall.lastArg).to.equal('1'); + }); + + it('should focus the first item if no selected item is visible (multi select)', () => { + const onNodeFocus = spy(); + + const { getByRole } = render( + + + + + + , + ); + + act(() => { + getByRole('tree').focus(); + }); + + expect(onNodeFocus.lastCall.lastArg).to.equal('1'); + }); + }); + describe('Accessibility', () => { it('(TreeView) should have the role `tree`', () => { const { getByRole } = render(); diff --git a/packages/x-tree-view/src/TreeView/TreeView.tsx b/packages/x-tree-view/src/TreeView/TreeView.tsx index 9606582fd357d..abb4a9d327390 100644 --- a/packages/x-tree-view/src/TreeView/TreeView.tsx +++ b/packages/x-tree-view/src/TreeView/TreeView.tsx @@ -32,7 +32,7 @@ const TreeViewRoot = styled('ul', { outline: 0, }); -type TreeViewComponent = (( +type TreeViewComponent = (( props: TreeViewProps & React.RefAttributes, ) => React.JSX.Element) & { propTypes?: any }; @@ -46,10 +46,9 @@ type TreeViewComponent = (( * * - [TreeView API](https://mui.com/x/api/tree-view/tree-view/) */ -const TreeView = React.forwardRef(function TreeView( - inProps: TreeViewProps, - ref: React.Ref, -) { +const TreeView = React.forwardRef(function TreeView< + Multiple extends boolean | undefined = undefined, +>(inProps: TreeViewProps, ref: React.Ref) { const themeProps = useThemeProps({ props: inProps, name: 'MuiTreeView' }); const ownerState = themeProps as TreeViewProps; 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 7072a35c0dfa4..6f12f844cfd3a 100644 --- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts +++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.ts @@ -58,10 +58,23 @@ export const useTreeViewFocus: TreeViewPlugin = ({ // if the event bubbled (which is React specific) we don't want to steal focus if (event.target === event.currentTarget) { - const firstSelected = Array.isArray(models.selected.value) - ? models.selected.value[0] - : models.selected.value; - instance.focusNode(event, firstSelected || instance.getNavigableChildrenIds(null)[0]); + const isNodeVisible = (nodeId: string) => { + const node = instance.getNode(nodeId); + return node && (node.parentId == null || instance.isNodeExpanded(node.parentId)); + }; + + let nodeToFocusId: string | null | undefined; + if (Array.isArray(models.selected.value)) { + nodeToFocusId = models.selected.value.find(isNodeVisible); + } else if (models.selected.value != null && isNodeVisible(models.selected.value)) { + nodeToFocusId = models.selected.value; + } + + if (nodeToFocusId == null) { + nodeToFocusId = instance.getNavigableChildrenIds(null)[0]; + } + + instance.focusNode(event, nodeToFocusId); } }; 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 629e28f0933fa..b88ee531e2ef2 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 @@ -2,6 +2,7 @@ import * as React from 'react'; import { TreeViewPluginSignature } from '../../models'; import type { UseTreeViewNodesSignature } from '../useTreeViewNodes'; import type { UseTreeViewSelectionSignature } from '../useTreeViewSelection'; +import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion'; export interface UseTreeViewFocusInstance { isNodeFocused: (nodeId: string) => boolean; @@ -31,5 +32,5 @@ export type UseTreeViewFocusSignature = TreeViewPluginSignature< {}, UseTreeViewFocusState, never, - [UseTreeViewNodesSignature, UseTreeViewSelectionSignature] + [UseTreeViewNodesSignature, UseTreeViewSelectionSignature, UseTreeViewExpansionSignature] >; diff --git a/packages/x-tree-view/tsconfig.json b/packages/x-tree-view/tsconfig.json index 05b4710dac129..b615deabf7972 100644 --- a/packages/x-tree-view/tsconfig.json +++ b/packages/x-tree-view/tsconfig.json @@ -7,7 +7,7 @@ "include": [ "src/**/*", "../../test/utils/addChaiAssertions.ts", - "../../node_modules/@mui/monorepo/test/utils/initMatchers.ts", + "../../node_modules/@mui/monorepo/packages/test-utils/src/initMatchers.ts", "../../node_modules/@mui/material/themeCssVarsAugmentation" ] } diff --git a/renovate.json b/renovate.json index b4de94018b3b1..101e09ac40380 100644 --- a/renovate.json +++ b/renovate.json @@ -18,6 +18,14 @@ "groupName": "babel", "matchPackagePatterns": "@babel/*" }, + { + "groupName": "D3", + "matchPackagePatterns": ["d3-*", "@types/d3-*"] + }, + { + "groupName": "react-spring", + "matchPackagePatterns": "@react-spring/*" + }, { "groupName": "Emotion", "matchPackagePatterns": "@emotion/*" @@ -34,7 +42,14 @@ }, { "groupName": "React", - "matchPackageNames": ["react", "react-dom", "react-is", "react-test-renderer"] + "matchPackageNames": [ + "react", + "react-dom", + "react-is", + "react-test-renderer", + "@types/react", + "@types/react-dom" + ] }, { "groupName": "React router", @@ -72,10 +87,11 @@ { "groupName": "MUI Core", "matchPackageNames": [ - "@mui/material", - "@mui/joy", - "@mui/icons-material", "@mui/base", + "@mui/icons-material", + "@mui/joy", + "@mui/lab", + "@mui/material", "@mui/styles", "@mui/system", "@mui/utils" diff --git a/scripts/README.md b/scripts/README.md index 9a9a674f1c9ec..ddf72d121ea22 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -2,6 +2,9 @@ ## Release +> Tip: For people who are doing the release for the first time, make sure you sign in to NPM from the command line using security-key flow as well as have two-factor authentication enabled. +> You can follow [this guide](https://docs.npmjs.com/accessing-npm-using-2fa) for more information on how to set it up. + > Tip: You can copy raw markdown checklist below to the release Pull Request and follow it step by step marking completed items. A typical release goes like this: @@ -47,11 +50,11 @@ The documentation must be updated on the `docs-vX` branch (`docs-v4` for `v4.X` ```bash -git push upstream master:docs-v6 -f +git push upstream next:docs-next -f ``` -You can follow the deployment process [on the Netlify Dashboard](https://app.netlify.com/sites/material-ui-x/deploys?filter=docs-v6) -Once deployed, it will be accessible at https://material-ui-x.netlify.app/ for the `docs-v6` deployment. +You can follow the deployment process [on the Netlify Dashboard](https://app.netlify.com/sites/material-ui-x/deploys?filter=docs-next) +Once deployed, it will be accessible at https://material-ui-x.netlify.app/ for the `docs-next` deployment. ### Publish GitHub release diff --git a/scripts/l10n.ts b/scripts/l10n.ts index 932119f26b745..25e9d2ceaee18 100644 --- a/scripts/l10n.ts +++ b/scripts/l10n.ts @@ -67,7 +67,7 @@ function plugin(existingTranslations: Translations): babel.PluginObj { } // Test if the variable name follows the pattern xxXXGrid or xxXXPickers - if (!/[a-z]{2}[A-Z]{2}(Grid|Pickers)/.test(node.id.name)) { + if (!/[a-z]{2}[A-Z]{2}|[a-z]{2}(Grid|Pickers)/.test(node.id.name)) { visitorPath.skip(); return; } @@ -164,7 +164,7 @@ function extractTranslations(translationsPath: string): [TranslationsByGroup, Tr function findLocales(localesDirectory: string, constantsPath: string) { const items = fse.readdirSync(localesDirectory); const locales: any[] = []; - const localeRegex = /^[a-z]{2}[A-Z]{2}/; + const localeRegex = /^[a-z]{2}[A-Z]{2}|^[a-z]{2}(?=.ts)/; items.forEach((item) => { const match = item.match(localeRegex); @@ -173,7 +173,10 @@ function findLocales(localesDirectory: string, constantsPath: string) { } const localePath = path.resolve(localesDirectory, item); - const code = match[0]; + if (fse.lstatSync(localePath).isDirectory()) { + return; + } + const code = match[0] || match[1]; if (constantsPath !== localePath) { // Ignore the locale used as a reference locales.push([localePath, code]); @@ -321,9 +324,10 @@ const generateDocReport = async ( return; } - const languageTag = `${importName.slice(0, 2).toLowerCase()}-${importName - .slice(2) - .toUpperCase()}`; + const languageTag = + importName.length > 2 + ? `${importName.slice(0, 2).toLowerCase()}-${importName.slice(2).toUpperCase()}` + : importName; const localeName = localeNames[languageTag]; if (localeName === undefined) { diff --git a/scripts/localeNames.js b/scripts/localeNames.js index f989f27b97c57..24ff47a663615 100644 --- a/scripts/localeNames.js +++ b/scripts/localeNames.js @@ -6,6 +6,7 @@ module.exports = { 'hy-AM': 'Armenian', 'az-AZ': 'Azerbaijani', 'bn-BD': 'Bangla', + eu: 'Basque', 'be-BY': 'Belarusian', 'bg-BG': 'Bulgarian', 'ca-ES': 'Catalan', diff --git a/scripts/performance.mjs b/scripts/performance.mjs deleted file mode 100644 index 0a3bb879e59a4..0000000000000 --- a/scripts/performance.mjs +++ /dev/null @@ -1,169 +0,0 @@ -/* eslint-disable no-await-in-loop */ -import yargs from 'yargs'; -import { selectors, chromium } from '@playwright/test'; -import capitalize from 'lodash/capitalize.js'; -import path from 'path'; -import fse from 'fs-extra'; -import { getWorkspaceRoot } from './utils.mjs'; - -const PORT = 5001; - -const snapshotDestPath = path.join(getWorkspaceRoot(), 'performance-snapshot.json'); - -let browser; - -function getMedian(values) { - const length = values.length; - values.sort(); - if (length % 2 === 0) { - return (values[length / 2] + values[length / 2 - 1]) / 2; - } - return values[parseInt(length / 2, 10)]; -} - -function getMean(values) { - const sum = values.reduce((acc, value) => acc + value, 0); - return sum / values.length; -} - -function getStdDev(values, mean) { - const squareDiffs = values.map((value) => { - const diff = value - mean; - return diff * diff; - }); - return Math.sqrt(getMean(squareDiffs)); -} - -function getMeasures(samples) { - const min = Math.min(...samples); - const max = Math.max(...samples); - const median = getMedian(samples); - const mean = getMean(samples); - const stdDev = getStdDev(samples, mean); - return { min, max, median, mean, stdDev, samples }; -} - -function createLabelNameEngine() { - const getControlOfLabel = (label, root) => { - const htmlFor = label.getAttribute('for'); - return root.getElementById(htmlFor); - }; - - return { - query(root, selector) { - const label = Array.from(root.querySelectorAll('label')).find( - (el) => el.innerText.trim() === selector, - ); - return getControlOfLabel(label, root); - }, - queryAll(root, selector) { - const labels = Array.from(root.querySelectorAll('label')).filter( - (el) => el.innerText.trim() === selector, - ); - return labels.map((label) => getControlOfLabel(label, root)); - }, - }; -} - -async function testFilter100kRows(page) { - const baseUrl = `http://localhost:${PORT}/performance/DataGrid/FilterRows100000`; - await page.goto(baseUrl); - - await page.selectOption('label=Columns', 'currencyPair'); - await page.selectOption('label=Operator', 'startsWith'); - const t0 = await page.evaluate(() => performance.now()); - await page.type('label=Value', 'usd'); - - // Ensure that the filter icon is not in the DOM. - await page.waitForSelector('.MuiDataGrid-filterIcon', { state: 'detached' }); - - // We use "attached", instead of the default "visible", because the icon is in the DOM but hidden. - await page.waitForSelector('.MuiDataGrid-filterIcon', { state: 'attached' }); - const t1 = await page.evaluate(() => performance.now()); - - return t1 - t0 - 500; // Subtract the debounce time -} - -async function testSort100kRows(page) { - const baseUrl = `http://localhost:${PORT}/performance/DataGrid/SortRows100000`; - await page.goto(baseUrl); - - const t0 = await page.evaluate(() => performance.now()); - await page.click('text="Currency Pair"'); - const t1 = await page.evaluate(() => performance.now()); - return t1 - t0; -} - -async function testSelect100kRows(page) { - const baseUrl = `http://localhost:${PORT}/performance/DataGrid/SelectRows100000`; - await page.goto(baseUrl); - - const t0 = await page.evaluate(() => performance.now()); - await page.click('[aria-label="Select all rows"]'); - const t1 = await page.evaluate(() => performance.now()); - return t1 - t0; -} - -async function testDeselect100kRows(page) { - const baseUrl = `http://localhost:${PORT}/performance/DataGrid/SelectRows100000`; - await page.goto(baseUrl); - - await page.click('[aria-label="Select all rows"]'); - const t0 = await page.evaluate(() => performance.now()); - await page.click('[aria-label="Unselect all rows"]'); - const t1 = await page.evaluate(() => performance.now()); - return t1 - t0; -} - -async function run() { - await selectors.register('label', createLabelNameEngine); - - browser = await chromium.launch({ - args: ['--font-render-hinting=none'], - headless: false, - }); - - const cases = [testFilter100kRows, testSort100kRows, testSelect100kRows, testDeselect100kRows]; - - const results = cases.map(async (testCase) => { - const samples = []; - - for (let j = 0; j < 5; j += 1) { - const page = await browser.newPage(); - const time = await testCase(page); - await page.close(); - samples.push(time); - } - - const name = capitalize( - testCase.name - .replace(/([A-Z]|[0-9]+)/g, ' $1') - .replace(/^test/, '') - .trim(), - ); - - return [name, getMeasures(samples)]; - }); - - const allResults = await Promise.all(results); - - const snapshot = allResults.reduce((acc, [name, values]) => { - acc[name] = values; - return acc; - }, {}); - - await browser.close(); - - await fse.writeJSON(snapshotDestPath, snapshot, { spaces: 2 }); -} - -yargs(process.argv.slice(2)) - .command({ - command: '$0', - description: 'Runs the performance tests.', - handler: run, - }) - .help() - .strict(true) - .version(false) - .parse(); diff --git a/scripts/releaseChangelog.mjs b/scripts/releaseChangelog.mjs index 5648ebc8b648c..990239b4e19e3 100644 --- a/scripts/releaseChangelog.mjs +++ b/scripts/releaseChangelog.mjs @@ -259,11 +259,11 @@ Same changes as in \`@mui/x-date-pickers@__VERSION__\`${ } ${logChangelogSection(pickersProCommits)} -### Charts / \`@mui/x-charts@__CHARTS_VERSION__\` +### Charts / \`@mui/x-charts@__VERSION__\` ${logChangelogSection(chartsCommits)} -### Tree View / \`@mui/x-tree-view@__TREE_VIEW_VERSION__\` +### Tree View / \`@mui/x-tree-view@__VERSION__\` ${logChangelogSection(treeViewCommits)} ${logChangelogSection(codemodCommits, `### \`@mui/x-codemod@__VERSION__\``)} diff --git a/scripts/x-charts.exports.json b/scripts/x-charts.exports.json index 4415776d8920d..5362b443a672a 100644 --- a/scripts/x-charts.exports.json +++ b/scripts/x-charts.exports.json @@ -16,6 +16,7 @@ { "name": "blueberryTwilightPalette", "kind": "Variable" }, { "name": "blueberryTwilightPaletteDark", "kind": "Variable" }, { "name": "blueberryTwilightPaletteLight", "kind": "Variable" }, + { "name": "CardinalDirections", "kind": "Interface" }, { "name": "CartesianContextProvider", "kind": "Function" }, { "name": "CartesianSeriesType", "kind": "TypeAlias" }, { "name": "ChartContainer", "kind": "Variable" }, @@ -24,15 +25,32 @@ { "name": "ChartsAxisClasses", "kind": "Interface" }, { "name": "ChartsAxisClassKey", "kind": "TypeAlias" }, { "name": "ChartsAxisHighlight", "kind": "Function" }, + { "name": "chartsAxisHighlightClasses", "kind": "Variable" }, + { "name": "ChartsAxisHighlightClasses", "kind": "Interface" }, + { "name": "ChartsAxisHighlightClassKey", "kind": "TypeAlias" }, + { "name": "ChartsAxisHighlightPath", "kind": "Variable" }, { "name": "ChartsAxisHighlightProps", "kind": "TypeAlias" }, { "name": "ChartsAxisProps", "kind": "Interface" }, { "name": "ChartsClipPath", "kind": "Function" }, { "name": "ChartsClipPathProps", "kind": "TypeAlias" }, { "name": "ChartsColorPalette", "kind": "TypeAlias" }, { "name": "ChartsColorPaletteCallback", "kind": "TypeAlias" }, + { "name": "ChartsLegend", "kind": "Function" }, + { "name": "ChartsLegendClasses", "kind": "Interface" }, + { "name": "ChartsLegendClassKey", "kind": "TypeAlias" }, + { "name": "ChartsLegendProps", "kind": "TypeAlias" }, + { "name": "ChartsLegendRoot", "kind": "Variable" }, + { "name": "ChartsLegendRootOwnerState", "kind": "TypeAlias" }, + { "name": "ChartsLegendSlotProps", "kind": "Interface" }, + { "name": "ChartsLegendSlots", "kind": "Interface" }, { "name": "ChartsPieSorting", "kind": "TypeAlias" }, + { "name": "ChartsReferenceLine", "kind": "Function" }, + { "name": "ChartsReferenceLineClasses", "kind": "Interface" }, + { "name": "ChartsReferenceLineClassKey", "kind": "TypeAlias" }, { "name": "ChartsTooltip", "kind": "Function" }, { "name": "ChartsTooltipProps", "kind": "TypeAlias" }, + { "name": "ChartsTooltipSlotProps", "kind": "Interface" }, + { "name": "ChartsTooltipSlots", "kind": "Interface" }, { "name": "ChartsXAxis", "kind": "Function" }, { "name": "ChartsXAxisProps", "kind": "Interface" }, { "name": "ChartsYAxis", "kind": "Function" }, @@ -55,17 +73,22 @@ { "name": "DrawingProvider", "kind": "Function" }, { "name": "FadeOptions", "kind": "TypeAlias" }, { "name": "getAreaElementUtilityClass", "kind": "Function" }, + { "name": "getAxisHighlightUtilityClass", "kind": "Function" }, { "name": "getAxisUtilityClass", "kind": "Function" }, { "name": "getHighlightElementUtilityClass", "kind": "Function" }, + { "name": "getLegendUtilityClass", "kind": "Function" }, { "name": "getLineElementUtilityClass", "kind": "Function" }, { "name": "getMarkElementUtilityClass", "kind": "Function" }, { "name": "getPieArcLabelUtilityClass", "kind": "Function" }, { "name": "getPieArcUtilityClass", "kind": "Function" }, + { "name": "getReferenceLineUtilityClass", "kind": "Function" }, { "name": "getValueToPositionMapper", "kind": "Function" }, { "name": "HighlightElementClassKey", "kind": "TypeAlias" }, { "name": "HighlightOptions", "kind": "TypeAlias" }, { "name": "HighlightScope", "kind": "TypeAlias" }, { "name": "LayoutConfig", "kind": "TypeAlias" }, + { "name": "legendClasses", "kind": "Variable" }, + { "name": "LegendRendererProps", "kind": "Interface" }, { "name": "LineChart", "kind": "Variable" }, { "name": "LineElement", "kind": "Function" }, { "name": "lineElementClasses", "kind": "Variable" }, @@ -103,8 +126,9 @@ { "name": "PiePlot", "kind": "Function" }, { "name": "PieSeriesType", "kind": "Interface" }, { "name": "PieValueType", "kind": "TypeAlias" }, + { "name": "referenceLineClasses", "kind": "Variable" }, { "name": "ResponsiveChartContainer", "kind": "Variable" }, - { "name": "ResponsiveChartContainerProps", "kind": "TypeAlias" }, + { "name": "ResponsiveChartContainerProps", "kind": "Interface" }, { "name": "ScaleName", "kind": "TypeAlias" }, { "name": "Scatter", "kind": "Function" }, { "name": "ScatterChart", "kind": "Variable" }, diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 577959c886326..9bb94136aac7c 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -31,6 +31,7 @@ { "name": "DataGridPremiumProps", "kind": "Interface" }, { "name": "DataGridPro", "kind": "Function" }, { "name": "deDE", "kind": "Variable" }, + { "name": "DEFAULT_GRID_AUTOSIZE_OPTIONS", "kind": "Variable" }, { "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" }, { "name": "ElementSize", "kind": "Interface" }, { "name": "elGR", "kind": "Variable" }, @@ -45,8 +46,8 @@ { "name": "FooterRowCountOverrides", "kind": "Interface" }, { "name": "frFR", "kind": "Variable" }, { "name": "getAggregationFooterRowIdFromGroupId", "kind": "Variable" }, - { "name": "GetApplyFilterFnLegacy", "kind": "TypeAlias" }, - { "name": "GetApplyFilterFnV7", "kind": "TypeAlias" }, + { "name": "GetApplyFilterFn", "kind": "TypeAlias" }, + { "name": "GetApplyQuickFilterFn", "kind": "TypeAlias" }, { "name": "GetColumnForNewFilterArgs", "kind": "Interface" }, { "name": "getDataGridUtilityClass", "kind": "Function" }, { "name": "getDefaultGridFilterModel", "kind": "Variable" }, @@ -107,10 +108,12 @@ { "name": "GridApi", "kind": "TypeAlias" }, { "name": "GridApiCommon", "kind": "Interface" }, { "name": "GridApiContext", "kind": "Variable" }, + { "name": "GridApiPro", "kind": "Interface" }, { "name": "GridArrowDownwardIcon", "kind": "Variable" }, { "name": "GridArrowUpwardIcon", "kind": "Variable" }, { "name": "GridAutoGeneratedGroupNode", "kind": "Interface" }, { "name": "GridAutoGeneratedPinnedRowNode", "kind": "Interface" }, + { "name": "GridAutosizeOptions", "kind": "TypeAlias" }, { "name": "GridBasicGroupNode", "kind": "Interface" }, { "name": "GridBody", "kind": "Function" }, { "name": "GridBooleanCell", "kind": "Variable" }, @@ -209,6 +212,7 @@ { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnResizeApi", "kind": "Interface" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, @@ -257,7 +261,6 @@ { "name": "GridDetailPanelToggleCell", "kind": "Function" }, { "name": "GridDimensions", "kind": "Interface" }, { "name": "GridDimensionsApi", "kind": "Interface" }, - { "name": "GridDisableVirtualizationApi", "kind": "Interface" }, { "name": "GridDragIcon", "kind": "Variable" }, { "name": "GridEditBooleanCell", "kind": "Function" }, { "name": "GridEditBooleanCellProps", "kind": "Interface" }, @@ -430,6 +433,7 @@ { "name": "GridPrintExportMenuItemProps", "kind": "TypeAlias" }, { "name": "GridPrintExportOptions", "kind": "Interface" }, { "name": "GridPrintGetRowsToExportParams", "kind": "Interface" }, + { "name": "GridPrivateApiPro", "kind": "Interface" }, { "name": "GridProIconSlotsComponent", "kind": "Interface" }, { "name": "GridProSlotsComponent", "kind": "Interface" }, { "name": "GridPushPinLeftIcon", "kind": "Variable" }, @@ -571,7 +575,12 @@ { "name": "GridViewColumnIcon", "kind": "Variable" }, { "name": "GridViewHeadlineIcon", "kind": "Variable" }, { "name": "GridViewStreamIcon", "kind": "Variable" }, - { "name": "GridVirtualScrollerApi", "kind": "Interface" }, + { "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" }, { "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, @@ -619,9 +628,6 @@ { "name": "ToolbarPropsOverrides", "kind": "Interface" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, - { "name": "UncapitalizedGridPremiumSlotsComponent", "kind": "Interface" }, - { "name": "UncapitalizedGridProSlotsComponent", "kind": "Interface" }, - { "name": "UncapitalizedGridSlotsComponent", "kind": "Interface" }, { "name": "unstable_gridFocusColumnGroupHeaderSelector", "kind": "Variable" }, { "name": "unstable_gridFocusColumnHeaderFilterSelector", "kind": "Variable" }, { "name": "unstable_gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, @@ -641,8 +647,10 @@ { "name": "useGridNativeEventListener", "kind": "Variable" }, { "name": "useGridRootProps", "kind": "Variable" }, { "name": "useGridSelector", "kind": "Variable" }, + { "name": "useGridVirtualization", "kind": "Function" }, { "name": "useKeepGroupedColumnsHidden", "kind": "Variable" }, { "name": "ValueOptions", "kind": "TypeAlias" }, + { "name": "virtualizationStateInitializer", "kind": "Variable" }, { "name": "viVN", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, { "name": "zhTW", "kind": "Variable" } diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index 819d55f8450c1..397355204662d 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -30,6 +30,7 @@ { "name": "DataGridPro", "kind": "Variable" }, { "name": "DataGridProProps", "kind": "Interface" }, { "name": "deDE", "kind": "Variable" }, + { "name": "DEFAULT_GRID_AUTOSIZE_OPTIONS", "kind": "Variable" }, { "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" }, { "name": "ElementSize", "kind": "Interface" }, { "name": "elGR", "kind": "Variable" }, @@ -43,8 +44,8 @@ { "name": "FooterPropsOverrides", "kind": "Interface" }, { "name": "FooterRowCountOverrides", "kind": "Interface" }, { "name": "frFR", "kind": "Variable" }, - { "name": "GetApplyFilterFnLegacy", "kind": "TypeAlias" }, - { "name": "GetApplyFilterFnV7", "kind": "TypeAlias" }, + { "name": "GetApplyFilterFn", "kind": "TypeAlias" }, + { "name": "GetApplyQuickFilterFn", "kind": "TypeAlias" }, { "name": "GetColumnForNewFilterArgs", "kind": "Interface" }, { "name": "getDataGridUtilityClass", "kind": "Function" }, { "name": "getDefaultGridFilterModel", "kind": "Variable" }, @@ -85,10 +86,12 @@ { "name": "GridApi", "kind": "TypeAlias" }, { "name": "GridApiCommon", "kind": "Interface" }, { "name": "GridApiContext", "kind": "Variable" }, + { "name": "GridApiPro", "kind": "Interface" }, { "name": "GridArrowDownwardIcon", "kind": "Variable" }, { "name": "GridArrowUpwardIcon", "kind": "Variable" }, { "name": "GridAutoGeneratedGroupNode", "kind": "Interface" }, { "name": "GridAutoGeneratedPinnedRowNode", "kind": "Interface" }, + { "name": "GridAutosizeOptions", "kind": "TypeAlias" }, { "name": "GridBasicGroupNode", "kind": "Interface" }, { "name": "GridBody", "kind": "Function" }, { "name": "GridBooleanCell", "kind": "Variable" }, @@ -183,6 +186,7 @@ { "name": "gridColumnReorderDragColSelector", "kind": "Variable" }, { "name": "gridColumnReorderSelector", "kind": "Variable" }, { "name": "GridColumnReorderState", "kind": "Interface" }, + { "name": "GridColumnResizeApi", "kind": "Interface" }, { "name": "GridColumnResizeParams", "kind": "Interface" }, { "name": "gridColumnResizeSelector", "kind": "Variable" }, { "name": "GridColumnResizeState", "kind": "Interface" }, @@ -231,7 +235,6 @@ { "name": "GridDetailPanelToggleCell", "kind": "Function" }, { "name": "GridDimensions", "kind": "Interface" }, { "name": "GridDimensionsApi", "kind": "Interface" }, - { "name": "GridDisableVirtualizationApi", "kind": "Interface" }, { "name": "GridDragIcon", "kind": "Variable" }, { "name": "GridEditBooleanCell", "kind": "Function" }, { "name": "GridEditBooleanCellProps", "kind": "Interface" }, @@ -392,6 +395,7 @@ { "name": "GridPrintExportMenuItemProps", "kind": "TypeAlias" }, { "name": "GridPrintExportOptions", "kind": "Interface" }, { "name": "GridPrintGetRowsToExportParams", "kind": "Interface" }, + { "name": "GridPrivateApiPro", "kind": "Interface" }, { "name": "GridProIconSlotsComponent", "kind": "Interface" }, { "name": "GridProSlotsComponent", "kind": "Interface" }, { "name": "GridPushPinLeftIcon", "kind": "Variable" }, @@ -526,7 +530,12 @@ { "name": "GridViewColumnIcon", "kind": "Variable" }, { "name": "GridViewHeadlineIcon", "kind": "Variable" }, { "name": "GridViewStreamIcon", "kind": "Variable" }, - { "name": "GridVirtualScrollerApi", "kind": "Interface" }, + { "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" }, { "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, @@ -571,8 +580,6 @@ { "name": "ToolbarPropsOverrides", "kind": "Interface" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, - { "name": "UncapitalizedGridProSlotsComponent", "kind": "Interface" }, - { "name": "UncapitalizedGridSlotsComponent", "kind": "Interface" }, { "name": "unstable_gridFocusColumnGroupHeaderSelector", "kind": "Variable" }, { "name": "unstable_gridFocusColumnHeaderFilterSelector", "kind": "Variable" }, { "name": "unstable_gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, @@ -592,7 +599,9 @@ { "name": "useGridNativeEventListener", "kind": "Variable" }, { "name": "useGridRootProps", "kind": "Variable" }, { "name": "useGridSelector", "kind": "Variable" }, + { "name": "useGridVirtualization", "kind": "Function" }, { "name": "ValueOptions", "kind": "TypeAlias" }, + { "name": "virtualizationStateInitializer", "kind": "Variable" }, { "name": "viVN", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, { "name": "zhTW", "kind": "Variable" } diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 32e6c5ec47f07..9d2b801e6023b 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -41,8 +41,8 @@ { "name": "FooterPropsOverrides", "kind": "Interface" }, { "name": "FooterRowCountOverrides", "kind": "Interface" }, { "name": "frFR", "kind": "Variable" }, - { "name": "GetApplyFilterFnLegacy", "kind": "TypeAlias" }, - { "name": "GetApplyFilterFnV7", "kind": "TypeAlias" }, + { "name": "GetApplyFilterFn", "kind": "TypeAlias" }, + { "name": "GetApplyQuickFilterFn", "kind": "TypeAlias" }, { "name": "GetColumnForNewFilterArgs", "kind": "Interface" }, { "name": "getDataGridUtilityClass", "kind": "Function" }, { "name": "getDefaultGridFilterModel", "kind": "Variable" }, @@ -207,7 +207,6 @@ { "name": "gridDensityValueSelector", "kind": "Variable" }, { "name": "GridDimensions", "kind": "Interface" }, { "name": "GridDimensionsApi", "kind": "Interface" }, - { "name": "GridDisableVirtualizationApi", "kind": "Interface" }, { "name": "GridDragIcon", "kind": "Variable" }, { "name": "GridEditBooleanCell", "kind": "Function" }, { "name": "GridEditBooleanCellProps", "kind": "Interface" }, @@ -481,7 +480,12 @@ { "name": "GridViewColumnIcon", "kind": "Variable" }, { "name": "GridViewHeadlineIcon", "kind": "Variable" }, { "name": "GridViewStreamIcon", "kind": "Variable" }, - { "name": "GridVirtualScrollerApi", "kind": "Interface" }, + { "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" }, { "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" }, { "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" }, @@ -525,7 +529,6 @@ { "name": "ToolbarPropsOverrides", "kind": "Interface" }, { "name": "trTR", "kind": "Variable" }, { "name": "ukUA", "kind": "Variable" }, - { "name": "UncapitalizedGridSlotsComponent", "kind": "Interface" }, { "name": "unstable_gridFocusColumnGroupHeaderSelector", "kind": "Variable" }, { "name": "unstable_gridFocusColumnHeaderFilterSelector", "kind": "Variable" }, { "name": "unstable_gridHeaderFilteringEditFieldSelector", "kind": "Variable" }, @@ -545,7 +548,9 @@ { "name": "useGridNativeEventListener", "kind": "Variable" }, { "name": "useGridRootProps", "kind": "Variable" }, { "name": "useGridSelector", "kind": "Variable" }, + { "name": "useGridVirtualization", "kind": "Function" }, { "name": "ValueOptions", "kind": "TypeAlias" }, + { "name": "virtualizationStateInitializer", "kind": "Variable" }, { "name": "viVN", "kind": "Variable" }, { "name": "zhCN", "kind": "Variable" }, { "name": "zhTW", "kind": "Variable" } diff --git a/scripts/x-date-pickers-pro.exports.json b/scripts/x-date-pickers-pro.exports.json index d1a333c4baa12..5002ac2590cdb 100644 --- a/scripts/x-date-pickers-pro.exports.json +++ b/scripts/x-date-pickers-pro.exports.json @@ -10,6 +10,7 @@ { "name": "beBY", "kind": "Variable" }, { "name": "caES", "kind": "Variable" }, { "name": "CalendarIcon", "kind": "Variable" }, + { "name": "ClearIcon", "kind": "Variable" }, { "name": "clockClasses", "kind": "Variable" }, { "name": "ClockClasses", "kind": "Interface" }, { "name": "ClockClassKey", "kind": "TypeAlias" }, @@ -127,6 +128,7 @@ { "name": "elGR", "kind": "Variable" }, { "name": "enUS", "kind": "Variable" }, { "name": "esES", "kind": "Variable" }, + { "name": "eu", "kind": "Variable" }, { "name": "ExportedDateRangeCalendarProps", "kind": "Interface" }, { "name": "ExportedDigitalClockProps", "kind": "Interface" }, { "name": "ExportedMultiSectionDigitalClockSectionProps", "kind": "Interface" }, @@ -339,6 +341,7 @@ { "name": "unstable_useSingleInputTimeRangeField", "kind": "Variable" }, { "name": "unstable_useTimeField", "kind": "Variable" }, { "name": "urPK", "kind": "Variable" }, + { "name": "useClearableField", "kind": "Variable" }, { "name": "UseDateFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseDateFieldDefaultizedProps", "kind": "TypeAlias" }, { "name": "UseDateFieldProps", "kind": "Interface" }, diff --git a/scripts/x-date-pickers.exports.json b/scripts/x-date-pickers.exports.json index 93611304c31db..9b3b709474868 100644 --- a/scripts/x-date-pickers.exports.json +++ b/scripts/x-date-pickers.exports.json @@ -9,6 +9,7 @@ { "name": "beBY", "kind": "Variable" }, { "name": "caES", "kind": "Variable" }, { "name": "CalendarIcon", "kind": "Variable" }, + { "name": "ClearIcon", "kind": "Variable" }, { "name": "clockClasses", "kind": "Variable" }, { "name": "ClockClasses", "kind": "Interface" }, { "name": "ClockClassKey", "kind": "TypeAlias" }, @@ -97,6 +98,7 @@ { "name": "elGR", "kind": "Variable" }, { "name": "enUS", "kind": "Variable" }, { "name": "esES", "kind": "Variable" }, + { "name": "eu", "kind": "Variable" }, { "name": "ExportedDigitalClockProps", "kind": "Interface" }, { "name": "ExportedMultiSectionDigitalClockSectionProps", "kind": "Interface" }, { "name": "ExportedPickersCalendarHeaderProps", "kind": "TypeAlias" }, @@ -265,6 +267,7 @@ { "name": "unstable_useDateTimeField", "kind": "Variable" }, { "name": "unstable_useTimeField", "kind": "Variable" }, { "name": "urPK", "kind": "Variable" }, + { "name": "useClearableField", "kind": "Variable" }, { "name": "UseDateFieldComponentProps", "kind": "TypeAlias" }, { "name": "UseDateFieldDefaultizedProps", "kind": "TypeAlias" }, { "name": "UseDateFieldProps", "kind": "Interface" }, diff --git a/templates/x-data-grid/package.json b/templates/x-data-grid/package.json new file mode 100644 index 0000000000000..569a43b6fb606 --- /dev/null +++ b/templates/x-data-grid/package.json @@ -0,0 +1,24 @@ +{ + "dependencies": { + "@emotion/react": "latest", + "@emotion/styled": "latest", + "@mui/icons-material": "latest", + "@mui/material": "latest", + "@mui/x-data-grid-generator": "latest", + "@mui/x-data-grid": "latest", + "@mui/x-data-grid-pro": "latest", + "@mui/x-data-grid-premium": "latest", + "@types/react": "latest", + "@types/react-dom": "latest", + "react": "latest", + "react-dom": "latest", + "typescript": "latest" + }, + "devDependencies": { + "react-scripts": "latest" + }, + "main": "src/index.tsx", + "scripts": { + "start": "react-scripts start" + } +} diff --git a/templates/x-data-grid/public/index.html b/templates/x-data-grid/public/index.html new file mode 100644 index 0000000000000..607c9c672d56d --- /dev/null +++ b/templates/x-data-grid/public/index.html @@ -0,0 +1,16 @@ + + + + DataGridProDemo demo — MUI X + + + + + + +
                + + diff --git a/templates/x-data-grid/src/demo.tsx b/templates/x-data-grid/src/demo.tsx new file mode 100644 index 0000000000000..10cc6a348fe17 --- /dev/null +++ b/templates/x-data-grid/src/demo.tsx @@ -0,0 +1,24 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { DataGridPro } from '@mui/x-data-grid-pro'; +import { useDemoData } from '@mui/x-data-grid-generator'; + +export default function DataGridProDemo() { + const { data } = useDemoData({ + dataSet: 'Commodity', + rowLength: 100000, + editable: true, + }); + + return ( + + + + ); +} diff --git a/templates/x-data-grid/src/index.tsx b/templates/x-data-grid/src/index.tsx new file mode 100644 index 0000000000000..2aaeca271daa3 --- /dev/null +++ b/templates/x-data-grid/src/index.tsx @@ -0,0 +1,12 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom/client'; +import { StyledEngineProvider } from '@mui/material/styles'; +import Demo from './demo'; + +ReactDOM.createRoot(document.querySelector('#root')).render( + + + + + , +); diff --git a/templates/x-data-grid/template.json b/templates/x-data-grid/template.json new file mode 100644 index 0000000000000..06b9cc58191b3 --- /dev/null +++ b/templates/x-data-grid/template.json @@ -0,0 +1,6 @@ +{ + "title": "MUI X DataGrid template", + "description": "Base template for reporting a bug", + "tags": ["react", "typescript"], + "published": true +} diff --git a/templates/x-data-grid/tsconfig.json b/templates/x-data-grid/tsconfig.json new file mode 100644 index 0000000000000..af10394b4c375 --- /dev/null +++ b/templates/x-data-grid/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + }, + "include": ["src"] +} diff --git a/test/e2e/fixtures/DataGrid/NoRowsOverlayWithButton.tsx b/test/e2e/fixtures/DataGrid/NoRowsOverlayWithButton.tsx index 87c543af1f064..d4b05813e2bdb 100644 --- a/test/e2e/fixtures/DataGrid/NoRowsOverlayWithButton.tsx +++ b/test/e2e/fixtures/DataGrid/NoRowsOverlayWithButton.tsx @@ -16,7 +16,7 @@ function NoRowsOverlay() { export default function NoRowsOverlayWithButton() { return (
                - +
                ); } diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index 0937076ba053c..37a32aa13fff5 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -1,5 +1,15 @@ import { expect } from 'chai'; -import { chromium, webkit, firefox, Page, Browser, BrowserContext } from '@playwright/test'; +import { + chromium, + webkit, + firefox, + Page, + Browser, + BrowserContext, + devices, + BrowserContextOptions, + BrowserType, +} from '@playwright/test'; function sleep(timeoutMS: number): Promise { return new Promise((resolve) => { @@ -65,62 +75,71 @@ async function attemptGoto(page: Page, url: string): Promise { // Pick the new/fake "now" for you test pages. const fakeNow = new Date('2022-04-17T13:37:11').valueOf(); -[chromium, webkit, firefox].forEach((browserType) => { - describe(`e2e: ${browserType.name()}`, () => { - const baseUrl = 'http://localhost:5001'; - let browser: Browser; - let context: BrowserContext; - let page: Page; - - async function renderFixture(fixturePath: string) { - if (page.url().includes(fixturePath)) { - // ensure that the page is reloaded if the it's the same fixture - // ensures that page is reset on firefox - await page.reload(); - } else { - await page.goto(`${baseUrl}/e2e/${fixturePath}#no-dev`); +let browser: Browser; +let context: BrowserContext; +let page: Page; +const baseUrl = 'http://localhost:5001'; + +async function renderFixture(fixturePath: string) { + if (page.url().includes(fixturePath)) { + // ensure that the page is reloaded if the it's the same fixture + // ensures that page is reset on firefox + await page.reload(); + } else { + await page.goto(`${baseUrl}/e2e/${fixturePath}#no-dev`); + } +} + +async function initializeEnvironment( + browserType: BrowserType, + contextOptions?: BrowserContextOptions, +) { + browser = await browserType.launch({ + headless: true, + }); + // eslint-disable-next-line no-console + console.log(`Running on: ${browserType.name()}, version: ${browser.version()}.`); + context = await browser.newContext({ + // ensure consistent date formatting regardless of environment + locale: 'en-US', + ...contextOptions, + }); + // Circle CI has low-performance CPUs. + context.setDefaultTimeout((process.env.CIRCLECI === 'true' ? 4 : 2) * 1000); + page = await context.newPage(); + // taken from: https://github.com/microsoft/playwright/issues/6347#issuecomment-1085850728 + // Update the Date accordingly in your test pages + await page.addInitScript(`{ + // Extend Date constructor to default to fakeNow + Date = class extends Date { + constructor(...args) { + if (args.length === 0) { + super(${fakeNow}); + } else { + super(...args); + } } } + // Override Date.now() to start from fakeNow + const __DateNowOffset = ${fakeNow} - Date.now(); + const __DateNow = Date.now; + Date.now = () => __DateNow() + __DateNowOffset; + }`); + const isServerRunning = await attemptGoto(page, `${baseUrl}#no-dev`); + if (!isServerRunning) { + throw new Error( + `Unable to navigate to ${baseUrl} after multiple attempts. Did you forget to run \`yarn test:e2e:server\` and \`yarn test:e2e:build\`?`, + ); + } + return { browser, context, page }; +} +[chromium, webkit, firefox].forEach((browserType) => { + describe(`e2e: ${browserType.name()}`, () => { before(async function beforeHook() { this.timeout(20000); - browser = await browserType.launch({ - headless: true, - }); - // eslint-disable-next-line no-console - console.log(`Running on: ${browserType.name()}, version: ${browser.version()}.`); - context = await browser.newContext({ - // ensure consistent date formatting regardless or environment - locale: 'en-US', - }); - // Circle CI has low-performance CPUs. - context.setDefaultTimeout((process.env.CIRCLECI === 'true' ? 4 : 2) * 1000); - page = await context.newPage(); - // taken from: https://github.com/microsoft/playwright/issues/6347#issuecomment-1085850728 - // Update the Date accordingly in your test pages - await page.addInitScript(`{ - // Extend Date constructor to default to fakeNow - Date = class extends Date { - constructor(...args) { - if (args.length === 0) { - super(${fakeNow}); - } else { - super(...args); - } - } - } - // Override Date.now() to start from fakeNow - const __DateNowOffset = ${fakeNow} - Date.now(); - const __DateNow = Date.now; - Date.now = () => __DateNow() + __DateNowOffset; - }`); - const isServerRunning = await attemptGoto(page, `${baseUrl}#no-dev`); - if (!isServerRunning) { - throw new Error( - `Unable to navigate to ${baseUrl} after multiple attempts. Did you forget to run \`yarn test:e2e:server\` and \`yarn test:e2e:build\`?`, - ); - } + await initializeEnvironment(browserType); }); after(async () => { @@ -170,7 +189,7 @@ const fakeNow = new Date('2022-04-17T13:37:11').valueOf(); await waitFor(async () => { expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('100'); expect(await page.evaluate(() => document.activeElement?.getAttribute('role'))).to.equal( - 'button', + 'combobox', ); }); await page.keyboard.press('Shift+Tab'); @@ -571,3 +590,37 @@ const fakeNow = new Date('2022-04-17T13:37:11').valueOf(); }); }); }); + +describe('e2e: chromium on Android', () => { + before(async function beforeHook() { + this.timeout(20000); + + await initializeEnvironment(chromium, devices['Pixel 5']); + }); + + after(async () => { + await context.close(); + await browser.close(); + }); + + it('should allow re-selecting value to have the same start and end date', async () => { + await renderFixture('DatePicker/BasicDesktopDateRangePicker'); + + await page.getByRole('textbox', { name: 'Start' }).tap(); + + await page.getByRole('gridcell', { name: '11' }).first().tap(); + await page.getByRole('gridcell', { name: '17' }).first().tap(); + + const toolbarButtons = await page.getByRole('button', { name: /Apr/ }).all(); + expect(await toolbarButtons[0].textContent()).to.equal('Apr 11'); + expect(await toolbarButtons[1].textContent()).to.equal('Apr 17'); + + // tap twice on the same date to select a range within one day + const april11 = page.getByRole('gridcell', { name: '11' }).first(); + await april11.tap(); + await april11.tap(); + + expect(await toolbarButtons[0].textContent()).to.equal('Apr 11'); + expect(await toolbarButtons[1].textContent()).to.equal('Apr 11'); + }); +}); diff --git a/test/karma.tests.js b/test/karma.tests.js index a3e1363088440..c701b7a5916de 100644 --- a/test/karma.tests.js +++ b/test/karma.tests.js @@ -1,5 +1,5 @@ /* eslint-env mocha */ -import '@mui/monorepo/test/utils/setupKarma'; +import '@mui-internal/test-utils/setupKarma'; import './utils/init'; import { createXMochaHooks } from './utils/mochaHooks'; diff --git a/test/performance/TestViewer.js b/test/performance/TestViewer.js deleted file mode 100644 index 785f62db79be7..0000000000000 --- a/test/performance/TestViewer.js +++ /dev/null @@ -1,25 +0,0 @@ -import * as React from 'react'; -import PropTypes from 'prop-types'; - -function TestViewer(props) { - const { children } = props; - // We're simulating `act(() => ReactDOM.render(children))` - // In the end children passive effects should've been flushed. - // React doesn't have any such guarantee outside of `act()` so we're approximating it. - const [ready, setReady] = React.useState(false); - React.useEffect(() => { - setReady(true); - }, []); - - return ( -
                - {children} -
                - ); -} - -TestViewer.propTypes = { - children: PropTypes.node.isRequired, -}; - -export default TestViewer; diff --git a/test/performance/fixtures/DataGrid/FilterRows100000.tsx b/test/performance/fixtures/DataGrid/FilterRows100000.tsx deleted file mode 100644 index 01d95d843585f..0000000000000 --- a/test/performance/fixtures/DataGrid/FilterRows100000.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from 'react'; -import { DataGridPro, GridPreferencePanelsValue } from '@mui/x-data-grid-pro'; -import { useBasicDemoData } from '@mui/x-data-grid-generator'; - -export default function FilterRows100000() { - const data = useBasicDemoData(100000, 10); - return ( -
                - -
                - ); -} diff --git a/test/performance/fixtures/DataGrid/SelectRows100000.tsx b/test/performance/fixtures/DataGrid/SelectRows100000.tsx deleted file mode 100644 index df6e35e028f2d..0000000000000 --- a/test/performance/fixtures/DataGrid/SelectRows100000.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useBasicDemoData } from '@mui/x-data-grid-generator'; - -export default function SelectRows100000() { - const data = useBasicDemoData(100000, 10); - return ( -
                - -
                - ); -} diff --git a/test/performance/fixtures/DataGrid/SortRows100000.tsx b/test/performance/fixtures/DataGrid/SortRows100000.tsx deleted file mode 100644 index d53b4e3c56fef..0000000000000 --- a/test/performance/fixtures/DataGrid/SortRows100000.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import * as React from 'react'; -import { DataGridPro } from '@mui/x-data-grid-pro'; -import { useBasicDemoData } from '@mui/x-data-grid-generator'; - -export default function SortRows100000() { - const data = useBasicDemoData(100000, 10); - return ( -
                - -
                - ); -} diff --git a/test/performance/index.js b/test/performance/index.js deleted file mode 100644 index 3af25b31f3d66..0000000000000 --- a/test/performance/index.js +++ /dev/null @@ -1,103 +0,0 @@ -import * as React from 'react'; -import * as ReactDOM from 'react-dom/client'; -import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; -import TestViewer from './TestViewer'; - -const fixtures = []; - -const requireFixtures = require.context('./fixtures', true, /\.(js|ts|tsx)$/); -requireFixtures.keys().forEach((path) => { - // require.context contains paths for module alias imports and relative imports - if (!path.startsWith('.')) { - return; - } - const [suite, name] = path - .replace('./', '') - .replace(/\.\w+$/, '') - .split('/'); - fixtures.push({ - path, - suite: `performance/${suite}`, - name, - Component: requireFixtures(path).default, - }); -}); - -function App() { - function computeIsDev() { - if (window.location.hash === '#dev') { - return true; - } - if (window.location.hash === '#no-dev') { - return false; - } - return process.env.NODE_ENV === 'development'; - } - const [isDev, setDev] = React.useState(computeIsDev); - React.useEffect(() => { - function handleHashChange() { - setDev(computeIsDev()); - } - window.addEventListener('hashchange', handleHashChange); - - return () => { - window.removeEventListener('hashchange', handleHashChange); - }; - }, []); - - function computePath(fixture) { - return `/${fixture.suite}/${fixture.name}`; - } - - return ( - - - {fixtures.map((fixture) => { - const path = computePath(fixture); - const FixtureComponent = fixture.Component; - if (FixtureComponent === undefined) { - console.warn('Missing `Component` ', fixture); - return null; - } - - return ( - - - - } - /> - ); - })} - - - - ); -} - -ReactDOM.createRoot(document.getElementById('react-root')).render(); diff --git a/test/performance/serve.json b/test/performance/serve.json deleted file mode 100644 index ef9da9b562af5..0000000000000 --- a/test/performance/serve.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "public": "build", - "rewrites": [{ "source": "**", "destination": "index.html" }] -} diff --git a/test/performance/template.html b/test/performance/template.html deleted file mode 100644 index c5dfc8038d826..0000000000000 --- a/test/performance/template.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - Performance test - - - - - -
                - - diff --git a/test/performance/webpack.config.js b/test/performance/webpack.config.js deleted file mode 100644 index f292fe8e941a5..0000000000000 --- a/test/performance/webpack.config.js +++ /dev/null @@ -1,35 +0,0 @@ -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const path = require('path'); -const webpackBaseConfig = require('../../webpackBaseConfig'); - -module.exports = { - ...webpackBaseConfig, - entry: path.resolve(__dirname, 'index.js'), - mode: process.env.NODE_ENV || 'development', - optimization: { - // Helps debugging and build perf. - // Bundle size is irrelevant for local serving - minimize: false, - }, - output: { - path: path.resolve(__dirname, './build'), - publicPath: '/', - filename: 'tests.js', - }, - plugins: [ - new HtmlWebpackPlugin({ - template: path.resolve(__dirname, './template.html'), - }), - ], - module: { - ...webpackBaseConfig.module, - rules: webpackBaseConfig.module.rules.concat([ - { - test: /\.(jpg|gif|png)$/, - loader: 'url-loader', - }, - ]), - }, - // TODO: 'browserslist:modern' - target: 'web', -}; diff --git a/test/regressions/index.js b/test/regressions/index.js index 52be197c53419..8c15137e78e93 100644 --- a/test/regressions/index.js +++ b/test/regressions/index.js @@ -4,13 +4,17 @@ import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom'; import { LicenseInfo } from '@mui/x-license-pro'; import TestViewer from 'test/regressions/TestViewer'; import { useFakeTimers } from 'sinon'; - +import { Globals } from '@react-spring/web'; // This license key is only valid for use with Material UI SAS's projects // See the terms: https://mui.com/r/x-license-eula LicenseInfo.setLicenseKey( 'd483a722e0dc68f4d483487da0ccac45Tz1NVUktRG9jLEU9MTcxNTE2MzgwOTMwNyxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=', ); +Globals.assign({ + skipAnimation: true, +}); + const blacklist = [ /^docs-(.*)(?<=NoSnap)\.png$/, // Excludes demos that we don't want 'docs-data-grid-filtering/RemoveBuiltInOperators.png', // Needs interaction @@ -53,7 +57,7 @@ function excludeTest(suite, name) { } // Also use some of the demos to avoid code duplication. -const requireDocs = require.context('docsx/data', true, /js$/); +const requireDocs = require.context('docsx/data', true, /\.js$/); const tests = requireDocs.keys().reduce((res, path) => { const [name, ...suiteArray] = path.replace('./', '').replace('.js', '').split('/').reverse(); const suite = `docs-${suiteArray.reverse().join('-')}`; diff --git a/test/regressions/index.test.js b/test/regressions/index.test.js index 589858f752d39..7e8a390d1cbe8 100644 --- a/test/regressions/index.test.js +++ b/test/regressions/index.test.js @@ -101,27 +101,15 @@ async function main() { // Move cursor offscreen to not trigger unwanted hover effects. page.mouse.move(0, 0); - const pathsToNotWaitForFlagCDN = [ - '/docs-data-grid-filtering/HeaderFilteringDataGridPro', // No flag column - '/docs-data-grid-filtering/CustomHeaderFilterDataGridPro', // No flag column - '/docs-data-grid-filtering/CustomHeaderFilterSingleDataGridPro', // No flag column - '/docs-data-grid-filtering/SimpleHeaderFilteringDataGridPro', // No flag column - '/docs-data-grid-filtering/ServerFilterGrid', // No content rendered - '/docs-data-grid-filtering/CustomMultiValueOperator', // No content rendered - '/docs-data-grid-filtering/QuickFilteringInitialize', // No content rendered - '/docs-data-grid-sorting/FullyCustomSortComparator', // No flag column - '/docs-data-grid-sorting/ServerSortingGrid', // No flag column - '/docs-data-grid-filtering/QuickFilteringExcludeHiddenColumns', // No flag column - ]; - - if ( - /^\/docs-data-grid-(filtering|sorting)/.test(pathURL) && - !pathsToNotWaitForFlagCDN.includes(pathURL) - ) { - // Wait for the flags to load - await page.waitForResponse((response) => - response.url().startsWith('https://flagcdn.com'), - ); + // Wait for the flags to load + await page.waitForFunction(() => { + const images = Array.from(document.querySelectorAll('img')); + return images.every((img) => img.complete); + }); + + if (/^\docs-charts-.*/.test(pathURL)) { + // Run one tick of the clock to get the final animation state + await sleep(10); } const screenshotPath = path.resolve(screenshotDir, `${route.replace(baseUrl, '.')}.png`); diff --git a/test/regressions/webpack.config.js b/test/regressions/webpack.config.js index d5d7746e3dac9..5ed03d88068ee 100644 --- a/test/regressions/webpack.config.js +++ b/test/regressions/webpack.config.js @@ -44,6 +44,14 @@ module.exports = { }, resolve: { ...webpackBaseConfig.resolve, + fallback: { + // Exclude polyfill and treat 'fs' as an empty module since it is not required. next -> gzip-size relies on it. + fs: false, + // needed by enzyme > cheerio + stream: false, + // Exclude polyfill and treat 'zlib' as an empty module since it is not required. next -> gzip-size relies on it. + zlib: false, + }, alias: { ...webpackBaseConfig.resolve.alias, docs: false, // Disable this alias as it creates a circular resolution loop with the docsx alias diff --git a/test/utils/helperFn.ts b/test/utils/helperFn.ts index 7216af55c17b0..0c72d6fcc4212 100644 --- a/test/utils/helperFn.ts +++ b/test/utils/helperFn.ts @@ -1,5 +1,5 @@ import { spy } from 'sinon'; -import { act } from '@mui/monorepo/test/utils'; +import { act } from '@mui-internal/test-utils'; import { unwrapPrivateAPI } from '@mui/x-data-grid/internals'; import type { GridApiCommon } from '@mui/x-data-grid/models/api/gridApiCommon'; @@ -12,7 +12,7 @@ export function sleep(duration: number): Promise { } export function microtasks() { - return act(() => Promise.resolve()); + return act(() => Promise.resolve()) as unknown as Promise; } export function spyApi(api: GridApiCommon, methodName: string) { diff --git a/test/utils/init.ts b/test/utils/init.ts index 59ebefd4b81c1..9691ca052c837 100644 --- a/test/utils/init.ts +++ b/test/utils/init.ts @@ -1,4 +1,4 @@ -import '@mui/monorepo/test/utils/init'; +import '@mui-internal/test-utils/init'; import './licenseRelease'; import './addChaiAssertions'; import './setupPickers'; diff --git a/test/utils/pickers/calendar.ts b/test/utils/pickers/calendar.ts index 5074cf9f9d910..00d90d8d50595 100644 --- a/test/utils/pickers/calendar.ts +++ b/test/utils/pickers/calendar.ts @@ -1,4 +1,4 @@ -import { fireEvent, createEvent } from '@mui/monorepo/test/utils'; +import { fireEvent, createEvent } from '@mui-internal/test-utils'; export const rangeCalendarDayTouches = { '2018-01-01': { diff --git a/test/utils/pickers/clock.ts b/test/utils/pickers/clock.ts index e14c7cdad5fa4..c2f00bcc1f3a8 100644 --- a/test/utils/pickers/clock.ts +++ b/test/utils/pickers/clock.ts @@ -1,6 +1,6 @@ import { CLOCK_WIDTH } from '@mui/x-date-pickers/TimeClock/shared'; import { clockPointerClasses } from '@mui/x-date-pickers/TimeClock'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; export const getClockTouchEvent = ( value: number | string, diff --git a/test/utils/pickers/createPickerRenderer.tsx b/test/utils/pickers/createPickerRenderer.tsx index 16c7b5f5d2ae6..7d13e994f8cd6 100644 --- a/test/utils/pickers/createPickerRenderer.tsx +++ b/test/utils/pickers/createPickerRenderer.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; -import { createRenderer, CreateRendererOptions, RenderOptions } from '@mui/monorepo/test/utils'; +import { createRenderer, CreateRendererOptions, RenderOptions } from '@mui-internal/test-utils'; import { AdapterClassToUse, AdapterName, adapterToUse, availableAdapters } from './adapters'; interface CreatePickerRendererOptions extends CreateRendererOptions { diff --git a/packages/x-date-pickers/src/tests/describe.types.ts b/test/utils/pickers/describe.types.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describe.types.ts rename to test/utils/pickers/describe.types.ts diff --git a/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts b/test/utils/pickers/describeAdapters/describeAdapters.ts similarity index 96% rename from packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts rename to test/utils/pickers/describeAdapters/describeAdapters.ts index 432587949b4f3..c60e409274563 100644 --- a/packages/x-date-pickers/src/tests/describeAdapters/describeAdapters.ts +++ b/test/utils/pickers/describeAdapters/describeAdapters.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import moment from 'moment'; import momentTZ from 'moment-timezone'; -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { AdapterName, buildFieldInteractions, diff --git a/packages/x-date-pickers/src/tests/describeAdapters/index.ts b/test/utils/pickers/describeAdapters/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeAdapters/index.ts rename to test/utils/pickers/describeAdapters/index.ts diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.ts b/test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.ts similarity index 96% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.ts rename to test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.ts index 2f6d7ed0e3b2c..bf658a29826ed 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.ts +++ b/test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.ts @@ -1,4 +1,4 @@ -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { MuiPickersAdapter } from '@mui/x-date-pickers'; import { testCalculations } from './testCalculations'; import { testLocalization } from './testLocalization'; diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts b/test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.types.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.types.ts rename to test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.types.ts diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.utils.ts b/test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.utils.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/describeGregorianAdapter.utils.ts rename to test/utils/pickers/describeGregorianAdapter/describeGregorianAdapter.utils.ts diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/index.ts b/test/utils/pickers/describeGregorianAdapter/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/index.ts rename to test/utils/pickers/describeGregorianAdapter/index.ts diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts b/test/utils/pickers/describeGregorianAdapter/testCalculations.ts similarity index 99% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts rename to test/utils/pickers/describeGregorianAdapter/testCalculations.ts index d8d56cbd4a58b..00969e3f0d85b 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testCalculations.ts +++ b/test/utils/pickers/describeGregorianAdapter/testCalculations.ts @@ -169,6 +169,14 @@ export const testCalculations: DescribeGregorianAdapterTestSuite = ({ setDefaultTimezone(undefined); }); + it('should not mix Europe/London and UTC in winter', () => { + if (!adapter.isTimezoneCompatible) { + return; + } + const dateWithZone = adapter.dateWithTimezone('2023-10-30T11:44:00.000Z', 'Europe/London'); + expect(adapter.getTimezone(dateWithZone)).to.equal('Europe/London'); + }); + it('Method: setTimezone', () => { if (adapter.isTimezoneCompatible) { const test = (timezone: PickersTimezone) => { diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testFormat.ts b/test/utils/pickers/describeGregorianAdapter/testFormat.ts similarity index 80% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/testFormat.ts rename to test/utils/pickers/describeGregorianAdapter/testFormat.ts index a4704990456ba..5239f944a652a 100644 --- a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testFormat.ts +++ b/test/utils/pickers/describeGregorianAdapter/testFormat.ts @@ -2,6 +2,17 @@ import { expect } from 'chai'; import { AdapterFormats } from '@mui/x-date-pickers/models'; import { DescribeGregorianAdapterTestSuite } from './describeGregorianAdapter.types'; +const expectedWeekdayShortFormat = (adapterLib: string) => { + switch (adapterLib) { + case 'luxon': + return 'W'; + case 'moment': + return 'Wed'; + default: + return 'We'; + } +}; + export const testFormat: DescribeGregorianAdapterTestSuite = ({ adapter }) => { const expectDate = (format: keyof AdapterFormats, expected: string) => { const date = adapter.date('2020-01-01T23:44:00.000Z')!; @@ -18,7 +29,7 @@ export const testFormat: DescribeGregorianAdapterTestSuite = ({ adapter }) => { expectDate('month', 'January'); expectDate('monthAndDate', 'January 1'); expectDate('weekday', 'Wednesday'); - expectDate('weekdayShort', 'Wed'); + expectDate('weekdayShort', expectedWeekdayShortFormat(adapter.lib)); expectDate('dayOfMonth', '1'); expectDate('fullTime12h', '11:44 PM'); expectDate('fullTime24h', '23:44'); diff --git a/packages/x-date-pickers/src/tests/describeGregorianAdapter/testLocalization.ts b/test/utils/pickers/describeGregorianAdapter/testLocalization.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeGregorianAdapter/testLocalization.ts rename to test/utils/pickers/describeGregorianAdapter/testLocalization.ts diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts b/test/utils/pickers/describeHijriAdapter/describeHijriAdapter.ts similarity index 92% rename from packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts rename to test/utils/pickers/describeHijriAdapter/describeHijriAdapter.ts index 199c178b0ad12..4aacc9ced531c 100644 --- a/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.ts +++ b/test/utils/pickers/describeHijriAdapter/describeHijriAdapter.ts @@ -1,4 +1,4 @@ -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { MuiPickersAdapter } from '@mui/x-date-pickers'; import { testCalculations } from './testCalculations'; import { testLocalization } from './testLocalization'; diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts b/test/utils/pickers/describeHijriAdapter/describeHijriAdapter.types.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeHijriAdapter/describeHijriAdapter.types.ts rename to test/utils/pickers/describeHijriAdapter/describeHijriAdapter.types.ts diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts b/test/utils/pickers/describeHijriAdapter/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeHijriAdapter/index.ts rename to test/utils/pickers/describeHijriAdapter/index.ts diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts b/test/utils/pickers/describeHijriAdapter/testCalculations.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeHijriAdapter/testCalculations.ts rename to test/utils/pickers/describeHijriAdapter/testCalculations.ts diff --git a/packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts b/test/utils/pickers/describeHijriAdapter/testLocalization.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeHijriAdapter/testLocalization.ts rename to test/utils/pickers/describeHijriAdapter/testLocalization.ts diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts b/test/utils/pickers/describeJalaliAdapter/describeJalaliAdapter.ts similarity index 92% rename from packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts rename to test/utils/pickers/describeJalaliAdapter/describeJalaliAdapter.ts index fd7b4edd223f8..a1e7112457970 100644 --- a/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.ts +++ b/test/utils/pickers/describeJalaliAdapter/describeJalaliAdapter.ts @@ -1,4 +1,4 @@ -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { MuiPickersAdapter } from '@mui/x-date-pickers'; import { testCalculations } from './testCalculations'; import { testLocalization } from './testLocalization'; diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts b/test/utils/pickers/describeJalaliAdapter/describeJalaliAdapter.types.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeJalaliAdapter/describeJalaliAdapter.types.ts rename to test/utils/pickers/describeJalaliAdapter/describeJalaliAdapter.types.ts diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts b/test/utils/pickers/describeJalaliAdapter/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeJalaliAdapter/index.ts rename to test/utils/pickers/describeJalaliAdapter/index.ts diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts b/test/utils/pickers/describeJalaliAdapter/testCalculations.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeJalaliAdapter/testCalculations.ts rename to test/utils/pickers/describeJalaliAdapter/testCalculations.ts diff --git a/packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts b/test/utils/pickers/describeJalaliAdapter/testLocalization.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeJalaliAdapter/testLocalization.ts rename to test/utils/pickers/describeJalaliAdapter/testLocalization.ts diff --git a/packages/x-date-pickers/src/tests/describePicker/describePicker.tsx b/test/utils/pickers/describePicker/describePicker.tsx similarity index 98% rename from packages/x-date-pickers/src/tests/describePicker/describePicker.tsx rename to test/utils/pickers/describePicker/describePicker.tsx index 418b003a4cea3..d535c1140844d 100644 --- a/packages/x-date-pickers/src/tests/describePicker/describePicker.tsx +++ b/test/utils/pickers/describePicker/describePicker.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, fireEvent, createDescribe } from '@mui/monorepo/test/utils'; +import { screen, fireEvent, createDescribe } from '@mui-internal/test-utils'; import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon'; import { DescribePickerOptions } from './describePicker.types'; diff --git a/packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts b/test/utils/pickers/describePicker/describePicker.types.ts similarity index 80% rename from packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts rename to test/utils/pickers/describePicker/describePicker.types.ts index 8b6beec38800f..0d620ec32abc8 100644 --- a/packages/x-date-pickers/src/tests/describePicker/describePicker.types.ts +++ b/test/utils/pickers/describePicker/describePicker.types.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { MuiRenderResult } from '@mui/monorepo/test/utils/createRenderer'; +import { MuiRenderResult } from '@mui-internal/test-utils/createRenderer'; export interface DescribePickerOptions { fieldType: 'single-input' | 'multi-input'; diff --git a/packages/x-date-pickers/src/tests/describePicker/index.ts b/test/utils/pickers/describePicker/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describePicker/index.ts rename to test/utils/pickers/describePicker/index.ts diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.tsx b/test/utils/pickers/describeRangeValidation/describeRangeValidation.tsx similarity index 95% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.tsx rename to test/utils/pickers/describeRangeValidation/describeRangeValidation.tsx index 0220265975d02..c54414e161f6c 100644 --- a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.tsx +++ b/test/utils/pickers/describeRangeValidation/describeRangeValidation.tsx @@ -1,6 +1,6 @@ /* eslint-env mocha */ import * as React from 'react'; -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { testDayViewRangeValidation } from './testDayViewRangeValidation'; import { testTextFieldRangeValidation } from './testTextFieldRangeValidation'; import { DescribeRangeValidationInputOptions } from './describeRangeValidation.types'; diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.types.ts b/test/utils/pickers/describeRangeValidation/describeRangeValidation.types.ts similarity index 84% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.types.ts rename to test/utils/pickers/describeRangeValidation/describeRangeValidation.types.ts index a0fdb5426652e..415161e408d5d 100644 --- a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/describeRangeValidation.types.ts +++ b/test/utils/pickers/describeRangeValidation/describeRangeValidation.types.ts @@ -1,8 +1,5 @@ import * as React from 'react'; -import { - DescribeValidationInputOptions, - DescribeValidationOptions, -} from '@mui/x-date-pickers/tests/describeValidation'; +import { DescribeValidationInputOptions, DescribeValidationOptions } from '../describeValidation'; interface DescribeRangeValidationKeyboardOptions { inputValue?: ( diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/index.ts b/test/utils/pickers/describeRangeValidation/index.ts similarity index 100% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/index.ts rename to test/utils/pickers/describeRangeValidation/index.ts diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testDayViewRangeValidation.tsx b/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx similarity index 99% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/testDayViewRangeValidation.tsx rename to test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx index 32c43da0f63a1..febd79849cb48 100644 --- a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testDayViewRangeValidation.tsx +++ b/test/utils/pickers/describeRangeValidation/testDayViewRangeValidation.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; const isDisable = (el: HTMLElement) => el.getAttribute('disabled') !== null; diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx b/test/utils/pickers/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx similarity index 98% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx rename to test/utils/pickers/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx index 8224126c8b495..3399602ea1db2 100644 --- a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx +++ b/test/utils/pickers/describeRangeValidation/testTextFieldKeyboardRangeValidation.tsx @@ -1,9 +1,9 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; -import { act } from '@mui/monorepo/test/utils/createRenderer'; +import { act } from '@mui-internal/test-utils/createRenderer'; import { DescribeRangeValidationTestSuite } from './describeRangeValidation.types'; const testInvalidStatus = (expectedAnswer: boolean[], isSingleInput?: boolean) => { diff --git a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldRangeValidation.tsx b/test/utils/pickers/describeRangeValidation/testTextFieldRangeValidation.tsx similarity index 99% rename from packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldRangeValidation.tsx rename to test/utils/pickers/describeRangeValidation/testTextFieldRangeValidation.tsx index b550ed40efa1b..83a2844b4cc77 100644 --- a/packages/x-date-pickers-pro/src/tests/describeRangeValidation/testTextFieldRangeValidation.tsx +++ b/test/utils/pickers/describeRangeValidation/testTextFieldRangeValidation.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeRangeValidationTestSuite } from './describeRangeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/describeValidation.ts b/test/utils/pickers/describeValidation/describeValidation.ts similarity index 95% rename from packages/x-date-pickers/src/tests/describeValidation/describeValidation.ts rename to test/utils/pickers/describeValidation/describeValidation.ts index 24c37acbda448..2804f418ee9cc 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/describeValidation.ts +++ b/test/utils/pickers/describeValidation/describeValidation.ts @@ -1,6 +1,6 @@ /* eslint-env mocha */ import * as React from 'react'; -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { testDayViewValidation } from './testDayViewValidation'; import { testMonthViewValidation } from './testMonthViewValidation'; import { testTextFieldValidation } from './testTextFieldValidation'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/describeValidation.types.ts b/test/utils/pickers/describeValidation/describeValidation.types.ts similarity index 89% rename from packages/x-date-pickers/src/tests/describeValidation/describeValidation.types.ts rename to test/utils/pickers/describeValidation/describeValidation.types.ts index 49c5a59557baf..8c6c60dea047f 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/describeValidation.types.ts +++ b/test/utils/pickers/describeValidation/describeValidation.types.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { MuiRenderResult, createRenderer } from '@mui/monorepo/test/utils/createRenderer'; +import { MuiRenderResult, createRenderer } from '@mui-internal/test-utils/createRenderer'; import { DateOrTimeView } from '@mui/x-date-pickers/models'; import { PickerComponentFamily } from '../describe.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/index.ts b/test/utils/pickers/describeValidation/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeValidation/index.ts rename to test/utils/pickers/describeValidation/index.ts diff --git a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx b/test/utils/pickers/describeValidation/testDayViewValidation.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx rename to test/utils/pickers/describeValidation/testDayViewValidation.tsx index 30bd53274706c..074e32fa6c3c1 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testDayViewValidation.tsx +++ b/test/utils/pickers/describeValidation/testDayViewValidation.tsx @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as React from 'react'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeValidationTestSuite } from './describeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx b/test/utils/pickers/describeValidation/testMinutesViewValidation.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx rename to test/utils/pickers/describeValidation/testMinutesViewValidation.tsx index 647b609530c45..0bd0a4d01cbe5 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testMinutesViewValidation.tsx +++ b/test/utils/pickers/describeValidation/testMinutesViewValidation.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeValidationTestSuite } from './describeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx b/test/utils/pickers/describeValidation/testMonthViewValidation.tsx similarity index 98% rename from packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx rename to test/utils/pickers/describeValidation/testMonthViewValidation.tsx index 31df27359c558..1e7896e371fb6 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testMonthViewValidation.tsx +++ b/test/utils/pickers/describeValidation/testMonthViewValidation.tsx @@ -1,6 +1,6 @@ import { expect } from 'chai'; import * as React from 'react'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeValidationTestSuite } from './describeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx b/test/utils/pickers/describeValidation/testTextFieldValidation.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx rename to test/utils/pickers/describeValidation/testTextFieldValidation.tsx index 432b2ef51b6f8..15557750bd2f8 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testTextFieldValidation.tsx +++ b/test/utils/pickers/describeValidation/testTextFieldValidation.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { TimeView } from '@mui/x-date-pickers/models'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeValidationTestSuite } from './describeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx b/test/utils/pickers/describeValidation/testYearViewValidation.tsx similarity index 98% rename from packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx rename to test/utils/pickers/describeValidation/testYearViewValidation.tsx index d5f9e647e41f9..4e94df8f10192 100644 --- a/packages/x-date-pickers/src/tests/describeValidation/testYearViewValidation.tsx +++ b/test/utils/pickers/describeValidation/testYearViewValidation.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; import { expect } from 'chai'; -import { screen } from '@mui/monorepo/test/utils'; +import { screen } from '@mui-internal/test-utils'; import { adapterToUse } from 'test/utils/pickers'; import { DescribeValidationTestSuite } from './describeValidation.types'; diff --git a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx b/test/utils/pickers/describeValue/describeValue.tsx similarity index 97% rename from packages/x-date-pickers/src/tests/describeValue/describeValue.tsx rename to test/utils/pickers/describeValue/describeValue.tsx index 9c371cc03922f..bf4e8f4240ccb 100644 --- a/packages/x-date-pickers/src/tests/describeValue/describeValue.tsx +++ b/test/utils/pickers/describeValue/describeValue.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import createDescribe from '@mui/monorepo/test/utils/createDescribe'; +import createDescribe from '@mui-internal/test-utils/createDescribe'; import { BasePickerInputProps, UsePickerValueNonStaticProps } from '@mui/x-date-pickers/internals'; import { FieldSection } from '@mui/x-date-pickers/models'; import { buildFieldInteractions, BuildFieldInteractionsResponse } from 'test/utils/pickers'; diff --git a/packages/x-date-pickers/src/tests/describeValue/describeValue.types.ts b/test/utils/pickers/describeValue/describeValue.types.ts similarity index 93% rename from packages/x-date-pickers/src/tests/describeValue/describeValue.types.ts rename to test/utils/pickers/describeValue/describeValue.types.ts index f7eef83e131bc..b728d5a61cf99 100644 --- a/packages/x-date-pickers/src/tests/describeValue/describeValue.types.ts +++ b/test/utils/pickers/describeValue/describeValue.types.ts @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, MuiRenderResult } from '@mui/monorepo/test/utils/createRenderer'; +import { createRenderer, MuiRenderResult } from '@mui-internal/test-utils/createRenderer'; import { BuildFieldInteractionsResponse, FieldSectionSelector, diff --git a/packages/x-date-pickers/src/tests/describeValue/index.ts b/test/utils/pickers/describeValue/index.ts similarity index 100% rename from packages/x-date-pickers/src/tests/describeValue/index.ts rename to test/utils/pickers/describeValue/index.ts diff --git a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx b/test/utils/pickers/describeValue/testControlledUnControlled.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx rename to test/utils/pickers/describeValue/testControlledUnControlled.tsx index b7d7be538fc51..70a04814db846 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testControlledUnControlled.tsx +++ b/test/utils/pickers/describeValue/testControlledUnControlled.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, act, userEvent } from '@mui/monorepo/test/utils'; +import { screen, act, userEvent } from '@mui-internal/test-utils'; import { inputBaseClasses } from '@mui/material/InputBase'; import { getExpectedOnChangeCount } from 'test/utils/pickers'; import { DescribeValueOptions, DescribeValueTestSuite } from './describeValue.types'; diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx b/test/utils/pickers/describeValue/testPickerActionBar.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx rename to test/utils/pickers/describeValue/testPickerActionBar.tsx index 225692e064d12..cf47839de4d2f 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerActionBar.tsx +++ b/test/utils/pickers/describeValue/testPickerActionBar.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { adapterToUse, getExpectedOnChangeCount, diff --git a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx b/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx similarity index 99% rename from packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx rename to test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx index 4ca193922b13b..ee4810988742f 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testPickerOpenCloseLifeCycle.tsx +++ b/test/utils/pickers/describeValue/testPickerOpenCloseLifeCycle.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; import { getExpectedOnChangeCount, getTextbox, openPicker } from 'test/utils/pickers'; import { DescribeValueTestSuite } from './describeValue.types'; diff --git a/packages/x-date-pickers/src/tests/describeValue/testShortcuts.tsx b/test/utils/pickers/describeValue/testShortcuts.tsx similarity index 98% rename from packages/x-date-pickers/src/tests/describeValue/testShortcuts.tsx rename to test/utils/pickers/describeValue/testShortcuts.tsx index e3e5fad5b77f3..bf19d543468e0 100644 --- a/packages/x-date-pickers/src/tests/describeValue/testShortcuts.tsx +++ b/test/utils/pickers/describeValue/testShortcuts.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { expect } from 'chai'; import { spy } from 'sinon'; import { expectPickerChangeHandlerValue } from 'test/utils/pickers'; -import { userEvent, screen } from '@mui/monorepo/test/utils'; +import { userEvent, screen } from '@mui-internal/test-utils'; import { DescribeValueTestSuite } from './describeValue.types'; export const testShortcuts: DescribeValueTestSuite = (ElementToTest, options) => { diff --git a/test/utils/pickers/fields.tsx b/test/utils/pickers/fields.tsx index af0004e863625..04b1e1c276841 100644 --- a/test/utils/pickers/fields.tsx +++ b/test/utils/pickers/fields.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { createRenderer, screen, userEvent, act, fireEvent } from '@mui/monorepo/test/utils'; +import { createRenderer, screen, userEvent, act, fireEvent } from '@mui-internal/test-utils'; import { FieldRef, FieldSection, FieldSectionType } from '@mui/x-date-pickers/models'; import { expectInputValue } from './assertions'; @@ -174,7 +174,7 @@ export const buildFieldInteractions =

                ({ return { clickOnInput, testFieldKeyPress, testFieldChange, renderWithProps }; }; -export const cleanText = (text, specialCase?: 'singleDigit' | 'RTL') => { +export const cleanText = (text: string, specialCase?: 'singleDigit' | 'RTL') => { const clean = text.replace(/\u202f/g, ' '); switch (specialCase) { case 'singleDigit': diff --git a/test/utils/pickers/index.ts b/test/utils/pickers/index.ts index 8c29c7719668f..3016a7488f713 100644 --- a/test/utils/pickers/index.ts +++ b/test/utils/pickers/index.ts @@ -7,3 +7,11 @@ export * from './fields'; export * from './misc'; export * from './openPicker'; export * from './viewHandlers'; +export * from './describeAdapters'; +export * from './describeGregorianAdapter'; +export * from './describeHijriAdapter'; +export * from './describeJalaliAdapter'; +export * from './describePicker'; +export * from './describeValue'; +export * from './describeValidation'; +export * from './describeRangeValidation'; diff --git a/test/utils/pickers/misc.ts b/test/utils/pickers/misc.ts index 0120bd0ac4921..97a08af557017 100644 --- a/test/utils/pickers/misc.ts +++ b/test/utils/pickers/misc.ts @@ -1,6 +1,6 @@ import sinon from 'sinon'; import { MuiPickersAdapter } from '@mui/x-date-pickers/models'; -import { PickerComponentFamily } from '@mui/x-date-pickers/tests/describe.types'; +import { PickerComponentFamily } from './describe.types'; import { OpenPickerParams } from './openPicker'; export const stubMatchMedia = (matches = true) => @@ -56,3 +56,11 @@ export const getDateOffset = ( const cleanUtcHour = utcHour > 12 ? 24 - utcHour : -utcHour; return cleanUtcHour * 60; }; + +export const formatFullTimeValue = ( + adapter: MuiPickersAdapter, + value: TDate, +) => { + const hasMeridiem = adapter.is12HourCycleInCurrentLocale(); + return adapter.format(value, hasMeridiem ? 'fullTime12h' : 'fullTime24h'); +}; diff --git a/test/utils/pickers/openPicker.ts b/test/utils/pickers/openPicker.ts index 831c923055826..acda78eb35bcc 100644 --- a/test/utils/pickers/openPicker.ts +++ b/test/utils/pickers/openPicker.ts @@ -1,4 +1,4 @@ -import { screen, userEvent } from '@mui/monorepo/test/utils'; +import { screen, userEvent } from '@mui-internal/test-utils'; export type OpenPickerParams = | { diff --git a/test/utils/pickers/viewHandlers.ts b/test/utils/pickers/viewHandlers.ts index 0546a06f5b506..8a723c2bf2c7f 100644 --- a/test/utils/pickers/viewHandlers.ts +++ b/test/utils/pickers/viewHandlers.ts @@ -1,5 +1,5 @@ -import { fireTouchChangedEvent, userEvent, screen } from '@mui/monorepo/test/utils'; -import { getClockTouchEvent } from 'test/utils/pickers'; +import { fireTouchChangedEvent, userEvent, screen } from '@mui-internal/test-utils'; +import { getClockTouchEvent, formatFullTimeValue } from 'test/utils/pickers'; import { MuiPickersAdapter, TimeView } from '@mui/x-date-pickers/models'; import { formatMeridiem } from '@mui/x-date-pickers/internals/utils/date-utils'; @@ -35,9 +35,7 @@ export const timeClockHandler: ViewHandler = { export const digitalClockHandler: ViewHandler = { setViewValue: (adapter, value) => { - const hasMeridiem = adapter.is12HourCycleInCurrentLocale(); - const formattedLabel = adapter.format(value, hasMeridiem ? 'fullTime12h' : 'fullTime24h'); - userEvent.mousePress(screen.getByRole('option', { name: formattedLabel })); + userEvent.mousePress(screen.getByRole('option', { name: formatFullTimeValue(adapter, value) })); }, }; diff --git a/test/utils/setupJSDOM.js b/test/utils/setupJSDOM.js index 9f17daa80826a..c99ab9ee34078 100644 --- a/test/utils/setupJSDOM.js +++ b/test/utils/setupJSDOM.js @@ -1,4 +1,5 @@ -const coreExports = require('@mui/monorepo/test/utils/setupJSDOM'); +const coreExports = require('@mui-internal/test-utils/setupJSDOM'); + require('./licenseRelease'); require('./addChaiAssertions'); require('./setupPickers'); diff --git a/tsconfig.json b/tsconfig.json index 86ff716be8543..92f7b095fcea0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,6 +34,8 @@ ], "@mui-internal/docs-utilities": ["./node_modules/@mui/monorepo/packages/docs-utilities"], "@mui-internal/docs-utilities/*": ["./node_modules/@mui/monorepo/packages/docs-utilities/*"], + "@mui-internal/test-utils": ["./node_modules/@mui/monorepo/packages/test-utils/src"], + "@mui-internal/test-utils/*": ["./node_modules/@mui/monorepo/packages/test-utils/src/*"], "typescript-to-proptypes": [ "./node_modules/@mui/monorepo/packages/typescript-to-proptypes/src" ], @@ -41,7 +43,7 @@ "./node_modules/@mui/monorepo/packages/typescript-to-proptypes/src/*" ], "test/*": ["./test/*"], - "docs/*": ["./node_modules/@mui/monorepo/docs"], + "docs/*": ["./node_modules/@mui/monorepo/docs/*"], "docsx/*": ["./docs/*"] } }, diff --git a/webpackBaseConfig.js b/webpackBaseConfig.js index 6426167f47fd3..6720867f6ae14 100644 --- a/webpackBaseConfig.js +++ b/webpackBaseConfig.js @@ -23,6 +23,10 @@ module.exports = { '@mui/x-tree-view': path.resolve(__dirname, './packages/x-tree-view/src'), '@mui/x-license-pro': path.resolve(__dirname, './packages/x-license-pro/src'), '@mui/markdown': path.resolve(__dirname, './node_modules/@mui/monorepo/packages/markdown'), + '@mui-internal/test-utils': path.resolve( + __dirname, + './node_modules/@mui/monorepo/packages/test-utils/src', + ), docs: path.resolve(__dirname, './node_modules/@mui/monorepo/docs'), docsx: path.resolve(__dirname, './docs'), }, diff --git a/yarn.lock b/yarn.lock index f300cba29f265..2f33e40cde94e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -146,27 +146,32 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@argos-ci/core@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@argos-ci/core/-/core-0.11.1.tgz#6503382098897b86ecd425bc17a68413b8653f4d" - integrity sha512-Z6Sc3peuc7MMyIAk9aRsXwalJSfDXolrTC8kpg4SzJrABrVfD+e4URycgM/8RhDfEmZMMENV7gt9mBzogxD+uA== +"@argos-ci/core@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@argos-ci/core/-/core-1.0.0.tgz#7f6ca7f3ce07fd050282e1016e397828757bc37e" + integrity sha512-ZrD7p6sOAk8gnOSJOEp94y0GHns8O9QFAbClsp4Hax5gSlFMcN0UxoVk2K8f7SFKFwudHJeoK3NzypBrotAlIw== dependencies: + "@argos-ci/util" "1.0.0" axios "^1.5.0" convict "^6.2.4" debug "^4.3.4" - env-ci "^9.1.1" fast-glob "^3.3.1" sharp "^0.32.5" tmp "^0.2.1" -"@babel/cli@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.15.tgz#22ed82d76745a43caa60a89917bedb7c9b5bd145" - integrity sha512-prtg5f6zCERIaECeTZzd2fMtVjlfjhUcO+fBLQ6DXXdq5FljN+excVitJ2nogsusdf31LeqkjAfXZ7Xq+HmN8g== +"@argos-ci/util@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@argos-ci/util/-/util-1.0.0.tgz#52f5270f5377e25f471951d0620c8a6b82e5aee6" + integrity sha512-aljQTWre/WoLY88WlXyK72cxxI391bQYoSVDFfcZ17dKw/BKnzVSfL7yB/oD9XrsZahzEwGZmooFPs8c27hNaQ== + +"@babel/cli@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.23.0.tgz#1d7f37c44d4117c67df46749e0c86e11a58cc64b" + integrity sha512-17E1oSkGk2IwNILM4jtfAvgjt+ohmpfBky8aLerUfYZhiPNg7ca+CRCxZn8QDxwNhV/upsc2VHBCqGFIR+iBfA== dependencies: "@jridgewell/trace-mapping" "^0.3.17" commander "^4.0.1" - convert-source-map "^1.1.0" + convert-source-map "^2.0.0" fs-readdir-recursive "^1.1.0" glob "^7.2.0" make-dir "^2.1.0" @@ -183,43 +188,43 @@ "@babel/highlight" "^7.22.13" chalk "^2.4.2" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9", "@babel/compat-data@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.2.tgz#6a12ced93455827037bfb5ed8492820d60fc32cc" + integrity sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ== -"@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.22.15", "@babel/core@^7.7.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.15.tgz#15d4fd03f478a459015a4b94cfbb3bd42c48d2f4" - integrity sha512-PtZqMmgRrvj8ruoEOIwVA3yoF91O+Hgw9o7DAUTNBA6Mo2jpu31clx9a7Nz/9JznqetTR6zwfC4L3LAjKQXUwA== +"@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.23.2", "@babel/core@^7.7.5": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.2.tgz#ed10df0d580fff67c5f3ee70fd22e2e4c90a9f94" + integrity sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.22.15" + "@babel/generator" "^7.23.0" "@babel/helper-compilation-targets" "^7.22.15" - "@babel/helper-module-transforms" "^7.22.15" - "@babel/helpers" "^7.22.15" - "@babel/parser" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.0" + "@babel/helpers" "^7.23.2" + "@babel/parser" "^7.23.0" "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.15" - "@babel/types" "^7.22.15" - convert-source-map "^1.7.0" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.12.11", "@babel/generator@^7.22.15", "@babel/generator@^7.6.2": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.15.tgz#1564189c7ec94cb8f77b5e8a90c4d200d21b2339" - integrity sha512-Zu9oWARBqeVOW0dZOjXc3JObrzuqothQ3y/n1kUtrjCoCPLkXUwMvOo/F/TCfoHMbWIFlWwpZtkZVb9ga4U2pA== +"@babel/generator@^7.12.11", "@babel/generator@^7.23.0", "@babel/generator@^7.6.2": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" + integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== dependencies: - "@babel/types" "^7.22.15" + "@babel/types" "^7.23.0" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": +"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== @@ -268,10 +273,10 @@ regexpu-core "^5.3.1" semver "^6.3.0" -"@babel/helper-define-polyfill-provider@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" - integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== +"@babel/helper-define-polyfill-provider@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.3.tgz#a71c10f7146d809f4a256c373f462d9bba8cf6ba" + integrity sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug== dependencies: "@babel/helper-compilation-targets" "^7.22.6" "@babel/helper-plugin-utils" "^7.22.5" @@ -279,18 +284,18 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== +"@babel/helper-environment-visitor@^7.22.20", "@babel/helper-environment-visitor@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== +"@babel/helper-function-name@^7.22.5", "@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" @@ -306,23 +311,23 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": +"@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.15", "@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.15.tgz#40ad2f6950f143900e9c1c72363c0b431a606082" - integrity sha512-l1UiX4UyHSFsYt17iQ3Se5pQQZZHa22zyIXURmvkmLCD4t/aU+dvNWHatKac/D9Vm9UES7nvIqHs4jZqKviUmQ== +"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz#3ec246457f6c842c0aee62a01f60739906f7047e" + integrity sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.15" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -336,14 +341,14 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" - integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== +"@babel/helper-remap-async-to-generator@^7.22.20", "@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" + integrity sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.9" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-wrap-function" "^7.22.20" "@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": version "7.22.9" @@ -380,33 +385,33 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== -"@babel/helper-validator-identifier@^7.22.15", "@babel/helper-validator-identifier@^7.22.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.15.tgz#601fa28e4cc06786c18912dca138cec73b882044" - integrity sha512-4E/F9IIEi8WR94324mbDUMo074YTheJmd7eZF5vITTeYchqAi6sYXRLHUVsmkdmY4QjfKTcB2jB7dVP3NaBElQ== +"@babel/helper-validator-identifier@^7.22.20", "@babel/helper-validator-identifier@^7.22.5": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== "@babel/helper-validator-option@^7.18.6", "@babel/helper-validator-option@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz#694c30dfa1d09a6534cdfcafbe56789d36aba040" integrity sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA== -"@babel/helper-wrap-function@^7.22.9": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz#d845e043880ed0b8c18bd194a12005cb16d2f614" - integrity sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ== +"@babel/helper-wrap-function@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz#15352b0b9bfb10fc9c76f79f6342c00e3411a569" + integrity sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw== dependencies: "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.10" + "@babel/template" "^7.22.15" + "@babel/types" "^7.22.19" -"@babel/helpers@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.15.tgz#f09c3df31e86e3ea0b7ff7556d85cdebd47ea6f1" - integrity sha512-7pAjK0aSdxOwR+CcYAqgWOGy5dcfvzsTIfFTb2odQqW47MDfv14UaJDY6eng8ylM2EaeKXdxaSWESbkmaQHTmw== +"@babel/helpers@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.2.tgz#2832549a6e37d484286e15ba36a5330483cac767" + integrity sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ== dependencies: "@babel/template" "^7.22.15" - "@babel/traverse" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.23.0" "@babel/highlight@^7.22.13": version "7.22.13" @@ -417,10 +422,10 @@ chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/node@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.22.15.tgz#43c2621b726864f7fd049cd73c55baa06bbba8af" - integrity sha512-DCHvKYVAC8w2Tvt2fgyyYteIwAEHejbVlBU1GlcBXFDEcdWqsADnK1tD/vgrCbsk/rt0tkgpWAiYaJAPR7PKfg== +"@babel/node@^7.22.19": + version "7.22.19" + resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.22.19.tgz#d0bd1e84e3d45eb2eeb68046d6dc22161786222b" + integrity sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw== dependencies: "@babel/register" "^7.22.15" commander "^4.0.1" @@ -429,10 +434,10 @@ regenerator-runtime "^0.14.0" v8flags "^3.1.1" -"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.15.tgz#d34592bfe288a32e741aa0663dbc4829fcd55160" - integrity sha512-RWmQ/sklUN9BvGGpCDgSubhHWfAx24XDTDObup4ffvxaYsptOg2P3KG0j+1eWKLxpkX0j0uHxmpq2Z1SP/VhxA== +"@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" + integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.15": version "7.22.15" @@ -664,14 +669,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.15.tgz#3b153af4a6b779f340d5b80d3f634f55820aefa3" - integrity sha512-jBm1Es25Y+tVoTi5rfd5t1KLmL8ogLKpXszboWOTTtGFGz2RKnQe2yn7HbZ+kb/B8N0FVSGQo874NSlOU1T4+w== +"@babel/plugin-transform-async-generator-functions@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.2.tgz#054afe290d64c6f576f371ccc321772c8ea87ebb" + integrity sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.9" + "@babel/helper-remap-async-to-generator" "^7.22.20" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-transform-async-to-generator@^7.22.5": @@ -690,10 +695,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.15.tgz#494eb82b87b5f8b1d8f6f28ea74078ec0a10a841" - integrity sha512-G1czpdJBZCtngoK1sJgloLiOHUnkb/bLZwqVZD8kXmq0ZnVfTTWUcs9OWtp0mBtYJ+4LQY1fllqBkOIPhXmFmw== +"@babel/plugin-transform-block-scoping@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.0.tgz#8744d02c6c264d82e1a4bc5d2d501fd8aff6f022" + integrity sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -737,10 +742,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.15.tgz#e7404ea5bb3387073b9754be654eecb578324694" - integrity sha512-HzG8sFl1ZVGTme74Nw+X01XsUTqERVQ6/RLHo3XjGRzm7XD6QTtfS3NJotVgCGy8BzkDqRjRBD8dAyJn5TuvSQ== +"@babel/plugin-transform-destructuring@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.0.tgz#6447aa686be48b32eaf65a73e0e2c0bd010a266c" + integrity sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -837,32 +842,32 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-amd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" - integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== +"@babel/plugin-transform-modules-amd@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.0.tgz#05b2bc43373faa6d30ca89214731f76f966f3b88" + integrity sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw== dependencies: - "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-module-transforms" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.15.tgz#b11810117ed4ee7691b29bd29fd9f3f98276034f" - integrity sha512-jWL4eh90w0HQOTKP2MoXXUpVxilxsB2Vl4ji69rSjS3EcZ/v4sBmn+A3NpepuJzBhOaEBbR7udonlHHn5DWidg== +"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz#b3dba4757133b2762c00f4f94590cf6d52602481" + integrity sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ== dependencies: - "@babel/helper-module-transforms" "^7.22.15" + "@babel/helper-module-transforms" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-simple-access" "^7.22.5" -"@babel/plugin-transform-modules-systemjs@^7.22.11": - version "7.22.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz#3386be5875d316493b517207e8f1931d93154bb1" - integrity sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA== +"@babel/plugin-transform-modules-systemjs@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.0.tgz#77591e126f3ff4132a40595a6cccd00a6b60d160" + integrity sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg== dependencies: "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.9" + "@babel/helper-module-transforms" "^7.23.0" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/plugin-transform-modules-umd@^7.22.5": version "7.22.5" @@ -937,10 +942,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.15.tgz#d7a5996c2f7ca4ad2ad16dbb74444e5c4385b1ba" - integrity sha512-ngQ2tBhq5vvSJw2Q2Z9i7ealNkpDMU0rGWnHPKqRZO0tzZ5tlaoz4hDvhXioOoaE0X2vfNss1djwg0DXlfu30A== +"@babel/plugin-transform-optional-chaining@^7.22.15", "@babel/plugin-transform-optional-chaining@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.0.tgz#73ff5fc1cf98f542f09f29c0631647d8ad0be158" + integrity sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -1033,16 +1038,16 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-runtime@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.15.tgz#3a625c4c05a39e932d7d34f5d4895cdd0172fdc9" - integrity sha512-tEVLhk8NRZSmwQ0DJtxxhTrCht1HVo8VaMzYT4w6lwyKBuHsgoioAUA7/6eT2fRfc5/23fuGdlwIxXhRVgWr4g== +"@babel/plugin-transform-runtime@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.2.tgz#c956a3f8d1aa50816ff6c30c6288d66635c12990" + integrity sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA== dependencies: "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.22.5": @@ -1122,12 +1127,12 @@ "@babel/helper-create-regexp-features-plugin" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@babel/preset-env@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.15.tgz#142716f8e00bc030dae5b2ac6a46fbd8b3e18ff8" - integrity sha512-tZFHr54GBkHk6hQuVA8w4Fmq+MSPsfvMG0vPnOYyTnJpyfMqybL8/MbNCPRT9zc2KBO2pe4tq15g6Uno4Jpoag== +"@babel/preset-env@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.23.2.tgz#1f22be0ff0e121113260337dbc3e58fafce8d059" + integrity sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ== dependencies: - "@babel/compat-data" "^7.22.9" + "@babel/compat-data" "^7.23.2" "@babel/helper-compilation-targets" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.15" @@ -1153,15 +1158,15 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.15" + "@babel/plugin-transform-async-generator-functions" "^7.23.2" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.15" + "@babel/plugin-transform-block-scoping" "^7.23.0" "@babel/plugin-transform-class-properties" "^7.22.5" "@babel/plugin-transform-class-static-block" "^7.22.11" "@babel/plugin-transform-classes" "^7.22.15" "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.15" + "@babel/plugin-transform-destructuring" "^7.23.0" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" "@babel/plugin-transform-dynamic-import" "^7.22.11" @@ -1173,9 +1178,9 @@ "@babel/plugin-transform-literals" "^7.22.5" "@babel/plugin-transform-logical-assignment-operators" "^7.22.11" "@babel/plugin-transform-member-expression-literals" "^7.22.5" - "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.15" - "@babel/plugin-transform-modules-systemjs" "^7.22.11" + "@babel/plugin-transform-modules-amd" "^7.23.0" + "@babel/plugin-transform-modules-commonjs" "^7.23.0" + "@babel/plugin-transform-modules-systemjs" "^7.23.0" "@babel/plugin-transform-modules-umd" "^7.22.5" "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" "@babel/plugin-transform-new-target" "^7.22.5" @@ -1184,7 +1189,7 @@ "@babel/plugin-transform-object-rest-spread" "^7.22.15" "@babel/plugin-transform-object-super" "^7.22.5" "@babel/plugin-transform-optional-catch-binding" "^7.22.11" - "@babel/plugin-transform-optional-chaining" "^7.22.15" + "@babel/plugin-transform-optional-chaining" "^7.23.0" "@babel/plugin-transform-parameters" "^7.22.15" "@babel/plugin-transform-private-methods" "^7.22.5" "@babel/plugin-transform-private-property-in-object" "^7.22.11" @@ -1201,10 +1206,10 @@ "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.15" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" + "@babel/types" "^7.23.0" + babel-plugin-polyfill-corejs2 "^0.4.6" + babel-plugin-polyfill-corejs3 "^0.8.5" + babel-plugin-polyfill-regenerator "^0.5.3" core-js-compat "^3.31.0" semver "^6.3.1" @@ -1238,15 +1243,15 @@ "@babel/plugin-transform-react-jsx-development" "^7.22.5" "@babel/plugin-transform-react-pure-annotations" "^7.22.5" -"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.15.tgz#43db30516fae1d417d748105a0bc95f637239d48" - integrity sha512-HblhNmh6yM+cU4VwbBRpxFhxsTdfS1zsvH9W+gEjD0ARV9+8B4sNfpI6GuhePti84nuvhiwKS539jKPFHskA9A== +"@babel/preset-typescript@^7.13.0", "@babel/preset-typescript@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.2.tgz#c8de488130b7081f7e1482936ad3de5b018beef4" + integrity sha512-u4UJc1XsS1GhIGteM8rnGiIvf9rJpiVgMEeCnwlLA7WJPC+jcXWJAGxYmeqs5hOZD8BbAfnV5ezBOxQbb4OUxA== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.15" "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.15" + "@babel/plugin-transform-modules-commonjs" "^7.23.0" "@babel/plugin-transform-typescript" "^7.22.15" "@babel/register@^7.13.16", "@babel/register@^7.22.15": @@ -1265,18 +1270,18 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime-corejs2@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.22.15.tgz#3f1de780aabb51ca9bfdc857f541eb8a2a8356fd" - integrity sha512-CcAMW2o5uqgeXIXYYPrgmaYj1HOLvQb2DrBi7+bELD4nUVSKk+Sth+yYuGqSRf+aAiOv3sIZJTUiJBhoASJRLg== +"@babel/runtime-corejs2@^7.23.2": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.23.2.tgz#a482c6e233fb2efa6456ce299da1b440b87260ed" + integrity sha512-lTwRWGcAUBANnxD0A4c5/wKQ0eLhgdAy9kdY2rzTmmliumBQ8u8awykMnaQAnZR3PC47jLRjGoj+hozZqy9Bww== dependencies: core-js "^2.6.12" regenerator-runtime "^0.14.0" -"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.10", "@babel/runtime@^7.22.15", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" - integrity sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA== +"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.22.6", "@babel/runtime@^7.23.2", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== dependencies: regenerator-runtime "^0.14.0" @@ -1289,29 +1294,29 @@ "@babel/parser" "^7.22.15" "@babel/types" "^7.22.15" -"@babel/traverse@^7.1.6", "@babel/traverse@^7.22.15", "@babel/traverse@^7.4.5": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.15.tgz#75be4d2d6e216e880e93017f4e2389aeb77ef2d9" - integrity sha512-DdHPwvJY0sEeN4xJU5uRLmZjgMMDIvMPniLuYzUVXj/GGzysPl0/fwt44JBkyUIzGJPV8QgHMcQdQ34XFuKTYQ== +"@babel/traverse@^7.1.6", "@babel/traverse@^7.23.2", "@babel/traverse@^7.4.5": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" + integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== dependencies: "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.22.15" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/generator" "^7.23.0" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" + "@babel/parser" "^7.23.0" + "@babel/types" "^7.23.0" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.6.1": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.15.tgz#266cb21d2c5fd0b3931e7a91b6dd72d2f617d282" - integrity sha512-X+NLXr0N8XXmN5ZsaQdm9U2SSC3UbIYq/doL++sueHOTisgZHoKaQtZxGuV2cUPQHMfjKEfg/g6oy7Hm6SKFtA== +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.3.0", "@babel/types@^7.4.4", "@babel/types@^7.6.1": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" + integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== dependencies: "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.15" + "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1377,7 +1382,7 @@ resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.9.1.tgz#4ffb0055f7ef676ebc3a5a91fb621393294e2f43" integrity sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ== -"@emotion/is-prop-valid@^1.1.0", "@emotion/is-prop-valid@^1.2.1": +"@emotion/is-prop-valid@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw== @@ -1441,17 +1446,7 @@ "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" "@emotion/utils" "^1.2.1" -"@emotion/stylis@^0.8.4": - version "0.8.5" - resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" - integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== - -"@emotion/unitless@^0.7.4": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" - integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== - -"@emotion/unitless@^0.8.1": +"@emotion/unitless@^0.8.0", "@emotion/unitless@^0.8.1": version "0.8.1" resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.8.1.tgz#182b5a4704ef8ad91bde93f7a860a88fd92c79a3" integrity sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ== @@ -1507,10 +1502,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.49.0": - version "8.49.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" - integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== +"@eslint/js@8.52.0": + version "8.52.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.52.0.tgz#78fe5f117840f69dc4a353adf9b9cd926353378c" + integrity sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA== "@fast-csv/format@4.3.5": version "4.3.5" @@ -1552,7 +1547,7 @@ "@floating-ui/core" "^1.4.1" "@floating-ui/utils" "^0.1.1" -"@floating-ui/react-dom@^2.0.1": +"@floating-ui/react-dom@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.2.tgz#fab244d64db08e6bed7be4b5fcce65315ef44d20" integrity sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ== @@ -1569,42 +1564,44 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@gitbeaker/core@^21.7.0": - version "21.7.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-21.7.0.tgz#fcf7a12915d39f416e3f316d0a447a814179b8e5" - integrity sha512-cw72rE7tA27wc6JJe1WqeAj9v/6w0S7XJcEji+bRNjTlUfE1zgfW0Gf1mbGUi7F37SOABGCosQLfg9Qe63aIqA== +"@gitbeaker/core@^35.8.1": + version "35.8.1" + resolved "https://registry.yarnpkg.com/@gitbeaker/core/-/core-35.8.1.tgz#b4ce2d08d344ff50e76c38ff81b800bec6dfe851" + integrity sha512-KBrDykVKSmU9Q9Gly8KeHOgdc0lZSa435srECxuO0FGqqBcUQ82hPqUc13YFkkdOI9T1JRA3qSFajg8ds0mZKA== dependencies: - "@gitbeaker/requester-utils" "^21.7.0" - form-data "^3.0.0" + "@gitbeaker/requester-utils" "^35.8.1" + form-data "^4.0.0" li "^1.3.0" + mime "^3.0.0" + query-string "^7.0.0" xcase "^2.0.1" -"@gitbeaker/node@^21.3.0": - version "21.7.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/node/-/node-21.7.0.tgz#2c19613f44ee497a8808c555abec614ebd2dfcad" - integrity sha512-OdM3VcTKYYqboOsnbiPcO0XimXXpYK4gTjARBZ6BWc+1LQXKmqo+OH6oUbyxOoaFu9hHECafIt3WZU3NM4sZTg== +"@gitbeaker/node@^35.8.1": + version "35.8.1" + resolved "https://registry.yarnpkg.com/@gitbeaker/node/-/node-35.8.1.tgz#d67885c827f2d7405afd7e39538a230721756e5c" + integrity sha512-g6rX853y61qNhzq9cWtxIEoe2KDeFBtXAeWMGWJnc3nz3WRump2pIICvJqw/yobLZqmTNt+ea6w3/n92Mnbn3g== dependencies: - "@gitbeaker/core" "^21.7.0" - "@gitbeaker/requester-utils" "^21.7.0" - form-data "^3.0.0" - got "^11.1.4" + "@gitbeaker/core" "^35.8.1" + "@gitbeaker/requester-utils" "^35.8.1" + delay "^5.0.0" + got "^11.8.3" xcase "^2.0.1" -"@gitbeaker/requester-utils@^21.7.0": - version "21.7.0" - resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-21.7.0.tgz#e9a9cfaf268d2a99eb7bbdc930943240a5f88878" - integrity sha512-eLTaVXlBnh8Qimj6QuMMA06mu/mLcJm3dy8nqhhn/Vm/D25sPrvpGwmbfFyvzj6QujPqtHvFfsCHtyZddL01qA== +"@gitbeaker/requester-utils@^35.8.1": + version "35.8.1" + resolved "https://registry.yarnpkg.com/@gitbeaker/requester-utils/-/requester-utils-35.8.1.tgz#f345cdd05abd4169cfcd239d202db6283eb17dc8" + integrity sha512-MFzdH+Z6eJaCZA5ruWsyvm6SXRyrQHjYVR6aY8POFraIy7ceIHOprWCs1R+0ydDZ8KtBnd8OTHjlJ0sLtSFJCg== dependencies: - form-data "^3.0.0" - query-string "^6.12.1" + form-data "^4.0.0" + qs "^6.10.1" xcase "^2.0.1" -"@humanwhocodes/config-array@^0.11.11": - version "0.11.11" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" - integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.13" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297" + integrity sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" + "@humanwhocodes/object-schema" "^2.0.1" debug "^4.1.1" minimatch "^3.0.5" @@ -1613,10 +1610,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz#e5211452df060fa8522b55c7b3c0c4d1981cb044" + integrity sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -1694,21 +1691,21 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@lerna/child-process@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.2.0.tgz#42de5b0a4eb7479c2a72b4bf61685616740cf818" - integrity sha512-8cRsYYX8rGZTXL1KcLBv0RHD9PMvphWZay8yg4qf2giX6x86dQyTetSU4SplG2LBGVClilmNHJa/CQwvPQNUFA== +"@lerna/child-process@7.4.1": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@lerna/child-process/-/child-process-7.4.1.tgz#efacbbe79794ef977feb86873d853bb8708707be" + integrity sha512-Bx1cRCZcVcWoz+atDQc4CSVzGuEgGJPOpIAXjQbBEA2cX5nqIBWdbye8eHu31En/F03aH9BhpNEJghs6wy4iTg== dependencies: chalk "^4.1.0" execa "^5.0.0" strong-log-transformer "^2.1.0" -"@lerna/create@7.2.0": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.2.0.tgz#a8080b419c1f8ab5d575693fcb883a6a0d82c7e0" - integrity sha512-bBypNfwqOQNcfR2nXJ3mWUeIAIoSFpXg8MjuFSf87PzIiyeTEKa3Z57vAa3bDbHQtcB7x6f0rWysK1eQZSH15Q== +"@lerna/create@7.4.1": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@lerna/create/-/create-7.4.1.tgz#3e4bb7235bf5700e7e63c470eba5619171331c1a" + integrity sha512-zPO9GyWceRimtMD+j+aQ8xJgNPYn/Q/SzHf4wYN+4Rj5nrFKMyX+Et7FbWgUNpj0dRgyCCKBDYmTB7xQVVq4gQ== dependencies: - "@lerna/child-process" "7.2.0" + "@lerna/child-process" "7.4.1" "@npmcli/run-script" "6.0.2" "@nx/devkit" ">=16.5.1 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -1739,7 +1736,7 @@ libnpmpublish "7.3.0" load-json-file "6.2.0" lodash "^4.17.21" - make-dir "3.1.0" + make-dir "4.0.0" minimatch "3.0.5" multimatch "5.0.0" node-fetch "2.6.7" @@ -1790,61 +1787,71 @@ react-test-renderer "^18.0.0" semver "^5.7.0" -"@mui/base@5.0.0-beta.14", "@mui/base@^5.0.0-beta.14": - version "5.0.0-beta.14" - resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.14.tgz#315b67b0fd231cbd47e8d54f8f92be23122e4d66" - integrity sha512-Je/9JzzYObsuLCIClgE8XvXNFb55IEz8n2NtStUfASfNiVrwiR8t6VVFFuhofehkyTIN34tq1qbBaOjCnOovBw== +"@mui/base@5.0.0-beta.22", "@mui/base@^5.0.0-beta.22": + version "5.0.0-beta.22" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.22.tgz#9ea6be6c8bfc4d8f825660da36d228f5315d4706" + integrity sha512-l4asGID5tmyerx9emJfXOKLyXzaBtdXNIFE3M+IrSZaFtGFvaQKHhc3+nxxSxPf1+G44psjczM0ekRQCdXx9HA== dependencies: - "@babel/runtime" "^7.22.10" - "@emotion/is-prop-valid" "^1.2.1" - "@floating-ui/react-dom" "^2.0.1" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.8" + "@babel/runtime" "^7.23.2" + "@floating-ui/react-dom" "^2.0.2" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" "@popperjs/core" "^2.11.8" clsx "^2.0.0" prop-types "^15.8.1" - react-is "^18.2.0" -"@mui/core-downloads-tracker@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.8.tgz#9117bd29e94e96dc376f93a28e024666a2456696" - integrity sha512-8V7ZOC/lKkM03TRHqaThQFIq6bWPnj7L/ZWPh0ymldYFFyh8XdF0ywTgafsofDNYT4StlNknbaTjVHBma3SNjQ== - -"@mui/icons-material@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.14.8.tgz#e07418e792050eae611afd74f810ed1c234be687" - integrity sha512-YXcReLydTuNWb1/PxduAH5LgnHNH6spSQBaA0JOz9HD4J+vwst0IanAQgsXy9KKCJSjCsHywE3DB8X+w/b4eeQ== - dependencies: - "@babel/runtime" "^7.22.10" - -"@mui/joy@^5.0.0-beta.5": - version "5.0.0-beta.5" - resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-beta.5.tgz#cfc8454632f6b333a94b9919a3437e23585b10f4" - integrity sha512-PN/a24SPZY3sQMCtZdvG/ELMFXytSjnDBSYhwJVxdghJdWIhk40CltARxzg0puORTQfqeX8fub7WLHFAPU2Lwg== - dependencies: - "@babel/runtime" "^7.22.10" - "@mui/base" "5.0.0-beta.14" - "@mui/core-downloads-tracker" "^5.14.8" - "@mui/system" "^5.14.8" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.8" +"@mui/core-downloads-tracker@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.16.tgz#03ceb422d69a33e6c1cbd7e943cf60816878be2a" + integrity sha512-97isBjzH2v1K7oB4UH2f4NOkBShOynY6dhnoR2XlUk/g6bb7ZBv2I3D1hvvqPtpEigKu93e7f/jAYr5d9LOc5w== + +"@mui/icons-material@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.14.16.tgz#bd394183b0cfb068d4fa48292cd8d329be8d6b16" + integrity sha512-wmOgslMEGvbHZjFLru8uH5E+pif/ciXAvKNw16q6joK6EWVWU5rDYWFknDaZhCvz8ZE/K8ZnJQ+lMG6GgHzXbg== + dependencies: + "@babel/runtime" "^7.23.2" + +"@mui/joy@^5.0.0-beta.13": + version "5.0.0-beta.13" + resolved "https://registry.yarnpkg.com/@mui/joy/-/joy-5.0.0-beta.13.tgz#658ea4de15971cf40a9d9a8b244ea8738360b942" + integrity sha512-vXoCqGX/rRxAij56gwcId67cax3nv7oIaeIYPqCPz8OZTaCvE6ZxMANXOVPmY9GIWpTAABfqijL/jiF0qkz/eg== + dependencies: + "@babel/runtime" "^7.23.2" + "@mui/base" "5.0.0-beta.22" + "@mui/core-downloads-tracker" "^5.14.16" + "@mui/system" "^5.14.16" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" clsx "^2.0.0" - csstype "^3.1.2" prop-types "^15.8.1" - react-is "^18.2.0" -"@mui/material@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.8.tgz#1cad40f106f7c983639376589c3f21485fb1d166" - integrity sha512-fqvDGGF1pXwOOL/f0Gw+KHo/67hasRpf2ApTIJkbuONOk9AUb2jnYMEqCWmL2sUcbbE3ShMbHl8N7HPSsRv1/A== - dependencies: - "@babel/runtime" "^7.22.10" - "@mui/base" "5.0.0-beta.14" - "@mui/core-downloads-tracker" "^5.14.8" - "@mui/system" "^5.14.8" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.8" - "@types/react-transition-group" "^4.4.6" +"@mui/lab@^5.0.0-alpha.151": + version "5.0.0-alpha.151" + resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.151.tgz#4f0e904f0db8088c5c9a3c3da5bde676cb3ce7ca" + integrity sha512-EAIzoDZ0WATa31m71juG1LnURjsmdkUOjNqy2j5WUp4y80obdGYKTT1Yh1hdI5SKND6621vaBPiGoKITjCZJ8A== + dependencies: + "@babel/runtime" "^7.23.2" + "@mui/base" "5.0.0-beta.22" + "@mui/system" "^5.14.16" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" + "@mui/x-tree-view" "6.0.0-alpha.1" + clsx "^2.0.0" + prop-types "^15.8.1" + +"@mui/material@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.16.tgz#45cd62d312d10399d3813ee6dc43bd1f11179bf4" + integrity sha512-W4zZ4vnxgGk6/HqBwgsDHKU7x2l2NhX+r8gAwfg58Rhu3ikfY7NkIS6y8Gl3NkATc4GG1FNaGjjpQKfJx3U6Jw== + dependencies: + "@babel/runtime" "^7.23.2" + "@mui/base" "5.0.0-beta.22" + "@mui/core-downloads-tracker" "^5.14.16" + "@mui/system" "^5.14.16" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" + "@types/react-transition-group" "^4.4.8" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" @@ -1852,38 +1859,38 @@ react-transition-group "^4.4.5" "@mui/monorepo@https://github.com/mui/material-ui.git#master": - version "5.14.9" - resolved "https://github.com/mui/material-ui.git#93b3f9ccf16df63450dcdba127ef73e4c734abd4" + version "5.14.17" + resolved "https://github.com/mui/material-ui.git#22f9c295ba762ae49ab6513af9032266e6a5d991" -"@mui/private-theming@^5.14.7", "@mui/private-theming@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.8.tgz#8e224cd10c531d12b871dc59b1f9376028dd13bb" - integrity sha512-iBzpcl3Mh92XaYpYPdgzzRxNGkjpoDz8rf8/q5m+EBPowFEHV+CCS9hC0Q2pOKLW3VFFikA7w/GHt7n++40JGQ== +"@mui/private-theming@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.14.16.tgz#ffdc9a9d3deaa46af000f04c0a9cc3a982f73071" + integrity sha512-FNlL0pTSEBh8nXsVWreCHDSHk+jG8cBx1sxRbT8JVtL+PYbYPi802zfV4B00Kkf0LNRVRvAVQwojMWSR/MYGng== dependencies: - "@babel/runtime" "^7.22.10" - "@mui/utils" "^5.14.8" + "@babel/runtime" "^7.23.2" + "@mui/utils" "^5.14.16" prop-types "^15.8.1" -"@mui/styled-engine@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.8.tgz#b7a4d5dc6cbe3ecaa5af5189eb5ad90a62a255eb" - integrity sha512-LGwOav/Y40PZWZ2yDk4beUoRlc57Vg+Vpxi9V9BBtT2ESAucCgFobkt+T8eVLMWF9huUou5pwKgLSU5pF90hBg== +"@mui/styled-engine@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.14.16.tgz#a4a78a9980f138c2e705d04d67d44051f5005f22" + integrity sha512-FfvYvTG/Zd+KXMMImbcMYEeQAbONGuX5Vx3gBmmtB6KyA7Mvm9Pma1ly3R0gc44yeoFd+2wBjn1feS8h42HW5w== dependencies: - "@babel/runtime" "^7.22.10" + "@babel/runtime" "^7.23.2" "@emotion/cache" "^11.11.0" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/styles@^5.14.7": - version "5.14.7" - resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.14.7.tgz#e704f465b39e6dbfcd30090b191cf8140f7a9e42" - integrity sha512-5qA81gIRBUd3ur2FtGO10UmArpqaGWL+eUGFVSf68SjhahhHr86/JgqsXqUPyW/LPnyW92SZxhQ6El6Co8i7AQ== +"@mui/styles@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.14.16.tgz#663e5f2317c2b631d9d8bd873f23344ab0729815" + integrity sha512-pBA2eLBEfqLv/jmu9qGcErwml27upH2YBFRuRU2loZm5R57di5y/GjpM9EWc77+49axaTlHfO8LWbic4kPvxoQ== dependencies: - "@babel/runtime" "^7.22.10" + "@babel/runtime" "^7.23.2" "@emotion/hash" "^0.9.1" - "@mui/private-theming" "^5.14.7" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.7" + "@mui/private-theming" "^5.14.16" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" clsx "^2.0.0" csstype "^3.1.2" hoist-non-react-statics "^3.3.2" @@ -1897,41 +1904,59 @@ jss-plugin-vendor-prefixer "^10.10.0" prop-types "^15.8.1" -"@mui/system@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.8.tgz#1ca201b948310083e670352bae2d7963ad6f971e" - integrity sha512-Dxnasv7Pj5hYe4ZZFKJZu4ufKm6cxpitWt3A+qMPps22YhqyeEqgDBq/HsAB3GOjqDP40fTAvQvS/Hguf4SJuw== +"@mui/system@^5.14.16": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.16.tgz#5c30c5123767416358c3b73774eb985e189119a4" + integrity sha512-uKnPfsDqDs8bbN54TviAuoGWOmFiQLwNZ3Wvj+OBkJCzwA6QnLb/sSeCB7Pk3ilH4h4jQ0BHtbR+Xpjy9wlOuA== dependencies: - "@babel/runtime" "^7.22.10" - "@mui/private-theming" "^5.14.8" - "@mui/styled-engine" "^5.14.8" - "@mui/types" "^7.2.4" - "@mui/utils" "^5.14.8" + "@babel/runtime" "^7.23.2" + "@mui/private-theming" "^5.14.16" + "@mui/styled-engine" "^5.14.16" + "@mui/types" "^7.2.8" + "@mui/utils" "^5.14.16" clsx "^2.0.0" csstype "^3.1.2" prop-types "^15.8.1" -"@mui/types@^7.2.4": - version "7.2.4" - resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" - integrity sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA== +"@mui/types@^7.2.8": + version "7.2.8" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.8.tgz#2ed4402f104d65fcd4f460ca358654c8935e2285" + integrity sha512-9u0ji+xspl96WPqvrYJF/iO+1tQ1L5GTaDOeG3vCR893yy7VcWwRNiVMmPdPNpMDqx0WV1wtEW9OMwK9acWJzQ== -"@mui/utils@^5.14.7", "@mui/utils@^5.14.8": - version "5.14.8" - resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.8.tgz#e1737d5fcd54aa413d6b1aaea3ea670af2919402" - integrity sha512-1Ls2FfyY2yVSz9NEqedh3J8JAbbZAnUWkOWLE2f4/Hc4T5UWHMfzBLLrCqExfqyfyU+uXYJPGeNIsky6f8Gh5Q== +"@mui/utils@^5.14.16", "@mui/utils@^5.14.3": + version "5.14.16" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.16.tgz#09a15fd45530cadc642c5c08eb6cc660ea230506" + integrity sha512-3xV31GposHkwRbQzwJJuooWpK2ybWdEdeUPtRjv/6vjomyi97F3+68l+QVj9tPTvmfSbr2sx5c/NuvDulrdRmA== dependencies: - "@babel/runtime" "^7.22.10" - "@types/prop-types" "^15.7.5" - "@types/react-is" "^18.2.1" + "@babel/runtime" "^7.23.2" + "@types/prop-types" "^15.7.9" prop-types "^15.8.1" react-is "^18.2.0" +"@mui/x-tree-view@6.0.0-alpha.1": + version "6.0.0-alpha.1" + resolved "https://registry.yarnpkg.com/@mui/x-tree-view/-/x-tree-view-6.0.0-alpha.1.tgz#fe499f8c43c01d28aca95cfb17491746ffcc3080" + integrity sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA== + dependencies: + "@babel/runtime" "^7.22.6" + "@mui/utils" "^5.14.3" + "@types/react-transition-group" "^4.4.6" + clsx "^2.0.0" + prop-types "^15.8.1" + react-transition-group "^4.4.5" + "@next/env@13.4.19": version "13.4.19" resolved "https://registry.yarnpkg.com/@next/env/-/env-13.4.19.tgz#46905b4e6f62da825b040343cbc233144e9578d3" integrity sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ== +"@next/eslint-plugin-next@^13.5.6": + version "13.5.6" + resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.6.tgz#cf279b94ddc7de49af8e8957f0c3b7349bc489bf" + integrity sha512-ng7pU/DDsxPgT6ZPvuprxrkeew3XaRf4LAT4FabaEO/hAbvVx4P7wqnqdbTdDn1kgTvsI4tpIgT4Awn/m0bGbg== + dependencies: + glob "7.1.7" + "@next/swc-darwin-arm64@13.4.19": version "13.4.19" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz#77ad462b5ced4efdc26cb5a0053968d2c7dac1b6" @@ -2274,6 +2299,11 @@ resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-18.0.0.tgz#f43d765b3c7533fd6fb88f3f25df079c24fccf69" integrity sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw== +"@octokit/openapi-types@^19.0.0": + version "19.0.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-19.0.0.tgz#0101bf62ab14c1946149a0f8385440963e1253c4" + integrity sha512-PclQ6JGMTE9iUStpzMkwLCISFn/wDeRjkZFIKALpvJQNBGwDoYYi2fFvuHwssoQ1rXI5mfh6jgTgWuddeUzfWw== + "@octokit/plugin-enterprise-rest@6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -2294,12 +2324,12 @@ "@octokit/tsconfig" "^1.0.2" "@octokit/types" "^9.2.3" -"@octokit/plugin-paginate-rest@^8.0.0": - version "8.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-8.0.0.tgz#417b5367da2ba3c2d255a59b87c1cc608228ec38" - integrity sha512-2xZ+baZWUg+qudVXnnvXz7qfrTmDeYPCzangBVq/1gXxii/OiS//4shJp9dnCCvj1x+JAm9ji1Egwm1BA47lPQ== +"@octokit/plugin-paginate-rest@^9.0.0": + version "9.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.0.0.tgz#21fd12816c2dc158a775ed20be5abcbc61052a46" + integrity sha512-oIJzCpttmBTlEhBmRvb+b9rlnGpmFgDtZ0bB6nq39qIod6A5DP+7RkVLMOixIgRCYSHDTeayWqmiJ2SZ6xgfdw== dependencies: - "@octokit/types" "^11.0.0" + "@octokit/types" "^12.0.0" "@octokit/plugin-request-log@^1.0.4": version "1.0.4" @@ -2311,6 +2341,13 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz#260fa6970aa97bbcbd91f99f3cd812e2b285c9f1" integrity sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA== +"@octokit/plugin-rest-endpoint-methods@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.0.0.tgz#040b36d6a15d4c7c534b0f44050051225f884cae" + integrity sha512-16VkwE2v6rXU+/gBsYC62M8lKWOphY5Lg4wpjYnVE9Zbu0J6IwiT5kILoj1YOB53XLmcJR+Nqp8DmifOPY4H3g== + dependencies: + "@octokit/types" "^12.0.0" + "@octokit/plugin-rest-endpoint-methods@^5.12.0": version "5.13.0" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" @@ -2327,20 +2364,13 @@ "@octokit/types" "^9.2.3" deprecation "^2.3.1" -"@octokit/plugin-rest-endpoint-methods@^9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-9.0.0.tgz#e15d54540893202da107305ded2bfd21ce6f769d" - integrity sha512-KquMF/VB1IkKNiVnzJKspY5mFgGyLd7HzdJfVEGTJFzqu9BRFNWt+nwTCMuUiWc72gLQhRWYubTwOkQj+w/1PA== - dependencies: - "@octokit/types" "^11.0.0" - -"@octokit/plugin-retry@^6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-6.0.0.tgz#4a83ca5d531bbd56e0822a644ab0ba4a3215f87a" - integrity sha512-a1/A4A+PB1QoAHQfLJxGHhLfSAT03bR1jJz3GgQJZvty2ozawFWs93MiBQXO7SL2YbO7CIq0Goj4qLOBj8JeMQ== +"@octokit/plugin-retry@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@octokit/plugin-retry/-/plugin-retry-6.0.1.tgz#3257404f7cc418e1c1f13a7f2012c1db848b7693" + integrity sha512-SKs+Tz9oj0g4p28qkZwl/topGcb0k0qPNX/i7vBKmDsjoeqnVfFUquqrE/O9oJY7+oLzdCtkiWSXLpLjvl6uog== dependencies: "@octokit/request-error" "^5.0.0" - "@octokit/types" "^11.0.0" + "@octokit/types" "^12.0.0" bottleneck "^2.15.3" "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": @@ -2425,15 +2455,15 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^5.12.0" -"@octokit/rest@^20.0.1": - version "20.0.1" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-20.0.1.tgz#b8ee194b7cf89772d1e3fea3209f741c76a5efd3" - integrity sha512-wROV21RwHQIMNb2Dgd4+pY+dVy1Dwmp85pBrgr6YRRDYRBu9Gb+D73f4Bl2EukZSj5hInq2Tui9o7gAQpc2k2Q== +"@octokit/rest@^20.0.2": + version "20.0.2" + resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-20.0.2.tgz#5cc8871ba01b14604439049e5f06c74b45c99594" + integrity sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ== dependencies: "@octokit/core" "^5.0.0" - "@octokit/plugin-paginate-rest" "^8.0.0" + "@octokit/plugin-paginate-rest" "^9.0.0" "@octokit/plugin-request-log" "^4.0.0" - "@octokit/plugin-rest-endpoint-methods" "^9.0.0" + "@octokit/plugin-rest-endpoint-methods" "^10.0.0" "@octokit/tsconfig@^1.0.2": version "1.0.2" @@ -2447,6 +2477,13 @@ dependencies: "@octokit/openapi-types" "^18.0.0" +"@octokit/types@^12.0.0": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-12.0.0.tgz#6b34309288b6f5ac9761d2589e3165cde1b95fee" + integrity sha512-EzD434aHTFifGudYAygnFlS1Tl6KhbTynEWELQXIbTY8Msvb5nEqTZIm7sbPEt4mQYLZwu3zPKVdeIrw0g7ovg== + dependencies: + "@octokit/openapi-types" "^19.0.0" + "@octokit/types@^5.0.0": version "5.0.1" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-5.0.1.tgz#5459e9a5e9df8565dcc62c17a34491904d71971e" @@ -2493,15 +2530,12 @@ picocolors "^1.0.0" tslib "^2.6.0" -"@playwright/test@1.37.1": - version "1.37.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.37.1.tgz#e7f44ae0faf1be52d6360c6bbf689fd0057d9b6f" - integrity sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg== +"@playwright/test@1.39.0": + version "1.39.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.39.0.tgz#d10ba8e38e44104499e25001945f07faa9fa91cd" + integrity sha512-3u1iFqgzl7zr004bGPYiN/5EZpRUSFddQBra8Rqll5N0/vfpqlP9I9EXqAoGacuAbX6c9Ulg/Cjqglp5VkK6UQ== dependencies: - "@types/node" "*" - playwright-core "1.37.1" - optionalDependencies: - fsevents "2.3.2" + playwright "1.39.0" "@polka/url@^1.0.0-next.20": version "1.0.0-next.21" @@ -2530,6 +2564,11 @@ "@react-spring/shared" "~9.7.3" "@react-spring/types" "~9.7.3" +"@react-spring/rafz@^9.7.3": + version "9.7.3" + resolved "https://registry.yarnpkg.com/@react-spring/rafz/-/rafz-9.7.3.tgz#d6a9695c581f58a49e047ff7ed5646e0b681396c" + integrity sha512-9vzW1zJPcC4nS3aCV+GgcsK/WLaB520Iyvm55ARHfM5AuyBqycjvh1wbmWmgCyJuX4VPoWigzemq1CaaeRSHhQ== + "@react-spring/shared@~9.7.3": version "9.7.3" resolved "https://registry.yarnpkg.com/@react-spring/shared/-/shared-9.7.3.tgz#4cf29797847c689912aec4e62e34c99a4d5d9e53" @@ -2689,10 +2728,10 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== -"@types/babel__core@^7.1.12", "@types/babel__core@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.2.tgz#215db4f4a35d710256579784a548907237728756" - integrity sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA== +"@types/babel__core@^7.1.12", "@types/babel__core@^7.20.3": + version "7.20.3" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.3.tgz#d5625a50b6f18244425a1359a858c73d70340778" + integrity sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA== dependencies: "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" @@ -2732,22 +2771,22 @@ "@types/node" "*" "@types/responselike" "*" -"@types/chai-dom@^1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-1.11.1.tgz#5f91fb34a612ccef177c70100c7c1b98a684d696" - integrity sha512-q+fs4jdKZFDhXOWBehY0jDGCp8nxVe11Ia8MxqlIsJC3Y2JU149PSBYF2li2F3uxJFSAl2Rf8XeLWonHglpcGw== +"@types/chai-dom@^1.11.2": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-1.11.2.tgz#2fcca7218610f25469a335b982d2d3b521b55644" + integrity sha512-6ltNv5QOV7pdW5JEkhT+GMMfvziRC90Rn6zwadC8PFf6tWbXBQRRCV/uXp7nVPdiTKHbqbPTX1jDDhKbu5MW2Q== dependencies: "@types/chai" "*" -"@types/chai@*", "@types/chai@^4.3.6": - version "4.3.6" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.6.tgz#7b489e8baf393d5dd1266fb203ddd4ea941259e6" - integrity sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw== +"@types/chai@*", "@types/chai@^4.3.9": + version "4.3.9" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.9.tgz#144d762491967db8c6dea38e03d2206c2623feec" + integrity sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg== -"@types/chance@^1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.4.tgz#bdb4f5b36ed7622640b597720e8affbfaa7bbfd7" - integrity sha512-et3alUWI9jEPnGai+QRCyDdRMYS+atq32IaldWURyxPRZBYg+cSwppxK2UHnDv9X/0pdoxR3Ufbz5hRmjD/uNg== +"@types/chance@^1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@types/chance/-/chance-1.1.5.tgz#c851fce6e7ed118b71322ad4efd90f5c5147b0af" + integrity sha512-aeEqLplovJlvHzbb9pFJw/pe7iGjPnhDFQmQ4MOoShCm3N09rCpU++A0WPCSavcMXAzc91ofRy3vXbIqLzn1aw== "@types/cheerio@*": version "0.22.30" @@ -2771,27 +2810,27 @@ resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.12.tgz#6b2c510a7ad7039e98e7b8d3d6598f4359e5c080" integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw== -"@types/d3-color@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.0.tgz#6594da178ded6c7c3842f3cc0ac84b156f12f2d4" - integrity sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA== +"@types/d3-color@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.2.tgz#7939eed011a908287cd1bcfd11580c17b2ac7f8a" + integrity sha512-At+Ski7dL8Bs58E8g8vPcFJc8tGcaC12Z4m07+p41+DRqnZQcAlp3NfYjLrhNYv+zEyQitU1CUxXNjqUyf+c0g== "@types/d3-path@*": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.0.tgz#939e3a784ae4f80b1fde8098b91af1776ff1312b" integrity sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg== -"@types/d3-scale@^4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.4.tgz#3c5e2263eea5a3670cd91043b9f4d150a94c43f1" - integrity sha512-eq1ZeTj0yr72L8MQk6N6heP603ubnywSDRfNpi5enouR112HzGLS6RIvExCzZTraFF4HdzNpJMwA/zGiMoHUUw== +"@types/d3-scale@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.6.tgz#9d221949f37b90b52696ec99f9b1e972d55fe10d" + integrity sha512-lo3oMLSiqsQUovv8j15X4BNEDOsnHuGjeVg7GRbAuB2PUa1prK5BNSOu6xixgNf3nqxPl4I1BqJWrPvFGlQoGQ== dependencies: "@types/d3-time" "*" -"@types/d3-shape@^3.1.2": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.2.tgz#a3d421d8b0bc0c6c67cb3f4b4471ddc133cb0117" - integrity sha512-NN4CXr3qeOUNyK5WasVUV8NCSAx/CRVcwcb0BuuS1PiTqwIm6ABi1SyasLZ/vsVCFDArF+W4QiGzSry1eKYQ7w== +"@types/d3-shape@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.4.tgz#748a256d5e499cdfb3e48beca9c557f3ea0ff15c" + integrity sha512-M2/xsWPsjaZc5ifMKp1EBp0gqJG0eO/zlldJNOC85Y/5DGsBQ49gDkRJ2h5GY7ZVD6KUumvZWsylSbvTaJTqKg== dependencies: "@types/d3-path" "*" @@ -2800,10 +2839,10 @@ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== -"@types/doctrine@^0.0.6": - version "0.0.6" - resolved "https://registry.yarnpkg.com/@types/doctrine/-/doctrine-0.0.6.tgz#12ede1f7cd3797be5856277c85f031299ccd2641" - integrity sha512-KlEqPtaNBHBJ2/fVA4yLdD0Tc8zw34pKU4K5SHBIEwtLJ8xxumIC1xeG+4S+/9qhVj2MqC7O3Ld8WvDG4HqlgA== +"@types/doctrine@^0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/doctrine/-/doctrine-0.0.8.tgz#e6d6572ddc6f424aef73245cf411724e225dfa11" + integrity sha512-mGinUwtyZsYnU2ana2wyteVHD0PFPukcZMOZWMtSRJBTFMirYy6RlV286CjttvPoNYYgW9jWf0MXKniV7f2oVw== "@types/enzyme@^3.10.12": version "3.10.12" @@ -2821,10 +2860,10 @@ "@types/eslint" "*" "@types/estree" "*" -"@types/eslint@*", "@types/eslint@^8.44.2": - version "8.44.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.2.tgz#0d21c505f98a89b8dd4d37fa162b09da6089199a" - integrity sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg== +"@types/eslint@*", "@types/eslint@^8.44.6": + version "8.44.6" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.6.tgz#60e564551966dd255f4c01c459f0b4fb87068603" + integrity sha512-P6bY56TVmX8y9J87jHNgQh43h6VVU+6H7oN7hgvivV81K2XY8qJZ5vqPy/HdUoVIelii2kChYVzQanlswPWVFw== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -2834,10 +2873,10 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== -"@types/format-util@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/format-util/-/format-util-1.0.2.tgz#7d19feb1caf59b6ea99c83dfe795ffa3f06b87d7" - integrity sha512-9SrLCpgzWo2yHHhiMOX0WwgDh37nSbDbWUsRc1ss++o8O97E3tB6SJiyUQM21UeUsKvZNuhDCmkRaINZ4uJAfg== +"@types/format-util@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/format-util/-/format-util-1.0.3.tgz#076bf463d4a08d4f0fef3a90ba293fb0bd687bb6" + integrity sha512-Zs8j1fJtFwgidkq+ssW6sdQovx7kDBf5A1qsUf0khLjoqRVhIDNScYUjQVn8qe6WwTbI2IHVv4R1HmnIWzwYog== "@types/history@*", "@types/history@^4.7.11": version "4.7.11" @@ -2884,10 +2923,10 @@ dependencies: "@types/node" "*" -"@types/lodash@^4.14.198": - version "4.14.198" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.198.tgz#4d27465257011aedc741a809f1269941fa2c5d4c" - integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg== +"@types/lodash@^4.14.200": + version "4.14.200" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.200.tgz#435b6035c7eba9cdf1e039af8212c9e9281e7149" + integrity sha512-YI/M/4HRImtNf3pJgbF+W6FrXovqj+T+/HpENLTooK9PnkacBsDpeP3IpHab40CClUfhNmdM2WTNP2sa2dni5Q== "@types/lru-cache@^7.10.9": version "7.10.10" @@ -2896,10 +2935,10 @@ dependencies: lru-cache "*" -"@types/luxon@^3.3.2": - version "3.3.2" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.2.tgz#f6e3524c2486b949a4db445e85d93c8e9886dfe2" - integrity sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ== +"@types/luxon@^3.3.3": + version "3.3.3" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.3.tgz#b2e20a9536f91ab3e6e7895c91883e1a7ad49a6e" + integrity sha512-/BJF3NT0pRMuxrenr42emRUF67sXwcZCd+S1ksG/Fcf9O7C3kKCY4uJSbKBE4KDUIYr3WMsvfmWD8hRjXExBJQ== "@types/minimatch@^3.0.3": version "3.0.5" @@ -2911,29 +2950,29 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/mocha@^10.0.1": - version "10.0.1" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" - integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== +"@types/mocha@^10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.3.tgz#4804fe9cd39da26eb62fa65c15ea77615a187812" + integrity sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ== -"@types/moment-hijri@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@types/moment-hijri/-/moment-hijri-2.1.0.tgz#ac79f3d97d74bf10afe6631cd33c19ad3f329d56" - integrity sha512-2+jaCpPqiwcrejg8oCex7jITtRDjcQIu8s7GAY7uVlDoAaq3LiyLDoe1l9EiWZRCECahtdUDmdZHHS5THRC/hA== +"@types/moment-hijri@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@types/moment-hijri/-/moment-hijri-2.1.2.tgz#d416eaebf337e920c6328504a15a6beec2d13776" + integrity sha512-6VrmS3qmOHs+h1WlH0EqTlPu9JP/Tb1pokXecj8cfa0C+avsjit7QYx7aZWHEB7suXazC6n/qUMvhIgH3A0D1A== dependencies: moment ">=2.14.0" -"@types/moment-jalaali@^0.7.6": - version "0.7.6" - resolved "https://registry.yarnpkg.com/@types/moment-jalaali/-/moment-jalaali-0.7.6.tgz#8debf51f7bc265a00ac98c4bbd77b260c71b46d8" - integrity sha512-TyqrJVGpuqadpQt49sPdBlWqLDYU2W3PzAoJ934CjE/u8iNf/wi/oIYv9x9vKB1XU6PhzFOXerDZn43LW1K8xA== +"@types/moment-jalaali@^0.7.8": + version "0.7.8" + resolved "https://registry.yarnpkg.com/@types/moment-jalaali/-/moment-jalaali-0.7.8.tgz#42ab59989928c94207aff35e5d834f55c2b8bdb0" + integrity sha512-Yfbgpos2bZBlSclui5iccntZ06eBUnSDypKbZwt9PENPv4Jpl1DHsvsd3XCgWGd9AC4gcHDeInsR8nCv9Sx+vA== dependencies: moment ">=2.14.0" -"@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@^14.0.1", "@types/node@^18.17.18": - version "18.17.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.18.tgz#acae19ad9011a2ab3d792232501c95085ba1838f" - integrity sha512-/4QOuy3ZpV7Ya1GTRz5CYSz3DgkKpyUptXuQ5PPce7uuyJAOR7r9FhkmxJfvcNUXyklbC63a+YvB3jxy7s9ngw== +"@types/node@*", "@types/node@>= 8", "@types/node@>=10.0.0", "@types/node@^14.0.1", "@types/node@^18.18.6": + version "18.18.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.18.6.tgz#26da694f75cdb057750f49d099da5e3f3824cb3e" + integrity sha512-wf3Vz+jCmOQ2HV1YUJuCWdL64adYxumkrxtc+H1VUQlnQI04+5HtH+qZCOE21lBE7gIrt+CwX2Wv8Acrw5Ak6w== "@types/normalize-package-data@^2.4.0", "@types/normalize-package-data@^2.4.1": version "2.4.1" @@ -2950,22 +2989,15 @@ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" integrity sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA== -"@types/prop-types@*", "@types/prop-types@^15.7.5": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== - -"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.7": - version "18.2.7" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63" - integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA== - dependencies: - "@types/react" "*" +"@types/prop-types@*", "@types/prop-types@^15.7.9": + version "15.7.9" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.9.tgz#b6f785caa7ea1fe4414d9df42ee0ab67f23d8a6d" + integrity sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g== -"@types/react-is@^18.2.1": - version "18.2.1" - resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.1.tgz#61d01c2a6fc089a53520c0b66996d458fdc46863" - integrity sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw== +"@types/react-dom@^18.0.0", "@types/react-dom@^18.2.14": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.14.tgz#c01ba40e5bb57fc1dc41569bb3ccdb19eab1c539" + integrity sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ== dependencies: "@types/react" "*" @@ -2986,33 +3018,33 @@ "@types/history" "*" "@types/react" "*" -"@types/react-test-renderer@^18.0.1": - version "18.0.1" - resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.1.tgz#d3f308912fcc4491e4fbc134b906bb65bdee73f6" - integrity sha512-LjEF+jTUCjzd+Qq4eWqsmZvEWPA/l4L0my+YWN5US8Fo3wZOMiyrpBshHDFbkO8usjdO1B430mEWNU/i1MF7Qg== +"@types/react-test-renderer@^18.0.5": + version "18.0.5" + resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.5.tgz#b67a6ff37acd93d1b971ec4c838f69d52e772db0" + integrity sha512-PsnmF4Hpi61PTRX+dTxkjgDdtZ09kFFgPXczoF+yBfOVxn7xBLPvKP1BUrSasYHmerj33rhoJuvpIMsJuyRqHw== dependencies: "@types/react" "*" -"@types/react-transition-group@^4.4.6": - version "4.4.6" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e" - integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew== +"@types/react-transition-group@^4.4.6", "@types/react-transition-group@^4.4.8": + version "4.4.8" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.8.tgz#46f87d80512959cac793ecc610a93d80ef241ccf" + integrity sha512-QmQ22q+Pb+HQSn04NL3HtrqHwYMf4h3QKArOy5F8U5nEVMaihBs3SR10WiOM1iwPz5jIo8x/u11al+iEGZZrvg== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.2.21": - version "18.2.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" - integrity sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA== +"@types/react@*", "@types/react@^18.2.33": + version "18.2.33" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.33.tgz#055356243dc4350a9ee6c6a2c07c5cae12e38877" + integrity sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" -"@types/requestidlecallback@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@types/requestidlecallback/-/requestidlecallback-0.3.5.tgz#132529751a4717fe7dc55fef5e930336f229543c" - integrity sha512-Uh49VrVTPfU0y/qIvXXYuRmd/sKLfVgQWZU1t8FWH22AIJyQbCei1aSmXdMDAijwGUFhBDpJmksiHEDsfiE/cg== +"@types/requestidlecallback@^0.3.6": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@types/requestidlecallback/-/requestidlecallback-0.3.6.tgz#96a4b1eaa7587170fff72a8277d5ddce3c15c4db" + integrity sha512-0KKbiCnbiEMX0C6sDCF/K8qHrbb9N/Eg/gTAwcb+fW37AsherVgrINKr9oCnTphONaQvEb7k7hQLrS2TPw1pIA== "@types/responselike@*", "@types/responselike@^1.0.0": version "1.0.0" @@ -3031,10 +3063,10 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" integrity sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw== -"@types/sinon@^10.0.16": - version "10.0.16" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.16.tgz#4bf10313bd9aa8eef1e50ec9f4decd3dd455b4d3" - integrity sha512-j2Du5SYpXZjJVJtXBokASpPRj+e2z+VUhCPHmM6WMfe3dpHu6iVKJMU6AiBcMp/XTAYnEj6Wc1trJUWwZ0QaAQ== +"@types/sinon@^10.0.20": + version "10.0.20" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.20.tgz#f1585debf4c0d99f9938f4111e5479fb74865146" + integrity sha512-2APKKruFNCAZgx3daAyACGzWuJ028VVCUDk6o2rw/Z4PXT0ogwdV4KUegW0MwVs0Zu59auPXbbuBJHF12Sx1Eg== dependencies: "@types/sinonjs__fake-timers" "*" @@ -3043,28 +3075,33 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3" integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== +"@types/stylis@^4.0.2", "@types/stylis@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@types/stylis/-/stylis-4.2.2.tgz#baabb6b3aa6787e90a6bd6cd75cd8fb9a4f256a3" + integrity sha512-Rm17MsTpQQP5Jq4BF7CdrxJsDufoiL/q5IbJZYZmOZAJALyijgF7BzLgobXUqraNcQdqFYLYGeglDp6QzaxPpg== + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== -"@types/yargs@^17.0.24": - version "17.0.24" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" - integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== +"@types/yargs@^17.0.29": + version "17.0.29" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.29.tgz#06aabc72497b798c643c812a8b561537fea760cf" + integrity sha512-nacjqA3ee9zRF/++a3FUY1suHTFKZeHba2n8WeDw9cCVdmzmHpIxyzOJBcpHvvEmS8E9KqWlSnWHUkOrkhWcvA== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz#ed2a38867190f8a688af85ad7c8a74670b8b3675" - integrity sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag== +"@typescript-eslint/eslint-plugin@^6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz#d8ce497dc0ed42066e195c8ecc40d45c7b1254f4" + integrity sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg== dependencies: "@eslint-community/regexpp" "^4.5.1" - "@typescript-eslint/scope-manager" "6.7.0" - "@typescript-eslint/type-utils" "6.7.0" - "@typescript-eslint/utils" "6.7.0" - "@typescript-eslint/visitor-keys" "6.7.0" + "@typescript-eslint/scope-manager" "6.9.1" + "@typescript-eslint/type-utils" "6.9.1" + "@typescript-eslint/utils" "6.9.1" + "@typescript-eslint/visitor-keys" "6.9.1" debug "^4.3.4" graphemer "^1.4.0" ignore "^5.2.4" @@ -3079,15 +3116,15 @@ dependencies: "@typescript-eslint/utils" "5.62.0" -"@typescript-eslint/parser@^6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.0.tgz#332fe9c7ecf6783d3250b4c8a960bd4af0995807" - integrity sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng== +"@typescript-eslint/parser@^6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.9.1.tgz#4f685f672f8b9580beb38d5fb99d52fc3e34f7a3" + integrity sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg== dependencies: - "@typescript-eslint/scope-manager" "6.7.0" - "@typescript-eslint/types" "6.7.0" - "@typescript-eslint/typescript-estree" "6.7.0" - "@typescript-eslint/visitor-keys" "6.7.0" + "@typescript-eslint/scope-manager" "6.9.1" + "@typescript-eslint/types" "6.9.1" + "@typescript-eslint/typescript-estree" "6.9.1" + "@typescript-eslint/visitor-keys" "6.9.1" debug "^4.3.4" "@typescript-eslint/scope-manager@5.62.0": @@ -3098,21 +3135,21 @@ "@typescript-eslint/types" "5.62.0" "@typescript-eslint/visitor-keys" "5.62.0" -"@typescript-eslint/scope-manager@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz#6b3c22187976e2bf5ed0dc0d9095f1f2cbd1d106" - integrity sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA== +"@typescript-eslint/scope-manager@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz#e96afeb9a68ad1cd816dba233351f61e13956b75" + integrity sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg== dependencies: - "@typescript-eslint/types" "6.7.0" - "@typescript-eslint/visitor-keys" "6.7.0" + "@typescript-eslint/types" "6.9.1" + "@typescript-eslint/visitor-keys" "6.9.1" -"@typescript-eslint/type-utils@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz#21a013d4c7f96255f5e64ac59fb41301d1e052ba" - integrity sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg== +"@typescript-eslint/type-utils@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz#efd5db20ed35a74d3c7d8fba51b830ecba09ce32" + integrity sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg== dependencies: - "@typescript-eslint/typescript-estree" "6.7.0" - "@typescript-eslint/utils" "6.7.0" + "@typescript-eslint/typescript-estree" "6.9.1" + "@typescript-eslint/utils" "6.9.1" debug "^4.3.4" ts-api-utils "^1.0.1" @@ -3121,10 +3158,10 @@ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== -"@typescript-eslint/types@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.0.tgz#8de8ba9cafadc38e89003fe303e219c9250089ae" - integrity sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q== +"@typescript-eslint/types@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.9.1.tgz#a6cfc20db0fcedcb2f397ea728ef583e0ee72459" + integrity sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ== "@typescript-eslint/typescript-estree@5.62.0": version "5.62.0" @@ -3139,13 +3176,13 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz#20ce2801733bd46f02cc0f141f5b63fbbf2afb63" - integrity sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ== +"@typescript-eslint/typescript-estree@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz#8c77910a49a04f0607ba94d78772da07dab275ad" + integrity sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw== dependencies: - "@typescript-eslint/types" "6.7.0" - "@typescript-eslint/visitor-keys" "6.7.0" + "@typescript-eslint/types" "6.9.1" + "@typescript-eslint/visitor-keys" "6.9.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" @@ -3166,17 +3203,17 @@ eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/utils@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.0.tgz#61b6f1f1b82ad529abfcee074d21764e880886fb" - integrity sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA== +"@typescript-eslint/utils@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.9.1.tgz#763da41281ef0d16974517b5f0d02d85897a1c1e" + integrity sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA== dependencies: "@eslint-community/eslint-utils" "^4.4.0" "@types/json-schema" "^7.0.12" "@types/semver" "^7.5.0" - "@typescript-eslint/scope-manager" "6.7.0" - "@typescript-eslint/types" "6.7.0" - "@typescript-eslint/typescript-estree" "6.7.0" + "@typescript-eslint/scope-manager" "6.9.1" + "@typescript-eslint/types" "6.9.1" + "@typescript-eslint/typescript-estree" "6.9.1" semver "^7.5.4" "@typescript-eslint/visitor-keys@5.62.0": @@ -3187,14 +3224,19 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@6.7.0": - version "6.7.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz#34140ac76dfb6316d17012e4469acf3366ad3f44" - integrity sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ== +"@typescript-eslint/visitor-keys@6.9.1": + version "6.9.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz#6753a9225a0ba00459b15d6456b9c2780b66707d" + integrity sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw== dependencies: - "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/types" "6.9.1" eslint-visitor-keys "^3.4.1" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@webassemblyjs/ast@1.11.5", "@webassemblyjs/ast@^1.11.5": version "1.11.5" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.5.tgz#6e818036b94548c1fb53b754b5cae3c9b208281c" @@ -3730,15 +3772,15 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-includes@^3.1.5, array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.5, array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-parallel@~0.1.3: @@ -3761,17 +3803,17 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.find@^2.1.1, array.prototype.find@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.1.tgz#769b8182a0b535c3d76ac025abab98ba1e12467b" - integrity sha512-I2ri5Z9uMpMvnsNrHre9l3PaX+z9D0/z6F7Yt2u15q7wt0I62g5kX6xUKR1SJiefgG+u2/gJUmM8B47XRvQR6w== +array.prototype.find@^2.1.1, array.prototype.find@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.2.tgz#e862cf891e725d8f2a10e5e42d750629faaabd32" + integrity sha512-DRumkfW97iZGOfn+lIXbkVrXL04sfYKX+EfOodo8XboR5sxPDVvOjZTF/rysusa9lmhmSOeD6Vp6RKQP+eP4Tg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.findlastindex@^1.2.2: +array.prototype.findlastindex@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== @@ -3782,24 +3824,24 @@ array.prototype.findlastindex@^1.2.2: es-shim-unscopables "^1.0.0" get-intrinsic "^1.2.1" -array.prototype.flat@^1.2.3, array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.flat@^1.2.3, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" array.prototype.tosorted@^1.1.1: @@ -3913,14 +3955,14 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@^10.4.15: - version "10.4.15" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.15.tgz#a1230f4aeb3636b89120b34a1f513e2f6834d530" - integrity sha512-KCuPB8ZCIqFdA4HwKXsvz7j6gvSDNhDP7WnUjBleRkKjPdvCmHFuQ77ocavI8FT6NdvlBnE2UFr2H4Mycn8Vew== +autoprefixer@^10.4.16: + version "10.4.16" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.16.tgz#fad1411024d8670880bdece3970aa72e3572feb8" + integrity sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ== dependencies: browserslist "^4.21.10" - caniuse-lite "^1.0.30001520" - fraction.js "^4.2.0" + caniuse-lite "^1.0.30001538" + fraction.js "^4.3.6" normalize-range "^0.1.2" picocolors "^1.0.0" postcss-value-parser "^4.2.0" @@ -3930,10 +3972,10 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axe-core@4.8.1, axe-core@^4.6.2: - version "4.8.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.1.tgz#6948854183ee7e7eae336b9877c5bafa027998ea" - integrity sha512-9l850jDDPnKq48nbad8SiEelCv4OrUWrKab/cPj0GScVg6cb6NbCCt/Ulk26QEq5jP9NnGr04Bit1BHyV6r5CQ== +axe-core@4.8.2, axe-core@^4.6.2: + version "4.8.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.2.tgz#2f6f3cde40935825cf4465e3c1c9e77b240ff6ae" + integrity sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g== axios@^1.0.0, axios@^1.5.0: version "1.5.0" @@ -4012,29 +4054,29 @@ babel-plugin-optimize-clsx@^2.6.2: lodash "^4.17.15" object-hash "^2.0.3" -babel-plugin-polyfill-corejs2@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" - integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== +babel-plugin-polyfill-corejs2@^0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.6.tgz#b2df0251d8e99f229a8e60fc4efa9a68b41c8313" + integrity sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q== dependencies: "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.4.3" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" - integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== +babel-plugin-polyfill-corejs3@^0.8.5: + version "0.8.5" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.5.tgz#a75fa1b0c3fc5bd6837f9ec465c0f48031b8cab1" + integrity sha512-Q6CdATeAvbScWPNLB8lzSO7fgUVBkQt6zLgNlfyeCr/EQaEQR+bWiBYYPYAFyE528BMjRhL+1QBMOI4jc/c5TA== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" - core-js-compat "^3.31.0" + "@babel/helper-define-polyfill-provider" "^0.4.3" + core-js-compat "^3.32.2" -babel-plugin-polyfill-regenerator@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" - integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== +babel-plugin-polyfill-regenerator@^0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.3.tgz#d4c49e4b44614607c13fb769bcd85c72bb26a4a5" + integrity sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" + "@babel/helper-define-polyfill-provider" "^0.4.3" babel-plugin-preval@^5.1.0: version "5.1.0" @@ -4056,21 +4098,6 @@ babel-plugin-search-and-replace@^1.1.1: resolved "https://registry.yarnpkg.com/babel-plugin-search-and-replace/-/babel-plugin-search-and-replace-1.1.1.tgz#2e5b4488e41d9eba1c220651b1a9b350fdf10915" integrity sha512-fjP2VTF3mxxOUnc96mdK22llH92A6gu7A5AFapJmgnqsQi3bqLduIRP0FpA2r5vRZOYPpnX4rE5izQlpsMBjSA== -"babel-plugin-styled-components@>= 1.12.0": - version "1.13.2" - resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.2.tgz#ebe0e6deff51d7f93fceda1819e9b96aeb88278d" - integrity sha512-Vb1R3d4g+MUfPQPVDMCGjm3cDocJEUTR7Xq7QS95JWWeksN1wdFRYpD2kulDgI3Huuaf1CZd+NK4KQmqUFh5dA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.0.0" - "@babel/helper-module-imports" "^7.0.0" - babel-plugin-syntax-jsx "^6.18.0" - lodash "^4.17.11" - -babel-plugin-syntax-jsx@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" - integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= - babel-plugin-transform-react-remove-prop-types@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a" @@ -4243,15 +4270,15 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5, browserslist@^4.21.10, browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== +browserslist@^4.14.5, browserslist@^4.21.10, browserslist@^4.21.9, browserslist@^4.22.1: + version "4.22.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.1.tgz#ba91958d1a59b87dab6fed8dfbcb3da5e2e9c619" + integrity sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ== dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" + caniuse-lite "^1.0.30001541" + electron-to-chromium "^1.4.535" node-releases "^2.0.13" - update-browserslist-db "^1.0.11" + update-browserslist-db "^1.0.13" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" @@ -4501,28 +4528,28 @@ camelize@^1.0.0: resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.0.tgz#164a5483e630fa4321e5af07020e531831b2609b" integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs= -caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001520: - version "1.0.30001522" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" - integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== +caniuse-lite@^1.0.30001406, caniuse-lite@^1.0.30001538, caniuse-lite@^1.0.30001541: + version "1.0.30001551" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz#1f2cfa8820bd97c971a57349d7fd8f6e08664a3e" + integrity sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg== chai-dom@^1.11.0: version "1.11.0" resolved "https://registry.yarnpkg.com/chai-dom/-/chai-dom-1.11.0.tgz#aa3af405b3d9b0470d185b17081ed23ca5fdaeb4" integrity sha512-ZzGlEfk1UhHH5+N0t9bDqstOxPEXmn3EyXvtsok5rfXVDOFDJbHVy12rED6ZwkJAUDs2w7/Da4Hlq2LB63kltg== -chai@^4.3.8: - version "4.3.8" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.8.tgz#40c59718ad6928da6629c70496fe990b2bb5b17c" - integrity sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ== +chai@^4.3.10: + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" chainsaw@~0.1.0: version "0.1.0" @@ -4586,10 +4613,12 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" cheerio@^1.0.0-rc.3: version "1.0.0-rc.3" @@ -4957,10 +4986,10 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -concurrently@^8.2.1: - version "8.2.1" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.1.tgz#bcab9cacc38c23c503839583151e0fa96fd5b584" - integrity sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ== +concurrently@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784" + integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg== dependencies: chalk "^4.1.2" date-fns "^2.30.0" @@ -5082,13 +5111,18 @@ conventional-recommended-bump@7.0.1: git-semver-tags "^5.0.0" meow "^8.1.2" -convert-source-map@^1.1.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + convict@^6.2.4: version "6.2.4" resolved "https://registry.yarnpkg.com/convict/-/convict-6.2.4.tgz#be290672bf6397eec808d3b11fc5f71785b02a4b" @@ -5117,12 +5151,12 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -core-js-compat@^3.31.0: - version "3.31.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" - integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== +core-js-compat@^3.31.0, core-js-compat@^3.32.2: + version "3.33.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.33.0.tgz#24aa230b228406450b2277b7c8bfebae932df966" + integrity sha512-0w4LcLXsVEuNkIqwjjf9rjCoPhK8uqA4tMRh4Ge26vfLtUutshn+aRJU21I9LCJlh2QQHfisNToLjw1XEJLTWw== dependencies: - browserslist "^4.21.9" + browserslist "^4.22.1" core-js@^2.6.12: version "2.6.12" @@ -5276,10 +5310,10 @@ css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" -css-to-react-native@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" - integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== +css-to-react-native@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.2.0.tgz#cdd8099f71024e149e4f6fe17a7d46ecd55f1e32" + integrity sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ== dependencies: camelize "^1.0.0" css-color-keywords "^1.0.0" @@ -5391,12 +5425,13 @@ damerau-levenshtein@^1.0.8: resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== -danger@^11.2.8: - version "11.2.8" - resolved "https://registry.yarnpkg.com/danger/-/danger-11.2.8.tgz#0e0375a5c311f93be8f53e9be1b44d8c5434b72f" - integrity sha512-d3iYhIJmo3V5WatWjsHbFpx/V5nz7fKsM7rQi91f/9CemLCH8wt3Jg1JKsEpiTHUtzNplOpudk0yFsWeHygd/w== +danger@^11.3.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/danger/-/danger-11.3.0.tgz#5a2cbe3e45f367ed0899156c480c7d7e40d90b3d" + integrity sha512-h4zkvmEfRVZp2EIKlQSky0IotxrDbJZtXgMTvyN1nwPCfg0JgvQVmVbvOZXrOgNVlgL+42ZDjNL2qAwVmJypNw== dependencies: - "@gitbeaker/node" "^21.3.0" + "@gitbeaker/core" "^35.8.1" + "@gitbeaker/node" "^35.8.1" "@octokit/rest" "^18.12.0" async-retry "1.2.3" chalk "^2.3.0" @@ -5475,10 +5510,10 @@ dateformat@^3.0.3: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.11.9, dayjs@^1.8.34: - version "1.11.9" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" - integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== +dayjs@^1.11.10, dayjs@^1.8.34: + version "1.11.10" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0" + integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ== debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -5539,7 +5574,7 @@ decimal.js@^10.4.3: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== -decode-uri-component@^0.2.0: +decode-uri-component@^0.2.0, decode-uri-component@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== @@ -5556,10 +5591,10 @@ dedent@0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-eql@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.2.tgz#270ceb902f87724077e6f6449aed81463f42fc1c" - integrity sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w== +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== dependencies: type-detect "^4.0.0" @@ -5673,6 +5708,11 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -5926,10 +5966,10 @@ ejs@^3.1.7: dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.477: - version "1.4.496" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.496.tgz#a57534b70d2bdee7e1ad7dbd4c91e560cbd08db1" - integrity sha512-qeXC3Zbykq44RCrBa4kr8v/dWzYJA8rAwpyh9Qd+NKWoJfjG5vvJqy9XOJ9H4P/lqulZBCgUWAYi+FeK5AuJ8g== +electron-to-chromium@^1.4.535: + version "1.4.559" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.559.tgz#050483c22c5eb2345017a8976a67b060559a33f4" + integrity sha512-iS7KhLYCSJbdo3rUSkhDTVuFNCV34RKs2UaB9Ecr7VlqzjjWW//0nfsFF5dtDmyXlZQaDYYtID5fjtC/6lpRug== emoji-regex@^8.0.0: version "8.0.0" @@ -6037,14 +6077,6 @@ entities@~3.0.1: resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== -env-ci@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-9.1.1.tgz#f081684c64a639c6ff5cb801bd70464bd40498a4" - integrity sha512-Im2yEWeF4b2RAMAaWvGioXk6m0UNaIjD8hj28j2ij5ldnIFrDQT0+pzDvpbRkcjurhXhf/AsBKv8P2rtmGi9Aw== - dependencies: - execa "^7.0.0" - java-properties "^1.0.2" - env-paths@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" @@ -6292,30 +6324,30 @@ eslint-config-prettier@^9.0.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-import-resolver-webpack@^0.13.7: - version "0.13.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.7.tgz#49cd0108767b1f8ff81123c7e1ae362305aad47b" - integrity sha512-2a+meyMeABBRO4K53Oj1ygkmt5lhQS79Lmx2f684Qnv6gjvD4RLOM5jfPGTXwQ0A2K03WSoKt3HRQu/uBgxF7w== +eslint-import-resolver-webpack@^0.13.8: + version "0.13.8" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.8.tgz#5f64d1d653eefa19cdfd0f0165c996b6be7012f9" + integrity sha512-Y7WIaXWV+Q21Rz/PJgUxiW/FTBOWmU8NTLdz+nz9mMoiz5vAev/fOaQxwD7qRzTfE3HSm1qsxZ5uRd7eX+VEtA== dependencies: - array.prototype.find "^2.2.1" + array.prototype.find "^2.2.2" debug "^3.2.7" enhanced-resolve "^0.9.1" find-root "^1.1.0" - has "^1.0.3" + hasown "^2.0.0" interpret "^1.4.0" - is-core-module "^2.13.0" + is-core-module "^2.13.1" is-regex "^1.1.4" lodash "^4.17.21" - resolve "^2.0.0-next.4" + resolve "^2.0.0-next.5" semver "^5.7.2" eslint-module-utils@^2.8.0: @@ -6335,26 +6367,26 @@ eslint-plugin-filenames@^1.3.2: lodash.snakecase "4.1.1" lodash.upperfirst "4.3.1" -eslint-plugin-import@^2.28.1: - version "2.28.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz#63b8b5b3c409bfc75ebaf8fb206b07ab435482c4" - integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== +eslint-plugin-import@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz#8133232e4329ee344f2f612885ac3073b0b7e155" + integrity sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg== dependencies: - array-includes "^3.1.6" - array.prototype.findlastindex "^1.2.2" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" + eslint-import-resolver-node "^0.3.9" eslint-module-utils "^2.8.0" - has "^1.0.3" - is-core-module "^2.13.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.fromentries "^2.0.6" - object.groupby "^1.0.0" - object.values "^1.1.6" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" semver "^6.3.1" tsconfig-paths "^3.14.2" @@ -6395,18 +6427,18 @@ eslint-plugin-jsx-a11y@^6.7.1: object.fromentries "^2.0.6" semver "^6.3.0" -eslint-plugin-mocha@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.1.0.tgz#69325414f875be87fb2cb00b2ef33168d4eb7c8d" - integrity sha512-xLqqWUF17llsogVOC+8C6/jvQ+4IoOREbN7ZCHuOHuD6cT5cDD4h7f2LgsZuzMAiwswWE21tO7ExaknHVDrSkw== +eslint-plugin-mocha@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.2.0.tgz#15b05ce5be4b332bb0d76826ec1c5ebf67102ad6" + integrity sha512-ZhdxzSZnd1P9LqDPF0DBcFLpRIGdh1zkF2JHnQklKQOvrQtT73kdP5K9V2mzvbLR+cCAO9OI48NXK/Ax9/ciCQ== dependencies: eslint-utils "^3.0.0" - rambda "^7.1.0" + rambda "^7.4.0" -eslint-plugin-prettier@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz#6887780ed95f7708340ec79acfdf60c35b9be57a" - integrity sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w== +eslint-plugin-prettier@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz#a3b399f04378f79f066379f544e42d6b73f11515" + integrity sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg== dependencies: prettier-linter-helpers "^1.0.0" synckit "^0.8.5" @@ -6471,18 +6503,19 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.49.0: - version "8.49.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" - integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== +eslint@^8.52.0: + version "8.52.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.52.0.tgz#d0cd4a1fac06427a61ef9242b9353f36ea7062fc" + integrity sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "8.49.0" - "@humanwhocodes/config-array" "^0.11.11" + "@eslint/js" "8.52.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -6654,7 +6687,7 @@ execa@^5.0.0, execa@^5.1.1: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -execa@^7.0.0, execa@^7.1.1: +execa@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/execa/-/execa-7.2.0.tgz#657e75ba984f42a70f38928cedc87d6f2d4fe4e9" integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== @@ -7072,15 +7105,6 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -7100,10 +7124,10 @@ forwarded@0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fraction.js@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== +fraction.js@^4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.6.tgz#e9e3acec6c9a28cf7bc36cbe35eea4ceb2c5c92d" + integrity sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg== fragment-cache@^0.2.1: version "0.2.1" @@ -7199,10 +7223,10 @@ fstream@^1.0.12: mkdirp ">=0.5 0" rimraf "2" -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== function.prototype.name@^1.1.2, function.prototype.name@^1.1.3, function.prototype.name@^1.1.5: version "1.1.5" @@ -7243,10 +7267,10 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= +get-func-name@^2.0.0, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: version "1.2.1" @@ -7436,6 +7460,18 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.1.7: + version "7.1.7" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" + integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -7551,10 +7587,10 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -got@^11.1.4: - version "11.8.5" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046" - integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ== +got@^11.8.3: + version "11.8.6" + resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" + integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== dependencies: "@sindresorhus/is" "^4.0.0" "@szmarczak/http-timer" "^4.0.5" @@ -7697,12 +7733,19 @@ hasha@^5.0.0: is-stream "^2.0.0" type-fest "^0.8.0" +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: +hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -8156,12 +8199,12 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" is-data-descriptor@^0.1.4: version "0.1.4" @@ -8620,11 +8663,6 @@ jalaali-js@^1.1.0: resolved "https://registry.yarnpkg.com/jalaali-js/-/jalaali-js-1.2.6.tgz#f4ee4bf686ed32bb9656f101225be4b7a1c3fd21" integrity sha512-io974va+Qyu+UfuVX3UIAgJlxLhAMx9Y8VMfh+IG00Js7hXQo1qNQuwSiSa0xxco0SVgx5HWNkaiCcV+aZ8WPw== -java-properties@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" - integrity sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ== - "jest-diff@>=29.4.3 < 30": version "29.5.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" @@ -9090,13 +9128,13 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -lerna@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.2.0.tgz#55ab3de42032168a75fc7a2ce184a8acb7efc5ea" - integrity sha512-E13iAY4Tdo+86m4ClAe0j0bP7f8QG2neJReglILPOe+gAOoX17TGqEWanmkDELlUXOrTTwnte0ewc6I6/NOqpg== +lerna@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/lerna/-/lerna-7.4.1.tgz#d124fa5f0a1fe10ae9a6081bc363d98f3f6caca9" + integrity sha512-c6sOO0dlJU689vStIsko+zjRdn2fJOWH8aNjePLNv2AubAdABKqfrDCpE2H/Q7+O80Duo68ZQtWYkUUk7hRWDw== dependencies: - "@lerna/child-process" "7.2.0" - "@lerna/create" "7.2.0" + "@lerna/child-process" "7.4.1" + "@lerna/create" "7.4.1" "@npmcli/run-script" "6.0.2" "@nx/devkit" ">=16.5.1 < 17" "@octokit/plugin-enterprise-rest" "6.0.1" @@ -9133,7 +9171,7 @@ lerna@^7.2.0: libnpmpublish "7.3.0" load-json-file "6.2.0" lodash "^4.17.21" - make-dir "3.1.0" + make-dir "4.0.0" minimatch "3.0.5" multimatch "5.0.0" node-fetch "2.6.7" @@ -9485,7 +9523,7 @@ lodash.upperfirst@4.3.1: resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce" integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984= -lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.15.0, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -9516,10 +9554,10 @@ loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -loupe@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.1.tgz#a2e1192c9f452e4e85089766da10ac8288383947" - integrity sha512-EN1D3jyVmaX4tnajVlfbREU4axL647hLec1h/PXAb8CPDMJiYitcWF2UeLVNttRqaIqQs4x+mRvXf+d+TlDrCA== +loupe@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" + integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== dependencies: get-func-name "^2.0.0" @@ -9587,12 +9625,12 @@ make-array@^1.0.5: resolved "https://registry.yarnpkg.com/make-array/-/make-array-1.0.5.tgz#326a7635c756a9f61ce0b2a6fdd5cc3460419bcb" integrity sha512-sgK2SAzxT19rWU+qxKUcn6PAh/swiIiz2F8C2cZjLc1z4iwYIfdoihqFIDQ8BDzAGtWPYJ6Sr13K1j/DXynDLA== -make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== +make-dir@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== dependencies: - semver "^6.0.0" + semver "^7.5.3" make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" @@ -9602,6 +9640,13 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-fetch-happen@^10.0.3: version "10.2.0" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.0.tgz#0bde3914f2f82750b5d48c6d2294d2c74f985e5b" @@ -9856,6 +9901,11 @@ mime@^2.5.2: resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== +mime@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" + integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -10727,14 +10777,14 @@ object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.5, object.entr define-properties "^1.1.4" es-abstract "^1.20.4" -object.fromentries@^2.0.3, object.fromentries@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== +object.fromentries@^2.0.3, object.fromentries@^2.0.6, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" object.getownpropertydescriptors@^2.0.3: version "2.1.2" @@ -10745,7 +10795,7 @@ object.getownpropertydescriptors@^2.0.3: define-properties "^1.1.3" es-abstract "^1.18.0-next.2" -object.groupby@^1.0.0: +object.groupby@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== @@ -10770,14 +10820,14 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.1, object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.values@^1.1.1, object.values@^1.1.6, object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" on-finished@2.4.1: version "2.4.1" @@ -11390,10 +11440,19 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.37.1: - version "1.37.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.37.1.tgz#cb517d52e2e8cb4fa71957639f1cd105d1683126" - integrity sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA== +playwright-core@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.39.0.tgz#efeaea754af4fb170d11845b8da30b2323287c63" + integrity sha512-+k4pdZgs1qiM+OUkSjx96YiKsXsmb59evFoqv8SKO067qBA+Z2s/dCzJij/ZhdQcs2zlTAgRKfeiiLm8PQ2qvw== + +playwright@1.39.0: + version "1.39.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.39.0.tgz#184c81cd6478f8da28bcd9e60e94fcebf566e077" + integrity sha512-naE5QT11uC/Oiq0BwZ50gDmy8c8WLPRTEWuSSFVG2egBka/1qMoSqYQcROMT9zLwJ86oPofcTH2jBY/5wWOgIw== + dependencies: + playwright-core "1.39.0" + optionalDependencies: + fsevents "2.3.2" posix-character-classes@^0.1.0: version "0.1.1" @@ -11414,10 +11473,10 @@ postcss@8.4.14: picocolors "^1.0.0" source-map-js "^1.0.2" -postcss@^8.4.29: - version "8.4.29" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.29.tgz#33bc121cf3b3688d4ddef50be869b2a54185a1dd" - integrity sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw== +postcss@^8.4.31: + version "8.4.31" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d" + integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ== dependencies: nanoid "^3.3.6" picocolors "^1.0.0" @@ -11637,12 +11696,19 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" -query-string@^6.12.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== +qs@^6.10.1: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== dependencies: - decode-uri-component "^0.2.0" + side-channel "^1.0.4" + +query-string@^7.0.0: + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== + dependencies: + decode-uri-component "^0.2.2" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" @@ -11689,10 +11755,10 @@ railroad-diagrams@^1.0.0: resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= -rambda@^7.1.0: - version "7.2.1" - resolved "https://registry.yarnpkg.com/rambda/-/rambda-7.2.1.tgz#c533f6e2def4edcd59f967df938ace5dd6da56af" - integrity sha512-Wswj8ZvzdI3VhaGPkZAxaCTwuMmGtgWt7Zxsgyo4P+iTmVnkojvyWaOep5q3ZjMIecW0wtQa66GWxaKkZ24RAA== +rambda@^7.4.0: + version "7.5.0" + resolved "https://registry.yarnpkg.com/rambda/-/rambda-7.5.0.tgz#1865044c59bc0b16f63026c6e5a97e4b1bbe98fe" + integrity sha512-y/M9weqWAH4iopRd7EHDEQQvpFPHj1AA3oHozE9tfITHUtTR7Z9PSlIRRG2l1GuW7sefC1cXFfIcF+cgnShdBA== randexp@0.4.6: version "0.4.6" @@ -11771,10 +11837,10 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" -react-hook-form@^7.46.1: - version "7.46.1" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.46.1.tgz#39347dbff19d980cb41087ac32a57abdc6045bb3" - integrity sha512-0GfI31LRTBd5tqbXMGXT1Rdsv3rnvy0FjEk8Gn9/4tp6+s77T7DPZuGEpBRXOauL+NhyGT5iaXzdIM2R6F/E+w== +react-hook-form@^7.47.0: + version "7.47.0" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.47.0.tgz#a42f07266bd297ddf1f914f08f4b5f9783262f31" + integrity sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg== "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^16.13.1, react-is@^16.7.0, react-is@^17.0.1, react-is@^18.0.0, react-is@^18.2.0: version "18.2.0" @@ -12212,21 +12278,21 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== -resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== +resolve@^1.10.0, resolve@^1.13.1, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== +resolve@^2.0.0-next.4, resolve@^2.0.0-next.5: + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -12664,10 +12730,10 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -sinon@^16.0.0: - version "16.0.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-16.0.0.tgz#06da4e63624b946c9d7e67cce21c2f67f40f23a9" - integrity sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ== +sinon@^16.1.3: + version "16.1.3" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-16.1.3.tgz#b760ddafe785356e2847502657b4a0da5501fba8" + integrity sha512-mjnWWeyxcAf9nC0bXcPmiDut+oE8HYridTNzBbF98AYVLmWwGRp2ISEpyhYflG1ifILT+eNn3BmKUJPxjXUPlA== dependencies: "@sinonjs/commons" "^3.0.0" "@sinonjs/fake-timers" "^10.3.0" @@ -13140,21 +13206,20 @@ strong-log-transformer@2.1.0, strong-log-transformer@^2.1.0: minimist "^1.2.0" through "^2.3.4" -styled-components@^5.3.11: - version "5.3.11" - resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.11.tgz#9fda7bf1108e39bf3f3e612fcc18170dedcd57a8" - integrity sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw== +styled-components@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-6.1.0.tgz#228e3ab9c1ee1daa4b0a06aae30df0ed14fda274" + integrity sha512-VWNfYYBuXzuLS/QYEeoPgMErP26WL+dX9//rEh80B2mmlS1yRxRxuL5eax4m6ybYEUoHWlTy2XOU32767mlMkg== dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/traverse" "^7.4.5" - "@emotion/is-prop-valid" "^1.1.0" - "@emotion/stylis" "^0.8.4" - "@emotion/unitless" "^0.7.4" - babel-plugin-styled-components ">= 1.12.0" - css-to-react-native "^3.0.0" - hoist-non-react-statics "^3.0.0" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/unitless" "^0.8.0" + "@types/stylis" "^4.0.2" + css-to-react-native "^3.2.0" + csstype "^3.1.2" + postcss "^8.4.31" shallowequal "^1.1.0" - supports-color "^5.5.0" + stylis "^4.3.0" + tslib "^2.5.0" styled-jsx@5.1.1: version "5.1.1" @@ -13163,7 +13228,7 @@ styled-jsx@5.1.1: dependencies: client-only "0.0.1" -"stylis-plugin-rtl-sc@npm:stylis-plugin-rtl@^2.1.1", stylis-plugin-rtl@^2.1.1: +stylis-plugin-rtl@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/stylis-plugin-rtl/-/stylis-plugin-rtl-2.1.1.tgz#16707809c878494835f77e5d4aadaae3db639b5e" integrity sha512-q6xIkri6fBufIO/sV55md2CbgS5c6gg9EhSVATtHHCdOnbN/jcI0u3lYhNVeuI65c4lQPo67g8xmq5jrREvzlg== @@ -13199,7 +13264,7 @@ supports-color@8.1.1, supports-color@^8.0.0, supports-color@^8.1.1: dependencies: has-flag "^4.0.0" -supports-color@^5.0.0, supports-color@^5.3.0, supports-color@^5.5.0: +supports-color@^5.0.0, supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== @@ -13568,7 +13633,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: +type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -13824,10 +13889,10 @@ upath@2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -14058,10 +14123,10 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.88.2: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== +webpack@^5.89.0: + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0"