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

feat: Add DateRangePicker Component #983

Merged
merged 28 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b749362
DateRangePicker initial commit
brandonlenz Mar 1, 2021
a6facab
Show date range on calendars
brandonlenz Mar 2, 2021
32629ff
Add basic tests
brandonlenz Mar 2, 2021
28924cb
Add test more deeply rooted in funcitonality
brandonlenz Mar 2, 2021
502bc57
Dynamically set min and max dates
brandonlenz Mar 2, 2021
9d810aa
Add missing newline
brandonlenz Mar 2, 2021
559ee5e
Add missing newline
brandonlenz Mar 2, 2021
2043332
Missing ":" in comment
brandonlenz Mar 2, 2021
997d645
Add default value story
brandonlenz Mar 3, 2021
8042873
Add test for date range selection constraints
brandonlenz Mar 3, 2021
6180ab6
Add story for date ranges and fix date range bug
brandonlenz Mar 3, 2021
2fedaec
Refactor and fix bug with onChange for typed input
brandonlenz Mar 4, 2021
f754742
Fix broken unit test
brandonlenz Mar 4, 2021
ad82800
Add unit test covering recent bug fix
brandonlenz Mar 4, 2021
f39ae90
Add missing ":"
brandonlenz Mar 4, 2021
f95d317
Merge branch 'main' into bl-date-range-picker-339
brandonlenz Mar 4, 2021
ac7e457
Merge branch 'main' into bl-date-range-picker-339
brandonlenz Mar 8, 2021
aa73ba1
Remove unused import and superfluous typing
brandonlenz Mar 8, 2021
e2318b2
Remove unused import
brandonlenz Mar 8, 2021
de11085
Update DatePickerProps
brandonlenz Mar 12, 2021
298aff9
Merge branch 'main' into bl-date-range-picker-339
brandonlenz Mar 12, 2021
bd99ba0
Review fixes:
brandonlenz Mar 15, 2021
a77722b
Fix whitespace Typo
brandonlenz Mar 15, 2021
03161af
Include aria-describedby properties dynamically:
brandonlenz Mar 15, 2021
cb45ebe
Check aria-describedby on external inputs as well
brandonlenz Mar 15, 2021
217929e
Merge branch 'main' into bl-date-range-picker-339
brandonlenz Mar 15, 2021
0dc0523
Merge branch 'main' into bl-date-range-picker-339
brandonlenz Mar 16, 2021
1708db0
Fix type declaration
brandonlenz Mar 16, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions src/components/forms/DateRangePicker/DateRangePicker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,21 @@ describe("DateRangePicker component", () => {
expect(startDatePicker).toHaveClass('usa-date-range-picker__range-start')
expect(endDatePicker).toHaveClass('usa-date-range-picker__range-end')

const internalInputs = getAllByTestId('date-picker-internal-input')
expect(internalInputs).toHaveLength(2)

const startDatePickerInternalInput = internalInputs[0]
const endDatePickerInternalInput = internalInputs[1]
expect(startDatePickerInternalInput).not.toHaveAttribute("aria-describedby")
expect(endDatePickerInternalInput).not.toHaveAttribute("aria-describedby")

const externalInputs = getAllByTestId('date-picker-external-input')
expect(externalInputs).toHaveLength(2)

const startDatePickerExternalInput = externalInputs[0]
const endDatePickerExternalInput = externalInputs[1]
expect(startDatePickerExternalInput).not.toHaveAttribute("aria-describedby")
expect(endDatePickerExternalInput).not.toHaveAttribute("aria-describedby")
})

