Skip to content
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

Support selecting multiple dates #3999

Merged
merged 11 commits into from
Feb 28, 2024
Merged

Conversation

nandorojo
Copy link
Contributor

@nandorojo nandorojo commented Apr 14, 2023

React.Datepicker.crafted.by.HackerOne.-.14.April.2023.mp4

What

Closes #3157

This PR adds 2 props which allow users to select multiple isolated dates on the calendar picker.

You can now select multiple dates at once, which is very convenient for products that need users to select multiple dates, such as Calendly and the like.

How

Following the API for selectsRange, this PR adds a selectsMultiple boolean, in addition to a selectedDates array.

I thought it was best for selectedDates to be explicitly different from selected. This is also following the convention from selectsRange, which uses startDate and endDate.

Example

As shown in the video above, here is the example code:

() => {
  const [selectedDates, setSelectedDates] = useState([new Date()]);
  const onChange = (dates) => {
    setSelectedDates(dates);
  }
  return (
    <DatePicker
      selectedDates={selectedDates}
      selectsMultiple
      onChange={onChange}
      shouldCloseOnSelect={false}
      disabledKeyboardNavigation
    />
  )
}

Breakdown

This PR adds a few minor changes to achieve adding multiple dates.

First, in the Picker component, it adds 2 core changes:

  1. Formatting the input with safeMultipleDatesFormat.
  2. Properly accounting for selecting multiple dates in onChange.

The rest is just prop drilling down to <Day />, which has 2 key changes:

  1. isKeyboardSelected needs to account for selectedDates rather than selected.
  2. isSelected also checks for selectedDates

I've tested this out and it all looks good to me!

…>> This PR adds 2 props which allow users to select multiple isolated dates on the calendar picker. It adds changes to Picker.onChange(), Day.isSelected(), and adds formatting for the input.
@codecov
Copy link

codecov bot commented May 24, 2023

Codecov Report

Attention: Patch coverage is 56.52174% with 20 lines in your changes are missing coverage. Please review.

Project coverage is 94.74%. Comparing base (3d8e0cb) to head (a5225ec).
Report is 104 commits behind head on main.

❗ Current head a5225ec differs from pull request most recent head 3151397. Consider uploading reports for the commit 3151397 to get more accurate results

Files Patch % Lines
src/index.jsx 33.33% 12 Missing ⚠️
src/date_utils.js 63.63% 4 Missing ⚠️
src/day.jsx 76.47% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3999      +/-   ##
==========================================
- Coverage   95.46%   94.74%   -0.73%     
==========================================
  Files          29       29              
  Lines        2513     2548      +35     
  Branches     1026     1043      +17     
==========================================
+ Hits         2399     2414      +15     
- Misses        114      134      +20     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

✅ This pull request was sent to the PullRequest network.


@nandorojo you can click here to see the review status or cancel the code review job - or - cancel by adding [!pr] to the title of the pull request.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

PullRequest Breakdown

Reviewable lines of change

+ 146
- 13

80% JavaScript
20% JavaScript (tests)

Type of change

Feature - These changes are adding a new feature or improvement to existing code.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

This is looking like a great start. The code is looking clean, but has no test coverage. I have only one minor note left inline for your consideration.

Image of Graham C Graham C


Reviewed with ❤️ by PullRequest

