Skip to content

[core] Allow opt-in data-* attributes on slotProps#22128

Merged
LukasTy merged 44 commits into
mui:masterfrom
LukasTy:claude/trusting-ishizaka-77d81a
Jul 17, 2026
Merged

[core] Allow opt-in data-* attributes on slotProps#22128
LukasTy merged 44 commits into
mui:masterfrom
LukasTy:claude/trusting-ishizaka-77d81a

Conversation

@LukasTy

@LukasTy LukasTy commented Apr 20, 2026

Copy link
Copy Markdown
Member

Fixes #22126.
Fixes #21817.

Summary

Allows data-* attributes on MUI X slotProps without an as any cast. It is opt-in and typed: MUI X inherits the augmentable DataAttributesOverrides hook that ships in @mui/material / @mui/utils v9.2 (mui/material-ui#48554, merged), so a consumer augments one module and it applies to both Material UI and every supported MUI X slot. Without augmentation nothing changes -- data-* on slotProps stays a TS error, matching today's behavior.

aria-* is untouched by this PR -- the augmentation adds only data-* keys. It already type-checks on the DOM-backed slots, whose inner type inherits React.AriaAttributes via an element or component base (HTMLAttributes, SVGAttributes, ButtonBaseProps, PopperProps, ...). A few non-DOM controller slots (for example the desktop FocusTrap) do not inherit it, but those are excluded from the widening anyway (see Coverage).

What consumers see

Augment @mui/material/utils once, anywhere reachable from the project's tsconfig:

declare module '@mui/material/utils' {
  interface DataAttributesOverrides {
    // Loose: accept any data-* key. Or list keys explicitly for autocomplete + typo-checking.
    [key: `data-${string}`]: string | number | boolean | undefined;
  }
}

Then data-* type-checks on any supported MUI X slot, including slots that rejected it before:

<DateCalendar slotProps={{ day: { 'data-testid': 'foo' } }} />;
<RichTreeView slotProps={{ item: { 'data-testid': 'bar' } }} />;

The same single augmentation covers Material UI slots too. Full docs: the Material UI TypeScript guide (https://mui.com/material-ui/guides/typescript/#allowing-data-attributes-on-slotprops).

How it works

  • MUI X imports WithDataAttributes from @mui/utils/types (shipped in 9.2) and widens each supported slot prop type with it. WithDataAttributes<T> = T | (T & DataAttributesOverrides) is a union, so pre-typed values stay assignable and the default (empty DataAttributesOverrides) makes the widening a no-op until a consumer augments.
  • Element-type slots keep using @mui/utils's SlotComponentProps directly, which now applies the same WithDataAttributes widening. MUI X's own SlotComponentPropsFromProps is used only where a slot is typed from a bare props type (PopperProps, TransitionProps, etc.), which SlotComponentProps cannot express since its first parameter must be a React.ElementType.
  • MUI X does not define its own hook: it consumes the shared @mui/utils DataAttributesOverrides interface directly, so there is a single augmentation surface rather than a competing MUI-X-only one.

Composition with slot module augmentation (#22519)

master shipped per-slot *PropsOverrides interfaces (#22519). This PR composes with them rather than replacing: a slot that carries a per-slot overrides interface becomes WithDataAttributes<Partial<X> & XxxPropsOverrides>, so per-slot overrides and the global data-* switch both work (slots without an overrides interface are simply WithDataAttributes<Partial<X>>). Whether to keep both mechanisms long-term is a follow-up decision (see below).

Requirements

Requires @mui/material / @mui/utils >= 9.2 for the @mui/material/utils augmentation path (already on master via the catalog bump). Consumers on older @mui/material, or packages that do not depend on Material UI at all (such as Chat Headless), can augment @mui/utils/types directly instead; that reaches MUI X slots only. MUI X ships @mui/utils >= 9.2 as its own dependency, so the slot types always resolve. On that fallback path a Material UI older than 9.2 (e.g. v7) has no DataAttributesOverrides to re-export and ships its own @mui/utils that never dedupes with MUI X's v9.2, so on v7 the @mui/material/utils path does not work at all. Instead, augment @mui/utils/types directly AND add @mui/utils (^9.2.0) as a direct dependency, so your augmentation resolves to the same v9.2 copy MUI X imports (confirmed with a Material 7 repro under pnpm and npm); upgrading Material UI to 9.2+ avoids the split entirely.

Coverage

Every slot-bearing package outside Data Grid is widened: x-charts, x-charts-pro, x-charts-premium, x-date-pickers, x-date-pickers-pro, x-tree-view, x-tree-view-pro, x-chat, x-chat-headless. Several carry no source change of their own -- x-tree-view, x-tree-view-pro, and x-chat-headless inherit the widening transitively (through their widened base *SlotProps, or through @mui/utils's SlotComponentProps for element-type slots) and are verified by their specs.

A few individual slots are deliberately left un-widened because their default component never forwards arbitrary props to the DOM: xAxis/yAxis and scatter in Charts (ChartsAxis never spreads the axis slots, and no default scatter renderer spreads unknown props onto an element) and desktopTrapFocus in the Pickers (FocusTrap renders no element and rejects unknown props via exactProp). This is the same type/runtime-forwarding reason Data Grid is excluded; the per-package specs mark these exclusions explicitly.

Data Grid is intentionally excluded. An earlier revision of this PR widened every grid slot as well, but that over-promised: many default grid slots do not forward arbitrary props at runtime, so a blanket type widening would have type-checked data-* that silently never renders. After review (@MBilalShafi), the grid widening was reverted and deferred to a follow-up (see below). Data Grid keeps its existing data-* support in the meantime (root/main index signatures, per-slot *PropsOverrides).

Two layers of test coverage back this:

  • Compile-time: each supported package has a spec (src/tests/slotDataAttributes.spec.ts) asserting every slot of every exported component and provider *SlotProps a consumer can pass slotProps to accepts data-* once DataAttributesOverrides is augmented -- including nested/plot-level and standalone components (e.g. ChatMessage) that declare their own slot props -- so a regression names the offending slot. Callback-form slots additionally assert that the callback return is widened independently -- both the shared SlotComponentPropsFromProps helper (in x-internals) and the hand-written Chat callbacks (messageActions, ChatMessage.actions) -- so wrapping the whole object-or-callback union cannot slip through.
  • Runtime: describeSlotsConformance gains a check that data-* and aria-* passed via slotProps actually reach the slot's DOM element (wired for TreeItem); the Pickers tabs slot additionally has a focused data-* forwarding test.

The data-* augmentation is documented in the shared "Custom slots" TypeScript guide, with per-package examples and the v9.2 / @mui/utils/types fallback callout.

Follow-ups

  • Data Grid data-*: re-introduce grid-wide data-* backed by real runtime forwarding plus tests, rather than a blanket type widening. This is the deferred half of this work, agreed with @MBilalShafi.
  • Runtime forwarding coverage: extend the new describeSlotsConformance data-*/aria-* check beyond TreeItem to the other packages' slots, so runtime forwarding is verified alongside the compile-time acceptance specs. Wiring data-* forwarding for the currently-excluded slots (xAxis/yAxis) can then re-enable their widening.
  • Charts mechanisms: decide whether to keep both the global WithDataAttributes switch and the per-slot *PropsOverrides interfaces ([charts] Support module augmentation for slots #22519) long-term, or converge on one -- a call for the Charts team.

Verification

  • pnpm typescript passes across all affected packages.
  • End-to-end: augmenting DataAttributesOverrides makes data-* assignable on every slot of every supported package's *SlotProps (enforced by the per-package compile-time specs); without the augmentation slots reject it.
  • pnpm eslint and pnpm prettier are clean on the changed files.

Related

Previously, passing `data-testid` or any `data-*` / `aria-*` attribute
through `slotProps` required an `as any` cast in TypeScript, even though
the attributes are forwarded at runtime. Users hit this any time they
wanted to attach a test id to a slot.

- Introduce `DataAttributes` in `@mui/x-internals/types` combining
  `React.AriaAttributes` with a `data-*` index signature and an
  augmentable `DataAttributesOverrides` interface for strongly-typed
  custom keys.
- Generalize `SlotComponentPropsFromProps` to accept either a props type
  or a React element type, and widen its output with `DataAttributes`
  via a union variant (so existing `as CustomProps` casts keep working).
- Drop `SlotComponentProps` from `@mui/utils/types` in favor of the
  unified helper across pickers, tree-view, chat, chat-headless.
- Consolidate Charts' ad-hoc `data-*` / `aria-*` index signatures on
  `ChartBaseCommonProps` to reuse `DataAttributes`.
- Extend `describeSlotsConformance` to assert `data-*` / `aria-*`
  forwarding per slot, and clean up the `as any` workarounds in
  PickerDay, tree-view, and chat-headless tests.

Closes mui#22126
@LukasTy LukasTy added typescript type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. labels Apr 20, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Apr 20, 2026

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 🔺+86B(+0.04%) 🔺+28B(+0.05%)
@mui/x-date-pickers-pro 🔺+264B(+0.10%) 🔺+92B(+0.12%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-scheduler 0B(0.00%) 0B(0.00%)
@mui/x-scheduler-premium 0B(0.00%) 0B(0.00%)
@mui/x-chat 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes

Performance

Total duration: 1,514.59 ms +56.03 ms(+3.8%) | Renders: 63 (+0) | Paint: 2,118.04 ms +116.75 ms(+5.8%)

Test Duration Renders
BarChartPro with big data amount 45.16 ms 🔺+21.32 ms(+89.4%) 2 (+0)
Area chart with big data amount (no marks) 61.03 ms 🔺+13.88 ms(+29.5%) 2 (+0)
ScatterChartPro with big data amount (single renderer) 22.76 ms 🔺+7.06 ms(+45.0%) 2 (+0)
RadialBarChart stacked with multiple series 24.58 ms 🔺+5.64 ms(+29.8%) 2 (+0)
ScatterChartPro with big data amount and zoomed in (single renderer) 11.35 ms 🔺+4.17 ms(+58.1%) 2 (+0)

…and 2 more (+19 within noise) — details

Metric alarms

Test Metric Change
BarChartPro with big data amount bench:paint 🔺 +43.79 ms
Area chart with big data amount (no marks) bench:paint 🔺 +25.80 ms
ScatterChartPro with big data amount (single renderer) bench:paint 🔺 +16.36 ms
ScatterChartPro with big data amount and zoomed in (single renderer) bench:paint 🔺 +5.63 ms

Check out the code infra dashboard for more information about this PR.

LukasTy and others added 5 commits April 21, 2026 00:20
Resolves the OOM/assignability regressions caused by directly intersecting
`DataAttributes` into slot props. `WithDataAttributes<T> = T | (T & DataAttributes)`
keeps pre-typed values assignable to the original branch while object literals
can pick up the widened branch for `data-*` / `aria-*` keys.

Applied across pickers, charts, data-grid, chat, and tree-view slot types.
Added `slotDataAttributes.spec.ts` files using the new
`AssertAllSlotsAcceptDataAttributes` helper so every package compile-time
verifies every slot accepts `data-*` / `aria-*`.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Removes the `as any` data-*/aria-* casts from tests that the type widening
now makes unnecessary, and widens the remaining nested `htmlInput` /
`input` / `inputLabel` slot types inside `gridBaseSlots.ts` so Data Grid
consumers can reach the same attributes without a cast.

Also drops the `(other as any)['data-testid']` cast in
`DateRangePickerDay.tsx` and replaces the `props: any` parameter in
`describeSlotsConformance` with a documented loose interface. Per-slot
type-level coverage continues to live in the `slotDataAttributes.spec.ts`
files, since dynamic `[slotName]` keys cannot be statically matched to a
component's typed `slotProps` shape inside the helper.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Two review findings + one latent bug surfaced by fixing the second:

1. SlotComponentPropsFromProps: widen the callback branch via
   WithDataAttributes (union preserving T) instead of an unconditional
   `& DataAttributes` intersection. Consumers that return a cast custom
   type from the callback stay assignable without having to declare a
   data-* index signature on that type.

2. AssertAllSlotsAcceptDataAttributes: probe both data-* and aria-label
   as marker keys, so the assertion actually enforces what its name and
   error message claim. The previous `V extends { [k: data-...]: any }`
   structural check was trivially satisfied by any object without
   conflicting keys, so unwidened slots silently passed.

3. Fixing (2) surfaced slots that had `Partial<SomeProps>` without
   widening. Wrapped each with `WithDataAttributes<>`:
     - charts: mark, tooltip (ChartsTooltipSlotProps), scatter,
       ScatterChartSlotProps.tooltip, pieArc, pieArcLabel
     - charts-pro: HeatmapSlotProps.tooltip, HeatmapItemSlotProps.cell,
       HeatmapTooltipSlotProps.tooltip, ChartsToolbarProSlotProps.toolbar,
       ScatterChartProSlotProps.tooltip
     - date-pickers: PickersModalDialogSlotProps.{dialog, mobilePaper,
       mobileTransition}

Distribution mechanics in the aria check: the outer `V extends any`
forces distribution over union variants, so a `T | (T & DataAttributes)`
shape passes as soon as the widened branch exposes both keys. Without
that, `keyof (A | B)` collapses to `keyof A & keyof B` and neither marker
is visible.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@LukasTy LukasTy self-assigned this Apr 21, 2026
Comment thread packages/x-internals/src/types/AssertSlotDataAttributes.spec.ts Outdated
Comment thread packages/x-internals/src/types/AssertSlotDataAttributes.ts Outdated
* }
* }
*/
export interface DataAttributesOverrides {}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but why does user needs module-augmentation if L:25 already there.

I have another proposal, how about removing "[k: data-${string}]: DataAttributeValue" and provide only DataAttributesOverrides? Meaning user needs to augment DataAttributesOverrides to pass the data-*

This will give them type-safe through out their app, I don't think data-* will be customized over 10 values for a project.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting to support data-* attributes only through augmentation?
That's not a bad idea, but still one that would cause way too much pain, especially given that the need for this is a result of the API we chose.

With the data-* TS accepts data attributes without needing to cast or add @ts-expect-error.
The Overrides allow specific typing (IntelliSense).

@siriwatknp siriwatknp Apr 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you suggesting to support data-* attributes only through augmentation?

Yes, users get type-safe benefits and autocompletion.

With the data-* TS accepts data attributes without needing to cast or add @ts-expect-error.
The Overrides allow specific typing (IntelliSense).

True, but if you type data-testiid, I believe that this will not throw a type error.

My argument is that we should let users decide on the level of type safety they want to have because they can also do:

declare module "…" {
  interface DataAttributesOverrides {
    [k: `data-${string}`]: DataAttributeValue
  }
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@siriwatknp, your proposal makes sense.
My only gripe is the fact that some packages (Data Grid and Charts) already have explicit data-* definitions, while others lack these explicit definitions.
Going with your proposal would keep the existing inconsistency between X libraries. 🤔

Do you still agree that an opt-in approach is the best compromise?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My only gripe is the fact that some packages (Data Grid and Charts) already have explicit data-* definitions, while others lack these explicit definitions.

I think that's our internal implementation and those works because it's directly used on JSX.

The opt-in approach is for consumer, which I still prefer this as starting point. If there is a push from users to make it data-{string}, it can still be done.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyway, it's your call. I don't have strong preference, just thinking that the opt-in is a good first step.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I was leaning towards doing this PR as an experiment and then apply the changes in the material ui repo (mui/material-ui#33175) so that it trickles down to this repo.
The only gripe is that is gonna be optional based on the material package version you use, but we can clarify it in the docs.
WDYT, should I try tackling that issue at the root location?

If possible, I would like to avoid the need to change the module declaration type from declare module '@mui/x-internals/types' to declare module '@mui/utils/types'.

@siriwatknp siriwatknp May 5, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, that makes sense. If fixing in Material UI benefits both, then better.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR has been updated and is ready for a re-review. 👌
It has become so much smaller due to the type widening from the SlotComponentProps from @mui/utils/types.

There is one missing piece in X docs - a doc section.
I haven't added it, because there is no single TypeScript page, and I didn't want to duplicate it in so many sub-product TypeScript sections.

If you feel like it is essential, I can add a small section also linking to material-ui docs.
It might be worth noting that to fully benefit from this, you need to use >=9.2.0 of material package.

LukasTy and others added 2 commits April 22, 2026 11:17
- AssertSlotDataAttributes.spec.ts: replace commented-out negative case
  with a real `interface BadSlotProps` + `@ts-expect-error` directive so
  the sanity check is now self-verifying (if the helper stops catching
  bad slots, the unused directive fails the typecheck).
- AssertSlotDataAttributes.ts: swap "iff" for plain "if" / "only when"
  in the two doc comments that used it.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
Every real slot inner type in MUI X already inherits React.AriaAttributes
through its element or component base type (HTMLAttributes, SVGAttributes,
ButtonBaseProps, PopperProps, etc.), so including aria-* in the shared
DataAttributes widener was redundant. Narrow the widener to only add the
data-* index signature that React's built-in types don't otherwise expose,
so the helper does exactly what its name says.

- DataAttributes no longer intersects React.AriaAttributes; only the
  `[k: `data-${string}`]` index signature plus user overrides remain.
- AssertAllSlotsAcceptDataAttributes now checks a single marker (`data-x`)
  instead of both data-x and aria-label, and its failure message is
  updated accordingly.
- ChartBaseCommonProps uses React.DOMAttributes (not HTMLAttributes), so
  aria is re-added explicitly there to preserve current behavior.
- Per-package slotDataAttributes.spec.ts comments updated.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@LukasTy

LukasTy commented Apr 22, 2026

Copy link
Copy Markdown
Member Author

Follow-up: tightened DataAttributes to only add the data-* index signature (commit ad974f0).

Why

DataAttributes previously intersected React.AriaAttributes as well. Every real slot inner type in this repo already inherits AriaAttributes through its element or component base:

  • HTML element strings ('li', 'div', 'input', ...) resolve to React.HTMLAttributes ‚Üí AriaAttributes.
  • SVG element strings ('rect', ...) resolve to React.SVGAttributes ‚Üí AriaAttributes.
  • Component references resolve via React.ComponentPropsWithRef<typeof X> to whatever X declares, which in MUI X consistently descends from the HTMLAttributes/SVGAttributes hierarchy (ButtonBaseProps, PopperProps, ChartsTooltipContainerProps extends Partial<PopperProps>, etc.).
  • HeatmapCellProps extends React.ComponentPropsWithRef<'rect'>, PickerDayProps extends Omit<ButtonBaseProps, ...>, GridSlotProps root/main use React.HTMLAttributes<HTMLDivElement>, and so on.

A repo-wide grep for narrow inline types (SlotComponentPropsFromProps<{ with an object-literal first argument) returned a single match: the synthetic spec added in this PR. No production slot relied on DataAttributes for aria coverage.

So including aria-* in DataAttributes was redundant for every real consumer. The cost of leaving it in was small but real:

  • Aria keys showed up twice in hover popups on every widened slot union (once from the inner HTMLAttributes branch, once from the intersection with DataAttributes).
  • Every conditional-type distribution over WithDataAttributes<T> had to carry the full AriaAttributes surface through with it.
  • The name DataAttributes was doing two things; now it does exactly what it says.

What changed

  • DataAttributes is now just DataAttributesOverrides & { [k: \data-${string}`]: DataAttributeValue }`.
  • AssertAllSlotsAcceptDataAttributes drops the 'aria-label' extends keyof V probe and checks only 'data-x' extends keyof V. Failure message updated to match.
  • ChartBaseCommonProps is the one type that intersects React.DOMAttributes (event handlers only) rather than React.HTMLAttributes, so it re-adds React.AriaAttributes explicitly to preserve its previous aria surface.
  • Per-package slotDataAttributes.spec.ts comments updated for wording consistency.

Verification

  • pnpm --filter "@mui/x-internals" run typescript - passes.
  • pnpm --filter "@mui/x-charts*" run typescript - passes (including the ChartBaseCommonProps compensating change).
  • pnpm --filter "@mui/x-date-pickers*" run typescript, @mui/x-tree-view*, @mui/x-data-grid*, @mui/x-chat* - all pass.
  • pnpm exec prettier --check and pnpm eslint on the changed files - clean.
  • All existing per-package slotDataAttributes.spec.ts assertions still pass: every slot still accepts data-*, and aria still flows through via each slot's own base type.

The runtime test in describeSlotsConformance that asserts aria-* forwarding remains unchanged and green: the underlying DOM still accepts aria regardless, and at the type level each slot's inner type already carries AriaAttributes.

Performance note

The existing perf numbers in the PR body represent the upper bound. Dropping React.AriaAttributes from the intersection strictly reduces distribution work, so the already-tiny +0.3-0.7% on type/instantiation counts only gets smaller after this follow-up.

@LukasTy LukasTy changed the title [core] Allow data-* and aria-* attributes on slotProps [core] Allow data-* attributes on slotProps Apr 22, 2026
@LukasTy LukasTy changed the title [core] Allow data-* attributes on slotProps [core] Expand slotProps to allow data-* attributes Apr 22, 2026
@LukasTy LukasTy added the scope: all components Widespread work has an impact on almost all components. label Apr 22, 2026
@LukasTy

LukasTy commented Apr 22, 2026

Copy link
Copy Markdown
Member Author

@siriwatknp I have noticed that the majority of the slots extend some React HTML element attributes, which downstream extend the AriaAttributes. Hence, I removed the explicit aria-* attributes as that would just be extra redundant noise.

@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label Apr 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

…shizaka-77d81a

# Conflicts:
#	packages/x-date-pickers-pro/src/DesktopDateRangePicker/tests/DesktopDateRangePicker.test.tsx
@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label Apr 29, 2026
Per team agreement, MUI X slot props should not include arbitrary `data-*`
keys by default. The single supported way for consumers to type those keys
is to augment the `DataAttributesOverrides` interface, which they can shape
to whichever level of strictness they want.

- DataAttributes is now equivalent to DataAttributesOverrides (empty by
  default). The default `[k: \`data-${string}\`]` index signature is
  removed; WithDataAttributes becomes a dormant widener that activates only
  when a consumer augments DataAttributesOverrides.
- Drop the AssertAllSlotsAcceptDataAttributes helper and its 13 per-package
  `slotDataAttributes.spec.ts` files: there's no default contract to assert
  anymore.
- Restore the `(other as any)['data-testid']` source compat casts in
  PickerDay and DateRangePickerDay (they were removed on the assumption
  data-* would be typed by default; reverting that assumption needs the
  casts back).
- Restore the `as any` casts in tests that pass `data-*` via slotProps.
  Tests still verify runtime forwarding; the cast just re-acknowledges
  that the type-level contract is opt-in.
- Revert chartsBaseSlotProps and gridSlotsComponentsProps RootProps/MainProps
  to their master shapes — those public types already had ad-hoc `data-*`
  index signatures; the new direction is to leave per-component decisions
  alone, not to centralize them.
- DesktopDateRangePicker popper test uses `getByRole('tooltip')` instead
  of `data-testid` to find the popper element.
- Update describeSlotsConformance comment to drop the reference to the
  deleted spec files; the runtime `data-*` / `aria-*` forwarding test on
  the helper itself stays — it's a runtime check, independent of the type
  surface.

Kept: WithDataAttributes wrapper, SlotComponentPropsFromProps unification,
all per-slot widenings across charts, pickers, tree-view, chat, and the
data-grid slot map. With empty DataAttributes these are no-ops, but they
form the load-bearing surface that activates as soon as a consumer
augments DataAttributesOverrides.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@LukasTy LukasTy changed the title [core] Expand slotProps to allow data-* attributes [core] Add opt-in DataAttributesOverrides augmentation for data-* on slot props Apr 29, 2026
The runtime event-forwarding test added on top of the latent slot
coverage relied on `screen.getByRole('tooltip')` to find the popper,
but in `<DesktopDateRangePicker open>` rendered without user
interaction the popper does not expose the `tooltip` role. The test
failed 100% on all 4 CI pipelines (regular and React 18). The check
isn't load-bearing for the rest of the slot type-coverage work.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
@LukasTy
LukasTy requested a review from siriwatknp April 29, 2026 12:07

@siriwatknp siriwatknp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great improvement!

@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 5, 2026
@LukasTy

LukasTy commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

Claude Opus 4.8 reply

I verified all four against the code (tracing the runtime paths, not just the types); all four are real. Addressed in three commits: 8af03a1 (exclude non-forwarding slots), c6254be (messageActions), 445b54b (docs).

  1. Fallback augmentation (P1) -- fixed. Confirmed: MUI X peer-deps @mui/material ^7.3.0 || ^9.0.0, so a Material UI 7 app (with its own @mui/utils@7) is a supported setup with two @mui/utils copies, and the consumer's declare module '@mui/utils/types' augments the wrong one. The docs now require adding @mui/utils (>= 9.2) as a direct dependency so both resolve to one copy (445b54b). I did not add an X-specific augmentation surface: that reintroduces the competing MUI-X-only augmentation API this PR deliberately removed (and that this review praises removing), so I'd rather keep the single shared surface and fix the resolution requirement in docs.

  2. xAxis / yAxis (P1) -- fixed. Confirmed ChartsAxis passes the whole slotProps bag down but never applies slotProps.xAxis / slotProps.yAxis, so both data-* and the pre-existing *PropsOverrides are discarded (that runtime gap predates this PR; the widening just made it visible). Un-widened both (8af03a1). I checked the sibling container slots too -- toolbar, legend, tooltip root, and the plot leaves (mark / scatter / pieArc) all spread onto their DOM/SVG, so this is scoped to xAxis / yAxis.

  3. desktopTrapFocus (P2) -- fixed, and it's worse than a silent no-op: FocusTrap renders no DOM element and its exactProp guard throws a dev-time error on unknown props, so data-* can never reach the DOM. Un-widened (8af03a1). Left desktopTransition / mobileTransition widened -- those forward through the transition clone to the Paper.

  4. messageActions callback (P2) -- fixed. It was the only slot wrapping the whole object-or-callback union; rewrote it to widen each branch (matching SlotComponentPropsFromProps) so the callback return is checked like the object form (c6254be). Added a spec asserting the callback return accepts data-* on its own -- you're right that the package assertion passes as long as any union branch has the marker, so it could not have caught this.

For the excluded slots, the per-package specs now pass an explicit exclusion list rather than silently dropping them, so the exclusion is documented and a future regression on the remaining slots still surfaces.

On the aria sub-claim: correct that MuiTrapFocusProps does not inherit AriaAttributes, though it is moot for the widening itself -- DataAttributesOverrides carries only data-* keys (aria was dropped earlier), so the augmentation never adds aria. I softened the description's "every slot" wording regardless.

On the broader recommendation (make runtime forwarding a prerequisite, audit + runtime-test every slot): I'd keep that as the agreed follow-up rather than a merge gate. Tracing the widened slots, nearly all forward at runtime, and the specific non-forwarding ones you found are now excluded on the same principle as Data Grid. A full per-slot runtime-forwarding audit with runtime tests across Charts / Pickers / Chat (alongside the Data Grid work) is the tracked follow-up.

@LukasTy

LukasTy commented Jul 14, 2026

Copy link
Copy Markdown
Member Author

GPT 5.6 Sol Follow-up finding

[P1] slotProps.scatter still advertises data-*, but every default renderer drops it.

[ScatterPlotSlotProps.scatter](https://github.com/mui/mui-x/blob/445b54b89c77a2dd93f00cb6b1542b7989dd9fde/packages/x-charts/src/ScatterChart/ScatterPlot.tsx#L30-L32) remains wrapped in WithDataAttributes, and the props are passed to ScatterItems [here](https://github.com/mui/mui-x/blob/445b54b89c77a2dd93f00cb6b1542b7989dd9fde/packages/x-charts/src/[Scatter](https://github.com/mui/mui-x/blob/445b54b89c77a2dd93f00cb6b1542b7989dd9fde/packages/x-charts/src/ScatterChart/Scatter.tsx#L82-L124)Chart/ScatterPlot.tsx#L139-L152). However:

Consequently this type-checks but produces no locator:

<ScatterChart
  slotProps={{
    scatter: { 'data-testid': 'scatter-series' },
  }}
/>

This directly contradicts the response’s statement that the checked plot leaves, including scatter, spread onto their SVG elements. It is the same type/runtime mismatch as the corrected xAxis/yAxis issue.

Recommended resolution: either remove WithDataAttributes from scatter and explicitly exclude it from the compile-time assertions, or define forwarding semantics for every renderer and add runtime coverage for svg-single, svg-batch, and progressive rendering.

Previous findings

The four original findings are otherwise addressed correctly:

  • xAxis and yAxis are no longer widened.
  • desktopTrapFocus is no longer widened.
  • Both messageActions forms are widened independently, and the callback return has targeted type coverage.
  • The docs now explain that fallback augmentation requires the consumer and MUI X to resolve the same @mui/utils.

Design assessment

The central design rule should be: WithDataAttributes is appropriate only when the default slot has a defined DOM root and forwards arbitrary props to it. The current compile-time test verifies only assignability; it cannot establish that an attribute reaches the DOM. The remaining scatter bug shows that postponing the runtime audit leaves the advertised contract unreliable.

Two non-blocking concerns remain:

  • The test utility still says every real slot inner type inherits React.AriaAttributes, despite MuiTrapFocusProps disproving that statement.
  • “Add @mui/utils v9.2.0 or later so the copies dedupe” is too absolute. A direct dependency does not itself guarantee one resolved version, especially with an existing lockfile or a future incompatible major. The docs should recommend a compatible range such as ^9.2.0 and explicitly tell consumers to verify/dedupe the lockfile.

I reviewed exact head 445b54b89c77a2dd93f00cb6b1542b7989dd9fde. Charts, Chat, and Date Pickers TypeScript checks pass, and the PR’s reported CI is green; those checks do not exercise this runtime contract. I did not post or modify anything.

LukasTy and others added 6 commits July 14, 2026 18:12
A follow-up audit traced every widened charts slot to the DOM. `scatter` was
the one remaining mismatch: ScatterPlot spreads slotProps.scatter onto
ScatterItems, but all three default renderers (svg-single Scatter, svg-batch
BatchScatter, progressive ScatterAsync) destructure named props with no
`...other`, so `data-*` never reaches an element. Un-widen it and exclude it
from the ScatterChart/ScatterChartPro assertions, same as xAxis/yAxis.

The other traced slots (marker, mark, pieArc, pieArcLabel, funnelSection,
funnelSectionLabel, cell, tooltip, root, and the four axis leaves) do forward
`...other` onto an intrinsic element and stay widened.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The comment claimed every slot inner type inherits React.AriaAttributes, which
MuiTrapFocusProps disproves. Reword to the accurate reason aria isn't checked:
this widener only adds data-* keys (DataAttributesOverrides carries no aria
keys), so aria is simply out of its scope.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adding a direct dependency alone doesn't guarantee a single resolved version
(an existing lockfile or a future major can keep two copies). Recommend the
`^9.2.0` range and tell consumers to verify the lockfile resolves @mui/utils
to one copy, running their package manager's dedupe if needed.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The previous wording implied dedupe collapses everything to a single
@mui/utils copy. It can't: an older Material UI (v7) pins @mui/utils@^7, which
is disjoint from MUI X's @mui/utils@^9.2, so two copies always coexist. Clarify
that a direct @mui/utils@^9.2 dependency helps only by making your augmentation
resolve to the same v9.2 copy MUI X uses (the v7 copy stays separate), that
this depends on the package manager, and that upgrading Material UI to 9.2+ is
the reliable fix.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Empirically checked the Material UI 7 + MUI X case (real @mui/[email protected]
pulling @mui/[email protected], a package consuming @mui/[email protected], and tsc):
without a direct @mui/[email protected] dependency the augmentation targets Material's
v7 copy and data-* is rejected; adding @mui/utils@^9.2.0 as a direct dependency
makes it resolve to MUI X's v9.2 copy and it type-checks (confirmed under both
pnpm and npm). The two copies still coexist -- the fix dedupes the v9.2 copies,
not the v7 one. Reword from the earlier speculative "best-effort" phrasing.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@LukasTy

LukasTy commented Jul 15, 2026

Copy link
Copy Markdown
Member Author

All points from both reviews are addressed. Summary with commits:

Original findings

  1. Fallback augmentation (P1) -- fixed, and now verified rather than argued. I built a real repro (Material UI 7.3.11 pulling @mui/[email protected], a package consuming @mui/[email protected], and tsc): without a direct @mui/[email protected] dependency the augmentation lands on Material's v7 copy and data-* is rejected; adding @mui/utils@^9.2.0 as a direct dependency makes it resolve to MUI X's v9.2 copy and it type-checks (confirmed under pnpm and npm). You were right that the v7 and v9.2 copies never dedupe -- the direct dep dedupes the two v9.2 copies, not the v7 one. Docs now say exactly this, with "upgrade Material UI to 9.2+" as the simplest fix (dc1d3bc). I did not add an X-specific augmentation surface: it reintroduces the competing MUI-X-only augmentation API this PR deliberately removed (and that the review praises removing).

  2. xAxis / yAxis (P1) -- fixed. ChartsAxis passes the whole slotProps bag down but never applies slotProps.xAxis / yAxis, so both data-* and the pre-existing *PropsOverrides are dropped (that runtime gap predates this PR). Un-widened both (8af03a1).

  3. desktopTrapFocus (P2) -- fixed, and worse than a no-op: FocusTrap renders no DOM element and its exactProp guard throws in dev on unknown props. Un-widened (8af03a1). desktopTransition / mobileTransition stay widened -- they forward through the transition clone to the Paper.

  4. messageActions callback (P2) -- fixed. Rewrote it to widen each branch (matching SlotComponentPropsFromProps) so the callback return is checked like the object form, and added a spec asserting the callback return accepts data-* on its own (c6254be).

Follow-up finding

  1. scatter (P1) -- fixed, and you were right that my "the plot leaves forward" claim was wrong. All three default renderers (svg-single Scatter, svg-batch BatchScatter, progressive ScatterAsync) destructure named props with no ...other, so data-* never reaches an element. Un-widened + excluded from the assertions (1953c3c). To avoid another round, I then traced every widened charts slot to its final element: scatter was the only remaining miss. The rest genuinely reach an intrinsic element via ...other -- marker -> <circle>, mark -> styled path/circle, pieArc/pieArcLabel, funnelSection/funnelSectionLabel, cell -> <rect>, tooltip/root -> Popper div, and the four axis leaves.

Non-blocking

  1. The assertion helper's aria comment no longer claims every slot inner type inherits React.AriaAttributes (MuiTrapFocusProps disproves it); it now states aria isn't checked because the widener only adds data-* keys (DataAttributesOverrides has no aria keys) (67f8807).

  2. The "add @mui/utils and it dedupes" guidance was too absolute -- rewritten per the verified repro in point 1 (two copies coexist; the direct dep only collapses the v9.2 copies).

On the design rule (widen only where the default slot has a DOM root that forwards): agreed, and the scatter miss is a fair hit against a compile-time-only check. I've now applied that rule across the full charts surface by tracing each slot; a broad per-slot runtime-forwarding test sweep across Charts / Pickers / Chat, plus Data Grid, stays the tracked follow-up rather than a merge gate.

@LukasTy

LukasTy commented Jul 15, 2026

Copy link
Copy Markdown
Member Author

GPT 5.6 Sol Findings

[P1] ScatterChartPremium.tooltip is still not widened

ScatterChartPremiumSlotProps removes the community tooltip definition and reintroduces it without WithDataAttributes:

tooltip?: Partial<ChartsTooltipProps<'item' | 'none'>> & TooltipPropsOverrides;

This makes Premium inconsistent with both ScatterChart and ScatterChartPro. A strict probe against the exact head produces:

TS2353: 'data-followup' does not exist in type
'Partial<ChartsTooltipProps<"none" | "item">> & TooltipPropsOverrides'

Yet the runtime does forward these props into ChartsTooltip here, so this is precisely a slot that should be widened.

The reason CI misses it is that the Premium assertion suite does not include ScatterChartPremiumSlotProps; despite claiming package-wide coverage, it currently covers only Bar, Candlestick, container/provider, and Heatmap types.

Recommended fix:

  • Prefer inheriting the already-correct community tooltip definition instead of omitting and recreating it; or wrap the recreated type in WithDataAttributes.
  • Add assertions for ScatterChartPremiumSlotProps, with xAxis | yAxis | scatter explicitly excluded.
  • Audit the other exported Premium types currently absent from the suite, especially Radial Bar and Radial Line.

[P2] The documented augmentation fails in a standalone declaration file

The example begins directly with declare module '@mui/material/utils'.

If a consumer follows “augment … once, anywhere in your project” by placing that block in a standalone mui.d.ts without imports or exports, TypeScript treats it as an ambient module declaration rather than augmentation. I reproduced this against Material/UI Utils 9.2: data-* remained rejected. Adding this line made the same repro pass:

import type {} from '@mui/material/utils';

That import should be included in the snippet. The @mui/utils/types fallback needs the equivalent import when shown as a standalone declaration.

[P2] The Material v7 fallback incorrectly calls its Utils copy “unused”

The warning says Material v7’s @mui/utils@7 copy “stays separate and unused.” It is still used internally by Material v7; only the consumer’s augmentation is redirected to the v9.2 copy used by MUI X.

More importantly, this workaround only enables data-* on MUI X slots. It does not retrofit Material v7 slots with the v9.2 augmentation mechanism. The docs should state that explicitly:

Material UI continues using its v7 copy. The direct v9.2 dependency makes the augmentation reach MUI X slots only.

The preceding claim that Material 9.2+ “always” resolves one shared copy is also stronger than package managers guarantee when an existing lockfile or override preserves different compatible 9.x versions. “Normally resolves” plus the same lockfile verification advice would be more accurate.

LukasTy and others added 3 commits July 15, 2026 15:35
ScatterChartPremium re-declared the tooltip slot without WithDataAttributes
(dropping the widening it inherits from the community ScatterChart), yet the
tooltip forwards data-* to the DOM at runtime -- so it should be widened. Wrap
it like community/pro.

The gap hid because the premium spec asserted only 5 of the 14 exported
*SlotProps. Add the rest (ScatterChartPremium, RadialBarChart, RadialLineChart,
the geo/radial providers, and the bar/scatter/radial plot types), excluding
xAxis/yAxis/scatter where they appear -- same as the other charts specs.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The comment claimed "every exported SlotProps type", but the specs assert the
top-level component and provider *SlotProps -- the surfaces a consumer passes
slotProps to. Reword to say that, and note that nested-component slot props
(plot elements, use* hooks) are exercised transitively through their parent.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Add `import type {} from '@mui/material/utils'` to the snippet: in a
  standalone declaration file the block is otherwise an ambient module
  declaration, not augmentation, and data-* stays rejected (verified).
- Note the same marker for the `@mui/utils/types` fallback.
- Material UI v7 keeps using its own @mui/utils copy; the workaround only
  enables data-* on MUI X slots, not Material UI v7 slots.
- Soften "always resolves one shared copy" to "normally", with the lockfile
  caveat for overrides/resolutions.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@LukasTy

LukasTy commented Jul 15, 2026

Copy link
Copy Markdown
Member Author

Claude Opus 4.8 reply

All three addressed (224af3e, 2bb1459, 6be5f68):

[P1] ScatterChartPremium.tooltip -- fixed. It re-declared the slot without WithDataAttributes while the tooltip forwards to the DOM at runtime, so it's now widened like community/pro. The gap hid because the premium spec covered only 5 of 14 exported *SlotProps; I added the rest (ScatterChartPremium, RadialBarChart, RadialLineChart, the geo/radial providers, and the bar/scatter/radial plot types), excluding xAxis/yAxis/scatter where present. I also grepped every charts package for the same re-declare-without-widening pattern -- tooltip was the only instance -- and confirmed no other top-level component is missing from a suite (SankeyChart etc. are covered).

[P2] standalone declaration file -- fixed, and confirmed with a repro: augmenting the re-export @mui/material/utils in a standalone .d.ts without a module marker is treated as an ambient declaration and data-* stays rejected; adding import type {} from '@mui/material/utils' makes it real augmentation. Added that import to the snippet and noted the same for the @mui/utils/types fallback.

[P2] wording -- fixed. Material UI v7 keeps using its own @mui/utils copy; the direct v9.2 dependency only redirects your augmentation, so it enables data-* on MUI X slots, not Material UI v7 slots. Softened "always resolves one shared copy" to "normally", with the lockfile/override verification caveat.

On coverage scope: the specs assert every top-level component and provider *SlotProps (the surfaces you pass slotProps to); nested slot props (plot elements, use* hooks) are exercised through their parent. I corrected the comments to say that instead of "every exported type".

LukasTy and others added 4 commits July 15, 2026 18:20
ChatMessage is a public component with its own ChatMessageSlotProps that
declared raw Partial<...> fields, so data-* was rejected on <ChatMessage
slotProps={{ avatar: { 'data-testid' } }} /> even though the props forward at
runtime. ChatBoxSlotProps was widened but does not transitively cover this
independent declaration. Widen the object fields and both actions branches,
matching ChatBox.messageActions.

Add spec coverage via NonNullable<ChatMessageProps['slotProps']> (the slot
props type is not itself exported), plus ChatMessageList/ChatMessageGroup and a
callback-return guard for ChatMessage.actions, so future independent
declarations cannot escape coverage.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Extend the compile-time specs to cover every consumer-reachable *SlotProps, not
just the top-level charts, so an independently-declared surface (as ChatMessage
was) cannot escape coverage:

- x-charts: the plot/element components exported from the root (Bar/Line/Pie/
  Scatter plots and elements, ChartsTooltip), imported via their chart subpaths.
- x-charts-pro: FunnelPlot, HeatmapPlot, HeatmapTooltip, SankeyTooltip and the
  pro container/provider/toolbar surfaces.
- x-chat-headless: MessageError, StreamingIndicator.
- x-date-pickers-pro: MultiInputDateRangeField.

Internal use*-hook slot-prop types and non-exported base interfaces are left to
their parent's coverage. Non-forwarding slots (xAxis/yAxis/scatter) stay
excluded where those surfaces inherit them.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
On Material UI 7 the @mui/material/utils path does not work at all: v7 has no
DataAttributesOverrides to re-export, and augmenting it targets Material's own
v7 @mui/utils copy, not the v9.2 copy MUI X imports (verified). Adding a direct
@mui/utils@^9.2 dependency alone is not enough -- the augmentation target must
also switch to @mui/utils/types. Spell that out.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ource

The per-package assertions accept a slot as long as one union arm exposes
data-*, so a regression that widened only the object arm of the shared
SlotComponentPropsFromProps utility (used across charts, pickers, tree view)
would keep every package-wide assertion green. Add an x-internals spec that
extracts the callback return independently and asserts it accepts data-*.
Verified it fails if the callback return is un-widened.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@LukasTy

LukasTy commented Jul 16, 2026

Copy link
Copy Markdown
Member Author

Final GPT 5.6 Sol follow-up review

No further findings.

I verified the two follow-up commits on the exact PR head, 619379a09e09943fd29367103d3bd30636d14bfe.

Verified fixes

  1. Material UI 7 documentation

    The documentation now correctly requires both parts of the workaround:

    • augment @mui/utils/types, rather than @mui/material/utils;
    • install @mui/utils@^9.2.0 as a direct dependency so the consumer augmentation and MUI X resolve to the same v9.2 copy.

    It also correctly explains that this enables data-* only on MUI X slots, not Material UI 7 slots.

  2. Central callback regression guard

    The new x-internals spec independently extracts the callback branch of SlotComponentPropsFromProps and validates its return type. This closes the existential-union blind spot in the package-wide assertions: widening only the object branch can no longer keep the suite green.

    The test is included by the x-internals src/**/* TypeScript configuration, and the exact-head x-internals typecheck passes.

Final assessment

  • The previously reported implementation and documentation issues are addressed.
  • The new changes are narrowly scoped and consistent with the design.
  • git diff --check is clean.
  • Current CI already has the type, lint, package, static, regression, browser, and one e2e checks passing. Some remaining lanes and the deploy preview were still pending when checked.

Verdict: looks good to merge once the remaining CI checks finish successfully.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds an opt-in, type-safe mechanism for allowing data-* attributes in MUI X slotProps by adopting WithDataAttributes / DataAttributesOverrides from @mui/utils (and the @mui/material/utils re-export path), and backs it with broad compile-time coverage plus an initial runtime forwarding conformance check.

Changes:

  • Widen slot prop types across X packages using WithDataAttributes (including callback-form slotProps via SlotComponentPropsFromProps).
  • Add reusable TS-only assertion utilities and per-package slotDataAttributes.spec.ts files to verify every exported *SlotProps accepts data-* once augmented (with explicit exclusions).
  • Extend describeSlotsConformance with a runtime check that data-* and aria-* passed via slotProps reach the slot’s DOM element; document the consumer augmentation setup in the “Custom components” guide.

Reviewed changes

Copilot reviewed 47 out of 47 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/utils/slotDataAttributes.ts Adds type-level utilities to assert data-* support across slot prop unions/callbacks.
test/utils/describeSlotsConformance.tsx Adds runtime conformance coverage for forwarding data-*/aria-* through slotProps.
packages/x-tree-view/src/tests/slotDataAttributes.spec.ts Compile-time coverage ensuring Tree View slot props accept data-* after augmentation.
packages/x-tree-view-pro/src/tests/slotDataAttributes.spec.ts Compile-time coverage ensuring Tree View Pro slot props accept data-* after augmentation.
packages/x-internals/src/types/SlotComponentPropsFromProps.ts Widens the callback-capable slot prop helper with WithDataAttributes for object + callback return.
packages/x-internals/src/tests/slotDataAttributes.spec.ts Guards that callback-return widening is applied correctly at the source helper type.
packages/x-date-pickers/src/TimePicker/shared.tsx Widens toolbar slot props to accept data-* (opt-in).
packages/x-date-pickers/src/tests/slotDataAttributes.spec.ts Adds broad pickers compile-time slot coverage, with explicit excluded slots.
packages/x-date-pickers/src/PickersTextField/PickersTextField.types.ts Widens PickersTextField slot prop types using WithDataAttributes.
packages/x-date-pickers/src/PickersLayout/PickersLayout.types.ts Widens layout-related slot props using WithDataAttributes.
packages/x-date-pickers/src/PickerDay/PickerDay.types.ts Attempts to add data-* support to PickerDay props (see review comments re: approach).
packages/x-date-pickers/src/internals/components/PickersModalDialog.tsx Widens modal dialog slot props using WithDataAttributes.
packages/x-date-pickers/src/internals/components/PickerPopper/PickerPopper.tsx Widens desktop transition slot props; documents excluded FocusTrap slot.
packages/x-date-pickers/src/DateTimePicker/shared.tsx Widens tabs/toolbar slot props using WithDataAttributes.
packages/x-date-pickers/src/DatePicker/shared.tsx Widens toolbar slot props using WithDataAttributes.
packages/x-date-pickers-pro/src/TimeRangePicker/shared.tsx Widens tabs/toolbar slot props using WithDataAttributes.
packages/x-date-pickers-pro/src/tests/slotDataAttributes.spec.ts Adds broad pickers-pro compile-time slot coverage with exclusions.
packages/x-date-pickers-pro/src/internals/hooks/useStaticRangePicker/useStaticRangePicker.types.ts Widens toolbar slot props in static range picker hook types.
packages/x-date-pickers-pro/src/internals/hooks/useMobileRangePicker/useMobileRangePicker.tsx Removes an explicit cast in merged toolbar slot props (relies on widened typing).
packages/x-date-pickers-pro/src/DateTimeRangePicker/shared.tsx Widens tabs/toolbar slot props using WithDataAttributes.
packages/x-date-pickers-pro/src/DateRangePicker/shared.tsx Widens toolbar slot props using WithDataAttributes.
packages/x-chat/src/tests/slotDataAttributes.spec.ts Adds compile-time slot coverage for Chat exported components, including callback-return guards.
packages/x-chat/src/ChatMessage/ChatMessage.tsx Widens multiple ChatMessage slot props (including callback form) with WithDataAttributes.
packages/x-chat/src/ChatBox/ChatBox.types.ts Widens ChatBox slot props with WithDataAttributes and documents callback-return behavior.
packages/x-chat-headless/src/tests/slotDataAttributes.spec.ts Adds extensive compile-time slot coverage for Chat Headless slot props.
packages/x-charts/src/Toolbar/Toolbar.types.ts Widens Charts toolbar slot props using WithDataAttributes.
packages/x-charts/src/tests/slotDataAttributes.spec.ts Adds broad charts compile-time slot coverage with explicit excluded slots.
packages/x-charts/src/ScatterChart/ScatterPlot.tsx Documents intentional exclusion of scatter slot from widening due to no DOM prop spreading.
packages/x-charts/src/ScatterChart/ScatterMarker.types.ts Widens marker slot props using WithDataAttributes.
packages/x-charts/src/ScatterChart/ScatterChart.tsx Widens tooltip slot props using WithDataAttributes.
packages/x-charts/src/PieChart/PieArcPlot.tsx Widens pieArc slot props using WithDataAttributes.
packages/x-charts/src/PieChart/PieArcLabelPlot.tsx Widens pieArcLabel slot props using WithDataAttributes.
packages/x-charts/src/models/axis.ts Widens axis-related SVG/text slot props; documents excluded whole-axis replacement slots.
packages/x-charts/src/LineChart/MarkPlot.tsx Widens mark slot props using WithDataAttributes.
packages/x-charts/src/ChartsTooltip/ChartTooltip.types.ts Widens tooltip slot props using WithDataAttributes.
packages/x-charts/src/ChartsTooltip/ChartsTooltipContainer.tsx Widens Popper root slot props using WithDataAttributes while preserving other slot props.
packages/x-charts/src/ChartsLegend/chartsLegend.types.ts Widens legend slot props using WithDataAttributes.
packages/x-charts-pro/src/tests/slotDataAttributes.spec.ts Adds charts-pro compile-time slot coverage with explicit excluded slots.
packages/x-charts-pro/src/ScatterChartPro/ScatterChartPro.tsx Widens tooltip slot props using WithDataAttributes.
packages/x-charts-pro/src/Heatmap/HeatmapTooltip/HeatmapTooltip.types.ts Widens heatmap tooltip slot props using WithDataAttributes.
packages/x-charts-pro/src/Heatmap/HeatmapItem.tsx Widens heatmap cell slot props using WithDataAttributes.
packages/x-charts-pro/src/Heatmap/Heatmap.tsx Widens heatmap tooltip slot props using WithDataAttributes.
packages/x-charts-pro/src/FunnelChart/funnelPlotSlots.types.ts Widens funnel section/label slot props using WithDataAttributes.
packages/x-charts-pro/src/ChartsToolbarPro/Toolbar.types.ts Widens pro toolbar slot props using WithDataAttributes.
packages/x-charts-premium/src/tests/slotDataAttributes.spec.ts Adds charts-premium compile-time slot coverage with explicit excluded slots.
packages/x-charts-premium/src/ScatterChartPremium/ScatterChartPremium.tsx Widens tooltip slot props using WithDataAttributes.
docs/data/common-concepts/custom-components/custom-components.md Documents the consumer-facing augmentation approach and version-dependent guidance.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/x-date-pickers/src/PickerDay/PickerDay.types.ts
Comment thread packages/x-date-pickers/src/PickerDay/PickerDay.types.ts
LukasTy and others added 4 commits July 16, 2026 14:20
Leftover from the PR's first iteration. The `day` slot is already widened via
`SlotComponentPropsFromProps<PickerDayProps>`, so `data-*` on `slotProps.day`
type-checks without it. Extending the base props type with the augmentable
index signature is redundant and reintroduces the assignability friction the
`WithDataAttributes<T> = T | (T & Overrides)` union is designed to avoid.
Removing it restores PickerDay.types.ts to master (per Copilot review).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ta-*

The three default tab components (DateTimePickerTabs, DateTimeRangePickerTabs,
TimeRangePickerTabs) destructure a fixed prop list with no `...other`, so a
`data-*` attribute passed via slotProps.tabs never reaches the DOM. It was
widened in PickersLayout.types.ts and the DateTime/TimeRange shared.tsx files,
producing a green typecheck for an attribute that silently doesn't render --
the same over-promise that got Data Grid excluded. Un-widen `tabs` (like
scatter/xAxis/desktopTrapFocus) and exclude it from the affected assertions.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The MUI docs render `--` as literal double hyphens (the convention is the
em-dash, used in 225 files vs 2). Replace the one remaining `--` sentence
separator in the data-* section.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
The three default tab components (DateTimePickerTabs, DateTimeRangePickerTabs,
TimeRangePickerTabs) now rest-spread `...other` onto their styled root, so
`data-*` passed via slotProps.tabs reaches the DOM. Re-widen `tabs` and drop
its assertion exclusions.

Adds a runtime test (StaticDateTimePicker) asserting a data-* attribute on the
tabs slot lands on the DOM root -- the DOM-forwarding coverage the type promise
was missing. (aria-* isn't asserted there because MUI Tabs routes aria-label to
its inner tablist, not the root.)

Co-Authored-By: Claude Opus 4.8 <[email protected]>

@siriwatknp siriwatknp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Huge thanks for the effort

@LukasTy
LukasTy merged commit 273aa3d into mui:master Jul 17, 2026
24 checks passed
@LukasTy
LukasTy deleted the claude/trusting-ishizaka-77d81a branch July 17, 2026 06:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: all components Widespread work has an impact on almost all components. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. typescript

Projects

None yet

5 participants