Skip to content

Commit d61ea17

Browse files
committed
feat(ffe-datepicker-react): nytt format på datepicker
!!Breaking change: fjernet inputProps.
1 parent 79f1784 commit d61ea17

File tree

12 files changed

+752
-393
lines changed

12 files changed

+752
-393
lines changed

packages/ffe-datepicker-react/src/datelogic/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ export type CalendarButtonState = {
77
isSelected: boolean;
88
isEnabled: boolean;
99
};
10+
11+
export type Locale = 'nb' | 'nn' | 'en';
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import React from 'react';
2-
import { Datepicker, DatepickerProps } from './Datepicker';
2+
import { Datepicker, DatepickerProviderProps } from './Datepicker';
33
import { render, screen } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
55

66
const defaultProps = {
77
value: '',
88
onChange: () => {},
9+
locale: 'nb' as const,
10+
labelId: 'datepicker-label',
911
};
1012

11-
const renderDatePicker = (props?: Partial<DatepickerProps>) =>
13+
const renderDatePicker = (props?: Partial<DatepickerProviderProps>) =>
1214
render(<Datepicker {...defaultProps} {...props} />);
1315

1416
describe('<Datepicker />', () => {
@@ -23,14 +25,40 @@ describe('<Datepicker />', () => {
2325

2426
it('contains a single DateInput component', () => {
2527
renderDatePicker();
26-
expect(screen.getByRole('textbox')).toBeInTheDocument();
28+
expect(screen.getByRole('group')).toBeInTheDocument();
2729
});
2830

2931
it('does not contain a Calendar component', () => {
3032
renderDatePicker();
3133
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
3234
});
3335

36+
it('responds to arrow up and down', async () => {
37+
renderDatePicker();
38+
const [dayInput] = screen.getAllByRole('spinbutton');
39+
await userEvent.type(dayInput, '{arrowup}');
40+
expect(dayInput).toHaveValue(1);
41+
await userEvent.type(dayInput, '{arrowdown}');
42+
expect(dayInput).toHaveValue(31);
43+
});
44+
45+
it('triggers onchange when arrows are used', async () => {
46+
const onChange = jest.fn();
47+
renderDatePicker({ onChange });
48+
const [dayInput] = screen.getAllByRole('spinbutton');
49+
await userEvent.type(dayInput, '{arrowup}');
50+
expect(onChange).toHaveBeenCalledTimes(1);
51+
});
52+
53+
it('reponds to arrow left and right', async () => {
54+
renderDatePicker();
55+
const [dayInput, monthInput] = screen.getAllByRole('spinbutton');
56+
await userEvent.type(dayInput, '{arrowright}');
57+
expect(monthInput).toHaveFocus();
58+
await userEvent.type(monthInput, '{arrowleft}');
59+
expect(dayInput).toHaveFocus();
60+
});
61+
3462
describe('with click on button', () => {
3563
const user = userEvent.setup();
3664
it('contains a Calendar', async () => {
@@ -54,8 +82,8 @@ describe('<Datepicker />', () => {
5482
it('calls onChange method', async () => {
5583
const onChange = jest.fn();
5684
renderDatePicker({ onChange });
57-
const input = screen.getByRole('textbox');
58-
await user.type(input, '1');
85+
const [dayInput] = screen.getAllByRole('spinbutton');
86+
await user.type(dayInput, '4');
5987
expect(onChange).toHaveBeenCalledTimes(1);
6088
});
6189
});
@@ -81,33 +109,24 @@ describe('<Datepicker />', () => {
81109
describe('ariaInvalid', () => {
82110
it('has correct aria-invalid value if given prop', () => {
83111
renderDatePicker({ ariaInvalid: true });
84-
const input = screen.getByRole('textbox');
85-
expect(input.getAttribute('aria-invalid')).toBe('true');
112+
const [date, month, year] =
113+
screen.getAllByRole('spinbutton');
114+
expect(date.getAttribute('aria-invalid')).toBe('true');
115+
expect(month.getAttribute('aria-invalid')).toBe('true');
116+
expect(year.getAttribute('aria-invalid')).toBe('true');
86117
});
87118

88119
it('has correct aria-describedby if aria-describedby given as input prop', () => {
89-
const inputProps = {
90-
'aria-describedby': 'test',
91-
};
92120
renderDatePicker({
93121
ariaInvalid: true,
94-
inputProps,
122+
ariaDescribedby: 'test',
95123
});
96-
const input = screen.getByRole('textbox');
97-
expect(input.getAttribute('aria-describedby')).toBe('test');
98-
});
99-
});
100124

101-
describe('inputProps', () => {
102-
it('is passed on to input field', () => {
103-
const inputProps = {
104-
className: 'customClass',
105-
id: 'custom-input-id',
106-
};
107-
renderDatePicker({ inputProps });
108-
const input = screen.getByRole('textbox');
109-
expect(input.classList.contains('customClass')).toBe(true);
110-
expect(input.getAttribute('id')).toBe('custom-input-id');
125+
const [date, month, year] =
126+
screen.getAllByRole('spinbutton');
127+
expect(date.getAttribute('aria-describedby')).toBe('test');
128+
expect(month.getAttribute('aria-describedby')).toBe('test');
129+
expect(year.getAttribute('aria-describedby')).toBe('test');
111130
});
112131
});
113132

@@ -126,43 +145,14 @@ describe('<Datepicker />', () => {
126145
});
127146
});
128147

129-
describe('try to be smart in which century to place an input of two digit years', () => {
130-
const user = userEvent.setup();
131-
it('defaults to the 20th century', async () => {
132-
const onChange = jest.fn();
133-
renderDatePicker({ onChange, value: '101099' });
134-
135-
const input = screen.getByRole('textbox');
136-
await user.type(input, '{Tab}');
137-
expect(onChange).toHaveBeenCalledWith('10.10.2099');
138-
});
139-
140-
it('assumes last century if maxDate is today-ish', async () => {
141-
const onChange = jest.fn();
142-
renderDatePicker({
143-
maxDate: '02.02.2022',
144-
onChange,
145-
value: '111199',
146-
});
147-
148-
const input = screen.getByRole('textbox');
149-
await user.type(input, '{Tab}');
150-
151-
expect(onChange).toHaveBeenCalledWith('11.11.1999');
152-
});
153-
154-
it('assumes this century if maxDate is today-ish but input is rather close to it', async () => {
155-
const onChange = jest.fn();
156-
renderDatePicker({
157-
maxDate: '02.02.2022',
158-
onChange,
159-
value: '121220',
160-
});
161-
162-
const input = screen.getByRole('textbox');
163-
await user.type(input, '{Tab}');
148+
describe('with value', () => {
149+
it('has correct value in input field', () => {
150+
renderDatePicker({ value: '01.01.2021' });
164151

165-
expect(onChange).toHaveBeenCalledWith('12.12.2020');
152+
const [date, month, year] = screen.getAllByRole('spinbutton');
153+
expect(date).toHaveValue(1);
154+
expect(month).toHaveValue(1);
155+
expect(year).toHaveValue(2021);
166156
});
167157
});
168158
});

packages/ffe-datepicker-react/src/datepicker/Datepicker.stories.tsx

+95-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useState } from 'react';
2-
import { Datepicker } from './Datepicker';
2+
import { Datepicker, DatepickerProviderProps } from './Datepicker';
33
import type { StoryObj, Meta } from '@storybook/react';
44
import { InputGroup } from '@sb1/ffe-form-react';
55

@@ -14,17 +14,105 @@ type Story = StoryObj<typeof Datepicker>;
1414
export const Standard: Story = {
1515
args: {
1616
locale: 'nb',
17-
maxDate: '31.12.2016',
18-
minDate: '01.01.2016',
17+
maxDate: '31.12.2025',
18+
minDate: '01.01.2024',
19+
labelId: 'datepicker-label',
1920
},
20-
render: function Render({ value, onChange, ...args }) {
21-
const [date, setDate] = useState('01.01.2016');
21+
render: function Render({
22+
value,
23+
onChange,
24+
...args
25+
}: DatepickerProviderProps) {
26+
const [date, setDate] = useState('01.12.2024');
27+
28+
return (
29+
<InputGroup label="Dato" labelId={Standard?.args?.labelId}>
30+
{inputProps => (
31+
<Datepicker
32+
value={value ?? date}
33+
onChange={setDate}
34+
{...inputProps}
35+
{...args}
36+
/>
37+
)}
38+
</InputGroup>
39+
);
40+
},
41+
};
42+
43+
export const FieldMessageString: Story = {
44+
args: {
45+
...Standard.args,
46+
},
47+
render: function Render({
48+
value,
49+
onChange,
50+
...args
51+
}: DatepickerProviderProps) {
52+
const [date, setDate] = useState('01.12.2024');
53+
54+
return (
55+
<InputGroup
56+
label="Dato"
57+
aria-invalid={true}
58+
fieldMessage={'Ugyldig dato'}
59+
labelId={Standard?.args?.labelId}
60+
>
61+
{inputProps => (
62+
<Datepicker
63+
value={value ?? date}
64+
onChange={setDate}
65+
{...inputProps}
66+
{...args}
67+
/>
68+
)}
69+
</InputGroup>
70+
);
71+
},
72+
};
73+
74+
export const FullWidth: Story = {
75+
args: {
76+
...Standard.args,
77+
fullWidth: true,
78+
},
79+
render: function Render({
80+
value,
81+
onChange,
82+
...args
83+
}: DatepickerProviderProps) {
84+
const [date, setDate] = useState('01.12.2024');
85+
2286
return (
23-
<InputGroup label="Dato">
87+
<InputGroup label="Dato" labelId={Standard?.args?.labelId}>
2488
<Datepicker
89+
value={value ?? date}
90+
onChange={setDate}
2591
{...args}
92+
/>
93+
</InputGroup>
94+
);
95+
},
96+
};
97+
98+
export const CalendarAbove: Story = {
99+
args: {
100+
...Standard.args,
101+
calendarAbove: true,
102+
},
103+
render: function Render({
104+
value,
105+
onChange,
106+
...args
107+
}: DatepickerProviderProps) {
108+
const [date, setDate] = useState('01.12.2024');
109+
110+
return (
111+
<InputGroup label="Dato" labelId={Standard?.args?.labelId}>
112+
<Datepicker
26113
value={value ?? date}
27-
onChange={onChange ?? setDate}
114+
onChange={setDate}
115+
{...args}
28116
/>
29117
</InputGroup>
30118
);

0 commit comments

Comments
 (0)