setSelectedDates(dates);
};
return (
<DatePicker
Copy link

Choose a reason for hiding this comment

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

Should the example show where to import this from?

🔸 Reduce Future Bugs (Important)

Image of Graham C Graham C

Copy link
Member

Choose a reason for hiding this comment

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

The examples added here are automatically published to https://reactdatepicker.com/

Copy link

Choose a reason for hiding this comment

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

Sure, but how does someone who what line to write in the code to get to the DatePicker component. I am suggesting we add an import line at the top of this example. I guess this is an existing problem with all the examples, so probably out of scope to change.

Image of Graham C Graham C

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Normally I would, but I just copied what the other examples did.

Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we can start by leading with example here and add the import to show how to get it done.

Copy link

Choose a reason for hiding this comment

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

Sounds good to me.

Image of Graham C Graham C

Copy link

Choose a reason for hiding this comment

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

That will fix the automation complaining about DatePicker not being defined...

Image of Andy W Andy W

@martijnrusschen
Copy link
Member

@nandorojo Can you add some tests to cover your new functionality?

@nandorojo
Copy link
Contributor Author

Sure, other than the example app updates?

@martijnrusschen
Copy link
Member

Ideally, add some automated tests to ensure we don't break this in future changes. You can see the tests folder with examples of how to test.

@nandorojo
Copy link
Contributor Author

Got it, I can do that today if it means getting it merged. Thanks for the feedback.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

This looks to support multiple days as described but given the new functionality it would be great to see some tests and documentation around the new feature

Image of James D James D


Reviewed with ❤️ by PullRequest

@danMateer
Copy link
Contributor

James D and Graham C from PullRequest - any test cases you can recommend to @nandorojo just so he can knock them out?

I'm 100% sure he's fully capable, just very busy juggling a lot 1 2. It helps sometimes to just have stuff listed in GitHub even if it's basic and obvious.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

For adding tests:

  • if there isn't already one, I would add a test around using this component to select a single day
  • Using this component to select multiple days
  • Using this component to select a single day and then change it
  • Using this component to select multiple days and then change one of them

It appears those are supported scenarios that would be useful to add here for coverage

Image of James D James D


Reviewed with ❤️ by PullRequest

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

I agree with the suggestions from James.

Image of Graham C Graham C


Reviewed with ❤️ by PullRequest

Copy link
Member

@martijnrusschen martijnrusschen left a comment

Choose a reason for hiding this comment

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

Pending test coverage

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

PullRequest reviewed the updates made to #3999 since our last review was posted. This includes comments that have been posted by non-PullRequest reviewers. No further issues were found.

Reviewed by:

Image of Graham C Graham C

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

Due to inactivity, PullRequest has cancelled this review job. You can reactivate the code review job from the PullRequest dashboard.

@garrettparris
Copy link

any way to just merge this? useful feature and the code looks fine.

@Zarthus
Copy link
Contributor

Zarthus commented Sep 11, 2023

any way to just merge this? useful feature and the code looks fine.

@garrettparris @williamtcastro @nandorojo

Seems like it's currently blocked on some missing tests, we recently migrated to jest so hopefully it's easier to add tests now. There's also a merge conflict error.

@nandorojo
Copy link
Contributor Author

nandorojo commented Jan 8, 2024

Hey guys. Is this worth me going into and fixing merge conflicts? I can do it, but I don't want to spend the time again if it won't get merged.

We used the code in prod for months with no issues. However, I just upgraded to 4.14.0 and lost all my changes, so I'm hoping to use this PR now. Please let me know if this is worth doing or not.

@nandorojo
Copy link
Contributor Author

Merge conflicts fixed.

@nandorojo
Copy link
Contributor Author

Not sure where tests are failing, I don't see them on here.

@martijnrusschen
Copy link
Member

@nandorojo I've started the workflows for test coverage. You should see those showing up in a minute. Per request, can you add some test coverage for this functionality? I'm happy to get this merged once ready.

@martijnrusschen
Copy link
Member

Per workflow results, the codecov check is complaining about missing test coverage.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

✅ This pull request was sent to the PullRequest network.


@nandorojo you can click here to see the review status or cancel the code review job - or - cancel by adding [!pr] to the title of the pull request.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

I love this component and this looks like a great update. It seems like there are some rather complicated logic improvements that could benefit from unit test coverage, though.

Image of Andy W Andy W


Reviewed with ❤️ by PullRequest

isSelected = () =>
this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);

isSelected = () => {
Copy link

Choose a reason for hiding this comment

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

this is the kind of logic update where it would be great to have unit tests. are there any unit tests?

🔸 Improve Test Coverage (Important)

Image of Andy W Andy W

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

This multi-selection functionality looks good overall. I just had the one comment suggesting that a code comment be left for readability in one spot that was pretty non-obvious. Other than that, I don't have anything that hasn't already been mentioned by other reviewers.

Image of Eric E Eric E


Reviewed with ❤️ by PullRequest

) &&
(this.isSameDay(this.props.preSelection) ||
this.isSameWeek(this.props.preSelection));
isKeyboardSelected = () => {
Copy link

Choose a reason for hiding this comment

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

IMO it would be good to leave a brief comment in the code here describing the intended condition. I think it's pretty hard to parse out why this code is as it is (at least without substantially more context).

🔸 Improve Readability (Important)

Image of Eric E Eric E

expect(datePicker.input.value).toBe("01/01/2024, 01/15/2024");
});

