Skip to content

Add possibility to disable multiple date intervals in Calendar and Datepicker components #2793

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Mar 3, 2022

Conversation

razvancir96
Copy link
Contributor

@razvancir96 razvancir96 commented Feb 1, 2022

Closes Add possibility to disable multiple date intervals in Calendar and Datepicker components

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

After starting Storybook (yarn start), check out those stories:

After starting the docs (yarn start:docs), check out those pages:

The design should look like the following screenshot:
date-picker-design

🧢 Your Project:

This feature is for the CJA app, inside Adobe DX.

@@ -60,6 +61,15 @@ export function CalendarCell({state, currentMonth, ...props}: CalendarCellProps)

let nativeDate = useMemo(() => date.toDate(state.timeZone), [date, state.timeZone]);
let formatted = useMemo(() => dateFormatter.format(nativeDate), [dateFormatter, nativeDate]);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if I should move this code somewhere else, it depends on the disabled & selected state, as well as the cell's ref 🤔

const previousCell = currentCell?.parentElement?.previousElementSibling?.firstElementChild;
const nextCell = currentCell?.parentElement?.nextElementSibling?.firstElementChild;

const isCellDisabled = (cell: Element): boolean => cell?.classList.value.includes('is-disabled');
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if this is the ideal way to verify if the sibling cell is disables, I am open to suggestions

Copy link
Member

Choose a reason for hiding this comment

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

better to use state if you can
I see there is already a function on the state for checking if the date is disabled
https://github.com/adobe/react-spectrum/pull/2793/files#diff-fd8b93994dbea995798535e4fb0d1323e12e7290667d0329b10ab4209db5199fR199

Copy link
Member

Choose a reason for hiding this comment

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

and there are functions for getting the next day and the previous day I believe

Copy link
Member

Choose a reason for hiding this comment

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

What happens for this if we're on the first cell or the last cell of a grid? just undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You can use state for the current cell, but from what I've seen, each cell has its own state. You can either control the state for the whole calendar or the state for one cell. I don't think you can use the states of two adjacent cells simultaneously (though I might be wrong) 🤔
There are functions for focusing the next day or calculating the next date. However, what I need is a function that returns the state of the next cell (or the effect of the state, like having a certain class), and I didn't find something like that (again, I might be wrong).
And about the last questions: yes, in that case, it returns undefined. Should I add a fallback to false (... || false), so that the API would be more consistent?

Copy link
Member

Choose a reason for hiding this comment

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

I mean, can't you check it like this?

state.isCellDisabled(props.date.subtract({days: 1}))
state.isCellDisabled(props.date.add({days: 1}))

code pulled from just below the link I shared, isPreviousVisibleRangeInvalid which I feel like you might just be able to use that function directly?

I'm quite sure we do not want to look in the dom for this state though. My suggestion may not pan out, I'm not super familiar with this code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I see now what you meant and you're 100% right. Thank you, I've updated this logic and it's way cleaner now 👌 7d9032c

Comment on lines +41 to +42
/** Callback that is called for each date of the calendar. If it returns true, then the date is disabled. */
isDateDisabled?: (date: DateValue) => boolean,
Copy link
Member

Choose a reason for hiding this comment

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

Open discussion for team, how do we feel about this api versus say something like disabledDates? Was mulling over this and I feel like the current api is more flexible especially when it comes to disabling a range of dates or specifying a special filter (like even days).

Copy link
Member

Choose a reason for hiding this comment

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

@LFDanLu is comparing this to the disabledKeys API
given that a collection will always be available in memory and you can change the disabledkeys based on what is shown, that API is fine for collections

however, for dates, there is always an infinite number of dates and you don’t have the ability to track what month or week or whatever is currently being shown, so you can’t update the disabled dates props
therefore a callback makes the most sense to me

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we go for disabledDates it would not be possible to make all the weekends disabled (for example). So, I think isDateDisabled is more flexible.

Copy link
Member

@snowystinger snowystinger left a comment

