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.55k stars 1.33k forks source link

[data grid] how to use `DateRangePicker` as `DataGrid` header filter properly #13977

Open layerok opened 4 months ago

layerok commented 4 months ago

Steps to reproduce

Link to live example: https://codesandbox.io/p/github/layerok/mui-data-grid-header-filter-data-range-picker/main?import=true

Steps:

  1. Click "Birth date" header filter

Current behavior

Passing custom ref to DateRange.slotProps.textField.InputProps.ref breaks DateRange popover positioning.

Expected behavior

Passsing custom ref to DateRange.slotProps.textField.InputProps.ref doesn't break DateRange popover positioning

<DataGrid slotProps={{ 
  textField: {
    InputProps: {
      ref: customRef
    }
  }
}}/>

Context

I want to be able to filter DataGrid rows by date range via header filter. To accomplish this I decided to create custom header filter based on DateRange component.

Currently focus management in custom header filters is messed up. Input inside custom header filter loses focus after first key is pressed. There is a workaround to this issue. We have to pass inputRef to underlying input element ourselfs.

const CustomHeaderFilter = (props) => {
    const { inputRef } = props;
    return <input ref={inputRef}/>
}

I don't like this workaround, but there are no alternatives right now.

But there is another issue. When I pass inputRef to the DateRange.slotProps.textField.InputProps.ref, then DataRange popover has wrong position.

// inside custom header filter
<DateRange slotProps={{textField: { InputProps: { ref: inputRef } }}}/>

https://github.com/user-attachments/assets/aef143ed-7b7a-42a2-a95a-bb5b7f6e00c5

If I don't pass inputRef to the underlying DataRange input, then I have this issue. In the video below after I focused header filter first time and typed any letter the input lost its focus and date calendar popover automatically closed. On the second try everything worked out as expected.

https://github.com/user-attachments/assets/ec31fc88-9f74-477f-8a60-efa5c31cf94b

So, currenty, as far as I know, you have to choose between correct popover position or correct focus management. I don't know any workarounds to this issue.

There is an alternative approach to filter rows by date range. I can use combination of isAfter and isBefore filters. But this approach doesn't work for me, because our backend doesn't support more than one filter for one column. And also I prefer applying single date range filter instead of applying two filters.

Your environment

No response

Search keywords: headerFilters focus DataGrid DateRange

michelengelen commented 4 months ago

Hey @layerok ... I did look into this and managed to get nearly everything working (I updated your example here). The only thing that's still wonky is the blurring of the input field.

https://github.com/user-attachments/assets/484b7bd1-35f0-42f1-96eb-72ffcc367726

@cherniavskii is going to take a look into this though! Stay tuned!

cherniavskii commented 4 months ago

Hi @layerok Can you check this demo? https://codesandbox.io/p/sandbox/nifty-dew-89m7sr Is this what you're looking for? Here are the changes I made:

layerok commented 4 months ago

@cherniavskii This is exactly what I was looking for. I wonder why passing inputRef directly to DateRangePicker fixed the issue What is the difference between DateRangePicker.inputRef and DateRange.slotProps.textField.InputProps.ref. Don't these refs target the same element? It is so confusing.

layerok commented 4 months ago

...and also defining InputComponent instead of defining renderHeaderFilter creates a new problem. I need somehow to get rid of this button.

image
LukasTy commented 3 months ago

I wonder why passing inputRef directly to DateRangePicker fixed the issue What is the difference between DateRangePicker.inputRef and DateRange.slotProps.textField.InputProps.ref. Don't these refs target the same element? It is so confusing.

@layerok There is a difference between these refs. The inputRef prop on the Picker component targets the <input /> element, just like the textField.InputProps.inputRef would do. Whereas the ref on the Picker component targets the <TextField /> root element, which is the wrapping <div /> element in the case of @mui/material/TextField, an equivalent with slotProps would be textField.InputProps.ref.

There is a bit of a problem here because using different field slots (SingleInputDateRangeField vs MultiInputDateRangeField) provides inconsistent results. I think in both cases the UI behavior should remain stable.

I'll look into fixing this inconsistency. 👌