it('renders labels when specified', () => {
Expand Down Expand Up @@ -66,6 +81,22 @@ describe("DateRangePicker component", () => {
const endDateLabel = queryByText("End Date")
expect(endDateLabel).toBeInTheDocument()
expect(endDateLabel).toHaveClass('usa-label')

const internalInputs = getAllByTestId('date-picker-internal-input')
expect(internalInputs).toHaveLength(2)

const startDatePickerInternalInput = internalInputs[0]
const endDatePickerInternalInput = internalInputs[1]
expect(startDatePickerInternalInput).toHaveAttribute("aria-describedby", "start-date-label")
expect(endDatePickerInternalInput).toHaveAttribute("aria-describedby", "end-date-label")

const externalInputs = getAllByTestId('date-picker-external-input')
expect(externalInputs).toHaveLength(2)

const startDatePickerExternalInput = externalInputs[0]
const endDatePickerExternalInput = externalInputs[1]
expect(startDatePickerExternalInput).toHaveAttribute("aria-describedby", "start-date-label")
expect(endDatePickerExternalInput).toHaveAttribute("aria-describedby", "end-date-label")
})

it('renders hints when specified', () => {
Expand All @@ -83,13 +114,79 @@ describe("DateRangePicker component", () => {
expect(dateRangePicker).toHaveClass('usa-date-range-picker')
expect(getAllByTestId('date-picker')).toHaveLength(2)

const startDateLabel = queryByText("start date format: mm/dd/yyyy")
const startDateHint = queryByText("start date format: mm/dd/yyyy")
expect(startDateHint).toBeInTheDocument()
expect(startDateHint).toHaveClass('usa-hint')

const endDateHint = queryByText("end date format: mm/dd/yyyy")
expect(endDateHint).toBeInTheDocument()
expect(endDateHint).toHaveClass('usa-hint')

const internalInputs = getAllByTestId('date-picker-internal-input')
expect(internalInputs).toHaveLength(2)

const startDatePickerInternalInput = internalInputs[0]
const endDatePickerInternalInput = internalInputs[1]
expect(startDatePickerInternalInput).toHaveAttribute("aria-describedby", "start-date-hint")
expect(endDatePickerInternalInput).toHaveAttribute("aria-describedby", "end-date-hint")

const externalInputs = getAllByTestId('date-picker-external-input')
expect(externalInputs).toHaveLength(2)

const startDatePickerExternalInput = externalInputs[0]
const endDatePickerExternalInput = externalInputs[1]
expect(startDatePickerExternalInput).toHaveAttribute("aria-describedby", "start-date-hint")
expect(endDatePickerExternalInput).toHaveAttribute("aria-describedby", "end-date-hint")
})

it('renders labels and hints simultaneously, properly populating the aria-describedby property on each DatePicker', () => {
const { getByTestId, getAllByTestId, queryByText } = render(
<DateRangePicker
startDateLabel="Start Date"
startDateHint="start date format: mm/dd/yyyy"
startDatePickerProps={startDatePickerTestProps}
endDateLabel="End Date"
endDateHint="end date format: mm/dd/yyyy"
endDatePickerProps={endDatePickerTestProps}
/>
)

const dateRangePicker = getByTestId('date-range-picker')
expect(dateRangePicker).toBeInTheDocument()
expect(dateRangePicker).toHaveClass('usa-date-range-picker')
expect(getAllByTestId('date-picker')).toHaveLength(2)

const startDateLabel = queryByText("Start Date")
expect(startDateLabel).toBeInTheDocument()
expect(startDateLabel).toHaveClass('usa-hint')
expect(startDateLabel).toHaveClass('usa-label')

const endDateLabel = queryByText("end date format: mm/dd/yyyy")
const endDateLabel = queryByText("End Date")
expect(endDateLabel).toBeInTheDocument()
expect(endDateLabel).toHaveClass('usa-hint')
expect(endDateLabel).toHaveClass('usa-label')

const startDateHint = queryByText("start date format: mm/dd/yyyy")
expect(startDateHint).toBeInTheDocument()
expect(startDateHint).toHaveClass('usa-hint')

const endDateHint = queryByText("end date format: mm/dd/yyyy")
expect(endDateHint).toBeInTheDocument()
expect(endDateHint).toHaveClass('usa-hint')

const internalInputs = getAllByTestId('date-picker-internal-input')
expect(internalInputs).toHaveLength(2)

const startDatePickerInternalInput = internalInputs[0]
const endDatePickerInternalInput = internalInputs[1]
expect(startDatePickerInternalInput).toHaveAttribute("aria-describedby", "start-date-label start-date-hint")
expect(endDatePickerInternalInput).toHaveAttribute("aria-describedby", "end-date-label end-date-hint")

const externalInputs = getAllByTestId('date-picker-external-input')
expect(externalInputs).toHaveLength(2)

const startDatePickerExternalInput = externalInputs[0]
const endDatePickerExternalInput = externalInputs[1]
expect(startDatePickerExternalInput).toHaveAttribute("aria-describedby", "start-date-label start-date-hint")
expect(endDatePickerExternalInput).toHaveAttribute("aria-describedby", "end-date-label end-date-hint")
})

it('allows a date range to be selected by using both date pickers to pick start and end dates', () => {
Expand Down
32 changes: 24 additions & 8 deletions src/components/forms/DateRangePicker/DateRangePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,25 +113,40 @@ export const DateRangePicker = (
const startDatePickerClasses = classnames(startDatePickerProps.className, 'usa-date-range-picker__range-start')
const endDatePickerClasses = classnames(endDatePickerProps.className, 'usa-date-range-picker__range-end')

const startDatePickerLabelId = `${startDatePickerProps.id}-label`
const startDatePickerHintId = `${startDatePickerProps.id}-hint`
const startDatePickerAriaDescribedBy = [
startDateLabel && startDatePickerLabelId,
startDateHint && startDatePickerHintId
].join(" ").trim() || undefined

const endDatePickerLabelId = `${endDatePickerProps.id}-label`
const endDatePickerHintId = `${endDatePickerProps.id}-hint`
const endDatePickerAriaDescribedBy = [
endDateLabel && endDatePickerLabelId,
endDateHint && endDatePickerHintId
].join(" ").trim() || undefined

return (
<div className={classes} data-testid="date-range-picker">
<FormGroup>
{startDateLabel &&
<Label
id={`${startDatePickerProps.id}-label`}
<Label
id={startDatePickerLabelId}
htmlFor={startDatePickerProps.id}>
{startDateLabel}
</Label>}
{startDateHint &&
<div
className="usa-hint"
id={`${startDatePickerProps.id}-hint`}>
id={startDatePickerHintId}>
{startDateHint}
</div>}
<DatePicker
className={startDatePickerClasses}
rangeDate={endDateInternalValue}
{ ...startDatePickerProps }
{ ...startDatePickerProps }
aria-describedby={startDatePickerAriaDescribedBy}
onChange={startDatePickerOnChange}
maxDate={getMaxStartDate()}
/>
Expand All @@ -140,20 +155,21 @@ export const DateRangePicker = (
<FormGroup>
{endDateLabel &&
<Label
id={`${endDatePickerProps.id}-label`}
id={endDatePickerLabelId}
htmlFor={endDatePickerProps.id}>
{endDateLabel}
</Label>}
{endDateHint &&
<div
className="usa-hint"
id={`${endDatePickerProps.id}-hint`}>
id={endDatePickerHintId}>
{endDateHint}
</div>}
<DatePicker
<DatePicker
className={endDatePickerClasses}
rangeDate={startDateInternalValue}
{ ...endDatePickerProps }
{ ...endDatePickerProps }
aria-describedby={endDatePickerAriaDescribedBy}
onChange={endDatePickerOnChange}
minDate={getMinEndDate()}
/>
Expand Down