Choose a reason for hiding this comment

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

I noticed something, and I'm not sure the answer, but right now if you use the arrow keys to keyboard navigate the available dates, when you reach a disabled date, it looks like focus gets lost. Either we need to skip disabled dates or we need to keep the focus ring visible even on disabled dates

It used to be that focus just couldn't move past the boundaries of min/max dates. But we do need to be able to traverse these disabled dates.

I'm leaning towards them being navigable with a visible focus ring because:
You could technically provide a disable range of infinite dates, and we wouldn't necessarily know that, so we can't just skip to the next non-disabled date
Up/Down arrow navigating becomes less predictable

Comment on lines 147 to 148
The `isDateDisabled` callback prop makes it possible to disable custom dates from the `Calendar`. It is being called for each date and if it returns `true`, the date cell is disabled.

Copy link
Member

Choose a reason for hiding this comment

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

Mind updating the wording in the rest of the docs files (Calendar, RangeCalendar, DatePicker) to match DateRangePicker's change?

Suggested change
The `isDateDisabled` callback prop makes it possible to disable custom dates from the `Calendar`. It is being called for each date and if it returns `true`, the date cell is disabled.
The `isDateDisabled` callback prop acts like a filter function. It is called for each visible date and if it returns `true`, the date cell is disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated it in all files 👌

@snowystinger
Copy link
Member

Hey we're in the midst of determining how the disabled ranges should work with keyboard navigation. Does your product team have any opinions or insight?

@razvancir96
Copy link
Contributor Author

I noticed something, and I'm not sure the answer, but right now if you use the arrow keys to keyboard navigate the available dates, when you reach a disabled date, it looks like focus gets lost. Either we need to skip disabled dates or we need to keep the focus ring visible even on disabled dates

It used to be that focus just couldn't move past the boundaries of min/max dates. But we do need to be able to traverse these disabled dates.

I'm leaning towards them being navigable with a visible focus ring because: You could technically provide a disable range of infinite dates, and we wouldn't necessarily know that, so we can't just skip to the next non-disabled date Up/Down arrow navigating becomes less predictable

From what I see, the AirBnb datepicker keeps the focus on disabled dates: http://react-dates.github.io/react-dates/?path=/story/drp-day-props--with-some-blocked-dates

@snowystinger
Copy link
Member

Haha, yeah, we were looking at AirBnb as well as the Chrome native input.

One issue with navigating them is that they are disabled dates, but they are focusable, so we're following up with Accessibiliy if those "disabled" dates would need to meet contrast because they are focusable and are therefore interactable even if you can't select them. It'd look pretty weird to someone who is visually impaired, it might look like an empty focusring.
One of us will let you know what accessibility comes back with.

@razvancir96
Copy link
Contributor Author

Haha, yeah, we were looking at AirBnb as well as the Chrome native input.

One issue with navigating them is that they are disabled dates, but they are focusable, so we're following up with Accessibiliy if those "disabled" dates would need to meet contrast because they are focusable and are therefore interactable even if you can't select them. It'd look pretty weird to someone who is visually impaired, it might look like an empty focusring. One of us will let you know what accessibility comes back with.

Do you have any timeline for this? We would like to know if it would impact our next release.

@snowystinger
Copy link
Member

Hey, so we are going to have the dates be navigable within the disabled ranges (as they currently are in this PR), we still need to determine the styling of it, but the team can always handle that in a followup. If you wouldn't mind putting the focus ring around the date that is currently being navigated, that should tie up the loose ends for this.

@devongovett devongovett force-pushed the disable-multiple-dates-calendar branch from 4f49ab3 to 74d79cb Compare March 3, 2022 01:13
Copy link
Member

@devongovett devongovett left a comment

Choose a reason for hiding this comment

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

Went ahead and made the above changes. I'll open a followup PR to implement the keyboard navigation changes. Thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add possibility to disable multiple date intervals in Calendar and Datepicker components
5 participants