@ceed/cds 1.36.0-next.1 → 1.36.1

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.
@@ -14,7 +14,7 @@ export interface CustomAlertProps {
14
14
  size?: AlertSize;
15
15
  }
16
16
  export type AlertProps = CustomAlertProps & Omit<JoyAlertProps, 'content' | 'actions' | 'title' | 'size'> & Omit<MotionProps, 'children'>;
17
- declare function Alert(inProps: AlertProps): React.JSX.Element;
17
+ declare function Alert(props: AlertProps): React.JSX.Element;
18
18
  declare namespace Alert {
19
19
  var displayName: string;
20
20
  }
@@ -16,7 +16,7 @@ export type FilterableCheckboxGroupProps = {
16
16
  maxHeight?: string | number;
17
17
  disabled?: boolean;
18
18
  };
19
- declare function FilterableCheckboxGroup(inProps: FilterableCheckboxGroupProps): React.JSX.Element;
19
+ declare function FilterableCheckboxGroup(props: FilterableCheckboxGroupProps): React.JSX.Element;
20
20
  declare namespace FilterableCheckboxGroup {
21
21
  var displayName: string;
22
22
  }
@@ -23,7 +23,7 @@ export interface PaginationProps extends React.ComponentProps<typeof PaginationR
23
23
  size?: 'sm' | 'md' | 'lg';
24
24
  variant?: 'standard' | 'compact';
25
25
  }
26
- declare function Pagination(inProps: PaginationProps): React.JSX.Element;
26
+ declare function Pagination(props: PaginationProps): React.JSX.Element;
27
27
  declare namespace Pagination {
28
28
  var displayName: string;
29
29
  }
@@ -1,5 +1,8 @@
1
1
  import React, { ComponentProps } from 'react';
2
2
  import { Select as JoySelect, type SelectProps as JoySelectProps } from '@mui/joy';
3
+ import FormControl from '../FormControl';
4
+ import FormLabel from '../FormLabel';
5
+ import FormHelperText from '../FormHelperText';
3
6
  import { SelectValue } from '@mui/base';
4
7
  export type { JoySelectProps };
5
8
  export type { OptionProps } from '@mui/joy';
@@ -24,11 +27,16 @@ export { Option };
24
27
  export type InferOptionValue<OptionValue> = OptionValue extends {
25
28
  value: infer V;
26
29
  } ? V : OptionValue;
