mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.12k stars 1.28k forks source link

[pickers] Support module augmentation for slots #9775

Open LukasTy opened 1 year ago

LukasTy commented 1 year ago

We currently provide the option for users to provide a custom slot component to our components, but their slotProps are hard-typed given the components that we use.

Example: I want to override a DatePicker textField slot with CustomTextFieldWithMyProp:

<DatePicker
  slots={{ textField: CustomTextFieldWithMyProp }}
  slotProps={{
    textField: {
      myProp: 'fancy value'
    } as any // I'm forced to cast it to avoid TS error stating that `myProp` does not exist on this slot
  }}

xGrid team has already made the initiative of introducing the SlotNamePropsOverrides types to allow for custom props to be introduced.

Should we wait for a definitive solution from MUI Core or just go the Data Grid route? 🤔


EDIT: We will support module augmentation just like the data grid does, once we have enough traction for this feature.

All the slots in @mui/x-date-pickers and @mui/x-date-pickers-pro packages should support module augmentation. This allows you to pass custom props to your slots without casting the types.

Here is how it works on the data grid:

declare module '@mui/x-data-grid' {
  interface ToolbarPropsOverrides {
    name: string;
    setName: (name: string) => void;
  }
}

You can then use your custom slot without any type casting:

function CustomToolbar({ name, setName }: PropsFromSlot<GridSlots['toolbar']>) {
  return <input value={name} onChange={(event) => setName(event.target.value)} />;
}

function MyApp() {
  const [name, setName] = React.useState('');
  return (
    <DataGrid
      rows={[]}
      columns={[]}
      slots={{ toolbar: CustomToolbar }}
      slotProps={{
        toolbar: { name, setName },
      }}
    />
  );
}
alexfauquette commented 11 months ago

Should be done on top of https://github.com/mui/mui-x/pull/10700

Should also take care if docs demos were already using any and replace them with proper solution

ilbrando commented 4 months ago

Isn’t it very difficult to implement using module augmentation? Why isn’t a slot more like a render function so you can use it like this

<Foo slots={{ root: () => <MyOverride prop1=“1” /> }} />
LukasTy commented 4 months ago

Isn’t it very difficult to implement using module augmentation?

That's an excellent point. We haven't discussed this option as far as I remember. @mui/xgrid could you clarify why you went with explicit Overrides interfaces as compared to documenting how to extend the existing props? Is it because @mui/material uses it or did you have any other reasons? 🤔

Why isn’t a slot more like a render function so you can use it like this

@ilbrando Here is a documentation section to answer your question. 😉

flaviendelangle commented 2 months ago

Why isn’t a slot more like a render function so you can use it like this

To use slots: () => <MyComponent /> we would need to rework how we call the slots (to avoid the issues mentioned my @LukasTy). I see two main not to do it reasons:

  1. It adds one additional component to the component tree. This has a small performance overhead that can quickly add up for intensively used slots.
  2. Right now people must pass slots: MyComponent instead of slots: () => <MyComponent /> otherwise it remounts on every render. If you ask people to pass slots: () => <MyComponent /> and some people pass slots: MyComponent then the amount of hooks executed can vary and cause problems. Basically whatever approach you take, the other one is problematic.