@popsure/dirty-swan 0.45.0-alpha → 0.47.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/dist/cjs/index.js +421 -362
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/lib/components/button/index.d.ts +4 -9
- package/dist/cjs/lib/components/button/index.stories.d.ts +2 -10
- package/dist/cjs/lib/components/dateSelector/components/Calendar.d.ts +16 -0
- package/dist/cjs/lib/components/dateSelector/index.d.ts +5 -7
- package/dist/cjs/lib/components/dateSelector/index.stories.d.ts +1 -1
- package/dist/cjs/lib/components/input/currency/input.stories.d.ts +7 -0
- package/dist/cjs/lib/components/input/index.d.ts +5 -3
- package/dist/cjs/lib/components/input/input.stories.d.ts +11 -3
- package/dist/cjs/lib/components/input/stories/config.d.ts +7 -0
- package/dist/esm/{index-f0e3bc9a.js → Calendar-62c6cf21.js} +24 -150
- package/dist/esm/Calendar-62c6cf21.js.map +1 -0
- package/dist/esm/components/button/index.js +2 -3
- package/dist/esm/components/button/index.js.map +1 -1
- package/dist/esm/components/button/index.stories.js +3 -16
- package/dist/esm/components/button/index.stories.js.map +1 -1
- package/dist/esm/components/card/index.js.map +1 -1
- package/dist/esm/components/card/index.stories.js +2 -2
- package/dist/esm/components/card/index.stories.js.map +1 -1
- package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js +7 -10
- package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js.map +1 -1
- package/dist/esm/components/comparisonTable/index.js +2 -0
- package/dist/esm/components/comparisonTable/index.js.map +1 -1
- package/dist/esm/components/dateSelector/components/Calendar.js +11 -0
- package/dist/esm/components/dateSelector/components/Calendar.js.map +1 -0
- package/dist/esm/components/dateSelector/index.js +203 -7
- package/dist/esm/components/dateSelector/index.js.map +1 -1
- package/dist/esm/components/dateSelector/index.stories.js +14 -5
- package/dist/esm/components/dateSelector/index.stories.js.map +1 -1
- package/dist/esm/components/dateSelector/index.test.js +177 -23
- package/dist/esm/components/dateSelector/index.test.js.map +1 -1
- package/dist/esm/components/icon/icons/Info.js +2 -2
- package/dist/esm/components/icon/icons/Info.js.map +1 -1
- package/dist/esm/components/icon/icons.stories.js +1 -1
- package/dist/esm/components/icon/index.stories.js +1 -1
- package/dist/esm/components/input/currency/input.stories.js +1 -1
- package/dist/esm/components/input/iban/index.js +1 -1
- package/dist/esm/components/input/iban/index.js.map +1 -1
- package/dist/esm/components/input/index.js +10 -7
- package/dist/esm/components/input/index.js.map +1 -1
- package/dist/esm/components/input/input.stories.js +3 -3
- package/dist/esm/components/input/input.stories.js.map +1 -1
- package/dist/esm/{config-56f12c98.js → config-1d276a9d.js} +7 -2
- package/dist/esm/config-1d276a9d.js.map +1 -0
- package/dist/esm/{index-03b0133a.js → index-e506c4ca.js} +3 -3
- package/dist/esm/{index-03b0133a.js.map → index-e506c4ca.js.map} +1 -1
- package/dist/esm/index.js +4 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/components/button/index.d.ts +4 -9
- package/dist/esm/lib/components/button/index.stories.d.ts +2 -10
- package/dist/esm/lib/components/dateSelector/components/Calendar.d.ts +16 -0
- package/dist/esm/lib/components/dateSelector/index.d.ts +5 -7
- package/dist/esm/lib/components/dateSelector/index.stories.d.ts +1 -1
- package/dist/esm/lib/components/input/currency/input.stories.d.ts +7 -0
- package/dist/esm/lib/components/input/index.d.ts +5 -3
- package/dist/esm/lib/components/input/input.stories.d.ts +11 -3
- package/dist/esm/lib/components/input/stories/config.d.ts +7 -0
- package/package.json +1 -1
- package/src/lib/components/button/index.stories.tsx +1 -26
- package/src/lib/components/button/index.tsx +10 -21
- package/src/lib/components/card/index.tsx +1 -1
- package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +5 -30
- package/src/lib/components/comparisonTable/components/TableInfoButton/style.module.scss +6 -2
- package/src/lib/components/dateSelector/components/Calendar.tsx +112 -0
- package/src/lib/components/dateSelector/{datepicker.scss → components/datepicker.scss} +4 -4
- package/src/lib/components/dateSelector/components/style.module.scss +3 -0
- package/src/lib/components/dateSelector/index.stories.tsx +18 -8
- package/src/lib/components/dateSelector/index.test.tsx +118 -20
- package/src/lib/components/dateSelector/index.tsx +196 -227
- package/src/lib/components/dateSelector/style.module.scss +6 -79
- package/src/lib/components/input/iban/index.tsx +1 -1
- package/src/lib/components/input/index.tsx +13 -7
- package/src/lib/components/input/input.stories.tsx +2 -0
- package/src/lib/components/input/stories/config.ts +6 -1
- package/src/lib/components/input/style.module.scss +8 -1
- package/dist/esm/config-56f12c98.js.map +0 -1
- package/dist/esm/index-f0e3bc9a.js.map +0 -1
- package/src/lib/components/dateSelector/icons/chevron-left.svg +0 -3
- package/src/lib/components/dateSelector/icons/chevron-right.svg +0 -3
- package/src/lib/components/dateSelector/index.test.ts +0 -33
|
@@ -1,56 +1,21 @@
|
|
|
1
|
-
import { useState, useEffect, useRef } from 'react';
|
|
1
|
+
import { useState, useEffect, ChangeEvent, useRef, KeyboardEvent } from 'react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import localeData from 'dayjs/plugin/localeData';
|
|
4
4
|
import { CalendarDate } from '@popsure/public-models';
|
|
5
|
-
import DayPicker from 'react-day-picker';
|
|
6
5
|
|
|
7
6
|
import {
|
|
8
7
|
calendarDateToISODate,
|
|
9
8
|
isoStringtoCalendarDate,
|
|
10
9
|
} from '../../util/calendarDate';
|
|
11
|
-
import { useOnClickOutside } from '../../hooks/useOnClickOutside';
|
|
12
|
-
import { CalendarIcon } from '../icon/icons';
|
|
13
10
|
|
|
14
11
|
import styles from './style.module.scss';
|
|
15
|
-
import '
|
|
12
|
+
import { Input } from '../input';
|
|
13
|
+
import classNames from 'classnames';
|
|
14
|
+
import { Calendar } from './components/Calendar';
|
|
16
15
|
|
|
17
16
|
dayjs.extend(localeData);
|
|
18
17
|
const COLLECTABLE_DATE_FORMAT = 'YYYY-MM-DD';
|
|
19
|
-
|
|
20
|
-
/*
|
|
21
|
-
Fill an array with an increment from a number to another number.
|
|
22
|
-
i.e. fillArray from 1 to 4 will return the following: [1, 2, 3, 4]
|
|
23
|
-
|
|
24
|
-
You can fill descending by flipping the to value
|
|
25
|
-
i.e. fillArray from 4 to 1 will return the following: [4, 3, 2, 1]
|
|
26
|
-
*/
|
|
27
|
-
export const fillArray = (from: number, to: number): number[] => {
|
|
28
|
-
const ascending = from > to;
|
|
29
|
-
const arraySize = Math.abs(from - to) + 1;
|
|
30
|
-
const toReturn = new Array(arraySize).fill(0).map((_, index) => {
|
|
31
|
-
return ascending ? to + index : from + index;
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
if (ascending) {
|
|
35
|
-
return toReturn.reverse();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return toReturn;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/*
|
|
42
|
-
Return the maximum number of days given a month and a year.
|
|
43
|
-
*/
|
|
44
|
-
export const daysInMonthOfYear = ({
|
|
45
|
-
month,
|
|
46
|
-
year,
|
|
47
|
-
}: {
|
|
48
|
-
month: number;
|
|
49
|
-
year: number;
|
|
50
|
-
}) => {
|
|
51
|
-
return dayjs(`${year}-${month}`).daysInMonth();
|
|
52
|
-
};
|
|
53
|
-
|
|
18
|
+
type FormatPlaceholder = 'dayFormat' | 'monthFormat' | 'yearFormat';
|
|
54
19
|
export interface DateSelectorProps {
|
|
55
20
|
value?: string;
|
|
56
21
|
onChange: (date: string) => void;
|
|
@@ -59,229 +24,233 @@ export interface DateSelectorProps {
|
|
|
59
24
|
dayjsLocale?: ILocale;
|
|
60
25
|
placeholders?: {
|
|
61
26
|
day?: string;
|
|
27
|
+
dayFormat?: string;
|
|
62
28
|
month?: string;
|
|
29
|
+
monthFormat?: string;
|
|
63
30
|
year?: string;
|
|
31
|
+
yearFormat?: string;
|
|
32
|
+
error?: string;
|
|
64
33
|
};
|
|
65
34
|
firstDayOfWeek?: number;
|
|
66
35
|
}
|
|
67
36
|
|
|
37
|
+
const defaultPlaceholders: DateSelectorProps["placeholders"] = {
|
|
38
|
+
day: "Day",
|
|
39
|
+
dayFormat: "DD",
|
|
40
|
+
month: "Month",
|
|
41
|
+
monthFormat: "MM",
|
|
42
|
+
year: "Year",
|
|
43
|
+
yearFormat: "YYYY",
|
|
44
|
+
error: "Please enter a valid date"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
type ErrorField = 'all' | 'day' | 'month' | 'year' | undefined;
|
|
48
|
+
|
|
49
|
+
const isDateValid = (
|
|
50
|
+
date: string | undefined,
|
|
51
|
+
yearBoundaries: DateSelectorProps["yearBoundaries"]
|
|
52
|
+
): {
|
|
53
|
+
isValid: boolean;
|
|
54
|
+
field?: ErrorField;
|
|
55
|
+
} => {
|
|
56
|
+
const { min = 0, max = 0 } = yearBoundaries;
|
|
57
|
+
|
|
58
|
+
if (!date) {
|
|
59
|
+
return { isValid: false, field: 'all' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (max && dayjs(date).isAfter(`${max}-01-01`, 'year')) {
|
|
63
|
+
return { isValid: false, field: 'year' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (min && dayjs(date).isBefore(`${min}-01-01`, 'year')) {
|
|
67
|
+
return { isValid: false, field: 'year' };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const isDateValid = dayjs(date, COLLECTABLE_DATE_FORMAT, true).isValid();
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
isValid: isDateValid,
|
|
74
|
+
field: isDateValid ? undefined : 'all',
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
export const DateSelector = ({
|
|
69
79
|
value,
|
|
70
80
|
onChange,
|
|
81
|
+
placeholders: placeholdersProps,
|
|
71
82
|
yearBoundaries,
|
|
72
83
|
displayCalendar,
|
|
73
|
-
placeholders,
|
|
74
84
|
dayjsLocale,
|
|
75
85
|
firstDayOfWeek = 0,
|
|
76
86
|
}: DateSelectorProps) => {
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
year: calendarDateValue.year,
|
|
82
|
-
})
|
|
83
|
-
: 31;
|
|
84
|
-
|
|
85
|
-
const localeDate = dayjsLocale
|
|
86
|
-
? dayjs().locale(dayjsLocale).localeData()
|
|
87
|
-
: dayjs().locale('en').localeData();
|
|
87
|
+
const placeholders = {
|
|
88
|
+
...defaultPlaceholders,
|
|
89
|
+
...placeholdersProps
|
|
90
|
+
}
|
|
88
91
|
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
92
|
-
const
|
|
92
|
+
const itemsRef = useRef<HTMLInputElement[]>([]);
|
|
93
|
+
const [isDirty, setIsDirty] = useState(false);
|
|
94
|
+
const [hasError, setHasError] = useState<ErrorField>();
|
|
95
|
+
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
|
|
96
|
+
const [internalValue, setInternalValue] = useState<Partial<CalendarDate>>({});
|
|
93
97
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const availableMonths = localizedMonthsShort;
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
const calendarDateValue = value ? isoStringtoCalendarDate(value) : undefined;
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
if(value !== calendarDateValue && calendarDateValue?.day && calendarDateValue?.month && calendarDateValue?.year) {
|
|
102
|
+
const { isValid, field } = isDateValid(value, yearBoundaries)
|
|
103
|
+
setInternalValue(calendarDateValue)
|
|
104
|
+
setHasError(isValid ? undefined : field);
|
|
105
|
+
setIsDirty(true);
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
108
|
+
}, [value]);
|
|
102
109
|
|
|
103
|
-
const calendarContainerRef = useRef<HTMLDivElement | null>(null);
|
|
104
110
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
: dayjs().set('year', yearBoundaries.max).toDate();
|
|
109
|
-
const selectedDateInDateType = value
|
|
110
|
-
? dayjs(value).toDate()
|
|
111
|
-
: calendarDefaultDate;
|
|
112
|
-
const dateCalendarFromMonth = dayjs(String(yearBoundaries.min))
|
|
113
|
-
.startOf('year')
|
|
114
|
-
.toDate();
|
|
115
|
-
const dateCalendarToMonth = dayjs(String(yearBoundaries.max))
|
|
116
|
-
.endOf('year')
|
|
117
|
-
.toDate();
|
|
111
|
+
const handleOnChange = (key: keyof CalendarDate, v?: string) => {
|
|
112
|
+
const tempValue = { ...internalValue, [key]: v || undefined };
|
|
113
|
+
setInternalValue(tempValue);
|
|
118
114
|
|
|
119
|
-
|
|
115
|
+
const formattedDate = calendarDateToISODate({
|
|
116
|
+
day: tempValue.day || 0,
|
|
117
|
+
month: tempValue.month || 0,
|
|
118
|
+
year: tempValue.year || 0,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
const { isValid, field } = isDateValid(formattedDate, yearBoundaries);
|
|
120
122
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
setDate(calendarDateValue);
|
|
123
|
+
if (dayjs(formattedDate, COLLECTABLE_DATE_FORMAT, true).isValid()) {
|
|
124
|
+
setIsDirty(true);
|
|
124
125
|
}
|
|
125
|
-
}, [value]); //eslint-disable-line react-hooks/exhaustive-deps
|
|
126
126
|
|
|
127
|
-
|
|
127
|
+
setHasError(isValid ? undefined : field);
|
|
128
|
+
onChange(isValid ? formattedDate : "");
|
|
129
|
+
setIsCalendarOpen(false);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const handleOnKeyDown = (event: KeyboardEvent<HTMLInputElement>, index: number) => {
|
|
133
|
+
const currentInput = itemsRef.current?.[index];
|
|
134
|
+
const inputSelectionStart = currentInput?.selectionStart;
|
|
135
|
+
const inputSelectionEnd = currentInput?.selectionEnd;
|
|
136
|
+
|
|
128
137
|
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
138
|
+
// is not day input
|
|
139
|
+
index > 0 &&
|
|
140
|
+
// has clicked backspace or arrow left
|
|
141
|
+
['Backspace', "ArrowLeft"].includes(event.key) &&
|
|
142
|
+
// is focused at the first character of the input
|
|
143
|
+
inputSelectionStart === 0 && inputSelectionEnd === 0
|
|
132
144
|
) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
145
|
+
const prevInput = itemsRef.current?.[index - 1];
|
|
146
|
+
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
prevInput?.focus();
|
|
149
|
+
prevInput?.setSelectionRange(0, 3);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
// is not year input
|
|
154
|
+
index < 2
|
|
155
|
+
// has clicked arrow right
|
|
156
|
+
&& event.key === "ArrowRight"
|
|
157
|
+
// is focused at the last character of the input value
|
|
158
|
+
&& inputSelectionStart === currentInput.value.length
|
|
138
159
|
) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
})
|
|
145
|
-
);
|
|
146
|
-
}
|
|
160
|
+
const nextInput = itemsRef.current?.[index + 1];
|
|
161
|
+
|
|
162
|
+
event.preventDefault();
|
|
163
|
+
nextInput?.focus();
|
|
164
|
+
nextInput?.setSelectionRange(0, index === 1 ? 4 : 3);
|
|
147
165
|
}
|
|
148
|
-
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const handleOnKeyUp = (event: KeyboardEvent<HTMLInputElement>, index: number) => {
|
|
169
|
+
const currentInput = itemsRef.current?.[index];
|
|
170
|
+
const inputSelectionStart = currentInput?.selectionStart;
|
|
149
171
|
|
|
150
|
-
const handleOnChange = (key: keyof CalendarDate, v: number) => {
|
|
151
|
-
const newValue = { ...date, [key]: v };
|
|
152
172
|
if (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
// is not year input
|
|
174
|
+
index < 2
|
|
175
|
+
// is a number key
|
|
176
|
+
&& /^\d+$/.test(event.key)
|
|
177
|
+
&& (
|
|
178
|
+
// is focused at the last character of the input value
|
|
179
|
+
inputSelectionStart === currentInput.maxLength
|
|
180
|
+
// or month value is over 1 or day value is over 3
|
|
181
|
+
|| Number(currentInput.value) > (index === 1 ? 1 : 3)
|
|
182
|
+
)
|
|
183
|
+
) {
|
|
184
|
+
const nextInput = itemsRef.current?.[index + 1];
|
|
185
|
+
|
|
186
|
+
event.preventDefault();
|
|
187
|
+
event.stopPropagation();
|
|
188
|
+
|
|
189
|
+
nextInput?.focus();
|
|
190
|
+
nextInput?.setSelectionRange(0, index === 1 ? 4 : 3);
|
|
165
191
|
}
|
|
192
|
+
}
|
|
166
193
|
|
|
167
|
-
|
|
168
|
-
|
|
194
|
+
const getInputProps = (key: keyof CalendarDate, index: number) => ({
|
|
195
|
+
'data-cy': `date-selector-${key}`,
|
|
196
|
+
'data-testid': `date-selector-${key}`,
|
|
197
|
+
className: styles[`${key}Input`],
|
|
198
|
+
id: key,
|
|
199
|
+
name: key,
|
|
200
|
+
maxLength: 2,
|
|
201
|
+
required: true,
|
|
202
|
+
label: placeholders?.[key],
|
|
203
|
+
placeholder: placeholders?.[`${key}Format` as FormatPlaceholder] ?? "",
|
|
204
|
+
labelInsideInput: true,
|
|
205
|
+
value: internalValue[key] ?? '',
|
|
206
|
+
error: (hasError && [key, 'all'].includes(hasError)) && isDirty,
|
|
207
|
+
type: "text",
|
|
208
|
+
ref: (el: HTMLInputElement) => { itemsRef.current[index] = el },
|
|
209
|
+
onKeyUp: (event: KeyboardEvent<HTMLInputElement>) => handleOnKeyUp(event, index),
|
|
210
|
+
onKeyDown: (event: KeyboardEvent<HTMLInputElement>) => handleOnKeyDown(event, index),
|
|
211
|
+
onChange: ({ target }: ChangeEvent<HTMLInputElement>) => handleOnChange(key, target.value),
|
|
212
|
+
});
|
|
169
213
|
|
|
170
214
|
return (
|
|
171
|
-
<div
|
|
172
|
-
<div className=
|
|
173
|
-
<div className={styles
|
|
174
|
-
<
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
name="day"
|
|
179
|
-
required={true}
|
|
180
|
-
value={date.day ?? ''}
|
|
181
|
-
onChange={(e) => {
|
|
182
|
-
handleOnChange('day', parseInt(e.target.value, 10));
|
|
183
|
-
}}
|
|
184
|
-
>
|
|
185
|
-
<option value="" disabled={true}>
|
|
186
|
-
{placeholders?.day || 'Day'}
|
|
187
|
-
</option>
|
|
188
|
-
{availableDays.map((day) => (
|
|
189
|
-
<option key={day} value={day}>
|
|
190
|
-
{day}
|
|
191
|
-
</option>
|
|
192
|
-
))}
|
|
193
|
-
</select>
|
|
194
|
-
<select
|
|
195
|
-
data-cy="date-selector-month"
|
|
196
|
-
className={`p-select ${styles['month-select']}`}
|
|
197
|
-
id="month"
|
|
198
|
-
name="month"
|
|
199
|
-
required={true}
|
|
200
|
-
value={date.month ?? ''}
|
|
201
|
-
onChange={(e) => {
|
|
202
|
-
handleOnChange('month', parseInt(e.target.value, 10));
|
|
203
|
-
}}
|
|
204
|
-
>
|
|
205
|
-
<option value="" disabled={true}>
|
|
206
|
-
{placeholders?.month || 'Month'}
|
|
207
|
-
</option>
|
|
208
|
-
{availableMonths.map((month, i) => (
|
|
209
|
-
<option key={month} value={i + 1}>
|
|
210
|
-
{month}
|
|
211
|
-
</option>
|
|
212
|
-
))}
|
|
213
|
-
</select>
|
|
214
|
-
</div>
|
|
215
|
-
<select
|
|
216
|
-
data-cy="date-selector-year"
|
|
217
|
-
className={`p-select ${styles['year-select']}`}
|
|
218
|
-
id="year"
|
|
219
|
-
name="year"
|
|
220
|
-
required={true}
|
|
221
|
-
value={date.year ?? ''}
|
|
222
|
-
onChange={(e) => {
|
|
223
|
-
handleOnChange('year', parseInt(e.target.value, 10));
|
|
224
|
-
}}
|
|
225
|
-
>
|
|
226
|
-
<option value="" disabled={true}>
|
|
227
|
-
{placeholders?.year || 'Year'}
|
|
228
|
-
</option>
|
|
229
|
-
{availableYears.map((year) => (
|
|
230
|
-
<option key={year} value={year}>
|
|
231
|
-
{year}
|
|
232
|
-
</option>
|
|
233
|
-
))}
|
|
234
|
-
</select>
|
|
235
|
-
</div>
|
|
236
|
-
{displayCalendar && (
|
|
237
|
-
<div
|
|
238
|
-
className={styles['date-calendar-container']}
|
|
239
|
-
ref={calendarContainerRef}
|
|
240
|
-
>
|
|
241
|
-
<button
|
|
242
|
-
type="button"
|
|
243
|
-
onClick={() => setOpenCalendar(!openCalendar)}
|
|
244
|
-
className={styles.calendarButton}
|
|
245
|
-
data-testid="calendar-button"
|
|
246
|
-
>
|
|
247
|
-
<CalendarIcon
|
|
248
|
-
color={'purple-500'}
|
|
249
|
-
size={24}
|
|
250
|
-
className={`${styles.calendarIcon}`}
|
|
215
|
+
<div>
|
|
216
|
+
<div className="d-flex ai-center">
|
|
217
|
+
<div className={classNames(styles.container, "d-flex gap8")}>
|
|
218
|
+
<div className={"d-flex gap8 jc-between"}>
|
|
219
|
+
<Input
|
|
220
|
+
{...getInputProps('day', 0)}
|
|
221
|
+
inputMode='numeric'
|
|
251
222
|
/>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
showOutsideDays={true}
|
|
257
|
-
fromMonth={dateCalendarFromMonth}
|
|
258
|
-
toMonth={dateCalendarToMonth}
|
|
259
|
-
selectedDays={selectedDateInDateType}
|
|
260
|
-
onDayClick={(date: Date) => {
|
|
261
|
-
if (
|
|
262
|
-
dayjs(date).isAfter(dateCalendarFromMonth) ||
|
|
263
|
-
dayjs(date).isBefore(dateCalendarToMonth)
|
|
264
|
-
) {
|
|
265
|
-
const selectedDate = dayjs(date).format(
|
|
266
|
-
COLLECTABLE_DATE_FORMAT
|
|
267
|
-
);
|
|
268
|
-
onChange(selectedDate);
|
|
269
|
-
setOpenCalendar(false);
|
|
270
|
-
}
|
|
271
|
-
}}
|
|
272
|
-
pagedNavigation={true}
|
|
273
|
-
disabledDays={{
|
|
274
|
-
before: dateCalendarFromMonth,
|
|
275
|
-
after: dateCalendarToMonth,
|
|
276
|
-
}}
|
|
277
|
-
firstDayOfWeek={firstDayOfWeek}
|
|
278
|
-
locale={dayjsLocale?.name || 'en'}
|
|
279
|
-
months={localizedMonths}
|
|
280
|
-
weekdaysLong={localizedWeekdays}
|
|
281
|
-
weekdaysShort={localizedWeekdaysShort}
|
|
223
|
+
|
|
224
|
+
<Input
|
|
225
|
+
{...getInputProps('month', 1)}
|
|
226
|
+
inputMode='numeric'
|
|
282
227
|
/>
|
|
283
|
-
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<Input
|
|
231
|
+
{...getInputProps('year', 2)}
|
|
232
|
+
inputMode='numeric'
|
|
233
|
+
maxLength={4}
|
|
234
|
+
/>
|
|
284
235
|
</div>
|
|
236
|
+
|
|
237
|
+
<Calendar
|
|
238
|
+
dateFormat={COLLECTABLE_DATE_FORMAT}
|
|
239
|
+
yearBoundaries={yearBoundaries}
|
|
240
|
+
displayCalendar={displayCalendar}
|
|
241
|
+
dayjsLocale={dayjsLocale}
|
|
242
|
+
firstDayOfWeek={firstDayOfWeek}
|
|
243
|
+
isOpen={isCalendarOpen}
|
|
244
|
+
setCalendarOpen={setIsCalendarOpen}
|
|
245
|
+
value={value}
|
|
246
|
+
onChange={onChange}
|
|
247
|
+
/>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
{hasError && isDirty && (
|
|
251
|
+
<p className="p-p--small tc-red-500 w100 mt8">
|
|
252
|
+
{placeholders.error}
|
|
253
|
+
</p>
|
|
285
254
|
)}
|
|
286
255
|
</div>
|
|
287
256
|
);
|
|
@@ -2,95 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
.container {
|
|
4
4
|
display: flex;
|
|
5
|
-
align-items: center;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.date-selector-container {
|
|
9
|
-
margin-left: -8px;
|
|
10
|
-
|
|
11
|
-
display: flex;
|
|
12
|
-
|
|
13
|
-
> select {
|
|
14
|
-
margin-left: 8px;
|
|
15
|
-
|
|
16
|
-
@include p-size-mobile {
|
|
17
|
-
margin-left: 0;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
5
|
|
|
21
6
|
@include p-size-mobile {
|
|
22
|
-
|
|
23
|
-
display: unset;
|
|
7
|
+
flex-direction: column;
|
|
24
8
|
}
|
|
25
9
|
}
|
|
26
10
|
|
|
27
|
-
.
|
|
28
|
-
|
|
29
|
-
margin-left: 8px;
|
|
30
|
-
|
|
31
|
-
@include p-size-mobile {
|
|
32
|
-
margin-left: 0;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@include p-size-mobile {
|
|
37
|
-
display: flex;
|
|
38
|
-
justify-content: space-between;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.day-select {
|
|
11
|
+
.dayInput,
|
|
12
|
+
.monthInput {
|
|
43
13
|
width: 88px;
|
|
44
|
-
|
|
45
|
-
@include p-size-mobile {
|
|
46
|
-
flex: 1;
|
|
47
|
-
margin-right: 8px;
|
|
48
|
-
}
|
|
14
|
+
flex: 1;
|
|
49
15
|
}
|
|
50
16
|
|
|
51
|
-
.
|
|
17
|
+
.yearInput {
|
|
52
18
|
width: 104px;
|
|
53
19
|
|
|
54
20
|
@include p-size-mobile {
|
|
55
|
-
flex: 1;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.year-select {
|
|
60
|
-
width: 104px;
|
|
61
|
-
|
|
62
|
-
@include p-size-mobile {
|
|
63
|
-
display: block;
|
|
64
|
-
|
|
65
|
-
margin-top: 8px;
|
|
66
21
|
width: 100%;
|
|
67
22
|
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.date-calendar-container {
|
|
71
|
-
position: relative;
|
|
72
|
-
margin-left: 24px;
|
|
73
|
-
|
|
74
|
-
@include p-size-mobile {
|
|
75
|
-
margin-left: 16px;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
.calendarButton {
|
|
80
|
-
background: none;
|
|
81
|
-
border: none;
|
|
82
|
-
padding: 0;
|
|
83
|
-
margin: 0;
|
|
84
|
-
color: inherit;
|
|
85
|
-
text-align: inherit;
|
|
86
|
-
outline: none;
|
|
87
|
-
box-shadow: none;
|
|
88
|
-
appearance: none;
|
|
89
|
-
-webkit-appearance: none;
|
|
90
|
-
-moz-appearance: none;
|
|
91
|
-
cursor: pointer;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
.calendarIcon {
|
|
95
|
-
margin: 0px;
|
|
96
|
-
}
|
|
23
|
+
}
|
|
@@ -12,7 +12,7 @@ const IbanInput = ({
|
|
|
12
12
|
} & Omit<InputProps, 'onChange' | 'value' | 'ref'>) => (
|
|
13
13
|
<Input
|
|
14
14
|
value={formatIban(value)}
|
|
15
|
-
onChange={(e) => onChange(formatIban(e.target.value))}
|
|
15
|
+
onChange={(e) => onChange?.(formatIban(e.target.value))}
|
|
16
16
|
{...props}
|
|
17
17
|
/>
|
|
18
18
|
);
|
|
@@ -7,11 +7,12 @@ import styles from './style.module.scss';
|
|
|
7
7
|
// Something weird is going on with enterKeyHint that makes it a required field under certain circumstances. The & Omit<…> and & Pick<…> is a hacky way to go around that.
|
|
8
8
|
export type InputProps = Omit<JSX.IntrinsicElements['input'], 'enterKeyHint'> &
|
|
9
9
|
Partial<Pick<JSX.IntrinsicElements['input'], 'enterKeyHint'>> & {
|
|
10
|
-
error?: string;
|
|
10
|
+
error?: string | boolean;
|
|
11
11
|
prefix?: string;
|
|
12
12
|
label?: string;
|
|
13
13
|
id?: string;
|
|
14
14
|
hideLabel?: boolean;
|
|
15
|
+
labelInsideInput?: boolean;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
export const Input = React.forwardRef(
|
|
@@ -25,14 +26,16 @@ export const Input = React.forwardRef(
|
|
|
25
26
|
error,
|
|
26
27
|
disabled,
|
|
27
28
|
hideLabel = false,
|
|
29
|
+
labelInsideInput = false,
|
|
28
30
|
...props
|
|
29
31
|
}: InputProps,
|
|
30
32
|
ref?: React.ForwardedRef<HTMLInputElement>
|
|
31
33
|
) => {
|
|
32
34
|
const [uniqueId] = useState(id ?? generateId());
|
|
35
|
+
|
|
33
36
|
return (
|
|
34
37
|
<div className={`${styles.container} ${className ?? ''}`}>
|
|
35
|
-
{label && (
|
|
38
|
+
{label && !labelInsideInput && (
|
|
36
39
|
<label
|
|
37
40
|
htmlFor={uniqueId}
|
|
38
41
|
className={classnames('p-p', styles.label, {
|
|
@@ -51,12 +54,15 @@ export const Input = React.forwardRef(
|
|
|
51
54
|
ref={ref}
|
|
52
55
|
className={classnames(
|
|
53
56
|
error ? 'p-input--error' : 'p-input',
|
|
54
|
-
!label && placeholder && placeholder.length > 0
|
|
57
|
+
(!label || labelInsideInput) && placeholder && placeholder.length > 0
|
|
55
58
|
? styles.input
|
|
56
59
|
: styles['input--no-placeholder'],
|
|
57
|
-
{
|
|
60
|
+
{
|
|
61
|
+
[styles['input--with-prefix']]: prefix,
|
|
62
|
+
[styles['input--with-inside-label']]: labelInsideInput
|
|
63
|
+
}
|
|
58
64
|
)}
|
|
59
|
-
placeholder={label ? placeholder : ' '}
|
|
65
|
+
placeholder={label || labelInsideInput ? placeholder : ' '}
|
|
60
66
|
disabled={disabled}
|
|
61
67
|
{...props}
|
|
62
68
|
/>
|
|
@@ -71,7 +77,7 @@ export const Input = React.forwardRef(
|
|
|
71
77
|
{prefix}
|
|
72
78
|
</span>
|
|
73
79
|
)}
|
|
74
|
-
{!label && (
|
|
80
|
+
{(!label || labelInsideInput) && (
|
|
75
81
|
<label
|
|
76
82
|
htmlFor={uniqueId}
|
|
77
83
|
className={classnames(
|
|
@@ -80,7 +86,7 @@ export const Input = React.forwardRef(
|
|
|
80
86
|
{ [styles['placeholder--with-error']]: error }
|
|
81
87
|
)}
|
|
82
88
|
>
|
|
83
|
-
{placeholder}
|
|
89
|
+
{labelInsideInput ? label : placeholder}
|
|
84
90
|
</label>
|
|
85
91
|
)}
|
|
86
92
|
</div>
|
|
@@ -19,6 +19,7 @@ export const InputStory = ({
|
|
|
19
19
|
value,
|
|
20
20
|
label,
|
|
21
21
|
hideLabel,
|
|
22
|
+
labelInsideInput,
|
|
22
23
|
prefix,
|
|
23
24
|
error
|
|
24
25
|
}: InputProps) => {
|
|
@@ -37,6 +38,7 @@ export const InputStory = ({
|
|
|
37
38
|
placeholder={placeholder}
|
|
38
39
|
label={label}
|
|
39
40
|
hideLabel={hideLabel}
|
|
41
|
+
labelInsideInput={labelInsideInput}
|
|
40
42
|
prefix={prefix}
|
|
41
43
|
error={error}
|
|
42
44
|
/>
|
|
@@ -20,7 +20,12 @@ const sharedConfig = {
|
|
|
20
20
|
control: { type: 'text' }
|
|
21
21
|
},
|
|
22
22
|
hideLabel: {
|
|
23
|
-
description: 'Whether or not a label should be hidden
|
|
23
|
+
description: 'Whether or not a label should be hidden. This is needed for accessibility purposes and a label should always be provided',
|
|
24
|
+
defaultValue: false,
|
|
25
|
+
control: { type: 'boolean' }
|
|
26
|
+
},
|
|
27
|
+
labelInsideInput: {
|
|
28
|
+
description: 'Whether or not a label should be visually displayed inside the input borders.',
|
|
24
29
|
defaultValue: false,
|
|
25
30
|
control: { type: 'boolean' }
|
|
26
31
|
},
|