@tui-cruises/mein-schiff-web-react-component-library 3.1.0 → 3.1.2

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 (40) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/index.tsx +1 -1
  3. package/package.json +7 -7
  4. package/src/components/core/Alert/Alert.tsx +7 -1
  5. package/src/components/core/AlertDialog/AlertDialog.tsx +20 -3
  6. package/src/components/core/Badge/Badge.tsx +4 -3
  7. package/src/components/core/BirthdateField/BirthdateField.tsx +16 -15
  8. package/src/components/core/ButtonGroup/ButtonGroup.tsx +0 -1
  9. package/src/components/core/Calendar/Calendar.tsx +10 -3
  10. package/src/components/core/CheckboxField/CheckboxField.tsx +2 -0
  11. package/src/components/core/Chip/Chip.tsx +101 -0
  12. package/src/components/core/Chip/index.ts +1 -0
  13. package/src/components/core/Countdown/Countdown.tsx +4 -1
  14. package/src/components/core/DataTable/DataTable.tsx +11 -2
  15. package/src/components/core/Dialog/Dialog.tsx +11 -2
  16. package/src/components/core/Divider/Divider.tsx +1 -1
  17. package/src/components/core/Icon/Icon.tsx +3 -15
  18. package/src/components/core/IconButton/IconButton.tsx +7 -1
  19. package/src/components/core/InputField/InputField.tsx +26 -3
  20. package/src/components/core/Pagination/Pagination.tsx +9 -24
  21. package/src/components/core/PasswordField/PasswordField.tsx +43 -14
  22. package/src/components/core/PhoneNumberInput/PhoneNumberInput.tsx +81 -56
  23. package/src/components/core/Pictogram/Pictogram.tsx +9 -3
  24. package/src/components/core/Popover/Popover.tsx +16 -9
  25. package/src/components/core/Price/Price.tsx +2 -9
  26. package/src/components/core/QuantityItem/QuantityItem.tsx +1 -1
  27. package/src/components/core/RadioField/RadioField.tsx +14 -4
  28. package/src/components/core/RadixSelect/RadixSelect.tsx +6 -7
  29. package/src/components/core/Select/Select.tsx +16 -2
  30. package/src/components/core/SelectField/SelectField.tsx +11 -4
  31. package/src/components/core/Skeleton/Skeleton.tsx +1 -0
  32. package/src/components/core/SwitchToggle/SwitchToggle.tsx +9 -12
  33. package/src/components/core/Tabs/Tabs.tsx +144 -127
  34. package/src/components/core/Tag/Tag.tsx +14 -2
  35. package/public/videos/placeholder.mp4 +0 -0
  36. package/src/assets/images/placeholder-blueish.jpg +0 -0
  37. package/src/assets/images/placeholder-sepia.jpg +0 -0
  38. package/src/assets/images/placeholder.jpg +0 -0
  39. package/src/components/core/Portlist/Portlist.tsx +0 -28
  40. package/src/components/core/Portlist/index.ts +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,49 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [3.1.2](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/compare/v3.1.1...v3.1.2) (2025-11-06)
