Skip to content

Commit d12aac5

Browse files
authored
feat: Custom formats in date picker (#3719)
1 parent e965603 commit d12aac5

File tree

14 files changed

+349
-80
lines changed

14 files changed

+349
-80
lines changed

pages/date-input/permutations-formats.page.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,20 @@ import PermutationsView from '../utils/permutations-view';
1010
import ScreenshotArea from '../utils/screenshot-area';
1111
import { locales } from './common';
1212

13-
const permutationsFormats = createPermutations<DateInputProps>([
13+
const permutationsFormatsDay = createPermutations<DateInputProps>([
1414
{
1515
value: ['2020-01-02'],
1616
ariaLabel: ['Some label'],
1717
format: ['iso', 'slashed', 'long-localized'],
18-
granularity: ['day', 'month'],
18+
granularity: ['day'],
19+
},
20+
]);
21+
const permutationsFormatsMonth = createPermutations<DateInputProps>([
22+
{
23+
value: ['2020-01'],
24+
ariaLabel: ['Some label'],
25+
format: ['iso', 'slashed', 'long-localized'],
26+
granularity: ['month'],
1927
},
2028
]);
2129

@@ -35,7 +43,11 @@ export default function DateInputPermutations() {
3543
<h1>Date Input permutations - formats</h1>
3644
<ScreenshotArea>
3745
<PermutationsView
38-
permutations={permutationsFormats}
46+
permutations={permutationsFormatsDay}
47+
render={permutation => <DateInput {...permutation} onChange={() => {}} />}
48+
/>
49+
<PermutationsView
50+
permutations={permutationsFormatsMonth}
3951
render={permutation => <DateInput {...permutation} onChange={() => {}} />}
4052
/>
4153

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import React from 'react';
5+
import MockDate from 'mockdate';
6+
7+
import { Box, DatePicker, DatePickerProps, SpaceBetween } from '~components';
8+
9+
import { locales } from '../date-input/common';
10+
import createPermutations from '../utils/permutations';
11+
import PermutationsView from '../utils/permutations-view';
12+
import ScreenshotArea from '../utils/screenshot-area';
13+
14+
// Mock the date in order to have the current day styling in place for screenshot testing.
15+
MockDate.set(new Date(2020, 9, 8));
16+
17+
const openCalendarAriaLabel = (selectedDate: string | null) =>
18+
'Choose Date' + (selectedDate ? `, selected date is ${selectedDate}` : '');
19+
20+
const common = {
21+
todayAriaLabel: ['Today'],
22+
nextMonthAriaLabel: ['Next Month'],
23+
previousMonthAriaLabel: ['Previous Month'],
24+
name: ['date-picker-name'],
25+
ariaLabel: ['date-picker-label'],
26+
openCalendarAriaLabel: [openCalendarAriaLabel],
27+
} as const;
28+
29+
const permutationsFormatsDay = createPermutations<DatePickerProps>([
30+
{
31+
value: ['2020-01-02'],
32+
format: ['iso', 'slashed', 'long-localized'],
33+
granularity: ['day'],
34+
...common,
35+
},
36+
]);
37+
38+
const permutationsFormatsMonth = createPermutations<DatePickerProps>([
39+
{
40+
value: ['2020-01'],
41+
format: ['iso', 'slashed', 'long-localized'],
42+
granularity: ['month'],
43+
...common,
44+
},
45+
]);
46+
47+
const permutationsLongLocalizedLocales = createPermutations<DatePickerProps>([
48+
{
49+
value: ['2020-01-02'],
50+
format: ['long-localized'],
51+
granularity: ['day', 'month'],
52+
locale: locales,
53+
...common,
54+
},
55+
]);
56+
57+
export default function DatePickerScenario() {
58+
return (
59+
<Box padding="s">
60+
<h1>Date picker permutations - formats</h1>
61+
<ScreenshotArea>
62+
<SpaceBetween size="m">
63+
<PermutationsView
64+
permutations={permutationsFormatsDay}
65+
render={permutation => <DatePicker {...permutation} onChange={() => {}} />}
66+
/>
67+
<PermutationsView
68+
permutations={permutationsFormatsMonth}
69+
render={permutation => <DatePicker {...permutation} onChange={() => {}} />}
70+
/>
71+
72+
<br />
73+
<hr />
74+
<br />
75+
76+
<PermutationsView
77+
permutations={permutationsLongLocalizedLocales}
78+
render={permutation => <DatePicker {...permutation} onChange={() => {}} />}
79+
/>
80+
</SpaceBetween>
81+
</ScreenshotArea>
82+
</Box>
83+
);
84+
}

pages/date-picker/permutations.page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3+
34
import React from 'react';
45
import MockDate from 'mockdate';
56

@@ -36,7 +37,7 @@ const permutations = createPermutations<DatePickerProps>([
3637
export default function DatePickerScenario() {
3738
return (
3839
<Box padding="s">
39-
<h1>Date picker permutations</h1>
40+
<h1>Date picker permutations - states</h1>
4041
<ScreenshotArea>
4142
<SpaceBetween size="m">
4243
<div style={{ blockSize: '300px' }} data-testid="date-picker-expanded-example">

pages/date-picker/simple.page.tsx

Lines changed: 105 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,147 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3+
34
import React, { useContext, useEffect, useState } from 'react';
45

5-
import { Box, Checkbox, DatePicker, DatePickerProps, FormField, Link, SpaceBetween } from '~components';
6+
import {
7+
Box,
8+
DatePicker,
9+
DatePickerProps,
10+
FormField,
11+
Link,
12+
Select,
13+
SelectProps,
14+
SpaceBetween,
15+
Toggle,
16+
} from '~components';
617

718
import { AppContextType } from '../app/app-context';
819
import AppContext from '../app/app-context';
20+
import { getPlaceholder, locales } from '../date-input/common';
921
import i18nStrings from './i18n-strings';
1022

11-
export type DatePickerDemoContext = React.Context<
23+
export type PageContext = React.Context<
1224
AppContextType<{
13-
monthOnly?: boolean;
25+
granularity?: DatePickerProps.Granularity;
26+
readOnly?: boolean;
27+
disabled?: boolean;
28+
inputFormat?: DatePickerProps.InputFormat;
29+
format?: DatePickerProps.Format;
30+
locale?: string;
1431
hasValue?: boolean;
1532
}>
1633
>;
1734

18-
export const dateRangePickerDemoDefaults = {
19-
monthOnly: false,
20-
hasValue: false,
21-
};
35+
const inputFormatOptions: SelectProps.Option[] = [{ value: 'iso' }, { value: 'slashed' }];
2236

23-
export default function DateInputScenario() {
24-
const { urlParams, setUrlParams } = useContext(AppContext as DatePickerDemoContext);
25-
const monthOnly = urlParams.monthOnly ?? dateRangePickerDemoDefaults.monthOnly;
26-
const hasValue = urlParams.hasValue ?? dateRangePickerDemoDefaults.hasValue;
37+
const formatOptions: SelectProps.Option[] = [...inputFormatOptions, { value: 'long-localized' }];
38+
39+
const localeOptions: SelectProps.Option[] = locales.map(locale => ({ value: locale }));
2740

41+
export default function DateInputScenario() {
42+
const { urlParams, setUrlParams } = useContext(AppContext as PageContext);
43+
const initValue = '2018-01-02';
44+
const hasValue = urlParams.hasValue ?? false;
2845
const [value, setValue] = useState<DatePickerProps['value']>('');
2946

3047
useEffect(() => {
31-
const initValue = monthOnly ? '2025-02' : '2025-02-00';
3248
if (hasValue) {
3349
setValue(initValue);
3450
} else {
3551
setValue('');
3652
}
37-
}, [hasValue, monthOnly]);
53+
}, [hasValue]);
3854

3955
return (
40-
<Box padding="s">
56+
<Box padding="l">
4157
<SpaceBetween direction="vertical" size="m">
42-
<h1>Date picker simple version</h1>
43-
<SpaceBetween direction="horizontal" size="s">
44-
<Checkbox checked={monthOnly} onChange={({ detail }) => setUrlParams({ monthOnly: detail.checked })}>
45-
Month-only
46-
</Checkbox>
47-
<Checkbox checked={hasValue} onChange={({ detail }) => setUrlParams({ hasValue: detail.checked })}>
48-
Has initial value
49-
</Checkbox>
58+
<h1>Date picker</h1>
59+
60+
<SpaceBetween size="m">
61+
<SpaceBetween direction="horizontal" size="s">
62+
<Toggle
63+
checked={urlParams.granularity === 'month'}
64+
onChange={({ detail }) => setUrlParams({ granularity: detail.checked ? 'month' : 'day' })}
65+
>
66+
Month only
67+
</Toggle>
68+
<Toggle
69+
checked={!!urlParams.readOnly}
70+
onChange={({ detail }) => setUrlParams({ readOnly: detail.checked })}
71+
>
72+
Read-only
73+
</Toggle>
74+
<Toggle
75+
checked={!!urlParams.disabled}
76+
onChange={({ detail }) => setUrlParams({ disabled: detail.checked })}
77+
>
78+
Disabled
79+
</Toggle>
80+
<Toggle checked={hasValue} onChange={({ detail }) => setUrlParams({ hasValue: detail.checked })}>
81+
Has initial value
82+
</Toggle>
83+
</SpaceBetween>
84+
85+
<SpaceBetween direction="horizontal" size="s">
86+
<div style={{ minWidth: 200 }}>
87+
<Select
88+
inlineLabelText="Input format"
89+
options={inputFormatOptions}
90+
selectedOption={inputFormatOptions.find(o => o.value === urlParams.inputFormat) ?? null}
91+
onChange={({ detail }) =>
92+
setUrlParams({ inputFormat: detail.selectedOption.value as DatePickerProps.InputFormat })
93+
}
94+
/>
95+
</div>
96+
97+
<div style={{ minWidth: 200 }}>
98+
<Select
99+
inlineLabelText="Format"
100+
options={formatOptions}
101+
selectedOption={formatOptions.find(o => o.value === urlParams.format) ?? null}
102+
onChange={({ detail }) =>
103+
setUrlParams({ format: detail.selectedOption.value as DatePickerProps.Format })
104+
}
105+
/>
106+
</div>
107+
108+
<div style={{ minWidth: 200 }}>
109+
<Select
110+
inlineLabelText="Locale"
111+
options={localeOptions}
112+
selectedOption={localeOptions.find(o => o.value === urlParams.locale) ?? null}
113+
onChange={({ detail }) => setUrlParams({ locale: detail.selectedOption.value })}
114+
/>
115+
</div>
116+
</SpaceBetween>
50117
</SpaceBetween>
118+
51119
<Link id="focus-dismiss-helper">Focusable element before the date picker</Link>
52-
<br />
53-
<FormField label="Certificate expiry date" constraintText={`Use YYYY/MM${monthOnly ? '' : '/DD'} format.`}>
120+
121+
<FormField
122+
label="Certificate expiry date"
123+
constraintText={`Use YYYY/MM${urlParams.granularity === 'month' ? '' : '/DD'} format.`}
124+
>
54125
<DatePicker
55126
value={value}
56-
name={'date-picker-name'}
57-
granularity={monthOnly ? 'month' : 'day'}
58-
locale="en-GB"
127+
name="date-picker-name"
128+
placeholder={getPlaceholder({
129+
granularity: urlParams.granularity ?? 'day',
130+
inputFormat: urlParams.inputFormat,
131+
format: urlParams.format,
132+
})}
59133
i18nStrings={i18nStrings}
60134
openCalendarAriaLabel={selectedDate =>
61135
'Choose certificate expiry date' + (selectedDate ? `, selected date is ${selectedDate}` : '')
62136
}
63-
placeholder={monthOnly ? 'YYYY/MM' : 'YYYY/MM/DD'}
64137
onChange={e => setValue(e.detail.value)}
138+
{...urlParams}
65139
/>
66140
</FormField>
67-
<br />
68-
<br />
141+
69142
<Link id="focusable-element-after-date-picker">Focusable element after the date picker</Link>
70-
<b>Raw value</b>
71-
<pre>{JSON.stringify(value, undefined, 2)}</pre>
143+
144+
<Box variant="code">Raw value (iso): {JSON.stringify(value, undefined, 2)}</Box>
72145
</SpaceBetween>
73146
</Box>
74147
);

pages/date-picker/with-default-date.page.tsx

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)