@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.
Files changed (81) hide show
  1. package/dist/cjs/index.js +421 -362
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/lib/components/button/index.d.ts +4 -9
  4. package/dist/cjs/lib/components/button/index.stories.d.ts +2 -10
  5. package/dist/cjs/lib/components/dateSelector/components/Calendar.d.ts +16 -0
  6. package/dist/cjs/lib/components/dateSelector/index.d.ts +5 -7
  7. package/dist/cjs/lib/components/dateSelector/index.stories.d.ts +1 -1
  8. package/dist/cjs/lib/components/input/currency/input.stories.d.ts +7 -0
  9. package/dist/cjs/lib/components/input/index.d.ts +5 -3
  10. package/dist/cjs/lib/components/input/input.stories.d.ts +11 -3
  11. package/dist/cjs/lib/components/input/stories/config.d.ts +7 -0
  12. package/dist/esm/{index-f0e3bc9a.js → Calendar-62c6cf21.js} +24 -150
  13. package/dist/esm/Calendar-62c6cf21.js.map +1 -0
  14. package/dist/esm/components/button/index.js +2 -3
  15. package/dist/esm/components/button/index.js.map +1 -1
  16. package/dist/esm/components/button/index.stories.js +3 -16
  17. package/dist/esm/components/button/index.stories.js.map +1 -1
  18. package/dist/esm/components/card/index.js.map +1 -1
  19. package/dist/esm/components/card/index.stories.js +2 -2
  20. package/dist/esm/components/card/index.stories.js.map +1 -1
  21. package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js +7 -10
  22. package/dist/esm/components/comparisonTable/components/TableInfoButton/index.js.map +1 -1
  23. package/dist/esm/components/comparisonTable/index.js +2 -0
  24. package/dist/esm/components/comparisonTable/index.js.map +1 -1
  25. package/dist/esm/components/dateSelector/components/Calendar.js +11 -0
  26. package/dist/esm/components/dateSelector/components/Calendar.js.map +1 -0
  27. package/dist/esm/components/dateSelector/index.js +203 -7
  28. package/dist/esm/components/dateSelector/index.js.map +1 -1
  29. package/dist/esm/components/dateSelector/index.stories.js +14 -5
  30. package/dist/esm/components/dateSelector/index.stories.js.map +1 -1
  31. package/dist/esm/components/dateSelector/index.test.js +177 -23
  32. package/dist/esm/components/dateSelector/index.test.js.map +1 -1
  33. package/dist/esm/components/icon/icons/Info.js +2 -2
  34. package/dist/esm/components/icon/icons/Info.js.map +1 -1
  35. package/dist/esm/components/icon/icons.stories.js +1 -1
  36. package/dist/esm/components/icon/index.stories.js +1 -1
  37. package/dist/esm/components/input/currency/input.stories.js +1 -1
  38. package/dist/esm/components/input/iban/index.js +1 -1
  39. package/dist/esm/components/input/iban/index.js.map +1 -1
  40. package/dist/esm/components/input/index.js +10 -7
  41. package/dist/esm/components/input/index.js.map +1 -1
  42. package/dist/esm/components/input/input.stories.js +3 -3
  43. package/dist/esm/components/input/input.stories.js.map +1 -1
  44. package/dist/esm/{config-56f12c98.js → config-1d276a9d.js} +7 -2
  45. package/dist/esm/config-1d276a9d.js.map +1 -0
  46. package/dist/esm/{index-03b0133a.js → index-e506c4ca.js} +3 -3
  47. package/dist/esm/{index-03b0133a.js.map → index-e506c4ca.js.map} +1 -1
  48. package/dist/esm/index.js +4 -3
  49. package/dist/esm/index.js.map +1 -1
  50. package/dist/esm/lib/components/button/index.d.ts +4 -9
  51. package/dist/esm/lib/components/button/index.stories.d.ts +2 -10
  52. package/dist/esm/lib/components/dateSelector/components/Calendar.d.ts +16 -0
  53. package/dist/esm/lib/components/dateSelector/index.d.ts +5 -7
  54. package/dist/esm/lib/components/dateSelector/index.stories.d.ts +1 -1
  55. package/dist/esm/lib/components/input/currency/input.stories.d.ts +7 -0
  56. package/dist/esm/lib/components/input/index.d.ts +5 -3
  57. package/dist/esm/lib/components/input/input.stories.d.ts +11 -3
  58. package/dist/esm/lib/components/input/stories/config.d.ts +7 -0
  59. package/package.json +1 -1
  60. package/src/lib/components/button/index.stories.tsx +1 -26
  61. package/src/lib/components/button/index.tsx +10 -21
  62. package/src/lib/components/card/index.tsx +1 -1
  63. package/src/lib/components/comparisonTable/components/TableInfoButton/index.tsx +5 -30
  64. package/src/lib/components/comparisonTable/components/TableInfoButton/style.module.scss +6 -2
  65. package/src/lib/components/dateSelector/components/Calendar.tsx +112 -0
  66. package/src/lib/components/dateSelector/{datepicker.scss → components/datepicker.scss} +4 -4
  67. package/src/lib/components/dateSelector/components/style.module.scss +3 -0
  68. package/src/lib/components/dateSelector/index.stories.tsx +18 -8
  69. package/src/lib/components/dateSelector/index.test.tsx +118 -20
  70. package/src/lib/components/dateSelector/index.tsx +196 -227
  71. package/src/lib/components/dateSelector/style.module.scss +6 -79
  72. package/src/lib/components/input/iban/index.tsx +1 -1
  73. package/src/lib/components/input/index.tsx +13 -7
  74. package/src/lib/components/input/input.stories.tsx +2 -0
  75. package/src/lib/components/input/stories/config.ts +6 -1
  76. package/src/lib/components/input/style.module.scss +8 -1
  77. package/dist/esm/config-56f12c98.js.map +0 -1
  78. package/dist/esm/index-f0e3bc9a.js.map +0 -1
  79. package/src/lib/components/dateSelector/icons/chevron-left.svg +0 -3
  80. package/src/lib/components/dateSelector/icons/chevron-right.svg +0 -3
  81. 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 './datepicker.scss';
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 calendarDateValue = value ? isoStringtoCalendarDate(value) : undefined;
78
- const daysInSelectedDate = calendarDateValue
79
- ? daysInMonthOfYear({
80
- month: calendarDateValue.month,
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 localizedWeekdays = localeDate.weekdays();
90
- const localizedWeekdaysShort = localeDate.weekdaysShort();
91
- const localizedMonths = localeDate.months();
92
- const localizedMonthsShort = localeDate.monthsShort();
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
- const availableDays = fillArray(1, daysInSelectedDate);
95
- const availableYears = fillArray(yearBoundaries.max, yearBoundaries.min);
96
- const availableMonths = localizedMonthsShort;
98
+ useEffect(() => {
99
+ const calendarDateValue = value ? isoStringtoCalendarDate(value) : undefined;
97
100
 
98
- const [date, setDate] = useState<Partial<CalendarDate>>(
99
- calendarDateValue ?? {}
100
- );
101
- const [openCalendar, setOpenCalendar] = useState(false);
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 calendarDefaultDate =
106
- dayjs().year() >= yearBoundaries.min && dayjs().year() <= yearBoundaries.max
107
- ? dayjs().toDate()
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
- useOnClickOutside(calendarContainerRef, () => setOpenCalendar(false));
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
- useEffect(() => {
122
- if (calendarDateValue) {
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
- useEffect(() => {
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
- date.year !== undefined &&
130
- date.month !== undefined &&
131
- date.day !== undefined
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
- if (
134
- calendarDateValue === undefined ||
135
- date.day !== calendarDateValue.day ||
136
- date.month !== calendarDateValue.month ||
137
- date.year !== calendarDateValue.year
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
- onChange(
140
- calendarDateToISODate({
141
- day: date.day,
142
- month: date.month,
143
- year: date.year,
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
- }, [date]); //eslint-disable-line react-hooks/exhaustive-deps
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
- key !== 'day' &&
154
- newValue.month !== undefined &&
155
- newValue.year !== undefined &&
156
- newValue.day !== undefined
157
- ) {
158
- const cappedDays = Math.min(
159
- daysInMonthOfYear({ month: newValue.month, year: newValue.year }),
160
- newValue.day
161
- );
162
- setDate({ ...newValue, day: cappedDays });
163
- } else {
164
- setDate(newValue);
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
- setOpenCalendar(false);
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 className={styles.container}>
172
- <div className={styles['date-selector-container']}>
173
- <div className={styles['row-container']}>
174
- <select
175
- data-cy="date-selector-day"
176
- className={`p-select ${styles['day-select']}`}
177
- id="day"
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
- </button>
253
- {openCalendar && (
254
- <DayPicker
255
- month={selectedDateInDateType}
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
- margin-left: 0;
23
- display: unset;
7
+ flex-direction: column;
24
8
  }
25
9
  }
26
10
 
27
- .row-container {
28
- > select {
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
- .month-select {
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
- { [styles['input--with-prefix']]: prefix }
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.. This is needed for accessibility purposes and a label should always be provided',
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
  },