6
+
7
+
8
+ ### Features
9
+
10
+ * **Alert:** A11Y enhancements ([fbd789a](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/fbd789ae3e1c5d8ea98c1ad63a92ff8a3b01edd6))
11
+ * **AlertDialog:** A11Y enhancements ([b78fef9](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/b78fef95e6313d0ebcb6e0d894eb649c7fa2fc9a))
12
+ * **BirthdateField:** A11Y enhancements ([545aaa8](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/545aaa851c9f3b061a9708d258b7e306a5972498))
13
+ * **Calendar:** A11Y enhancements ([c83bf5c](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/c83bf5c83cc48ddb1c2d4a5d8f2f18fda552d73f))
14
+ * **Dialog:** A11Y enhancements ([71b3283](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/71b32830d688036255e253579d388584bdbbdc8c))
15
+ * **Icon:** A11Y enhancements ([88249b5](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/88249b5677601c15577a031eef19fc590b5e69f4))
16
+ * **InputField:** A11Y enhancements ([f9834cd](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/f9834cdabfff5c2ac30c6da12d3579f7f11db2bc))
17
+ * **Pagination:** A11Y enhancements ([a807395](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/a807395835832c7e0def06cbec10d10de04aeda6))
18
+ * **PasswordField:** A11Y enhancements ([ffb97e1](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/ffb97e1e35c412a4395f7548532798bbc6ff06f4))
19
+ * **PhoneNumberInput:** A11Y enhancements ([d52df9c](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/d52df9c830339a5f23a13e6eda01c7959b28c4b2))
20
+ * **Pictogram:** A11Y enhancements ([b776286](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/b776286e867f6c0ced29b913705a210ed2496da7))
21
+ * **Popover:** A11Y enhancements ([efc44c2](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/efc44c2d193b7462d7399c3b4528703aa4d7ac11))
22
+ * **RadioField:** A11Y enhancements ([c932dda](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/c932ddafc59113f793645d2d3c494ca92b363e07))
23
+ * **RadixSelect:** A11Y enhancements ([edabf50](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/edabf50e6281b4c0e049330c3611c37ea89499be))
24
+ * **Select:** A11Y enhancements ([afbb337](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/afbb33708ffab2db2fa8cecb92ea7359d01dc1ce))
25
+ * **Select:** A11Y enhancements ([bab7a84](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/bab7a846facf2a5cfb5d9937cde723c1482d6e16))
26
+ * **SelectField:** A11Y enhancements ([edc178c](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/edc178ce216a5d3e70999d1461b893f089f4c176))
27
+ * **SwitchToggle:** A11Y enhancements ([06070db](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/06070dba2c0cadbe23ff61aa7287121001fe744e))
28
+ * **Tabs:** A11Y enhancements ([36f7b11](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/36f7b111999614bfee2d8f0170c2b8343b07dec1))
29
+
30
+
31
+ ### Bug Fixes
32
+
33
+ * **Chip:** focus style ([a71d57b](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/a71d57be51099ccd8b6df03de8fb13510ddca99c))
34
+
35
+ ### [3.1.1](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/compare/v3.1.0...v3.1.1) (2025-10-30)
36
+
37
+
38
+ ### Features
39
+
40
+ * **Chip:** add Chip component (EC-2491) ([3b7efbe](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/3b7efbe4c8edccb9187bcae9dd72fee1d7cadc07))
41
+
42
+
43
+ ### Bug Fixes
44
+
45
+ * **Calendar:** typography ([77578c0](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/77578c01e26b6544f124e427a3a51072d6d9d315))
46
+ * **Tabs:** add hover border style ([e9e45e5](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/commit/e9e45e54cdcb3efd5d5e11bddaeda9106707b499))
47
+
5
48
  ## [3.1.0](https://bitbucket.org/yours_truly/tuic-mein-schiff-web-react-component-library/compare/v3.0.7...v3.1.0) (2025-10-28)
6
49
 
7
50
 
package/index.tsx CHANGED
@@ -10,6 +10,7 @@ export * from './src/components/core/ButtonGroup';
10
10
  export * from './src/components/core/Calendar';
11
11
  export * from './src/components/core/Checkbox';
12
12
  export * from './src/components/core/CheckboxField';
13
+ export * from './src/components/core/Chip';
13
14
  export * from './src/components/core/Countdown';
14
15
  export * from './src/components/core/DataTable';
15
16
  export * from './src/components/core/Dialog';
@@ -19,7 +20,6 @@ export * from './src/components/core/FormLabel';
19
20
  export * from './src/components/core/Icon';
20
21
  export * from './src/components/core/LoadingSpinner';
21
22
  export * from './src/components/core/Pictogram';
22
- export * from './src/components/core/Portlist';
23
23
  export * from './src/components/core/Price';
24
24
  export * from './src/components/core/QuantityItem';
25
25
  export * from './src/components/core/RangeSlider';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tui-cruises/mein-schiff-web-react-component-library",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "main": "./index.tsx",
5
5
  "types": "./index.tsx",
6
6
  "type": "module",
@@ -73,10 +73,10 @@
73
73
  "@babel/preset-env": "^7.26.9",
74
74
  "@babel/preset-react": "^7.26.3",
75
75
  "@babel/preset-typescript": "^7.26.0",
