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

Filter by Date Pt. 2 - Custom date selection #3117

Open
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

angelathe
Copy link
Collaborator

PULL REQUEST

Summary

  • Add custom date selection options when filtering by date
    • Max date set to today
    • Start date time set to 00:00:00.000, and end date time set to 23:59:59.999
  • Add/update unit and snapshot tests

Related Issue

Fixes #2753

Additional Information

NOTE: Still looking to clean some things up (left a few comments), but would love to start the feedback process now.

Things that will probably get broken out into other tickets:

  • Apply Filter button disabled/enabled states - button should
  • Adding a custom error tooltip for when startDate > endDate. Right now, there's a placeholder Alert for this error

Acceptance Criteria

  • "Filter By Received Date" implemented for custom date range
  • When a user chooses "Custom date range", they must populate the start and end dates
  • When user applies the custom filter, only eCRs received in that date range are included
  • My selection is shown as the button text (e.g. "From 10/7/2024 to 10/10/2024")

Checklist

  • If this code affects the other scrum team, have they been notified? (In Slack, as reviewers, etc.)

@angelathe angelathe self-assigned this Jan 9, 2025
Comment on lines +352 to +358
if (new Date(startDate) > new Date(endDate)) {
alert(
"Invalid date range. Start date must be earlier or equal to the end date.",
);
resetFilterDate();
return;
}
Copy link
Collaborator Author

@angelathe angelathe Jan 9, 2025

Choose a reason for hiding this comment

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

Note: This is temporary -- a placeholder alert to safeguard against error where Start date > end date. Still working on how to make the custom error tooltip for this / might get brought out into a separate small ticket

Copy link

codecov bot commented Jan 9, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 85.67%. Comparing base (2055a30) to head (5604ed3).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3117      +/-   ##
==========================================
- Coverage   87.05%   85.67%   -1.39%     
==========================================
  Files         219       25     -194     
  Lines       13652     1424   -12228     
  Branches      709        0     -709     
==========================================
- Hits        11885     1220   -10665     
+ Misses       1758      204    -1554     
+ Partials        9        0       -9     
Flag Coverage Δ
ecr-viewer ?
fhir-converter ?
ingestion ?
message-parser ?
message-refiner ?
orchestration 85.67% <ø> (ø)
record-linkage ?
trigger-code-reference ?
validation ?

Flags with carried forward coverage won't be shown. Click here to find out more.

see 194 files with indirect coverage changes

@@ -77,7 +88,7 @@ const Filters = () => {
}
}, [filterBoxOpen]);

const paramKeys = ["condition", "dateRange"];
const paramKeys = [ParamName.Condition, ParamName.DateRange, ParamName.Dates];
Copy link
Collaborator

Choose a reason for hiding this comment

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

can this instead be computed from the enum? (is a formal enum worth it or just use a plain object?)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just made an update!

(Maybe an enum is overkill, but thought it would make things clearer in case we expand the scope of filters?)

