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.51k stars 1.31k forks source link

[pickers][DateRangePicker] Separately disable start and end date picking #13558

Open kbrueckner opened 4 months ago

kbrueckner commented 4 months ago

Summary

I am looking for a feature where it is possible to freeze selected values for start and end date. Means a start or end date cannot be changed. This shall be possible independently, thus disable on DAteRangePicker itself wouldn ot work, since it disabled edition completely for start and end date at the same time.

Examples

No response

Motivation

In some projects I have the following use case: Definition of settings which apply to a selected date range. Once the start date of the range is reached or in the past it shall not be possible to update the start date anymore while the end date shall still be editable until that one is in the past as well.

Search keywords: daterangepicker disable

LukasTy commented 4 months ago

Hello @kbrueckner, have you tried using the shouldDisableDate callback prop?

It receives the position argument that can be used to disable dates based on the selected range position, i.e.:

(day, position) => {
  if (value[0] && position === 'start' && day.isBefore(value[0])) {
    return true;
  }
  return false;
}
kbrueckner commented 4 months ago

Yes, I considered that and tried a lot of things with different provided attributes of DateRangePicker. The problem is that it doesn't give a full blown solution with nice UX.

The one you suggested - if I am not mistaking - would simply disable a specific date in the calendar but it does not prevent from setting the from date to another date which is not disabled.

LukasTy commented 4 months ago

The one you suggested - if I am not mistaking - would simply disable a specific date in the calendar but it does not prevent from setting the from date to another date which is not disabled.

Do you mean setting the date in the input? Are validation errors and form submission blocking not an option in your use case? 🤔

If not, it seems like you are talking about the following requirement: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date (year is disabled in input given present validation rules). Such enhancement could be possible, but only in the new accessible DOM behavior. Could you confirm if this is the behavior you need? 🤔

kbrueckner commented 4 months ago

Uploading a screenshot to explain the expected behaviour: Screenshot 2024-06-20 at 12 53 07

If start date is configured to be fixed/not editable the input field shall be disabled. Whenever you open the calendar widget i.e. by editing the the end date the start date (in the screenshot June 19) shall be fixed. It shall not be possible to move the start date to earlier or later while the end date can still be updated/moved. the selected range nevertheless shall still be in the light highlight color.

In addition it shall also be possible to set the end date fixed while keeping the start date updateable.

With the current control attributes I can achieve to set the start date input field disabled but it can still be updated via the calendar widget unfortunately.

DanailH commented 3 months ago

@LukasTy @arthurbalduini are we still triaging this one?

flaviendelangle commented 3 months ago

The described use case make sense IMHO.

For me the main challenge is to have a correct lifecycle (if you want only the start date to be editable, then onAccept should be called after the selection of the start date), most of the hacks will probably not achieve this.

And I would love this solution to automatically apply the right disabling status to the field as well.

I see several potential DX:

  1. Something similar to the views. We add an availableRangePositions?: ('start' | 'end')[] prop

  2. New boolean props disableStartDate / disableEndDate

  3. Another approach?


Side note, I did not find a way to disable a single text field (if you try to pass disabled: true to slotProps.textField it gets overridden by props.disabled.

kbrueckner commented 3 months ago

It might help you as inspiration - i found some hack which comes close to a solution for me :)

I have 3 control parameters for disabling which are all passed into a wrapper component.

const disabledFromToUse = disabled || disabledFrom;
const disabledToToUse = disabled || disabledTo;

<DateRangePicker
    disabled={disabled || (disabledFrom && disabledTo)}
    slotProps={{
        textField: ({ position }) => ({
            InputProps: {
                endAdornment: ((position === 'start' && !disabledFromToUse) || (position === 'end' && !disabledToToUse)) && <Calendar />,
                disabled: position === 'start' ? disabledFromToUse : disabledToToUse,
            },
        })
        ,
    }}
/>

This basically disallows editing the disabled start or end date but is quite hacky since I completely remove the calendar icon from the input. I found no way to just disable it so that a click does not open the calendar picker anymore.

From what i can see I would favor your option 2. It is simple to use and also seem to allow to a controlled state for the disabling so that you first can collect a date and conditionally later freez the date by using the disableX prop.

michelengelen commented 3 months ago

I think we all agree that this should be handled as a feature request. Lets add it to the board for further investigation and estimation. Objections @flaviendelangle @LukasTy ?

flaviendelangle commented 3 months ago

No objection

LukasTy commented 3 months ago

I'll just mention that we already have availableRangePositions on the DateRangeCalendar that is currently used internal for controlling the dragging behavior. It could be nice if we could reuse this prop by exposing it publically, but that would need thorough testing if it would "play nicely" with drag editing. 👍

svidskiy commented 3 months ago

I think you need to use rangePosition