@porsche-design-system/components-react 4.6.0-rc.1 → 4.6.0-rc.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 (82) hide show
  1. package/CHANGELOG.md +61 -2
  2. package/OSS_NOTICE +5532 -2115
  3. package/cjs/lib/components/ai-tag.wrapper.cjs +1 -1
  4. package/cjs/lib/components/radio-group.wrapper.cjs +1 -1
  5. package/esm/lib/components/ai-tag.wrapper.d.ts +4 -4
  6. package/esm/lib/components/ai-tag.wrapper.mjs +1 -1
  7. package/esm/lib/components/input-number.wrapper.d.ts +6 -6
  8. package/esm/lib/components/input-text.wrapper.d.ts +6 -6
  9. package/esm/lib/components/multi-select-option.wrapper.d.ts +2 -2
  10. package/esm/lib/components/pin-code.wrapper.d.ts +6 -6
  11. package/esm/lib/components/radio-group-option.wrapper.d.ts +4 -4
  12. package/esm/lib/components/radio-group.wrapper.d.ts +4 -6
  13. package/esm/lib/components/radio-group.wrapper.mjs +1 -1
  14. package/esm/lib/components/segmented-control.wrapper.d.ts +4 -4
  15. package/esm/lib/components/wordmark.wrapper.d.ts +2 -2
  16. package/esm/lib/types.d.ts +184 -213
  17. package/package.json +6 -2
  18. package/skills/pds-knowledge-react/SKILL.md +3 -3
  19. package/skills/pds-knowledge-react/references/components/p-ai-tag/p-ai-tag.md +2 -2
  20. package/skills/pds-knowledge-react/references/components/p-button/p-button.md +1 -1
  21. package/skills/pds-knowledge-react/references/components/p-button-pure/p-button-pure.md +1 -1
  22. package/skills/pds-knowledge-react/references/components/p-button-tile/p-button-tile.md +1 -1
  23. package/skills/pds-knowledge-react/references/components/p-carousel/p-carousel.md +1 -1
  24. package/skills/pds-knowledge-react/references/components/p-icon/examples/Overview.tsx +6 -0
  25. package/skills/pds-knowledge-react/references/components/p-icon/p-icon.md +1 -1
  26. package/skills/pds-knowledge-react/references/components/p-inline-notification/p-inline-notification.md +1 -1
  27. package/skills/pds-knowledge-react/references/components/p-input-number/p-input-number.md +3 -3
  28. package/skills/pds-knowledge-react/references/components/p-input-text/p-input-text.md +3 -3
  29. package/skills/pds-knowledge-react/references/components/p-link/p-link.md +1 -1
  30. package/skills/pds-knowledge-react/references/components/p-link-pure/p-link-pure.md +1 -1
  31. package/skills/pds-knowledge-react/references/components/p-multi-select/examples/AsyncFilter.tsx +29 -6
  32. package/skills/pds-knowledge-react/references/components/p-multi-select/p-multi-select.md +2 -2
  33. package/skills/pds-knowledge-react/references/components/p-pin-code/p-pin-code.md +2 -2
  34. package/skills/pds-knowledge-react/references/components/p-radio-group/examples/Form.tsx +2 -2
  35. package/skills/pds-knowledge-react/references/components/p-radio-group/p-radio-group.md +3 -3
  36. package/skills/pds-knowledge-react/references/components/p-segmented-control/p-segmented-control.md +2 -2
  37. package/skills/pds-knowledge-react/references/components/p-select/examples/AsyncFilter.tsx +29 -6
  38. package/skills/pds-knowledge-react/references/components/p-select/p-select.md +1 -1
  39. package/skills/pds-knowledge-react/references/components/p-tag/p-tag.md +1 -1
  40. package/skills/pds-knowledge-react/references/components/p-wordmark/p-wordmark.md +1 -1
  41. package/skills/pds-knowledge-react/references/icons.md +1 -1
  42. package/ssr/cjs/components/dist/styles/esm/styles-entry.cjs +203 -4
  43. package/ssr/cjs/components/dist/utils/esm/utils-entry.cjs +241 -40
  44. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/components/ai-tag.wrapper.cjs +1 -1
  45. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/components/radio-group.wrapper.cjs +1 -1
  46. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/carousel.cjs +4 -1
  47. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/filter-status-announcer.cjs +7 -0
  48. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/multi-select.cjs +3 -1
  49. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/no-results-option.cjs +1 -1
  50. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/pin-code.cjs +7 -3
  51. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/radio-group.cjs +4 -0
  52. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/segmented-control.cjs +5 -1
  53. package/ssr/cjs/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/select.cjs +3 -4
  54. package/ssr/esm/components/dist/styles/esm/styles-entry.mjs +203 -5
  55. package/ssr/esm/components/dist/utils/esm/utils-entry.mjs +237 -41
  56. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/ai-tag.wrapper.mjs +1 -1
  57. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/components/radio-group.wrapper.mjs +1 -1
  58. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/carousel.mjs +4 -1
  59. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/filter-status-announcer.mjs +5 -0
  60. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/multi-select.mjs +3 -1
  61. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/no-results-option.mjs +1 -1
  62. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/pin-code.mjs +7 -3
  63. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/radio-group.mjs +4 -0
  64. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/segmented-control.mjs +5 -1
  65. package/ssr/esm/components-react/projects/react-ssr-wrapper/src/lib/dsr-components/select.mjs +3 -4
  66. package/ssr/esm/lib/components/ai-tag.wrapper.d.ts +4 -4
  67. package/ssr/esm/lib/components/input-number.wrapper.d.ts +6 -6
  68. package/ssr/esm/lib/components/input-text.wrapper.d.ts +6 -6
  69. package/ssr/esm/lib/components/multi-select-option.wrapper.d.ts +2 -2
  70. package/ssr/esm/lib/components/pin-code.wrapper.d.ts +6 -6
  71. package/ssr/esm/lib/components/radio-group-option.wrapper.d.ts +4 -4
  72. package/ssr/esm/lib/components/radio-group.wrapper.d.ts +4 -6
  73. package/ssr/esm/lib/components/segmented-control.wrapper.d.ts +4 -4
  74. package/ssr/esm/lib/components/wordmark.wrapper.d.ts +2 -2
  75. package/ssr/esm/lib/dsr-components/carousel.d.ts +3 -0
  76. package/ssr/esm/lib/dsr-components/filter-status-announcer.d.ts +6 -0
  77. package/ssr/esm/lib/dsr-components/multi-select.d.ts +1 -0
  78. package/ssr/esm/lib/dsr-components/pin-code.d.ts +1 -0
  79. package/ssr/esm/lib/dsr-components/radio-group.d.ts +1 -0
  80. package/ssr/esm/lib/dsr-components/segmented-control.d.ts +1 -0
  81. package/ssr/esm/lib/dsr-components/select.d.ts +1 -1
  82. package/ssr/esm/lib/types.d.ts +184 -213