76
- "@storybook/addon-a11y": "9.1.15",
77
- "@storybook/addon-designs": "^10.0.2",
78
- "@storybook/addon-docs": "9.1.15",
79
- "@storybook/nextjs": "9.1.15",
76
+ "@storybook/addon-a11y": "10.0.1",
77
+ "@storybook/addon-designs": "^11.0.1",
78
+ "@storybook/addon-docs": "10.0.1",
79
+ "@storybook/nextjs": "10.0.1",
80
80
  "@svgr/cli": "^8.0.1",
81
81
  "@types/d3": "^7.4.3",
82
82
  "@types/lodash": "^4.17.13",
@@ -89,13 +89,13 @@
89
89
  "commitizen": "^3.0.0",
90
90
  "esbuild": "^0.25.11",
91
91
  "eslint": "^8.47.0",
92
- "eslint-plugin-storybook": "9.1.15",
92
+ "eslint-plugin-storybook": "10.0.1",
93
93
  "postcss": "^8.4.26",
94
94
  "prettier": "^3.0.3",
95
95
  "prettier-plugin-tailwindcss": "^0.4.1",
96
96
  "react-markdown": "^10.0.0",
97
97
  "standard-version": "^9.5.0",
98
- "storybook": "9.1.15",
98
+ "storybook": "10.0.1",
99
99
  "storybook-version": "^0.1.4",
100
100
  "tailwindcss": "^3.4.17",
101
101
  "ts-loader": "^9.5.2",
@@ -24,6 +24,7 @@ export type AlertProps = PropsWithChildren<{
24
24
  as?: keyof HTMLElementTagNameMap;
25
25
  onClose?: () => void | Promise<void>;
26
26
  cta?: TextButtonProps;
27
+ ariaLabelClose?: string;
27
28
  }>;
28
29
 