it("should have multiple highlighted days", () => {
Copy link
Member

Choose a reason for hiding this comment

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

It looks like you may have forgotten an expect in this test case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I was hoping to get some guidance on how to implement this one. I tried copying some helper functions from other tests but haven't been able to figure it out quite yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does anyone with more test / internals knowledge have guidance for these tests? I'm not totally sure what the requirement is

Copy link
Contributor

Choose a reason for hiding this comment

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

@nandorojo As I see it, the displaying of individual selected days should not be tested here.

I have provided a new test that checks if the input text box is rendered properly with 2 and 2+ dates selected.

I have a larger project where this addition could be very useful.

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

PullRequest reviewed the updates made to #3999 up until the latest commit (a5225ec). No further issues were found.

Reviewed by:

Image of Andy W Andy W

Copy link

@pullrequest pullrequest bot left a comment

Choose a reason for hiding this comment

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

Due to inactivity, PullRequest has cancelled this review job. You can reactivate the code review job from the PullRequest dashboard.

@tbowmo
Copy link
Contributor

tbowmo commented Feb 19, 2024

@martijnrusschen @nandorojo
is it only test that is keeping this from being merged? If so, is there anything we could help with in this regard? (Multiple dates could be really useful for my project)

test/multiple_selected_dates.test.js Outdated Show resolved Hide resolved
test/multiple_selected_dates.test.js Show resolved Hide resolved
test/multiple_selected_dates.test.js Outdated Show resolved Hide resolved
test/multiple_selected_dates.test.js Outdated Show resolved Hide resolved
test/multiple_selected_dates.test.js Outdated Show resolved Hide resolved
@martijnrusschen
Copy link
Member

@nandorojo can you resolve the merge conflicts?

Copy link
Contributor

@tbowmo tbowmo left a comment

Choose a reason for hiding this comment

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

Found a couple of minor issues (perhaps some is just nitpicking),

Not entirely sure about code coverage or how I can help with adding that without creating a new PR against @nandorojo s branch (if needed)

Comment on lines +682 to +684
const noSelectedDates = !selectedDates || selectedDates.length === 0;

if (noSelectedDates) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I tend to prefer these type of checks here

Suggested change
const noSelectedDates = !selectedDates || selectedDates.length === 0;
if (noSelectedDates) {
if (!selectedDates?.length) {

Comment on lines +107 to +115
} else {
return !(
this.isSameDay(this.props.selected) ||
this.isSameWeek(this.props.selected)
) && (
this.isSameDay(this.props.preSelection) ||
this.isSameWeek(this.props.preSelection)
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

the else clause should not be necessary, as the if above ends with a return, so we can skip an indentation here

Suggested change
} else {
return !(
this.isSameDay(this.props.selected) ||
this.isSameWeek(this.props.selected)
) && (
this.isSameDay(this.props.preSelection) ||
this.isSameWeek(this.props.preSelection)
);
}
}
return !(
this.isSameDay(this.props.selected) ||
this.isSameWeek(this.props.selected)
) && (
this.isSameDay(this.props.preSelection) ||
this.isSameWeek(this.props.preSelection)
);

Comment on lines +292 to +297
isSelected = () => {
if (this.props.selectsMultiple && this.props.selectedDates) {
return this.props.selectedDates.some((date) => this.isSameDay(date));
}
return this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);
};
Copy link
Contributor

@tbowmo tbowmo Feb 25, 2024

Choose a reason for hiding this comment

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

This looks sketchy, as it will fall through to checking on props.selected, if this.props.selectsMultiple is true, and this.props.selectedDates is undefined.

Perhaps something in the line of this instead (I added test on this.isSameWeek() as well, haven't dived enough into the code to see if that actually is necessary?

Suggested change
isSelected = () => {
if (this.props.selectsMultiple && this.props.selectedDates) {
return this.props.selectedDates.some((date) => this.isSameDay(date));
}
return this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);
};
isSelected = () => {
if (this.props.selectsMultiple) {
return this.props.selectedDates?.some((date) => this.isSameDay(date) || this.isSameWeek(date)) ?? false;
}
return this.isSameDay(this.props.selected) || this.isSameWeek(this.props.selected);
};

@@ -185,6 +185,23 @@ export function safeDateRangeFormat(startDate, endDate, props) {
return `${formattedStartDate} - ${formattedEndDate}`;
}

export function safeMultipleDatesFormat(dates, props) {
if (!dates || !dates.length) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!dates || !dates.length) {
if (!dates?.length) {

@martijnrusschen
Copy link
Member

@tbowmo Thanks for the suggestions. It would be great to get this PR over the finish line. If it's easier, perhaps we should consider opening a new PR with your changes included. Let me know if you prefer that and I can review the changes once its in.

@tbowmo
Copy link
Contributor

tbowmo commented Feb 25, 2024

So you suggest that I fork @nandorojo working branch, add my suggestions, and then create a new pr? (perhaps add a few tests when I get it figured out)

if (this.props.disabledKeyboardNavigation) {
return false;
}
if (this.props.selectsMultiple) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @nandorojo 👋
Was digging into this PR to investigate "select multiple months" and I think we need logic similar to this for the months and weeks selection, otherwise there's no selection styles there.
Happy to help with it if I can

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.

Select multiple dates
8 participants