@@ -89,7 +89,7 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
89
89
  | `download` | `string` | `undefined` | Sets the native `download` attribute to trigger a file download. Only applies when `href` is set. |
90
90
  | `hideLabel` | `boolean`<br>`BreakpointCustomizable<boolean>` | `false` | Hides the visible label while keeping it accessible to screen readers. Supports responsive breakpoint values. |
91
91
  | `href` | `string` | `undefined` | When set, the component renders as an anchor navigating to this URL. Otherwise, provide a slotted anchor element. |
92
- | `icon` | `''`<br>one of 290 icon names — see [icon names](references/icons.md) | `'arrow-right'` | Sets the icon displayed next to the label. |
92
+ | `icon` | `''`<br>one of 293 icon names — see [icon names](references/icons.md) | `'arrow-right'` | Sets the icon displayed next to the label. |
93
93
  | `iconSource` | `string` | `undefined` | Sets a path to a custom SVG icon, used instead of the built-in icon set. |
94
94
  | `rel` | `string` | `undefined` | Sets the `rel` attribute on the link (e.g. `noopener`). Only applies when `href` is set. |
95
95
  | `size` | `'2xs'` `'xs'` `'sm'` `'md'` `'lg'` `'xl'` `'2xl'` `'3xl'` `'4xl'` `'5xl'` `'inherit'`<br>_deprecated:_ `'xx-small'` `'x-small'` `'small'` `'medium'` `'large'` `'x-large'`<br>`BreakpointCustomizable<LinkPureSize>` | `'sm'` | Sets the font size of the link label. Supports responsive breakpoint values. |
@@ -9,7 +9,7 @@ import {
9
9
  PMultiSelectOption,
10
10
  type PMultiSelectProps,
11
11
  } from '@porsche-design-system/components-react';
12
- import { useCallback, useRef, useState } from 'react';
12
+ import { useCallback, useMemo, useRef, useState } from 'react';
13
13
 
14
14
  const useDebounce = <T,>(callback: (value: T) => void, delay = 400) => {
15
15
  const timer = useRef<number | undefined>(undefined);
@@ -19,6 +19,20 @@ const useDebounce = <T,>(callback: (value: T) => void, delay = 400) => {
19
19
  };
20
20
  };
21
21
 
