Skip to content

Commit

Permalink
feat: interaction test, add testId prop
Browse files Browse the repository at this point in the history
  • Loading branch information
skinread committed Feb 3, 2025
1 parent 81ac8f8 commit 30a0634
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 17 deletions.
8 changes: 7 additions & 1 deletion lib/components/DateTimePicker/CalendarGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ const CalendarCell = ({ state, date }: CalendarCellProps) => {
} = useCalendarCell({ date }, state, ref);

return (
<td {...cellProps} className={odStyle({ padding: '1' })}>
<td
{...cellProps}
className={odStyle({
padding: '1',
textAlign: 'center',
})}
>
<div
{...buttonProps}
ref={ref}
Expand Down
2 changes: 1 addition & 1 deletion lib/components/DateTimePicker/DateTimePicker.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const styledCell = recipe({
initial: 'pointer',
disabled: 'default',
},
display: 'flex',
display: 'inline-flex',
fontSize: 'md',
justifyContent: 'center',
size: '7',
Expand Down
81 changes: 78 additions & 3 deletions lib/components/DateTimePicker/DateTimePicker.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import type { Meta, StoryObj } from '@storybook/react';
import { expect, fn, getAllByRole, within, userEvent } from '@storybook/test';
import {
expect,
fn,
getAllByRole,
getByText,
userEvent,
within,
} from '@storybook/test';

import { DateTimePicker } from './DateTimePicker';

// demo times
const times = [
{
label: 'Early morning',
Expand Down Expand Up @@ -50,6 +56,7 @@ const meta: Meta<typeof DateTimePicker> = {
dateLabel: 'Date',
timeLabel: 'Time',
},
testId: 'demo-date-time-picker',
},
argTypes: {
allowPastDate: {
Expand All @@ -64,4 +71,72 @@ const meta: Meta<typeof DateTimePicker> = {
export default meta;
type Story = StoryObj<typeof DateTimePicker>;

export const DropOffSelection: Story = {};
export const BringInYourVehicle: Story = {};

const dataSelected = '[data-selected]';
export const Interactions: Story = {
args: {
title: 'Picker title',
timeOptions: {
items: times,
label: 'Aria label for time option grid',
},
lang: {
dateLabel: 'Date label',
timeLabel: 'Time label',
nextLabel: 'Next button label',
prevLabel: 'Previous button label',
},
},
play: async ({ args, canvasElement, step }) => {
const canvas = within(canvasElement);
const component = canvas.getAllByRole('group')[0];
const datePickerNav = canvas.getAllByRole('application')[0];
const [prevBtn, nextBtn] = getAllByRole(datePickerNav, 'button');
const calendar = canvas.getAllByRole('grid')[0];
const timePicker = canvas.getAllByRole('listbox')[0];

const nowDay = `${new Date().getDate()}`;

await step('Elements render label and attributes', async () => {
await expect(component).toHaveAccessibleName(`${args.title}`);
await expect(prevBtn).toBeDisabled();
await expect(nextBtn).toBeEnabled();
await expect(timePicker).toHaveAccessibleName(
args.timeOptions.label,
);
await expect(component).toHaveAttribute(
'data-test-id',
args.testId,
);
});

await step('Calendar selection and navigation', async () => {
let selectedDay = calendar.querySelector(dataSelected);
await expect(selectedDay).toBeInTheDocument();
await expect(selectedDay).toHaveRole('button');
await expect(selectedDay).toHaveTextContent(nowDay);

await userEvent.click(nextBtn);
await userEvent.click(nextBtn);
await expect(
calendar.querySelector(dataSelected),
).not.toBeInTheDocument();
await userEvent.click(prevBtn);

await userEvent.click(getByText(calendar, '13'));
await expect(args.onChange).not.toHaveBeenCalled();

await userEvent.keyboard('{ArrowLeft}{Enter}');
selectedDay = calendar.querySelector(dataSelected);
await expect(selectedDay).toHaveTextContent('12');
});

await step('Time selection', async () => {
const options = getAllByRole(timePicker, 'option');
await expect(options).toHaveLength(times.length);
await userEvent.click(options[1]);
await expect(args.onChange).toHaveBeenCalled();
});
},
};
24 changes: 16 additions & 8 deletions lib/components/DateTimePicker/DateTimePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
import { useCalendarState, type Selection } from 'react-stately';

import { odStyle } from '../../styles/sprinkles.css';
import type { WithTestId } from '../../types';
import { dataAttrs } from '../../utils/dataAttrs';
import { Icon } from '../Icon';
import {
OptionGrid,
Expand All @@ -30,8 +32,8 @@ import { CalendarGrid } from './CalendarGrid';
const defaultEnglish = {
dateLabel: 'Date',
timeLabel: 'Time',
nextLabel: 'Next',
prevLabel: 'Prev',
nextLabel: 'Next month',
prevLabel: 'Previous month',
} as const;

type LangContent = keyof typeof defaultEnglish;
Expand Down Expand Up @@ -109,7 +111,8 @@ export const DateTimePicker = <D extends DateValue>({
onChange,
timeOptions,
title,
}: DateTimePickerProps<D>) => {
testId,
}: WithTestId<DateTimePickerProps<D>>) => {
const selectedDate = useRef<DateValue>(null);
const selectedTimeOption = useRef<string>(null);

Expand Down Expand Up @@ -140,7 +143,7 @@ export const DateTimePicker = <D extends DateValue>({
...calendar,
};
const optionGridComponentProps: OptionGridProps<OptionItem> = {
columns: '2',
columns: '3',
onSelectionChange: handleTimeChange,
indicator: 'none',
selectionMode: 'single',
Expand Down Expand Up @@ -177,11 +180,15 @@ export const DateTimePicker = <D extends DateValue>({
// : '';

return (
<div role="group" aria-labelledby={titleId}>
<div
role="group"
aria-labelledby={titleId}
{...dataAttrs({ 'test-id': testId })}
>
{title && (
<h2
id={titleId}
className={odStyle({ fontSize: '2xl', fontWeight: 'bold' })}
className={odStyle({ font: '2xl', fontWeight: 'bold' })}
>
{title}
</h2>
Expand All @@ -195,7 +202,7 @@ export const DateTimePicker = <D extends DateValue>({
<div className={odStyle({ flexShrink: 0 })}>
<h3
className={odStyle({
fontSize: 'xl',
font: 'xl',
fontWeight: 'bold',
})}
>
Expand All @@ -208,6 +215,7 @@ export const DateTimePicker = <D extends DateValue>({
display: 'flex',
justifyContent: 'space-between',
marginBottom: '2',
paddingX: '1',
})}
>
<CalendarButton
Expand Down Expand Up @@ -245,7 +253,7 @@ export const DateTimePicker = <D extends DateValue>({
<div className={odStyle({ flexGrow: 1 })}>
<h3
className={odStyle({
fontSize: 'xl',
font: 'xl',
fontWeight: 'bold',
})}
>
Expand Down
5 changes: 2 additions & 3 deletions lib/components/OptionGrid/OptionGrid.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export const gridContainerStyle = style({
});

// == Grid styles
const minWidth640 = '(min-width: 640px)';
const minWidth1200 = '(min-width: 1200px)';
const repeat2Col = 'repeat(2, 1fr)';
const repeat3Col = 'repeat(3, 1fr)';
Expand Down Expand Up @@ -46,7 +45,7 @@ export const styledGrid = recipe({
'1': {},
'2': {
'@container': {
[`${gridContainer} ${minWidth640}`]: {
[`${gridContainer} (min-width: 640px)`]: {
gridTemplateColumns: repeat2Col,
},
[`${gridContainer} ${minWidth1200}`]: {
Expand All @@ -56,7 +55,7 @@ export const styledGrid = recipe({
},
'3': {
'@container': {
[`${gridContainer} ${minWidth640}`]: {
[`${gridContainer} (min-width: 550px)`]: {
gridTemplateColumns: repeat2Col,
},
[`${gridContainer} (min-width: ${breakpoints.desktop})`]: {
Expand Down
9 changes: 8 additions & 1 deletion lib/styles/sprinkles.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,14 @@ const displayProperties = defineProperties({
conditions: { ...responsiveConditions },
defaultCondition: 'mobile',
properties: {
display: ['none', 'block', 'flex', 'grid'],
display: [
'none',
'block',
'flex',
'grid',
'inline-block',
'inline-flex',
],
flexDirection: ['row', 'column'],
flexGrow: [0, 1],
flexShrink: [0, 1],
Expand Down

0 comments on commit 30a0634

Please sign in to comment.