@@ -124,7 +135,8 @@ const Filters = () => {
* - Updates the browser's query string when the filter is applied.
*/
const FilterReportableConditions = () => {
const { searchParams, updateQueryParam } = useQueryParam();
const { searchParams, updateQueryParam, pushQueryUpdate } = useQueryParam();
const params = new URLSearchParams(searchParams.toString());
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need to do this copy?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Needed to pass params to updateQueryParam in the submitHandler (can't pass searchParams which is read-only)

Could move Line 139 into the submitHandler so it's more clear why that's needed?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd move this into updateQueryParams so it always returns a new params and doesn't mutate. Especially since mutating state can be such a footgun, best to avoid it entirely by default


const submitHandler = () => {
const params = new URLSearchParams(searchParams.toString());
if (filterDateOption === "custom") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

what were the pros/cons/thinking on the two param approach vs a single param like custom(start,end)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, missed this comment on Friday! My thoughts are below, and I'm definitely open to changing the approach.

I went with two URL parameters to keep things simpler. One shows that it’s a custom date, and the other holds the actual dates. Keeping these definitions separate means we don’t have to deal with extra parsing every time we just need one of them (felt cleaner and easier to work with this approach). Also, having two separate parameters helps keep the radio button option name and the URL param values in sync, which makes it simpler to match what the user picks to what’s in the query string.

Also, keeping custom dates in their own parameter seemed like it would make it easier to extend things in the future—like if we wanted to add time ranges. Saves us from having to dig through and parse a single dateRange parameter if we need to add more customizations down the line.

The main con with maintaining the two parameters is maintaining consistency between dateRange and dates, i.e. making sure that dates only exists and is processed when dateRange=custom, and that dates does not exist when dateRange is set to one of the other predefined date ranges. Using the single dateRange parameter also to house the custom dates would eliminate this risk.

Thoughts?

Copy link
Collaborator

Choose a reason for hiding this comment

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

That matches what I see as well. We just need to make sure we've got robust handling of the keeping in sync and edge cases there

type="date"
className="usa-input width-card margin-top-0 border-base-dark custom-date"
defaultValue={endDate || ""}
required
Copy link
Collaborator

Choose a reason for hiding this comment

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

should we require an end date? or should it be implicitly today if not selected?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hm, I think this is a question for @ashton-skylight.

Do you prefer that 1) end date is always required, i.e. folks would have to manually set today as the end date, or 2) end date is not required, i.e. if users don't fill in the end date, the end date would implicitly be set as today?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@angelathe Since case investigation will usually take place with more recent cases, end date shouldn't necessarily be required. Let's go with 2, if the user does not manually set an end date, the end date is implicitly set as today, assuming that would reflect in the custom date range inputs after the user presses "apply filter".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok! Just making a note this slightly changes the acceptance criteria / scenario of the original ticket

@@ -286,52 +309,108 @@ const FilterByDate = () => {
};

const resetFilterDate = () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think there's a bug here where if you have a custom date range, then hit the reset button, then go back to the custom date range radio, the old custom dates are still there. Should those properly reset? Maybe just adding the setStartDate and end date to null in the default case?

@@ -286,52 +309,108 @@ const FilterByDate = () => {
};

const resetFilterDate = () => {
const queryDateRange = searchParams.get("dateRange");
const queryDateRange = searchParams.get(ParamName.DateRange);
Copy link
Collaborator

Choose a reason for hiding this comment

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

if a user manually deletes the dates param, it ends up looking like this

image

should we reset to the default instead at this point? cc @ashton-skylight

updatedParams,
ParamName.Dates,
datesParam,
false,
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe add false as the default on the function signature so we can skip this here?

Comment on lines +341 to +342
let updatedParams: URLSearchParams;
updatedParams = updateQueryParam(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
let updatedParams: URLSearchParams;
updatedParams = updateQueryParam(
let updatedParams = updateQueryParam(

return (
<div className="padding-x-105">
<div>
<Label htmlFor="start-date" className="margin-top-1">
Copy link
Collaborator

Choose a reason for hiding this comment

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

is it worth abstracting out these date inputs? there's a lot of overlap between the two

Copy link
Collaborator

Choose a reason for hiding this comment

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

or maybe make the CustomDateInput just do one input, then have the CustomDateRange handle both or at that point just inline it into the date filter jsx

>
<div className="display-flex flex-column">
{Object.values(DateRangeOptions).map((option) => (
<div
className="checkbox-color usa-radio padding-bottom-1 padding-x-105"
<RadioDateOption
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe move this abstraction up one level to RadioDateOptions? there's still a bit of "guts" in here that could have a nicer API if it handles the options bit. Then it could just pass what are all the options handler for option change and current option, which is pretty universal to any set of radio buttons

@@ -68,7 +80,7 @@ const HomePage = async ({
sortDirection={sortDirection}
searchTerm={searchTerm}
filterConditions={filterConditionsArr}
filterDate={filterDateRange}
filterDate={filterDates}
Copy link
Collaborator

Choose a reason for hiding this comment

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

minor nit - pluralization doesn't match

if (filterDateRange === "custom") {
const datesParam = searchParams?.dates as string | undefined;
if (datesParam) {
filterDates = buildCustomDateRange(datesParam);
Copy link
Collaborator

Choose a reason for hiding this comment

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

if the dates param has junk content, we get a system internal error, can we harden this?

Comment on lines +38 to +42
const filterDateRange =
(searchParams?.dateRange as string) || DEFAULT_DATE_RANGE;
let filterDates;

if (filterDateRange === "custom") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this logic feels a bit out of place here as it's likely something we'll want to sync with the component itself. Can we think about making this a bit more centralized then using that logic both here and in the UI component?

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.

FILTER: Filter By Date (Part 2 - Custom selection)
3 participants