@indico-data/design-system 2.54.0 → 2.55.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.d.ts +24 -0
- package/lib/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.stories.d.ts +6 -0
- package/lib/components/forms/date/inputDateTimePicker/helpers.d.ts +1 -0
- package/lib/components/forms/date/inputDateTimePicker/index.d.ts +1 -0
- package/lib/components/forms/subcomponents/DisplayFormError.d.ts +2 -1
- package/lib/components/forms/timePicker/TimePicker.d.ts +3 -1
- package/lib/components/forms/timePicker/helpers.d.ts +2 -5
- package/lib/index.css +0 -90
- package/lib/index.d.ts +3 -1
- package/lib/index.esm.css +0 -90
- package/lib/index.esm.js +187 -166
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +187 -166
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/forms/date/datePicker/DatePicker.stories.tsx +4 -4
- package/src/components/forms/date/datePicker/DatePicker.tsx +0 -2
- package/src/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.mdx +20 -0
- package/src/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.stories.tsx +262 -0
- package/src/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.tsx +141 -0
- package/src/components/forms/date/inputDateTimePicker/helpers.ts +3 -0
- package/src/components/forms/date/inputDateTimePicker/index.ts +1 -0
- package/src/components/forms/input/Input.tsx +1 -1
- package/src/components/forms/passwordInput/PasswordInput.stories.tsx +1 -1
- package/src/components/forms/subcomponents/DisplayFormError.tsx +7 -2
- package/src/components/forms/timePicker/TimePicker.mdx +3 -27
- package/src/components/forms/timePicker/TimePicker.stories.tsx +19 -1
- package/src/components/forms/timePicker/TimePicker.tsx +37 -80
- package/src/components/forms/timePicker/__tests__/TimePicker.test.tsx +33 -11
- package/src/components/forms/timePicker/helpers.ts +61 -13
- package/src/styles/index.scss +0 -2
- package/lib/components/forms/timePicker/constants.d.ts +0 -13
- package/src/components/forms/timePicker/constants.ts +0 -21
- package/src/components/forms/timePicker/styles/TimePicker.scss +0 -51
package/package.json
CHANGED
|
@@ -284,7 +284,7 @@ export const Default: Story = {
|
|
|
284
284
|
numberOfMonths: 1,
|
|
285
285
|
isRequired: false,
|
|
286
286
|
isDisabled: false,
|
|
287
|
-
hasTimePicker:
|
|
287
|
+
hasTimePicker: false,
|
|
288
288
|
timeValue: '00:00',
|
|
289
289
|
},
|
|
290
290
|
render: (args) => {
|
|
@@ -313,7 +313,7 @@ export const Single: Story = {
|
|
|
313
313
|
isRequired: false,
|
|
314
314
|
isDisabled: false,
|
|
315
315
|
mode: 'single',
|
|
316
|
-
hasTimePicker:
|
|
316
|
+
hasTimePicker: false,
|
|
317
317
|
},
|
|
318
318
|
render: (args) => {
|
|
319
319
|
const [{ selected }, updateArgs] = useArgs();
|
|
@@ -329,7 +329,7 @@ export const Range: Story = {
|
|
|
329
329
|
isRequired: false,
|
|
330
330
|
isDisabled: false,
|
|
331
331
|
mode: 'range',
|
|
332
|
-
hasTimePicker:
|
|
332
|
+
hasTimePicker: false,
|
|
333
333
|
},
|
|
334
334
|
render: (args) => {
|
|
335
335
|
const [{ selected, timeValue }, updateArgs] = useArgs();
|
|
@@ -358,7 +358,7 @@ export const Multi: Story = {
|
|
|
358
358
|
isRequired: false,
|
|
359
359
|
isDisabled: false,
|
|
360
360
|
mode: 'multiple',
|
|
361
|
-
hasTimePicker:
|
|
361
|
+
hasTimePicker: false,
|
|
362
362
|
},
|
|
363
363
|
render: (args) => {
|
|
364
364
|
const [{ selected }, updateArgs] = useArgs();
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import React, { ChangeEventHandler, useState } from 'react';
|
|
2
1
|
import { DateRange, DayPicker, Mode, OnSelectHandler } from 'react-day-picker';
|
|
3
2
|
import 'react-day-picker/style.css';
|
|
4
|
-
|
|
5
3
|
import { DatePickerProps } from './types';
|
|
6
4
|
import { getCommonProps } from './contants';
|
|
7
5
|
import { TimePicker } from '../../timePicker/TimePicker';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Canvas, Meta, Controls } from '@storybook/blocks';
|
|
2
|
+
import * as SingleInputDateTimePicker from './SingleInputDateTimePicker.stories';
|
|
3
|
+
import { Row, Col } from '../../../grid/index';
|
|
4
|
+
|
|
5
|
+
<Meta title="Forms/DatePicker/SingleInputDateTimePicker" name="SingleInputDateTimePicker" />
|
|
6
|
+
|
|
7
|
+
# Single Input Date Time Picker
|
|
8
|
+
The Single Input Date Time Picker is a visual representation of an input field that allows you to enter a date and time value to the input or select a date and time from the date picker. It leverages our DatePicker and TimePicker components inside of a FloatingUI window.
|
|
9
|
+
|
|
10
|
+
<Canvas of={SingleInputDateTimePicker.SingleInput} />
|
|
11
|
+
|
|
12
|
+
## Props
|
|
13
|
+
In addition to the props listed below, it also accepts all props listed [here](story=?path=/docs/forms-datepicker--datepicker)
|
|
14
|
+
<Controls of={SingleInputDateTimePicker.SingleInput} />
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
This input is identical to the Single Input Date Picker with the exception that it also allows you to select a time value. There are only two values you need to provide, the `timeValue` and the `onTimeChange` callback function. The time input field is also readonly.
|
|
18
|
+
|
|
19
|
+
## ToDo
|
|
20
|
+
- Create single component for all dropdown date pickers that accept an react element as the trigger, such as an icon, button, component, input, etc.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import {
|
|
3
|
+
SingleInputDateTimePicker,
|
|
4
|
+
SingleInputDateTimePickerProps,
|
|
5
|
+
} from './SingleInputDateTimePicker';
|
|
6
|
+
import { useArgs } from '@storybook/preview-api';
|
|
7
|
+
import { indiconDefinitions } from '@/components/icons/indicons';
|
|
8
|
+
import { registerFontAwesomeIcons } from '@/setup/setupIcons';
|
|
9
|
+
|
|
10
|
+
registerFontAwesomeIcons(...indiconDefinitions);
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof SingleInputDateTimePicker> = {
|
|
13
|
+
component: SingleInputDateTimePicker,
|
|
14
|
+
title: 'Forms/DatePicker/SingleInputDateTimePicker',
|
|
15
|
+
decorators: [
|
|
16
|
+
(Story) => (
|
|
17
|
+
<div style={{ height: '400px' }}>
|
|
18
|
+
<Story />
|
|
19
|
+
</div>
|
|
20
|
+
),
|
|
21
|
+
],
|
|
22
|
+
argTypes: {
|
|
23
|
+
timeValue: {
|
|
24
|
+
control: 'text',
|
|
25
|
+
description: 'The time value to display.',
|
|
26
|
+
table: {
|
|
27
|
+
category: 'Props',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
onTimeChange: {
|
|
31
|
+
action: 'timeChanged',
|
|
32
|
+
description: 'Callback function that is called when the time is changed.',
|
|
33
|
+
table: {
|
|
34
|
+
category: 'Events',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
ariaLabel: {
|
|
38
|
+
control: 'text',
|
|
39
|
+
description: 'A label for assistive technologies.',
|
|
40
|
+
table: {
|
|
41
|
+
category: 'Props',
|
|
42
|
+
type: {
|
|
43
|
+
summary: 'string',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
captionLayout: {
|
|
48
|
+
control: 'select',
|
|
49
|
+
options: ['dropdown', 'dropdown-months', 'dropdown-years', 'label'],
|
|
50
|
+
description:
|
|
51
|
+
'The layout of the caption. Enables you to add or remove the dropdown navigation for months/years',
|
|
52
|
+
table: {
|
|
53
|
+
category: 'Props',
|
|
54
|
+
type: {
|
|
55
|
+
summary: 'dropdown | dropdown-months | dropdown-years | label',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
className: {
|
|
60
|
+
control: false,
|
|
61
|
+
description: 'Accepts a CSS class name',
|
|
62
|
+
table: {
|
|
63
|
+
category: 'Props',
|
|
64
|
+
type: {
|
|
65
|
+
summary: 'string',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
id: {
|
|
70
|
+
control: 'text',
|
|
71
|
+
description: 'The id of the input field.',
|
|
72
|
+
table: {
|
|
73
|
+
category: 'Props',
|
|
74
|
+
type: {
|
|
75
|
+
summary: 'string',
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
label: {
|
|
80
|
+
control: 'text',
|
|
81
|
+
description: 'The label for the input field.',
|
|
82
|
+
table: {
|
|
83
|
+
category: 'Props',
|
|
84
|
+
type: {
|
|
85
|
+
summary: 'string',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
onSelect: {
|
|
90
|
+
action: 'selected',
|
|
91
|
+
description: 'Callback function that is called when a date is selected.',
|
|
92
|
+
table: {
|
|
93
|
+
category: 'Events',
|
|
94
|
+
type: {
|
|
95
|
+
summary: 'function',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
selected: {
|
|
100
|
+
control: 'date',
|
|
101
|
+
description: 'The selected date.',
|
|
102
|
+
table: {
|
|
103
|
+
category: 'Props',
|
|
104
|
+
type: {
|
|
105
|
+
summary: 'Date',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
inputPlaceholder: {
|
|
110
|
+
control: 'text',
|
|
111
|
+
description: 'The placeholder text for the input field.',
|
|
112
|
+
table: {
|
|
113
|
+
category: 'Props',
|
|
114
|
+
type: {
|
|
115
|
+
summary: 'string',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
inputIconName: {
|
|
120
|
+
control: 'text',
|
|
121
|
+
description: 'The icon to display in the input field.',
|
|
122
|
+
table: {
|
|
123
|
+
category: 'Props',
|
|
124
|
+
type: {
|
|
125
|
+
summary: 'string',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
isClearable: {
|
|
130
|
+
control: 'boolean',
|
|
131
|
+
description: 'Whether the input field should be clearable.',
|
|
132
|
+
table: {
|
|
133
|
+
category: 'Props',
|
|
134
|
+
type: {
|
|
135
|
+
summary: 'boolean',
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
isOpen: {
|
|
140
|
+
control: false,
|
|
141
|
+
description: 'Whether the date picker is open.',
|
|
142
|
+
table: {
|
|
143
|
+
category: 'Props',
|
|
144
|
+
type: {
|
|
145
|
+
summary: 'boolean',
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
clearOnClose: {
|
|
150
|
+
control: 'boolean',
|
|
151
|
+
description: 'Whether the input field should be cleared when the date picker is closed.',
|
|
152
|
+
table: {
|
|
153
|
+
category: 'Props',
|
|
154
|
+
type: {
|
|
155
|
+
summary: 'boolean',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
errorMessage: {
|
|
160
|
+
control: 'text',
|
|
161
|
+
description: 'An error message to display.',
|
|
162
|
+
table: {
|
|
163
|
+
category: 'Props',
|
|
164
|
+
type: {
|
|
165
|
+
summary: 'string',
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
disableAfterDate: {
|
|
170
|
+
control: 'date',
|
|
171
|
+
description: 'Disable dates after this date.',
|
|
172
|
+
table: {
|
|
173
|
+
category: 'Props',
|
|
174
|
+
type: {
|
|
175
|
+
summary: 'Date',
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
disableBeforeDate: {
|
|
180
|
+
control: 'date',
|
|
181
|
+
description: 'Disable dates before this date.',
|
|
182
|
+
table: {
|
|
183
|
+
category: 'Props',
|
|
184
|
+
type: {
|
|
185
|
+
summary: 'Date',
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
isDisabled: {
|
|
190
|
+
control: 'boolean',
|
|
191
|
+
description: 'Disable the date picker.',
|
|
192
|
+
table: {
|
|
193
|
+
category: 'Props',
|
|
194
|
+
type: {
|
|
195
|
+
summary: 'boolean',
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
initialMonth: {
|
|
200
|
+
control: 'date',
|
|
201
|
+
description: 'The month to display.',
|
|
202
|
+
table: {
|
|
203
|
+
category: 'Props',
|
|
204
|
+
type: {
|
|
205
|
+
summary: 'Date',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
'data-testid': {
|
|
210
|
+
table: {
|
|
211
|
+
disable: true,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export default meta;
|
|
218
|
+
|
|
219
|
+
type Story = StoryObj<typeof SingleInputDateTimePicker>;
|
|
220
|
+
|
|
221
|
+
export const SingleInput: Story = {
|
|
222
|
+
args: {
|
|
223
|
+
id: 'date-picker',
|
|
224
|
+
label: 'Pick a date:',
|
|
225
|
+
inputPlaceholder: 'MM/DD/YYYY',
|
|
226
|
+
inputIconName: 'calendar',
|
|
227
|
+
isClearable: true,
|
|
228
|
+
isDisabled: false,
|
|
229
|
+
clearOnClose: false,
|
|
230
|
+
timeValue: '12:00 PM',
|
|
231
|
+
onTimeChange: () => {},
|
|
232
|
+
errorMessage: '',
|
|
233
|
+
ariaLabel: 'Date Picker',
|
|
234
|
+
selected: new Date(),
|
|
235
|
+
},
|
|
236
|
+
render: (args) => {
|
|
237
|
+
const [{ selected, timeValue }, updateArgs] = useArgs<SingleInputDateTimePickerProps>();
|
|
238
|
+
|
|
239
|
+
const handleSelect = (date: Date | undefined) => {
|
|
240
|
+
updateArgs({ selected: date });
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const handleTimeChange = (time: string) => {
|
|
244
|
+
updateArgs({ timeValue: time });
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
<>
|
|
249
|
+
<h1>Single Input Date Time Picker</h1>
|
|
250
|
+
<p>Date: {selected?.toLocaleDateString()}</p>
|
|
251
|
+
<p>Time: {timeValue}</p>
|
|
252
|
+
<SingleInputDateTimePicker
|
|
253
|
+
{...args}
|
|
254
|
+
selected={selected}
|
|
255
|
+
timeValue={timeValue}
|
|
256
|
+
onSelect={handleSelect}
|
|
257
|
+
onTimeChange={handleTimeChange}
|
|
258
|
+
/>
|
|
259
|
+
</>
|
|
260
|
+
);
|
|
261
|
+
},
|
|
262
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { useId, useState, useEffect } from 'react';
|
|
2
|
+
import { isValid, parse } from 'date-fns';
|
|
3
|
+
import { DatePicker } from '../datePicker/DatePicker';
|
|
4
|
+
import { Input } from '../../input';
|
|
5
|
+
import type { IconName } from '../../../icons/types';
|
|
6
|
+
import { FloatUI } from '../../../floatUI';
|
|
7
|
+
import { formatDateAsString } from './helpers';
|
|
8
|
+
import { TimePicker } from '../../timePicker/TimePicker';
|
|
9
|
+
import { Row, Col } from '../../../grid';
|
|
10
|
+
|
|
11
|
+
export interface SingleInputDateTimePickerProps {
|
|
12
|
+
ariaLabel: string;
|
|
13
|
+
disableBeforeDate?: Date;
|
|
14
|
+
disableAfterDate?: Date;
|
|
15
|
+
isDisabled?: boolean;
|
|
16
|
+
captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
|
|
17
|
+
id?: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
onSelect: (selected: Date | undefined) => void;
|
|
20
|
+
initialMonth?: Date;
|
|
21
|
+
selected?: Date;
|
|
22
|
+
isOpen?: boolean;
|
|
23
|
+
clearOnClose?: boolean;
|
|
24
|
+
className?: string;
|
|
25
|
+
inputIconName?: IconName;
|
|
26
|
+
isClearable?: boolean;
|
|
27
|
+
inputPlaceholder?: string;
|
|
28
|
+
errorMessage?: string | undefined;
|
|
29
|
+
'data-testid'?: string;
|
|
30
|
+
timeValue?: string;
|
|
31
|
+
onTimeChange?: (time: string) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function SingleInputDateTimePicker(props: SingleInputDateTimePickerProps) {
|
|
35
|
+
const {
|
|
36
|
+
ariaLabel,
|
|
37
|
+
className,
|
|
38
|
+
isDisabled,
|
|
39
|
+
disableBeforeDate,
|
|
40
|
+
disableAfterDate,
|
|
41
|
+
captionLayout,
|
|
42
|
+
initialMonth,
|
|
43
|
+
id,
|
|
44
|
+
label,
|
|
45
|
+
selected,
|
|
46
|
+
isOpen,
|
|
47
|
+
inputPlaceholder,
|
|
48
|
+
clearOnClose,
|
|
49
|
+
inputIconName,
|
|
50
|
+
isClearable,
|
|
51
|
+
errorMessage,
|
|
52
|
+
onSelect,
|
|
53
|
+
timeValue,
|
|
54
|
+
onTimeChange,
|
|
55
|
+
...rest
|
|
56
|
+
} = props;
|
|
57
|
+
|
|
58
|
+
const inputId = useId();
|
|
59
|
+
|
|
60
|
+
// The text value is assumed to be unneeded by the consumer.
|
|
61
|
+
const [localTextValue, setLocalTextValue] = useState<string>(
|
|
62
|
+
selected ? formatDateAsString(selected) : '',
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const [localMonth, setLocalMonth] = useState<Date>(initialMonth ?? selected ?? new Date());
|
|
66
|
+
|
|
67
|
+
// When the day picker is selected, update the text value.
|
|
68
|
+
const handleDayPickerSelect = (date: Date | undefined) => {
|
|
69
|
+
if (!date) {
|
|
70
|
+
setLocalTextValue('');
|
|
71
|
+
onSelect(undefined);
|
|
72
|
+
} else {
|
|
73
|
+
setLocalTextValue(formatDateAsString(date));
|
|
74
|
+
onSelect(date);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// When the text input is changed, update the selected date.
|
|
79
|
+
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
80
|
+
setLocalTextValue(e.target.value); // keep the input value in sync
|
|
81
|
+
|
|
82
|
+
const parsedDate = parse(e.target.value, 'MM/dd/yyyy', new Date());
|
|
83
|
+
|
|
84
|
+
if (isValid(parsedDate)) {
|
|
85
|
+
onSelect(parsedDate);
|
|
86
|
+
} else {
|
|
87
|
+
onSelect(undefined);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// clear selection if clear on close is true
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
if (!isOpen && clearOnClose) {
|
|
94
|
+
onSelect(undefined);
|
|
95
|
+
setLocalTextValue('');
|
|
96
|
+
}
|
|
97
|
+
}, [isOpen, clearOnClose]);
|
|
98
|
+
|
|
99
|
+
const handleTimeChange = (time: string) => {
|
|
100
|
+
onTimeChange?.(time);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Row>
|
|
105
|
+
<Col>
|
|
106
|
+
<FloatUI isOpen={isOpen} ariaLabel={ariaLabel}>
|
|
107
|
+
<Input
|
|
108
|
+
id={inputId}
|
|
109
|
+
value={localTextValue}
|
|
110
|
+
placeholder={inputPlaceholder}
|
|
111
|
+
isDisabled={isDisabled}
|
|
112
|
+
iconName={inputIconName}
|
|
113
|
+
isClearable={isClearable}
|
|
114
|
+
onChange={handleInputChange}
|
|
115
|
+
errorMessage={errorMessage}
|
|
116
|
+
hasHiddenLabel
|
|
117
|
+
label={'Single Date Picker'}
|
|
118
|
+
name={`${id}-date-picker`}
|
|
119
|
+
/>
|
|
120
|
+
<DatePicker
|
|
121
|
+
captionLayout={captionLayout}
|
|
122
|
+
mode="single"
|
|
123
|
+
selected={selected}
|
|
124
|
+
onSelect={handleDayPickerSelect}
|
|
125
|
+
month={localMonth}
|
|
126
|
+
onMonthChange={setLocalMonth}
|
|
127
|
+
{...rest}
|
|
128
|
+
/>
|
|
129
|
+
</FloatUI>
|
|
130
|
+
</Col>
|
|
131
|
+
<Col xs="content">
|
|
132
|
+
<TimePicker
|
|
133
|
+
timeValue={timeValue}
|
|
134
|
+
name={`${id}-time-picker`}
|
|
135
|
+
hasHiddenLabel
|
|
136
|
+
onTimeChange={handleTimeChange}
|
|
137
|
+
/>
|
|
138
|
+
</Col>
|
|
139
|
+
</Row>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { SingleInputDateTimePicker } from './SingleInputDateTimePicker';
|
|
@@ -97,7 +97,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
97
97
|
/>
|
|
98
98
|
)}
|
|
99
99
|
</div>
|
|
100
|
-
{hasErrors && <DisplayFormError message={errorMessage} />}
|
|
100
|
+
{hasErrors && <DisplayFormError data-testid={`${name}-error`} message={errorMessage} />}
|
|
101
101
|
{helpText && (
|
|
102
102
|
<div data-testid={`${name}-help-text`} className="help-text" id={`${name}-helper`}>
|
|
103
103
|
{helpText}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { PasswordInput, PasswordInputProps } from './PasswordInput';
|
|
3
3
|
import { SetStateAction, useEffect, useState } from 'react';
|
|
4
|
-
import { baseInputArgTypes,
|
|
4
|
+
import { baseInputArgTypes, labelArgTypes } from '@/storybook/formArgTypes';
|
|
5
5
|
|
|
6
6
|
const meta: Meta = {
|
|
7
7
|
title: 'Forms/PasswordInput',
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
interface DisplayFormErrorProps {
|
|
2
2
|
message: string | undefined;
|
|
3
|
+
[key: string]: any;
|
|
3
4
|
}
|
|
4
5
|
|
|
5
|
-
export const DisplayFormError = ({ message }: DisplayFormErrorProps) => {
|
|
6
|
-
return
|
|
6
|
+
export const DisplayFormError = ({ message, ...rest }: DisplayFormErrorProps) => {
|
|
7
|
+
return (
|
|
8
|
+
<p className="form-errors" {...rest}>
|
|
9
|
+
{message}
|
|
10
|
+
</p>
|
|
11
|
+
);
|
|
7
12
|
};
|
|
@@ -5,33 +5,9 @@ import { Row, Col } from '../../grid/index';
|
|
|
5
5
|
<Meta title="Forms/TimePicker" name="TimePicker" />
|
|
6
6
|
|
|
7
7
|
# TimePicker
|
|
8
|
-
The time picker component
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
code: `
|
|
12
|
-
const handleInputChange = (value: any) => {
|
|
13
|
-
setTime(value.time + ' ' + value.period);
|
|
14
|
-
};
|
|
15
|
-
const [time, setTime] = useState('');
|
|
16
|
-
return
|
|
17
|
-
<Row>
|
|
18
|
-
<Col xs={12} className="mb-4">
|
|
19
|
-
<h1>Time Picker</h1>
|
|
20
|
-
<p>Time Value returned: {time}</p>
|
|
21
|
-
</Col>
|
|
22
|
-
<Col xs={12}>
|
|
23
|
-
<TimePicker
|
|
24
|
-
onInputChange={handleInputChange}
|
|
25
|
-
defaultValue={{
|
|
26
|
-
time: '12:00',
|
|
27
|
-
period: 'AM'
|
|
28
|
-
}}
|
|
29
|
-
/>
|
|
30
|
-
</Col>
|
|
31
|
-
</Row>
|
|
32
|
-
`,
|
|
33
|
-
}}
|
|
34
|
-
/>
|
|
8
|
+
The time picker component is a simple time input field that validates the user's input and formats it if the value is valid. If the value is invalid, it will display an error message and not fire the callback.
|
|
9
|
+
|
|
10
|
+
<Canvas of={TimePicker.Default} />
|
|
35
11
|
|
|
36
12
|
<Controls of={TimePicker.Default} />
|
|
37
13
|
|
|
@@ -7,13 +7,29 @@ const meta: Meta<typeof TimePicker> = {
|
|
|
7
7
|
title: 'Forms/TimePicker',
|
|
8
8
|
component: TimePicker,
|
|
9
9
|
argTypes: {
|
|
10
|
+
hasHiddenLabel: {
|
|
11
|
+
control: 'boolean',
|
|
12
|
+
description: 'Hides the input label in an accessible way (visually hides the label)',
|
|
13
|
+
defaultValue: false,
|
|
14
|
+
table: {
|
|
15
|
+
category: 'Props',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
name: {
|
|
19
|
+
control: 'text',
|
|
20
|
+
description: 'The name of the input field',
|
|
21
|
+
defaultValue: 'time-picker',
|
|
22
|
+
table: {
|
|
23
|
+
category: 'Props',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
10
26
|
onTimeChange: {
|
|
11
27
|
control: false,
|
|
12
28
|
description: 'returns an object with the time and errors if they exist',
|
|
13
29
|
table: {
|
|
14
30
|
category: 'Callbacks',
|
|
15
31
|
type: {
|
|
16
|
-
summary: '{ time: string
|
|
32
|
+
summary: '{ time: string } => void',
|
|
17
33
|
},
|
|
18
34
|
},
|
|
19
35
|
},
|
|
@@ -55,6 +71,8 @@ export const Default: Story = {
|
|
|
55
71
|
args: {
|
|
56
72
|
timeValue: '12:00 AM',
|
|
57
73
|
label: 'Time Picker',
|
|
74
|
+
name: 'time-picker',
|
|
75
|
+
hasHiddenLabel: true,
|
|
58
76
|
},
|
|
59
77
|
|
|
60
78
|
render: (args) => {
|