29
30
  export const Alert: FC<AlertProps> = ({
@@ -36,6 +37,7 @@ export const Alert: FC<AlertProps> = ({
36
37
  as: Element = 'aside',
37
38
  onClose,
38
39
  className,
40
+ ariaLabelClose = 'Close',
39
41
  }) => {
40
42
  const [isClosePending, setIsClosePending] = useState(false);
41
43
 
@@ -65,8 +67,10 @@ export const Alert: FC<AlertProps> = ({
65
67
 
66
68
  const iconName = icon ?? 'warning-circle';
67
69
 
70
+ const role = mode === 'error' || mode === 'attention' ? 'alert' : 'status';
71
+
68
72
  return (
69
- <Element className={classList}>
73
+ <Element className={classList} role={role}>
70
74
  {headline && (
71
75
  <div className="flex items-center gap-2">
72
76
  {iconName && (
@@ -90,6 +94,7 @@ export const Alert: FC<AlertProps> = ({
90
94
  size="sm"
91
95
  variant="ghost"
92
96
  disabled={isClosePending}
97
+ aria-label={ariaLabelClose}
93
98
  />
94
99
  </div>
95
100
  )}
@@ -121,6 +126,7 @@ export const Alert: FC<AlertProps> = ({
121
126
  size="sm"
122
127
  variant="ghost"
123
128
  disabled={isClosePending}
129
+ aria-label={ariaLabelClose}
124
130
  />
125
131
  </div>
126
132
  )}
@@ -8,6 +8,7 @@ import type {
8
8
  PropsWithChildren,
9
9
  ReactNode,
10
10
  } from 'react';
11
+ import { useId } from 'react';
11
12
  import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
12
13
  import { Button } from '../../core/Button';
13
14
  import { ButtonGroup } from '../../core/ButtonGroup';
@@ -143,6 +144,8 @@ const renderContent = (
143
144
  title: ReactNode,
144
145
  children: ReactNode,
145
146
  centerTitle: boolean,
147
+ titleId: string,
148
+ descriptionId: string,
146
149
  ) => {
147
150
  if (!title && !children) {
148
151
  return null;
@@ -152,13 +155,17 @@ const renderContent = (
152
155
  <div className="space-y-4">
153
156
  {title && (
154
157
  <AlertDialogPrimitive.Title
158
+ id={titleId}
155
159
  className={twJoin('headline-md', centerTitle && 'text-center')}
156
160
  >
157
161
  {title}
158
162
  </AlertDialogPrimitive.Title>
159
163
  )}
160
164
  {children && (
161
- <AlertDialogPrimitive.Description className="text-lg">
165
+ <AlertDialogPrimitive.Description
166
+ id={descriptionId}
167
+ className="text-lg"
168
+ >
162
169
  {children}
163
170
  </AlertDialogPrimitive.Description>
164
171
  )}
@@ -255,6 +262,9 @@ export const AlertDialog: FC<Props> = ({
255
262
  centerItems = true,
256
263
  containerClassName,
257
264
  }) => {
265
+ const titleId = useId();
266
+ const descriptionId = useId();
267
+
258
268
  return (
259
269
  <AlertDialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
260
270
  <AlertDialogPrimitive.Portal container={container || undefined}>
@@ -266,6 +276,8 @@ export const AlertDialog: FC<Props> = ({
266
276
  )}
267
277
  >
268
278
  <AlertDialogPrimitive.Content
279
+ aria-labelledby={titleId}
280
+ aria-describedby={descriptionId}
269
281
  data-track-click-area={EventClickArea.Dialog}
270
282
  data-track-dialog={`alert-dialog-${title}`}
271
283
  className={twJoin(
@@ -275,9 +287,14 @@ export const AlertDialog: FC<Props> = ({
275
287
  containerClassName,
276
288
  )}
277
289
  >
278
- <button className="h-0 w-0" name="trap-focus-placeholder" />
279
290
  <div className="space-y-8">
280
- {renderContent(title, children, centerItems)}
291
+ {renderContent(
292
+ title,
293
+ children,
294
+ centerItems,
295
+ titleId,
296
+ descriptionId,
297
+ )}
281
298
  {renderActionCollection(actions, centerItems)}
282
299
  </div>
283
300
  </AlertDialogPrimitive.Content>
@@ -1,10 +1,10 @@
1
- import type { ReactNode } from 'react';
1
+ import type { ReactNode, HTMLAttributes } from 'react';
2
2
  import { twJoin } from 'tailwind-merge';
3
3
 
4
4
  /**
5
5
  * Props for the Badge component.
6
6
  */
7
- export type BadgeProps = {
7
+ export type BadgeProps = HTMLAttributes<HTMLDivElement> & {
8
8
  /**
9
9
  * The content to be displayed inside the badge.
10
10
  * Can be a single ReactNode or an array of ReactNodes.
@@ -49,7 +49,7 @@ const variants = {
49
49
  * ```
50
50
  */
51
51
  const Badge = (props: BadgeProps) => {
52
- const { className, variant = 'solid', children } = props;
52
+ const { className, variant = 'solid', children, ...rest } = props;
53
53
 
54
54
  return (
55
55
  <div
@@ -59,6 +59,7 @@ const Badge = (props: BadgeProps) => {
59
59
  variants.variant[variant],
60
60
  className,
61
61
  )}
62
+ {...rest}
62
63
  >
63
64
  {children}
64
65
  </div>
@@ -6,8 +6,9 @@ import React, {
6
6
  forwardRef,
7
7
  useEffect,
8
8
  InputHTMLAttributes,
9
+ useId,
9
10
  } from 'react';
10
- import { twJoin, twMerge } from 'tailwind-merge';
11
+ import { twMerge } from 'tailwind-merge';
11
12
  import { Control as RadixFormControl } from '@radix-ui/react-form';
12
13
 
13
14
  /**
@@ -44,6 +45,7 @@ export type BirthdateFieldProps = InputAttributes & {
44
45
  defaultValue?: string; // YYYY-MM-JJ
45
46
  readOnly?: Boolean;
46
47
  disabled?: Boolean;
48
+ legend?: string;
47
49
  };
48
50
 
49
51
  type SetPartFunction = {
@@ -135,6 +137,7 @@ const BirthdateField = forwardRef<HTMLInputElement, BirthdateFieldProps>(
135
137
  defaultValue,
136
138
  readOnly,
137
139
  disabled,
140
+ legend,
138
141
  ...props
139
142
  },
140
143
  ref,
@@ -153,6 +156,7 @@ const BirthdateField = forwardRef<HTMLInputElement, BirthdateFieldProps>(
153
156
  const dayRef = useRef<HTMLInputElement>(null);
154
157
  const monthRef = useRef<HTMLInputElement>(null);
155
158
  const yearRef = useRef<HTMLInputElement>(null);
159
+ const baseId = useId();
156
160
 
157
161
  const inputClassNames = twMerge(
158
162
  'w-full rounded-md border border-stroke-secondary-40 bg-surface-white p-4 text-center text-sm placeholder-marine-medium-emphasis outline-none transition-all',
@@ -196,20 +200,22 @@ const BirthdateField = forwardRef<HTMLInputElement, BirthdateFieldProps>(
196
200
  }
197
201
  };
198
202
 
199
- // Simple function to generate a "unique" ID by appending a random number
200
- const uniqueId = (base: string) =>
201
- `${base}-${Math.floor(Math.random() * 1000)}`;
202
-
203
203
  const inputs = [
204
204
  { part: 'day', ref: dayRef, nextRef: monthRef },
205
205
  { part: 'month', ref: monthRef, nextRef: yearRef },
206
206
  { part: 'year', ref: yearRef, nextRef: undefined },
207
207
  ].map(({ part, ref, nextRef }) => {
208
208
  const formatDetail = format[part];
209
- const id = uniqueId(`birthdate-${part}`);
209
+ const id = `${baseId}-${part}`;
210
210
  if (!formatDetail) return null;
211
211
  return (
212
212
  <div key={part}>
213
+ <label
214
+ htmlFor={id}
215
+ className="mb-1 inline-block cursor-pointer text-sm text-marine-medium-emphasis"
216
+ >
217
+ {formatDetail.label}
218
+ </label>
213
219
  <input
214
220
  id={id}
215
221
  {...props}
@@ -235,12 +241,6 @@ const BirthdateField = forwardRef<HTMLInputElement, BirthdateFieldProps>(
235
241
  : 31
236
242
  }
237
243
  />
238
- <label
239
- htmlFor={id}
240
- className="mt-1 inline-block cursor-pointer text-sm text-marine-medium-emphasis"
241
- >
242
- {formatDetail.label}
243
- </label>
244
244
  </div>
245
245
  );
246
246
  });
@@ -256,9 +256,10 @@ const BirthdateField = forwardRef<HTMLInputElement, BirthdateFieldProps>(
256
256
  value={combinedValue}
257
257
  />
258
258
  </RadixFormControl>
259
- <div className={twJoin('grid grid-cols-3 gap-3', className)}>
260
- {inputs}
261
- </div>
259
+ <fieldset className={className}>
260
+ {legend && <legend className="sr-only">{legend}</legend>}
261
+ <div className="grid grid-cols-3 gap-3">{inputs}</div>
262
+ </fieldset>
262
263
  </>
263
264
  );
264
265
  },
@@ -42,7 +42,6 @@ const ButtonGroupRenderFunction: ForwardRefRenderFunction<
42
42
  orientation === 'horizontal-center' &&
43
43
  'justify-center gap-6 sm:flex-row',
44
44
  className,
45
- disabled && 'pointer-events-none',
46
45
  )}
47
46
  >
48
47
  {children}
@@ -24,6 +24,8 @@ export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
24
24
  showNextMonthsButtonLabel?: string;
25
25
  numberOfMonthsToShowMore?: number;
26
26
  pastIsPickable?: boolean;
27
+ previousMonthLabel?: string;
28
+ nextMonthLabel?: string;
27
29
  };
28
30
 
29
31
  const DEFAULT_LOCALE: I18nLocale = 'de';
@@ -51,6 +53,8 @@ function Calendar({
51
53
  fromMonth,
52
54
  pastIsPickable = false,
53
55
  toMonth,
56
+ previousMonthLabel,
57
+ nextMonthLabel,
54
58
  ...props
55
59
  }: CalendarProps) {
56
60
  const [usedLocale, setUsedLocale] = useState<Locale | undefined>(undefined);
@@ -220,18 +224,19 @@ function Calendar({
220
224
  [UI.Month]: 'space-y-4 lg:w-1/2',
221
225
  [UI.MonthCaption]:
222
226
  'flex justify-center pt-1 relative items-center pointer-events-none',
223
- [UI.CaptionLabel]: 'label-md text-marine-high-emphasis',
227
+ [UI.CaptionLabel]:
228
+ 'font-semibold text-base text-marine-high-emphasis',
224
229
  [UI.Nav]: 'space-x-1 flex items-center',
225
230
  [UI.MonthGrid]: 'w-full border-collapse space-y-1',
226
231
  [UI.Weekdays]: 'flex w-full flex-row',
227
232
  [UI.Weekday]:
228
- 'inline-flex items-center justify-center h-11 w-1/7 font-normal text-xs rounded-sm',
233
+ 'inline-flex items-center justify-center h-11 w-1/7 font-normal text-base rounded-sm',
229
234
  [UI.DayButton]: twJoin(
230
235
  'h-11 w-full p-0 font-normal aria-selected:opacity-100',
231
236
  ),
232
237
  [UI.Week]: 'flex flex-row nowrap gap-x-0.5 w-full mt-0.5',
233
238
  [UI.Day]: twJoin(
234
- 'relative w-1/7 rounded-sm p-0 text-center text-xs focus-within:relative focus-within:z-20',
239
+ 'relative w-1/7 rounded-sm p-0 text-center text-base focus-within:relative focus-within:z-20',
235
240
  'hover:border-stroke-primary-100 hover:bg-surface-primary-60 data-[disabled]:hover:bg-transparent',
236
241
  'active:border-stroke-primary-100 active:bg-surface-primary-60',
237
242
  'focus:outline-none focus-visible:shadow-focus-state',
@@ -282,6 +287,7 @@ function Calendar({
282
287
  variant="secondary"
283
288
  className={twJoin('!mr-4 h-5 w-5', navButtonClasses)}
284
289
  size="sm"
290
+ aria-label={nextMonthLabel ?? 'Go to next month'}
285
291
  {...props}
286
292
  />
287
293
  ),
@@ -291,6 +297,7 @@ function Calendar({
291
297
  variant="secondary"
292
298
  iconName="nav-arrow-left"
293
299
  size="sm"
300
+ aria-label={previousMonthLabel ?? 'Go to previous month'}
294
301
  {...props}
295
302
  />
296
303
  ),
@@ -78,6 +78,7 @@ const CheckboxFieldRenderFunction: ForwardRefRenderFunction<
78
78
  <Checkbox
79
79
  {...attrs}
80
80
  required={typeof required === 'boolean' ? required : undefined}
81
+ aria-required={required === 'label' ? true : undefined}
81
82
  ref={inputRef}
82
83
  />
83
84
 
@@ -115,6 +116,7 @@ const CheckboxFieldRenderFunction: ForwardRefRenderFunction<
115
116
  <Checkbox
116
117
  {...attrs}
117
118
  required={typeof required === 'boolean' ? required : undefined}
119
+ aria-required={required === 'label' ? true : undefined}
118
120
  />
119
121
  </RadixRoot>
120
122
  </RadixForm.Control>
@@ -0,0 +1,101 @@
1
+ import type { ReactNode, RefObject } from 'react';
2
+ import { twJoin, twMerge } from 'tailwind-merge';
3
+ import { Slot, Slottable } from '@radix-ui/react-slot';
4
+ import { Pictogram, PictogramName } from '../Pictogram';
5
+
6
+ /**
7
+ * All allowed variants for the Chip component.
8
+ */
9
+ type Variant = 'text' | 'pictogram';
10
+
11
+ /**
12
+ * Props for the Chip component.
13
+ */
14
+ export type ChipProps = {
15
+ asChild?: boolean;
16
+ className?: string;
17
+ variant?: Variant;
18
+ pictogram?: PictogramName;
19
+ on?: 'white' | 'gray';
20
+ children?: ReactNode;
21
+ active?: boolean;
22
+ disabled?: boolean;
23
+ ref?: RefObject<HTMLButtonElement>;
24
+ };
25
+
26
+ /**
27
+ * A Chip component designed for basic labeling or marking destinations
28
+ */
29
+ const Chip = ({
30
+ asChild,
31
+ className,
32
+ variant = 'text',
33
+ pictogram,
34
+ on = 'white',
35
+ active = false,
36
+ disabled = false,
37
+ children,
38
+ ref,
39
+ ...args
40
+ }: ChipProps) => {
41
+ const Element = asChild ? Slot : 'button';
42
+
43
+ const borderRadius = variant === 'text' ? 'rounded-full' : 'rounded-md';
44
+ const padding = variant === 'text' ? 'px-4 py-3' : 'p-3 lg:p-2';
45
+ const variantClasses = twMerge(
46
+ 'border-none text-center text-sm font-semibold transition-all',
47
+
48
+ // Variants
49
+ variant === 'text' && [
50
+ // Shared
51
+ 'text-marine-high-emphasis hover:bg-surface-primary-100',
52
+
53
+ // On surfaces
54
+ on === 'white' && 'bg-surface-secondary-7',
55
+ on === 'gray' && 'bg-surface-white',
56
+
57
+ // States
58
+ active && 'bg-surface-primary-100',
59
+ disabled &&
60
+ 'bg-surface-secondary-7 shadow-[inset_0_0_0_2px_theme(colors.stroke.secondary-20)]',
61
+ on === 'gray' &&
62
+ disabled &&
63
+ 'bg-surface-white shadow-[inset_0_0_0_2px_theme(colors.stroke.secondary-20)]',
64
+ ],
65
+
66
+ variant === 'pictogram' && [
67
+ // Shared
68
+ 'bg-surface-white shadow-[inset_0_0_0_2px_theme(colors.stroke.primary-100)] hover:shadow-[inset_0_0_0_4px_theme(colors.stroke.primary-100)]',
69
+
70
+ // States
71
+ active && 'bg-surface-primary-100',
72
+ disabled && 'bg-surface-secondary-7 shadow-none',
73
+ on === 'gray' && disabled && 'bg-surface-white shadow-none',
74
+ ],
75
+
76
+ // States
77
+ 'focus:outline-none focus:transition-none focus-visible:shadow-focus-state',
78
+ disabled && '!pointer-events-none !text-secondary-marine-48',
79
+ );
80
+
81
+ return (
82
+ <Element
83
+ ref={ref}
84
+ {...args}
85
+ className={twJoin(
86
+ 'inline-flex items-center justify-center',
87
+ borderRadius,
88
+ padding,
89
+ variantClasses,
90
+ className,
91
+ )}
92
+ >
93
+ <Slottable>{children ?? ''}</Slottable>
94
+ {pictogram && <Pictogram name={pictogram} size="sm" />}
95
+ </Element>
96
+ );
97
+ };
98
+
99
+ Chip.displayName = 'Chip';
100
+
101
+ export { Chip };
@@ -0,0 +1 @@
1
+ export * from './Chip';
@@ -40,7 +40,10 @@ export function Countdown({
40
40
  }, [destinationDate]);
41
41
 
42
42
  return (
43
- <div className={twMerge('flex w-full flex-row justify-center', className)}>
43
+ <div
44
+ className={twMerge('flex w-full flex-row justify-center', className)}
45
+ role="timer"
46
+ >
44
47
  <div className="flex flex-row gap-2 p-3">
45
48
  {calculatedSegmentTouples?.map(([label, value], index: number) => {
46
49
  return (
@@ -6,9 +6,15 @@ type DataTableRow = ReactNode;
6
6
  export type DataTableProps = HTMLAttributes<HTMLTableElement> & {
7
7
  headers: DataTableRow[];
8
8
  dataRows: DataTableRow[][];
9
+ caption?: string;
9
10
  };
10
11
 
11
- export const DataTable = ({ headers, dataRows, ...attrs }: DataTableProps) => {
12
+ export const DataTable = ({
13
+ headers,
14
+ dataRows,
15
+ caption,
16
+ ...attrs
17
+ }: DataTableProps) => {
12
18
  const baseClass = 'text-left text-sm text-marine-high-emphasis';
13
19
  const cellClass = 'max-w-[250px] gap-2 overflow-x-auto whitespace-normal p-4';
14
20
  const headerClass = 'md:text-base font-semibold gap-2 whitespace-normal p-4';
@@ -28,6 +34,7 @@ export const DataTable = ({ headers, dataRows, ...attrs }: DataTableProps) => {
28
34
  const renderTableForSmallScreens = () => (
29
35
  <div className="overflow-x-auto rounded-md bg-surface-white [clip-path:border-box] md:hidden">
30
36
  <table {...attrs} className={tableClass}>
37
+ {caption && <caption>{caption}</caption>}
31
38
  <tbody>
32
39
  {longestRow.map((_, rowIndex) => (
33
40
  <tr key={rowIndex}>
@@ -36,6 +43,7 @@ export const DataTable = ({ headers, dataRows, ...attrs }: DataTableProps) => {
36
43
  <th
37
44
  key={`${rowIndex}-${colIndex}`}
38
45
  className={stickyHeaderClass}
46
+ scope="row"
39
47
  >
40
48
  {column[rowIndex]}
41
49
  </th>
@@ -58,10 +66,11 @@ export const DataTable = ({ headers, dataRows, ...attrs }: DataTableProps) => {
58
66
  const renderTableForLargeScreens = () => (
59
67
  <div className="hidden overflow-x-auto rounded-md bg-surface-white [clip-path:border-box] md:block">
60
68
  <table {...attrs} className={tableClass}>
69
+ {caption && <caption>{caption}</caption>}
61
70
  <thead className="bg-surface-primary-40">
62
71
  <tr>
63
72
  {headers.map((header, i) => (
64
- <th key={i} className={headerClass}>
73
+ <th key={i} className={headerClass} scope="col">
65
74
  {header}
66
75
  </th>
67
76
  ))}
@@ -6,7 +6,7 @@ import {
6
6
  formatTrackingString,
7
7
  trackEvent,
8
8
  } from '../../../libs/tracking';
9
- import { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react';
9
+ import { ComponentProps, FC, PropsWithChildren, ReactNode, useId } from 'react';
10
10
  import { twJoin } from 'tailwind-merge';
11
11
  import { IconButton } from '../../core/IconButton';
12
12
 
@@ -295,6 +295,9 @@ const DialogContent = ({
295
295
  disableOnCloseFocusReset,
296
296
  ...rest
297
297
  }: DialogContentProps) => {
298
+ const titleId = useId();
299
+ const descriptionId = useId();
300
+
298
301
  return (
299
302
  <DialogPrimitive.Portal container={container || undefined}>
300
303
  <DialogPrimitive.Overlay
@@ -316,6 +319,8 @@ const DialogContent = ({
316
319
  onCloseAutoFocus={createOnCloseAutoFocusHandler(
317
320
  disableOnCloseFocusReset,
318
321
  )}
322
+ aria-labelledby={titleId}
323
+ aria-describedby={descriptionId}
319
324
  data-track-click-area={EventClickArea.Dialog}
320
325
  data-track-dialog={
321
326
  trackingName ? formatTrackingString(trackingName) : title
@@ -348,7 +353,10 @@ const DialogContent = ({
348
353
  !fullscreen && 'rounded-t-md',
349
354
  )}
350
355
  >
351
- <DialogPrimitive.Title className="relative max-w-full truncate text-nowrap text-lg font-semibold text-marine-high-emphasis">
356
+ <DialogPrimitive.Title
357
+ id={titleId}
358
+ className="relative max-w-full truncate text-nowrap text-lg font-semibold text-marine-high-emphasis"
359
+ >
352
360
  {title}
353
361
  </DialogPrimitive.Title>
354
362
  {showCloseButton && (
@@ -365,6 +373,7 @@ const DialogContent = ({
365
373
  )}
366
374
  </div>
367
375
  <div
376
+ id={descriptionId}
368
377
  className={twJoin(
369
378
  'mt-12 flex max-h-[calc(100%_-_theme(space.12))] w-full flex-col overflow-y-auto',
370
379
  bodyClassName,
@@ -33,5 +33,5 @@ export const Divider: FC<Props> = ({
33
33
 
34
34
  const Element = asChild ? Slot : 'div';
35
35
 
36
- return <Element {...rest} className={className} />;
36
+ return <Element {...rest} className={className} aria-hidden="true" />;
37
37
  };