Skip to content

Commit

Permalink
LG-3839: Date picker switch selects (#2178)
Browse files Browse the repository at this point in the history
* wip, allow flipping of selects

* add const

* add changeset

* only checks for iso

* fix test

* remove only

---------

Co-authored-by: Brooke Scarlett Yalof <[email protected]>
  • Loading branch information
shaneeza and bruugey authored Feb 1, 2024
1 parent 5249bd3 commit 253ef4e
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 82 deletions.
5 changes: 5 additions & 0 deletions .changeset/serious-lemons-lick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@leafygreen-ui/date-picker': patch
---

Rearranges the placement of the year select to come before the month select when the `locale` is `iso8601`. [LG-3839](https://jira.mongodb.org/browse/LG-3839)
54 changes: 47 additions & 7 deletions packages/date-picker/src/DatePicker/DatePicker.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ describe('packages/date-picker', () => {
options = await findAllByRole('option');
const _dec = options[11];
userEvent.click(_dec);
tabNTimes(3);
tabNTimes(2);
expect(queryCellByDate(testToday)).toHaveFocus();
});
});
Expand Down Expand Up @@ -1176,12 +1176,52 @@ describe('packages/date-picker', () => {
});
});

describe('when menu is open', () => {
const tabStops = expectedTabStopLabels['open'];
describe('when menu is open with en-US format', () => {
const tabStops = expectedTabStopLabels['openENUSFormat'];

test(`Tab order proceeds as expected`, async () => {
const renderResult = renderDatePicker({
initialOpen: true,
locale: 'en-US',
});

for (const label of tabStops) {
const elementMap = await findTabStopElementMap(renderResult);
const element = elementMap[label];

if (element !== null) {
expect(element).toHaveFocus();
} else {
expect(
renderResult.inputContainer.contains(
document.activeElement,
),
).toBeFalsy();
}

const errorElement = renderResult.queryByTestId(
'lg-form_field-error_message',
);

await waitFor(() =>
expect(errorElement).not.toBeInTheDocument(),
);

userEvent.tab();
// There are side-effects triggered on CSS transition-end events.
// Fire this event here to ensure these side-effects don't impact Tab order
if (element) fireEvent.transitionEnd(element);
}
});
});

describe('when menu is open with iso8601 format', () => {
const tabStops = expectedTabStopLabels['openISOFormat'];

test(`Tab order proceeds as expected`, async () => {
const renderResult = renderDatePicker({
initialOpen: true,
locale: 'iso8601',
});

for (const label of tabStops) {
Expand Down Expand Up @@ -1228,7 +1268,7 @@ describe('packages/date-picker', () => {
test('if month select is focused, opens the select menu', async () => {
const { openMenu, findAllByRole } = renderDatePicker();
const { monthSelect } = await openMenu();
tabNTimes(2);
tabNTimes(3);
expect(monthSelect).toHaveFocus();
userEvent.keyboard(`[${key}]`);
const options = await findAllByRole('option');
Expand Down Expand Up @@ -1355,7 +1395,7 @@ describe('packages/date-picker', () => {
renderDatePicker();
const { monthSelect, menuContainerEl } = await openMenu();

tabNTimes(2);
tabNTimes(3);
expect(monthSelect).toHaveFocus();

userEvent.keyboard('[Enter]');
Expand Down Expand Up @@ -3312,7 +3352,7 @@ describe('packages/date-picker', () => {
const options = await findAllByRole('option');
const Jan = options[0];
userEvent.click(Jan);
tabNTimes(3);
tabNTimes(2);
const jan31Cell = queryCellByDate(newUTC(2023, Month.January, 31));
await waitFor(() => expect(jan31Cell).toHaveFocus());
});
Expand All @@ -3325,7 +3365,7 @@ describe('packages/date-picker', () => {
const options = await findAllByRole('option');
const Dec = options[11];
userEvent.click(Dec);
tabNTimes(3);
tabNTimes(2);
const dec1Cell = queryCellByDate(newUTC(2023, Month.December, 1));
await waitFor(() => expect(dec1Cell).toHaveFocus());
});
Expand Down
17 changes: 15 additions & 2 deletions packages/date-picker/src/DatePicker/DatePicker.testutils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,11 @@ export const expectedTabStopLabels = {
'input > open menu button',
'none',
],
open: [
openENUSFormat: [
'none',
'input > year',
'input > month',
'input > day',
'input > year',
'input > open menu button',
'menu > today cell',
'menu > left chevron',
Expand All @@ -212,6 +212,19 @@ export const expectedTabStopLabels = {
'menu > right chevron',
'menu > today cell',
],
openISOFormat: [
'none',
'input > year',
'input > month',
'input > day',
'input > open menu button',
'menu > today cell',
'menu > left chevron',
'menu > year select',
'menu > month select',
'menu > right chevron',
'menu > today cell',
],
};

type TabStopLabel =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,21 @@
import React, { forwardRef, MouseEventHandler, useCallback } from 'react';
import range from 'lodash/range';
import React, { forwardRef, MouseEventHandler } from 'react';

import {
getLocaleMonths,
isSameUTCMonth,
setUTCMonth,
setUTCYear,
} from '@leafygreen-ui/date-utils';
import { cx } from '@leafygreen-ui/emotion';
import { isSameUTCMonth, setUTCMonth } from '@leafygreen-ui/date-utils';
import Icon from '@leafygreen-ui/icon';
import IconButton from '@leafygreen-ui/icon-button';
import { Option, Select } from '@leafygreen-ui/select';

import { selectElementProps } from '../../../shared/constants';
import { useSharedDatePickerContext } from '../../../shared/context';
import { useDatePickerContext } from '../../DatePickerContext';
import {
menuHeaderSelectContainerStyles,
menuHeaderStyles,
selectInputWidthStyles,
selectTruncateStyles,
} from '../DatePickerMenu.styles';
import {
DatePickerMenuSelectMonth,
DatePickerMenuSelectYear,
} from '../DatePickerMenuSelect';

import { shouldChevronBeDisabled, shouldMonthBeEnabled } from './utils';
import { shouldChevronBeDisabled } from './utils';

interface DatePickerMenuHeaderProps {
setMonth: (newMonth: Date) => void;
Expand All @@ -37,19 +30,17 @@ export const DatePickerMenuHeader = forwardRef<
HTMLDivElement,
DatePickerMenuHeaderProps
>(({ setMonth, ...rest }: DatePickerMenuHeaderProps, fwdRef) => {
const { min, max, setIsSelectOpen, locale, isInRange } =
useSharedDatePickerContext();
const { min, max, isInRange, locale } = useSharedDatePickerContext();
const { refs, month } = useDatePickerContext();

const monthOptions = getLocaleMonths(locale);
const yearOptions = range(min.getUTCFullYear(), max.getUTCFullYear() + 1);

const updateMonth = (newMonth: Date) => {
// We don't do any checks here.
// If the month is out of range, we still display it
setMonth(newMonth);
};

const isIsoFormat = locale === 'iso8601';

/**
* If the month is not in range and is not the last valid month
* e.g.
Expand Down Expand Up @@ -101,13 +92,6 @@ export const DatePickerMenuHeader = forwardRef<
}
};

/** Returns whether the provided month should be enabled */
const isMonthEnabled = useCallback(
(monthName: string) =>
shouldMonthBeEnabled(monthName, { month, min, max, locale }),
[locale, max, min, month],
);

return (
<div ref={fwdRef} className={menuHeaderStyles} {...rest}>
<IconButton
Expand All @@ -121,53 +105,17 @@ export const DatePickerMenuHeader = forwardRef<
<Icon glyph="ChevronLeft" />
</IconButton>
<div className={menuHeaderSelectContainerStyles}>
<Select
{...selectElementProps}
aria-label={`Select month - ${
monthOptions[month.getUTCMonth()].long
} selected`}
value={month.getUTCMonth().toString()}
onChange={m => {
const newMonth = setUTCMonth(month, Number(m));
updateMonth(newMonth);
}}
className={cx(selectTruncateStyles, selectInputWidthStyles)}
onEntered={() => setIsSelectOpen(true)}
onExited={() => setIsSelectOpen(false)}
placeholder={monthOptions[month.getUTCMonth()].short}
>
{monthOptions.map((m, i) => (
<Option
disabled={!isMonthEnabled(m.long)}
value={i.toString()}
key={m.short}
aria-label={m.long}
>
{m.short}
</Option>
))}
</Select>
<Select
{...selectElementProps}
aria-label={`Select year - ${month
.getUTCFullYear()
.toString()} selected`}
value={month.getUTCFullYear().toString()}
onChange={y => {
const newMonth = setUTCYear(month, Number(y));
updateMonth(newMonth);
}}
className={cx(selectTruncateStyles, selectInputWidthStyles)}
onEntered={() => setIsSelectOpen(true)}
onExited={() => setIsSelectOpen(false)}
placeholder={month.getUTCFullYear().toString()}
>
{yearOptions.map(y => (
<Option value={y.toString()} key={y} aria-label={y.toString()}>
{y}
</Option>
))}
</Select>
{isIsoFormat ? (
<>
<DatePickerMenuSelectYear updateMonth={updateMonth} />
<DatePickerMenuSelectMonth updateMonth={updateMonth} />
</>
) : (
<>
<DatePickerMenuSelectMonth updateMonth={updateMonth} />
<DatePickerMenuSelectYear updateMonth={updateMonth} />
</>
)}
</div>
<IconButton
ref={refs.chevronButtonRefs.right}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React, { useCallback } from 'react';

import { getLocaleMonths, setUTCMonth } from '@leafygreen-ui/date-utils';
import { cx } from '@leafygreen-ui/emotion';
import { Option, Select } from '@leafygreen-ui/select';

import { selectElementProps } from '../../../shared/constants';
import { useSharedDatePickerContext } from '../../../shared/context';
import { useDatePickerContext } from '../../DatePickerContext';
import {
selectInputWidthStyles,
selectTruncateStyles,
} from '../DatePickerMenu.styles';
import { shouldMonthBeEnabled } from '../DatePickerMenuHeader/utils';

interface DatePickerMenuSelectMonthProps {
updateMonth: (newMonth: Date) => void;
}

/**
* Month Select
* @internal
*/
export const DatePickerMenuSelectMonth = ({
updateMonth,
}: DatePickerMenuSelectMonthProps) => {
const { setIsSelectOpen, locale, min, max } = useSharedDatePickerContext();
const { month } = useDatePickerContext();
const monthOptions = getLocaleMonths(locale);

/** Returns whether the provided month should be enabled */
const isMonthEnabled = useCallback(
(monthName: string) =>
shouldMonthBeEnabled(monthName, { month, min, max, locale }),
[locale, max, min, month],
);

const handleMonthOnChange = (value: string) => {
const newMonth = setUTCMonth(month, Number(value));
updateMonth(newMonth);
};

return (
<Select
{...selectElementProps}
aria-label={`Select month - ${
monthOptions[month.getUTCMonth()].long
} selected`}
value={month.getUTCMonth().toString()}
onChange={handleMonthOnChange}
className={cx(selectTruncateStyles, selectInputWidthStyles)}
onEntered={() => setIsSelectOpen(true)}
onExited={() => setIsSelectOpen(false)}
placeholder={monthOptions[month.getUTCMonth()].short}
>
{monthOptions.map((m, i) => (
<Option
disabled={!isMonthEnabled(m.long)}
value={i.toString()}
key={m.short}
aria-label={m.long}
>
{m.short}
</Option>
))}
</Select>
);
};

DatePickerMenuSelectMonth.displayName = 'DatePickerMenuSelectMonth';
Loading

0 comments on commit 253ef4e

Please sign in to comment.