22
+ const getFilterStatusMessage = (
23
+ searchValue: string,
24
+ optionCount: number,
25
+ isLoading: boolean,
26
+ error: string | null
27
+ ): string => {
28
+ if (error) return '';
29
+ if (isLoading) return 'Loading options';
30
+ const term = searchValue.trim();
31
+ if (!term) return '';
32
+ if (optionCount === 0) return 'No results found';
33
+ return optionCount === 1 ? '1 result available' : `${optionCount} results available`;
34
+ };
35
+
22
36
  export const Example = () => {
23
37
  const [value, setValue] = useState<PMultiSelectProps['value']>([]);
24
38
  const [options, setOptions] = useState<{ value: string; label: string }[]>([]);
@@ -83,6 +97,11 @@ export const Example = () => {
83
97
  }
84
98
  };
85
99
 
100
+ const filterStatusMessage = useMemo(
101
+ () => getFilterStatusMessage(searchValue, options.length, initialLoading || loading, error),
102
+ [searchValue, options.length, initialLoading, loading, error]
103
+ );
104
+
86
105
  return (
87
106
  <PMultiSelect name="async-search-select" label="Async Search" value={value} onChange={onChange} onToggle={onToggle}>
88
107
  <PInputSearch
@@ -100,6 +119,11 @@ export const Example = () => {
100
119
  onChange={(e: any) => e.stopPropagation()}
101
120
  />
102
121
 
122
+ {/* Persistent status announcer for assistive technologies (keep always in the DOM) */}
123
+ <div slot="options-status" className="sr-only" aria-live="polite" aria-atomic="true">
124
+ {filterStatusMessage}
125
+ </div>
126
+
103
127
  {/* Initial skeleton loading */}
104
128
  {initialLoading &&
105
129
  !error &&
@@ -112,15 +136,14 @@ export const Example = () => {
112
136
  </PMultiSelectOption>
113
137
  ))}
114
138
 
115
- {/* No filter results */}
116
- {!initialLoading && options.length === 0 && !error && (
139
+ {/* No filter results (visual only; announcements come from the live region) */}
140
+ {!initialLoading && !loading && options.length === 0 && !error && (
117
141
  <div
118
142
  slot="options-status"
119
143
  className="text-contrast-medium cursor-not-allowed py-static-sm px-[12px]"
120
- role="alert"
144
+ aria-hidden="true"
121
145
  >
122
- <span aria-hidden="true">–</span>
123
- <span className="sr-only">No results found</span>
146
+
124
147
  </div>
125
148
  )}
126
149
 
@@ -4,7 +4,7 @@ The `p-multi-select` component is a versatile custom form element that facilitat
4
4
 
5
5
  Many of the properties closely resemble those found in the [select](../p-select/p-select.md) and other form components (e.g. `label`, `description`, `state`, `message`, `hideLabel`, `disabled`, `required` & `dropdownDirection`). The `p-multi-select` offers a search field by default. When the user types in a search string, the options are reduced by matching the options text.
6
6
 
7
- Options are slotted using the `p-multi-select-option` component. Each option needs to have an assigned value, which can be passed via an attribute or property and needs to be of type string. Initial selection states can only be achieved using the value property on the `p-multi-select` component ([More Info](./p-multi-select.md)). Options don't have a selected attribute or property. If an option should be visible but not selectable, it can be disabled by using the `disabled` attribute.
7
+ Options are slotted using the `p-multi-select-option` component. Each option needs to have an assigned value, which can be passed via an attribute or property and needs to be a string or number. `null` and `undefined` are not valid option values. Initial selection states can only be achieved using the value property on the `p-multi-select` component ([More Info](./p-multi-select.md)). Options don't have a selected attribute or property. If an option should be visible but not selectable, it can be disabled by using the `disabled` attribute.
8
8
 
9
9
  **Hint:** The `p-multi-select` utilizes the [Popover API](https://developer.mozilla.org/en-US/docs/Web/API/Popover_API) to render its dropdown on the `#top-layer` which enables it to be shown correctly even when used e.g. within a scroll container.
10
10
 
@@ -146,7 +146,7 @@ Allowed parents: `p-multi-select`, `p-optgroup`.
146
146
  | Property | Type | Default | Description |
147
147
  | --- | --- | --- | --- |
148
148
  | `disabled` | `boolean` | `false` | Disables the option, preventing it from being selected. |
149
- | `value` _(required)_ | `string | number` | `undefined` | Sets the value submitted with the form data when this option is selected in the parent multi-select. |
149
+ | `value` _(required)_ | `string | number` | `undefined` | Sets the required option value submitted with the form data when selected. Must be a string or number. |
150
150
 
151
151
  #### Slots
152
152
 
@@ -81,14 +81,14 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
81
81
  | `required` | `boolean` | `false` | Marks the pin code as required so the form cannot be submitted until all fields are filled. |
82
82
  | `state` | `'none'` `'error'` `'success'` | `'none'` | Sets the validation state of the pin code, which controls its visual appearance and feedback message style (`none`, `success`, `error`). |
83
83
  | `type` | `'number'` `'password'` | `'number'` | Controls whether the individual input fields mask their content as password dots (`password`) or show digits (`number`). |
84
- | `value` | `string` | `''` | Sets the current concatenated value of all pin code fields and allows setting the initial value. |
84
+ | `value` | `string | number | null` | `''` | Sets the current concatenated value. Numbers are accepted for programmatic assignment, but user input updates the value as a string. |
85
85
 
86
86
  ### Events
87
87
 
88
88
  | Event | Type | Description |
89
89
  | --- | --- | --- |
90
90
  | `blur` | `CustomEvent<void>` | Emitted when the pin code component loses focus after the user finishes entering characters. |
91
- | `change` | `CustomEvent<PinCodeChangeEventDetail>`<br>`{ value: string; isComplete: boolean }` | Emitted when the pin code value changes as the user types, carrying the new concatenated value in the event detail. |
91
+ | `change` | `CustomEvent<PinCodeChangeEventDetail>`<br>`{ value: string; isComplete: boolean }` | Emitted when the pin code value changes as the user types, carrying `{ value: string; isComplete: boolean }` in the event detail. |
92
92
 
93
93
  ### Controlled properties
94
94
 
@@ -10,7 +10,7 @@ import {
10
10
  import { FormEvent, useState } from 'react';
11
11
 
12
12
  export const Example = () => {
13
- const [form, setForm] = useState({ myRadioGroup: '' });
13
+ const [form, setForm] = useState<{ myRadioGroup: PRadioGroupProps['value'] }>({ myRadioGroup: null });
14
14
  const [lastSubmittedData, setLastSubmittedData] = useState<any>();
15
15
 
16
16
  const onChange = (e: CustomEvent<RadioGroupChangeEventDetail>) => {
@@ -28,7 +28,7 @@ export const Example = () => {
28
28
 
29
29
  const onReset = (e: FormEvent<HTMLFormElement>) => {
30
30
  e.preventDefault();
31
- setForm({ myRadioGroup: '' });
31
+ setForm({ myRadioGroup: null });
32
32
  };
33
33
 
34
34
  return (
@@ -6,7 +6,7 @@ The `p-radio-group` component is a versatile custom form element that enables th
6
6
  >
7
7
  > The `blur` event is emitted whenever the focused `p-radio-group-option` changes — not only when the entire `p-radio-group` loses focus. This means that switching between options within the same group will also trigger a `blur` event for the previously focused option.
8
8
 
9
- Options are slotted using the `p-radio-group-option` component. Each option needs to have an assigned value, which can be passed via an attribute or property and needs to be of type string. Initial selection states can only be achieved using the value property on the `p-radio-group` component. If an option should be visible but not selectable, it can be disabled by using the `disabled` attribute.
9
+ Options are slotted using the `p-radio-group-option` component. Each option needs an assigned `string` or `number` value. The `p-radio-group` value matches an option strictly by type and value, so a number does not match the equivalent string. `undefined` and `null` are reserved for the radio group's unselected state and are not valid option values. Initial selection states can only be achieved using the `value` property on the `p-radio-group` component. If an option should be visible but not selectable, it can be disabled using the `disabled` attribute.
10
10
 
11
11
  A `label` is a caption which informs the user what information a particular form field is asking for. The `p-radio-group` component can be used with or without a label, but it's recommended to keep the label visible for better accessibility whenever possible. When used without a label, it's best practice to provide a descriptive label text for screen readers.
12
12
 
@@ -109,7 +109,7 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
109
109
  | `name` _(required)_ | `string` | `undefined` | Sets the shared name attribute for all radio buttons in the group, grouping them for mutually exclusive selection. |
110
110
  | `required` | `boolean` | `false` | Marks the radio group as required so the form cannot be submitted until one option is selected. |
111
111
  | `state` | `'none'` `'error'` `'success'` | `'none'` | Sets the validation state of the radio group, controlling its visual appearance and feedback message style (`none`, `success`, `error`). |
112
- | `value` | `string` | `''` | Sets the currently selected value that pre-selects the matching radio option and reflects user changes. |
112
+ | `value` | `string | number | null` | `undefined` | Sets the currently selected value that pre-selects the matching radio option and reflects user changes. Matches an option strictly by type and value, meaning string or number only match an option whose value has the same type and equal value. Use undefined or null for no preselection; these values never match an option because every option requires a string or number value. Please note that FormData always serializes values as strings, so when participating in a native (uncontrolled) form a number value is restored as string via formStateRestoreCallback and will no longer strictly match a number-typed option. This limitation only applies to native form state restoration; in controlled forms (where the consumer manages value directly via the change event), the number type is preserved end-to-end. |
113
113
 
114
114
  ### Events
115
115
 
@@ -143,7 +143,7 @@ Allowed parent: `p-radio-group`.
143
143
  | `disabled` | `boolean` | `false` | Prevents this option from being selected and excludes its value from form submissions while it is disabled. |
144
144
  | `label` | `string` | `undefined` | Sets the visible label text displayed next to the radio button that the user reads to identify the option. |
145
145
  | `loading` _(experimental)_ | `boolean` | `false` | @experimental Disables this option and shows a spinner to indicate that this particular option is in a loading state. |
146
- | `value` | `string` | `undefined` | Sets the value submitted with the form data when this radio option is selected within its parent group. |
146
+ | `value` _(required)_ | `string | number` | `undefined` | Sets the required option value. Must be a string or number and is selected when it strictly matches the parent `p-radio-group` value by type and value. |
147
147
 
148
148
  #### Slots
149
149
 
@@ -108,7 +108,7 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
108
108
  | `noWrap` | `boolean` | `false` | Prevents items from wrapping to new rows and renders them in a single horizontally scrollable row instead. |
109
109
  | `required` | `boolean` | `false` | Marks the segmented control as required so the form cannot be submitted until one option is selected. |
110
110
  | `state` | `'none'` `'error'` `'success'` | `'none'` | Sets the validation state of the segmented control, controlling its visual appearance and feedback message style (`none`, `success`, `error`). |
111
- | `value` | `string | number` | `undefined` | Sets the currently selected item's value and pre-selects the matching option when the component renders. |
111
+ | `value` | `string | number | null` | `undefined` | Sets the currently selected item's value and pre-selects the matching option when the component renders. Matches an item strictly by type and value, meaning string or number only match an item whose value has the same type and equal value. Use undefined or null for no preselection. Please note that FormData always serializes values as strings, so when participating in a native (uncontrolled) form a number value is restored as string via formStateRestoreCallback and will no longer strictly match a number-typed item. This limitation only applies to native form state restoration; in controlled forms (where the consumer manages value directly via the change event), the number type is preserved end-to-end. |
112
112
 
113
113
  ### Events
114
114
 
@@ -145,7 +145,7 @@ Allowed parent: `p-segmented-control`.
145
145
  | --- | --- | --- | --- |
146
146
  | `aria` | `SegmentedControlItemAriaAttribute` | `undefined` | Sets ARIA attributes on the item's button element to improve accessibility for screen readers. |
147
147
  | `disabled` | `boolean` | `false` | Prevents this item from being selected and visually dims it; the parent's value will not change to this item's value. |
148
- | `icon` | `''`<br>one of 290 icon names — see [icon names](references/icons.md) | `undefined` | Sets an icon rendered inside the item button using an icon name from the PDS icon library. |
148
+ | `icon` | `''`<br>one of 293 icon names — see [icon names](references/icons.md) | `undefined` | Sets an icon rendered inside the item button using an icon name from the PDS icon library. |
149
149
  | `iconSource` | `string` | `undefined` | Sets a URL to a custom SVG icon for the item button, overriding the built-in icon set. |
150
150
  | `label` | `string` | `undefined` | Sets a visible text label rendered inside the item button, used when no slotted content is provided. |
151
151
  | `value` _(required)_ | `string | number` | `undefined` | Sets the value emitted by the parent `p-segmented-control` when this item is selected. This property is*required**. |
@@ -9,7 +9,7 @@ import {
9
9
  type SelectChangeEventDetail,
10
10
  type SelectToggleEventDetail,
11
11
  } from '@porsche-design-system/components-react';
12
- import { useCallback, useRef, useState } from 'react';
12
+ import { useCallback, useMemo, useRef, useState } from 'react';
13
13
 
14
14
  const useDebounce = <T,>(callback: (value: T) => void, delay = 400) => {
15
15
  const timer = useRef<number | undefined>(undefined);
@@ -19,6 +19,20 @@ const useDebounce = <T,>(callback: (value: T) => void, delay = 400) => {
19
19
  };
20
20
  };
21
21
 
22
+ const getFilterStatusMessage = (
23
+ searchValue: string,
24
+ optionCount: number,
25
+ isLoading: boolean,
26
+ error: string | null
27
+ ): string => {
28
+ if (error) return '';
29
+ if (isLoading) return 'Loading options';
30
+ const term = searchValue.trim();
31
+ if (!term) return '';
32
+ if (optionCount === 0) return 'No results found';
33
+ return optionCount === 1 ? '1 result available' : `${optionCount} results available`;
34
+ };
35
+
22
36
  export const Example = () => {
23
37
  const [value, setValue] = useState<PSelectProps['value']>(undefined);
24
38
  const [options, setOptions] = useState<{ value: string; label: string }[]>([]);
@@ -83,6 +97,11 @@ export const Example = () => {
83
97
  }
84
98
  };
85
99
 
100
+ const filterStatusMessage = useMemo(
101
+ () => getFilterStatusMessage(searchValue, options.length, initialLoading || loading, error),
102
+ [searchValue, options.length, initialLoading, loading, error]
103
+ );
104
+
86
105
  return (
87
106
  <PSelect name="async-search-select" label="Async Search" value={value} onChange={onChange} onToggle={onToggle}>
88
107
  <PInputSearch
@@ -100,6 +119,11 @@ export const Example = () => {
100
119
  onChange={(e: any) => e.stopPropagation()}
101
120
  />
102
121
 
122
+ {/* Persistent status announcer for assistive technologies (keep always in the DOM) */}
123
+ <div slot="options-status" className="sr-only" aria-live="polite" aria-atomic="true">
124
+ {filterStatusMessage}
125
+ </div>
126
+
103
127
  {/* Initial skeleton loading */}
104
128
  {initialLoading &&
105
129
  !error &&
@@ -112,15 +136,14 @@ export const Example = () => {
112
136
  </PSelectOption>
113
137
  ))}
114
138
 
115
- {/* No filter results */}
116
- {!initialLoading && options.length === 0 && !error && (
139
+ {/* No filter results (visual only; announcements come from the live region) */}
140
+ {!initialLoading && !loading && options.length === 0 && !error && (
117
141
  <div
118
142
  slot="options-status"
119
143
  className="text-contrast-medium cursor-not-allowed py-static-sm px-[12px]"
120
- role="alert"
144
+ aria-hidden="true"
121
145
  >
122
- <span aria-hidden="true">–</span>
123
- <span className="sr-only">No results found</span>
146
+
124
147
  </div>
125
148
  )}
126
149
 
@@ -141,7 +141,7 @@ Allowed parents: `p-multi-select`, `p-optgroup`.
141
141
  | Property | Type | Default | Description |
142
142
  | --- | --- | --- | --- |
143
143
  | `disabled` | `boolean` | `false` | Disables the option, preventing it from being selected. |
144
- | `value` _(required)_ | `string | number` | `undefined` | Sets the value submitted with the form data when this option is selected in the parent multi-select. |
144
+ | `value` _(required)_ | `string | number` | `undefined` | Sets the required option value submitted with the form data when selected. Must be a string or number. |
145
145
 
146
146
  #### Slots
147
147
 
@@ -50,7 +50,7 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
50
50
  | Property | Type | Default | Description |
51
51
  | --- | --- | --- | --- |
52
52
  | `compact` | `boolean` | `false` | Reduces the tag's padding and height for use in dense layouts where vertical space is limited. |
53
- | `icon` | `'none'`<br>one of 290 icon names — see [icon names](references/icons.md) | `'none'` | Sets the icon displayed inside the tag alongside the label. Use `none` to render the tag without an icon. |
53
+ | `icon` | `'none'`<br>one of 293 icon names — see [icon names](references/icons.md) | `'none'` | Sets the icon displayed inside the tag alongside the label. Use `none` to render the tag without an icon. |
54
54
  | `iconSource` | `string` | `undefined` | Sets a URL to a custom SVG icon, overriding the built-in icon set when a brand-specific icon is needed. |
55
55
  | `variant` | `'primary'` `'secondary'` `'info'` `'info-frosted'` `'warning'` `'warning-frosted'` `'success'` `'success-frosted'` `'error'` `'error-frosted'` | `'secondary'` | Sets the visual style of the tag, which controls its background and text colors (e.g. `primary`, `secondary`, `notification-info`). |
56
56
 
@@ -58,7 +58,7 @@ See [accessibility integration examples](./accessibility.md) for paired anti-pat
58
58
  | --- | --- | --- | --- |
59
59
  | `aria` | `WordmarkAriaAttribute` | `undefined` | Sets ARIA attributes on the anchor element to improve accessibility when the wordmark is used as a link. |
60
60
  | `href` | `string` | `undefined` | When set, wraps the wordmark in an anchor element that navigates to the given URL on click. |
61
- | `size` | `'small'` `'inherit'` | `'small'` | Sets the display size of the Porsche wordmark SVG using predefined PDS size tokens (`small`, `medium`, `large`, `inherit`). |
61
+ | `size` | `'small'` `'inherit'` | `'small'` | Sets the display size of the Porsche wordmark SVG using predefined PDS size tokens. |
62
62
  | `target` | `'_self'` `'_blank'` `'_parent'` `'_top'` `'string'` | `'_self'` | Specifies where to open the linked URL when `href` is set (e.g. `_self`, `_blank`). |
63
63
 
64
64
  ## Examples
@@ -2,4 +2,4 @@
2
2
 
3
3
  Valid values for the icon-typed props across PDS components (e.g. `p-icon` `name`, `p-button` `icon`, `p-link` `icon`, `p-inline-notification` `actionIcon`). Pass one as a string, e.g. `icon="arrow-right"`. Each component reference links here instead of repeating the full list. (`p-flag` `name` uses a separate set of flag names, not listed here.)
4
4
 
5
- `360` `4-wheel-drive` `accessibility` `active-cabin-ventilation` `add` `adjust` `aggregation` `ai-3d-object` `ai-code` `ai-edit` `ai-image` `ai-scale` `ai-sound` `ai-spark` `ai-spark-filled` `ai-text` `ai-video` `arrow-compact-down` `arrow-compact-left` `arrow-compact-right` `arrow-compact-up` `arrow-double-down` `arrow-double-left` `arrow-double-right` `arrow-double-up` `arrow-down` `arrow-down-left` `arrow-down-right` `arrow-first` `arrow-head-down` `arrow-head-left` `arrow-head-right` `arrow-head-up` `arrow-last` `arrow-left` `arrow-right` `arrow-up` `arrow-up-left` `arrow-up-right` `arrows` `attachment` `augmented-reality` `battery-empty` `battery-empty-co2` `battery-empty-fuel` `battery-full` `battery-half` `battery-one-quarter` `battery-three-quarters` `bell` `bookmark` `bookmark-filled` `brain` `broadcast` `cabriolet` `calculator` `calendar` `camera` `car` `car-battery` `card` `charging-active` `charging-network` `charging-state` `charging-station` `chart` `chat` `check` `city` `climate` `climate-control` `clock` `close` `closed-caption` `cloud` `co2-class` `co2-emission` `color-picker` `compare` `compass` `configurate` `copy` `country-road` `coupe` `cubic-capacity` `cut` `delete` `disable` `dislike` `dislike-filled` `document` `door` `download` `drag` `duration` `ear` `edit` `email` `error` `error-filled` `exclamation` `exclamation-filled` `external` `fast-backward` `fast-forward` `file-csv` `file-excel` `filter` `fingerprint` `flag` `flash` `fuel-station` `garage` `genuine-parts` `geo-localization` `gift` `globe` `grid` `grip` `group` `hand` `heart` `heart-filled` `highway` `highway-filled` `history` `home` `horn` `image` `increase` `information` `information-filled` `key` `laptop` `leaf` `leather` `light` `like` `like-filled` `limousine` `linked` `list` `locate` `lock` `lock-open` `logo-apple-carplay` `logo-apple-music` `logo-apple-podcast` `logo-baidu` `logo-delicious` `logo-digg` `logo-facebook` `logo-foursquare` `logo-gmail` `logo-google` `logo-hatena` `logo-instagram` `logo-kaixin` `logo-kakaotalk` `logo-kununu` `logo-linkedin` `logo-naver` `logo-pinterest` `logo-qq` `logo-qq-share` `logo-reddit` `logo-skyrock` `logo-snapchat` `logo-sohu` `logo-spotify` `logo-tecent` `logo-telegram` `logo-tiktok` `logo-tumblr` `logo-twitter` `logo-viber` `logo-vk` `logo-wechat` `logo-weibo` `logo-whatsapp` `logo-x` `logo-xing` `logo-yahoo` `logo-youku` `logo-youtube` `logout` `map` `menu-dots-horizontal` `menu-dots-vertical` `menu-lines` `microphone` `minus` `mobile` `moon` `new-chat` `news` `north-arrow` `oil-can` `online-search` `parking-brake` `parking-light` `paste` `pause` `phone` `pin` `pin-filled` `pivot` `play` `play-filled` `plug` `plus` `preheating` `price-tag` `printer` `purchase` `push-pin` `push-pin-off` `qr` `qr-off` `question` `question-filled` `racing-flag` `radar` `radio` `refresh` `replay` `reset` `return` `road` `roof-closed` `roof-open` `route` `rss` `save` `screen` `search` `seat` `send` `service-technician` `share` `shopping-bag` `shopping-bag-filled` `shopping-cart` `shopping-cart-filled` `sidebar` `sidelights` `skip-backward` `skip-forward` `snowflake` `sort` `stack` `star` `star-filled` `steering-wheel` `stop` `stopwatch` `subtract` `success` `success-filled` `sun` `suv` `switch` `tablet` `tachometer` `theme` `tire` `trigger-finger` `truck` `turismo` `unlinked` `upload` `user` `user-filled` `user-group` `user-manual` `video` `view` `view-off` `volume-off` `volume-up` `warning` `warning-filled` `weather` `weight` `wifi` `work` `wrench` `wrenches` `zoom-in` `zoom-out`
5
+ `360` `4-wheel-drive` `accessibility` `active-cabin-ventilation` `add` `adjust` `aggregation` `ai-3d-object` `ai-code` `ai-edit` `ai-image` `ai-scale` `ai-sound` `ai-spark` `ai-spark-filled` `ai-text` `ai-video` `arrow-compact-down` `arrow-compact-left` `arrow-compact-right` `arrow-compact-up` `arrow-double-down` `arrow-double-left` `arrow-double-right` `arrow-double-up` `arrow-down` `arrow-down-left` `arrow-down-right` `arrow-first` `arrow-head-down` `arrow-head-left` `arrow-head-right` `arrow-head-up` `arrow-last` `arrow-left` `arrow-right` `arrow-up` `arrow-up-left` `arrow-up-right` `arrows` `attachment` `augmented-reality` `battery-empty` `battery-empty-co2` `battery-empty-fuel` `battery-full` `battery-half` `battery-one-quarter` `battery-three-quarters` `bell` `bookmark` `bookmark-filled` `brain` `broadcast` `cabriolet` `calculator` `calendar` `camera` `car` `car-battery` `card` `charging-active` `charging-network` `charging-state` `charging-station` `chart` `chat` `check` `city` `climate` `climate-control` `clock` `close` `closed-caption` `cloud` `co2-class` `co2-emission` `color-picker` `compare` `compass` `configurate` `connect-services` `copy` `country-road` `coupe` `cubic-capacity` `customer-support` `cut` `delete` `disable` `dislike` `dislike-filled` `document` `door` `download` `drag` `duration` `ear` `edit` `email` `error` `error-filled` `exclamation` `exclamation-filled` `external` `fast-backward` `fast-forward` `file-csv` `file-excel` `filter` `fingerprint` `flag` `flash` `fuel-station` `garage` `genuine-parts` `geo-localization` `gift` `globe` `grid` `grip` `group` `hand` `heart` `heart-filled` `highway` `highway-filled` `history` `home` `horn` `image` `increase` `information` `information-filled` `key` `laptop` `leaf` `leather` `light` `like` `like-filled` `limousine` `linked` `list` `locate` `lock` `lock-open` `logo-apple-carplay` `logo-apple-music` `logo-apple-podcast` `logo-baidu` `logo-delicious` `logo-digg` `logo-facebook` `logo-foursquare` `logo-gmail` `logo-google` `logo-hatena` `logo-instagram` `logo-kaixin` `logo-kakaotalk` `logo-kununu` `logo-linkedin` `logo-naver` `logo-pinterest` `logo-qq` `logo-qq-share` `logo-reddit` `logo-skyrock` `logo-snapchat` `logo-sohu` `logo-spotify` `logo-tecent` `logo-telegram` `logo-tiktok` `logo-tumblr` `logo-twitter` `logo-viber` `logo-vk` `logo-wechat` `logo-weibo` `logo-whatsapp` `logo-x` `logo-xing` `logo-yahoo` `logo-youku` `logo-youtube` `logout` `map` `menu-dots-horizontal` `menu-dots-vertical` `menu-lines` `microphone` `minus` `mobile` `moon` `new-chat` `news` `north-arrow` `oil-can` `online-search` `parking-brake` `parking-light` `paste` `pause` `phone` `pin` `pin-filled` `pivot` `play` `play-filled` `plug` `plus` `preheating` `price-tag` `printer` `purchase` `push-pin` `push-pin-off` `qr` `qr-off` `question` `question-filled` `racing-flag` `radar` `radio` `refresh` `replay` `reset` `return` `road` `roof-closed` `roof-open` `route` `rss` `save` `screen` `search` `seat` `send` `service-technician` `share` `shopping-bag` `shopping-bag-filled` `shopping-cart` `shopping-cart-filled` `shopping-cart-off` `sidebar` `sidelights` `skip-backward` `skip-forward` `snowflake` `sort` `stack` `star` `star-filled` `steering-wheel` `stop` `stopwatch` `subtract` `success` `success-filled` `sun` `suv` `switch` `tablet` `tachometer` `theme` `tire` `trigger-finger` `truck` `turismo` `unlinked` `upload` `user` `user-filled` `user-group` `user-manual` `video` `view` `view-off` `volume-off` `volume-up` `warning` `warning-filled` `weather` `weight` `wifi` `work` `wrench` `wrenches` `zoom-in` `zoom-out`