27
- export interface SelectProps<OptionValue extends OptionType, Multiple extends boolean = false> extends Omit<ComponentProps<typeof JoySelect<OptionValue, any, Multiple>>, 'value' | 'onChange' | 'multiple'> {
30
+ export interface SelectProps<OptionValue extends OptionType, Multiple extends boolean = false> extends Omit<ComponentProps<typeof JoySelect<OptionValue, any, Multiple>>, 'value' | 'onChange' | 'multiple' | 'slotProps'> {
28
31
  options: OptionValue[];
29
32
  label?: string;
30
33
  helperText?: string;
31
34
  error?: boolean;
35
+ slotProps?: ComponentProps<typeof JoySelect<OptionValue, any, Multiple>>['slotProps'] & {
36
+ formControl?: ComponentProps<typeof FormControl>;
37
+ formLabel?: ComponentProps<typeof FormLabel>;
38
+ formHelperText?: ComponentProps<typeof FormHelperText>;
39
+ };
32
40
  value?: SelectValue<InferOptionValue<OptionValue>, Multiple>;
33
41
  onChange?: (event: Omit<React.SyntheticEvent<HTMLElement>, 'target'> & {
34
42
  target: {
@@ -8,9 +8,6 @@ import { MonthPickerProps } from '../MonthPicker';
8
8
  import { MonthRangePickerProps } from '../MonthRangePicker';
9
9
  import { PercentageInputProps } from '../PercentageInput';
10
10
  import type { SearchBarOwnerState, SearchBarProps, SearchBarSlot } from '../SearchBar/SearchBar';
11
- import type { AlertProps } from '../Alert/Alert';
12
- import type { PaginationProps } from '../Pagination/Pagination';
13
- import type { FilterableCheckboxGroupProps } from '../FilterableCheckboxGroup/FilterableCheckboxGroup';
14
11
  declare module '@mui/joy/styles' {
15
12
  interface TypographySystemOverrides {
16
13
  'marketing-lg': true;
@@ -50,18 +47,6 @@ declare module '@mui/joy/styles' {
50
47
  defaultProps?: Partial<SearchBarProps>;
51
48
  styleOverrides?: StyleOverrides<SearchBarSlot, SearchBarOwnerState, Theme>;
52
49
  };
53
- Alert?: {
54
- defaultProps?: Partial<AlertProps>;
55
- styleOverrides?: StyleOverrides<'root', {}, Theme>;
56
- };
57
- Pagination?: {
58
- defaultProps?: Partial<PaginationProps>;
59
- styleOverrides?: StyleOverrides<'root' | 'button' | 'container', {}, Theme>;
60
- };
61
- FilterableCheckboxGroup?: {
62
- defaultProps?: Partial<FilterableCheckboxGroupProps>;
63
- styleOverrides?: StyleOverrides<'root', {}, Theme>;
64
- };
65
50
  }
66
51
  }
67
52
  declare module '@mui/joy/Avatar' {
@@ -207,6 +207,58 @@ Combining `label`, `helperText`, and `error` produces a complete form field. Her
207
207
  </>
208
208
  ```
209
209
 
210
+ ## Slot Props and Deterministic Ids
211
+
212
+ The `slotProps` prop targets both Joy Select's own slots (`button`, `listbox`, `root`, …) and the wrapper slots that `Select` composes around it (`formControl`, `formLabel`, `formHelperText`). Pass `id` (or `slotProps.formControl.id`) to pin the field's ids deterministically — `FormControl` derives the button `id` and the label `id` (`${id}-label`) from it, which keeps `byLabelText` / `byRole('combobox', { name })` queries stable in tests.
213
+
214
+ ```tsx
215
+ <Stack
216
+ spacing={3}
217
+ sx={{
218
+ minWidth: 240
219
+ }}
220
+ >
221
+ {/* id seeds deterministic FormControl ids: button#pet, label#pet-label (testable via byLabelText/byRole) */}
222
+ <Select
223
+ id="pet"
224
+ label="Pet"
225
+ defaultValue="cat"
226
+ options={options}
227
+ slotProps={{
228
+ formLabel: {
229
+ sx: {
230
+ color: "primary.500"
231
+ }
232
+ },
233
+ formHelperText: {
234
+ sx: {
235
+ fontStyle: "italic"
236
+ }
237
+ },
238
+ button: {
239
+ "aria-describedby": "pet-extra"
240
+ }
241
+ }}
242
+ helperText="Wired via slotProps"
243
+ />
244
+ </Stack>
245
+ ```
246
+
247
+ ```tsx
248
+ <Select
249
+ id="pet"
250
+ label="Pet"
251
+ helperText="Wired via slotProps"
252
+ options={options}
253
+ slotProps={{
254
+ formLabel: { sx: { color: 'primary.500' } },
255
+ formHelperText: { sx: { fontStyle: 'italic' } },
256
+ button: { 'aria-describedby': 'pet-extra' },
257
+ }}
258
+ />
259
+ // → renders button#pet and label#pet-label, connected via aria-labelledby
260
+ ```
261
+
210
262
  ## Required Field
211
263
 
212
264
  Set `required` to mark the field as required. An asterisk is added to the label automatically.
@@ -373,9 +425,7 @@ const numericOptions = [
373
425
  <Select options={numericOptions} defaultValue={1} />;
374
426
  ```
375
427
 
376
- ## Common Use Cases
377
-
378
- ### Form with Validation
428
+ ## Form with Validation
379
429
 
380
430
  ```tsx
381
431
  import { useState } from 'react';
@@ -417,7 +467,7 @@ function UserForm() {
417
467
  }
418
468
  ```
419
469
 
420
- ### Filter Bar
470
+ ## Filter Bar
421
471
 
422
472
  ```tsx
423
473
  import { Select, Stack } from '@ceed/cds';
@@ -459,7 +509,7 @@ function ProductFilter({ onFilterChange }) {
459
509
  }
460
510
  ```
461
511
 
462
- ### Dependent Selects
512
+ ## Dependent Selects
463
513
 
464
514
  ```tsx
465
515
  import { useState, useMemo } from 'react';
@@ -559,25 +609,27 @@ function LocationSelect() {
559
609
 
560
610
  ### Key Props
561
611
 
562
- | Prop | Type | Default | Description |
563
- | ---------------- | ---------------------------------------------------------------- | ----------------- | ---------------------------------------------------- |
564
- | `options` | `OptionType[]` | `[]` | Array of options (objects or primitives) |
565
- | `value` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Selected value(s) for controlled mode |
566
- | `defaultValue` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Initial value for uncontrolled mode |
567
- | `onChange` | `(event: { target: { name?, value } }, newValue) => void` | - | Callback when selection changes |
568
- | `multiple` | `boolean` | `false` | Allow multiple selections (value becomes an array) |
569
- | `label` | `string` | - | Form label displayed above the select |
570
- | `helperText` | `string` | - | Helper text displayed below the select |
571
- | `error` | `boolean` | `false` | Applies danger color to indicate validation error |
572
- | `required` | `boolean` | `false` | Marks the field as required (adds asterisk to label) |
573
- | `disabled` | `boolean` | `false` | Disables the select |
574
- | `placeholder` | `string` | `'Choose one...'` | Placeholder text when no value is selected |
575
- | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
576
- | `variant` | `'outlined' \| 'plain' \| 'solid' \| 'soft'` | `'outlined'` | Visual style variant |
577
- | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
578
- | `startDecorator` | `ReactNode` | - | Content rendered before the select trigger text |
579
- | `endDecorator` | `ReactNode` | - | Content rendered after the select trigger text |
580
- | `sx` | `SxProps` | - | Custom styles using the MUI system |
612
+ | Prop | Type | Default | Description |
613
+ | ---------------- | ----------------------------------------------------------------------- | ----------------- | -------------------------------------------------------------- |
614
+ | `options` | `OptionType[]` | `[]` | Array of options (objects or primitives) |
615
+ | `value` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Selected value(s) for controlled mode |
616
+ | `defaultValue` | `InferOptionValue<OptionType> \| InferOptionValue<OptionType>[]` | - | Initial value for uncontrolled mode |
617
+ | `onChange` | `(event: { target: { name?, value } }, newValue) => void` | - | Callback when selection changes |
618
+ | `multiple` | `boolean` | `false` | Allow multiple selections (value becomes an array) |
619
+ | `label` | `string` | - | Form label displayed above the select |
620
+ | `helperText` | `string` | - | Helper text displayed below the select |
621
+ | `error` | `boolean` | `false` | Applies danger color to indicate validation error |
622
+ | `required` | `boolean` | `false` | Marks the field as required (adds asterisk to label) |
623
+ | `disabled` | `boolean` | `false` | Disables the select |
624
+ | `placeholder` | `string` | `'Choose one...'` | Placeholder text when no value is selected |
625
+ | `size` | `'sm' \| 'md' \| 'lg'` | `'md'` | Select size |
626
+ | `variant` | `'outlined' \| 'plain' \| 'solid' \| 'soft'` | `'outlined'` | Visual style variant |
627
+ | `color` | `'primary' \| 'neutral' \| 'danger' \| 'success' \| 'warning'` | `'neutral'` | Color scheme |
628
+ | `startDecorator` | `ReactNode` | - | Content rendered before the select trigger text |
629
+ | `endDecorator` | `ReactNode` | - | Content rendered after the select trigger text |
630
+ | `id` | `string` | - | Seeds deterministic ids: button `id`, label `${id}-label` |
631
+ | `slotProps` | `{ formControl?, formLabel?, formHelperText?, button?, listbox?, ... }` | - | Props forwarded to composed wrapper slots and Joy Select slots |
632
+ | `sx` | `SxProps` | - | Custom styles using the MUI system |
581
633
 
582
634
  ### Option Type
583
635
 
@@ -613,7 +665,8 @@ const options = [
613
665
 
614
666
  ## Accessibility
615
667
 
616
- - **Label association**: When you use the `label` prop, the component automatically connects the label to the select element via `aria-labelledby`. Always provide a label for screen reader users.
668
+ - **Label association**: When you use the `label` prop, the component automatically connects the label to the select element via `aria-labelledby` (the internal `FormControl` propagates a `labelId` to both the label and the button slot). You do not need to wire `useId` or a custom `FormLabel` externally. Always provide a label for screen reader users.
669
+ - **Deterministic ids for tests**: Pass `id` (or `slotProps.formControl.id`) to fix the generated ids, so `byLabelText` / `byRole('combobox', { name })` resolve to a stable accessible name.
617
670
  - **Keyboard navigation**: The select can be opened with Enter or Space, navigated with Arrow Up/Down and Home/End, confirmed with Enter, and dismissed with Escape.
618
671
  - **Error announcement**: When `error` is set, `aria-invalid` is applied. Pair it with a descriptive `helperText` so assistive technology can announce the error reason.
619
672
  - **Required state**: The `required` prop adds `aria-required` and a visible asterisk to the label, communicating the requirement both visually and programmatically.