@porsche-design-system/components-react 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -14,6 +14,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
14
14
 
15
15
  ## [Unreleased]
16
16
 
17
+ ## [4.1.0] - 2026-05-06
18
+
19
+ ## [4.1.0-rc.0] - 2026-05-05
20
+
21
+ ### Added
22
+
23
+ - `Input Search`: `aria` prop provides additional context for screen reader
24
+ ([#4321](https://github.com/porsche-design-system/porsche-design-system/pull/4321))
25
+
26
+ ### Fixed
27
+
28
+ - `Tabs Bar`: scrolling page down when below the fold
29
+ ([#4392](https://github.com/porsche-design-system/porsche-design-system/pull/4392))
30
+
17
31
  ## [4.0.0] - 2026-04-29
18
32
 
19
33
  ## [4.0.0-rc.2] - 2026-04-28
@@ -39,6 +53,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
39
53
 
40
54
  ### Fixed
41
55
 
56
+ - `Multi Select`, `Select`: not correctly updating when options within `p-optgroup` change dynamically
57
+ ([#4278](https://github.com/porsche-design-system/porsche-design-system/pull/4279))
42
58
  - `Flyout`, `Modal`, `Sheet`: `overflow: clip` causing issues
43
59
  ([#4296](https://github.com/porsche-design-system/porsche-design-system/pull/4296))
44
60
  - `Link Pure`: focus border of slotted anchor
@@ -6,16 +6,16 @@ var react = require('react');
6
6
  var hooks = require('../../hooks.cjs');
7
7
  var utils = require('../../utils.cjs');
8
8
 
9
- const PInputSearch = /*#__PURE__*/ react.forwardRef(({ autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, ...rest }, ref) => {
9
+ const PInputSearch = /*#__PURE__*/ react.forwardRef(({ aria, autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, ...rest }, ref) => {
10
10
  const elementRef = react.useRef(undefined);
11
11
  hooks.useEventCallback(elementRef, 'blur', onBlur);
12
12
  hooks.useEventCallback(elementRef, 'change', onChange);
13
13
  hooks.useEventCallback(elementRef, 'input', onInput);
14
14
  const WebComponentTag = hooks.usePrefix('p-input-search');
15
- const propsToSync = [autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
15
+ const propsToSync = [aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
16
16
  hooks.useBrowserLayoutEffect(() => {
17
17
  const { current } = elementRef;
18
- ['autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
18
+ ['aria', 'autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
19
19
  }, propsToSync);
20
20
  const props = {
21
21
  ...rest,
@@ -1,6 +1,10 @@
1
1
  import type { BaseProps } from '../../BaseProps';
2
- import type { BreakpointCustomizable, InputSearchBlurEventDetail, InputSearchChangeEventDetail, InputSearchInputEventDetail, InputSearchState } from '../types';
2
+ import type { SelectedAriaAttributes, InputSearchAriaAttribute, BreakpointCustomizable, InputSearchBlurEventDetail, InputSearchChangeEventDetail, InputSearchInputEventDetail, InputSearchState } from '../types';
3
3
  export type PInputSearchProps = BaseProps & {
4
+ /**
5
+ * Additional ARIA attributes for the native search input (e.g. `role="combobox"`, `aria-expanded`).
6
+ */
7
+ aria?: SelectedAriaAttributes<InputSearchAriaAttribute>;
4
8
  /**
5
9
  * Provides a hint to the browser about what type of data the field expects, which can assist with autofill features (e.g., autocomplete='on').
6
10
  */
@@ -105,6 +109,10 @@ export type PInputSearchProps = BaseProps & {
105
109
  value?: string;
106
110
  };
107
111
  export declare const PInputSearch: import("react").ForwardRefExoticComponent<Omit<import("react").DOMAttributes<{}>, "onChange" | "onInput" | "onToggle"> & Pick<import("react").HTMLAttributes<{}>, "suppressHydrationWarning" | "autoFocus" | "className" | "dir" | "hidden" | "id" | "inert" | "lang" | "slot" | "style" | "tabIndex" | "title" | "translate" | "role"> & {
112
+ /**
113
+ * Additional ARIA attributes for the native search input (e.g. `role="combobox"`, `aria-expanded`).
114
+ */
115
+ aria?: SelectedAriaAttributes<InputSearchAriaAttribute>;
108
116
  /**
109
117
  * Provides a hint to the browser about what type of data the field expects, which can assist with autofill features (e.g., autocomplete='on').
110
118
  */
@@ -4,16 +4,16 @@ import { forwardRef, useRef } from 'react';
4
4
  import { useEventCallback, usePrefix, useBrowserLayoutEffect, useMergedClass } from '../../hooks.mjs';
5
5
  import { syncRef } from '../../utils.mjs';
6
6
 
7
- const PInputSearch = /*#__PURE__*/ forwardRef(({ autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, ...rest }, ref) => {
7
+ const PInputSearch = /*#__PURE__*/ forwardRef(({ aria, autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, ...rest }, ref) => {
8
8
  const elementRef = useRef(undefined);
9
9
  useEventCallback(elementRef, 'blur', onBlur);
10
10
  useEventCallback(elementRef, 'change', onChange);
11
11
  useEventCallback(elementRef, 'input', onInput);
12
12
  const WebComponentTag = usePrefix('p-input-search');
13
- const propsToSync = [autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
13
+ const propsToSync = [aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
14
14
  useBrowserLayoutEffect(() => {
15
15
  const { current } = elementRef;
16
- ['autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
16
+ ['aria', 'autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
17
17
  }, propsToSync);
18
18
  const props = {
19
19
  ...rest,
@@ -1137,6 +1137,15 @@ export type InputSearchState = FormState;
1137
1137
  export type InputSearchChangeEventDetail = Event;
1138
1138
  export type InputSearchBlurEventDetail = Event;
1139
1139
  export type InputSearchInputEventDetail = InputEvent;
1140
+ declare const INPUT_SEARCH_ARIA_ATTRIBUTES: readonly [
1141
+ "role",
1142
+ "aria-autocomplete",
1143
+ "aria-controls",
1144
+ "aria-expanded",
1145
+ "aria-haspopup",
1146
+ "aria-label"
1147
+ ];
1148
+ export type InputSearchAriaAttribute = (typeof INPUT_SEARCH_ARIA_ATTRIBUTES)[number];
1140
1149
  export type InputTelState = FormState;
1141
1150
  export type InputTelChangeEventDetail = Event;
1142
1151
  export type InputTelBlurEventDetail = Event;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porsche-design-system/components-react",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "Porsche Design System is a component library designed to help developers create the best experience for software or services distributed by Dr. Ing. h.c. F. Porsche AG.",
5
5
  "keywords": [
6
6
  "porsche",
@@ -21,7 +21,7 @@
21
21
  "url": "https://github.com/porsche-design-system/porsche-design-system"
22
22
  },
23
23
  "dependencies": {
24
- "@porsche-design-system/components-js": "4.0.0"
24
+ "@porsche-design-system/components-js": "4.1.0"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "ag-grid-community": ">= 35.0.0 <36.0.0",
@@ -3775,7 +3775,7 @@ const OPTION_LIST_SAFE_ZONE = 6;
3775
3775
 
3776
3776
  const getCDNBaseURL = () => global.PORSCHE_DESIGN_SYSTEM_CDN_URL + "/porsche-design-system";
3777
3777
 
3778
- const prefix = `[Porsche Design System v${"4.0.0"}]` // this part isn't covered by unit tests
3778
+ const prefix = `[Porsche Design System v${"4.1.0"}]` // this part isn't covered by unit tests
3779
3779
  ;
3780
3780
  const consoleError = (...messages) => {
3781
3781
  console.error(prefix, ...messages);
@@ -3454,7 +3454,7 @@ const hasShowPickerSupport = () => (hasDocument &&
3454
3454
  'showPicker' in HTMLInputElement.prototype &&
3455
3455
  CSS.supports('selector(::-webkit-calendar-picker-indicator)'));
3456
3456
 
3457
- const prefix = `[Porsche Design System v${"4.0.0"}]` // this part isn't covered by unit tests
3457
+ const prefix = `[Porsche Design System v${"4.1.0"}]` // this part isn't covered by unit tests
3458
3458
  ;
3459
3459
  const consoleError$1 = (...messages) => {
3460
3460
  console.error(prefix, ...messages);
@@ -3605,6 +3605,11 @@ const isInfinitePagination = (amountOfPages) => {
3605
3605
  return amountOfPages > INFINITE_BULLET_THRESHOLD;
3606
3606
  };
3607
3607
 
3608
+ const mergeInputNativeAria = (passthrough, builtIn) => ({
3609
+ ...(parseAndGetAriaAttributes(passthrough) ?? {}),
3610
+ ...builtIn,
3611
+ });
3612
+
3608
3613
  const labelId = 'label';
3609
3614
  const descriptionId = 'description';
3610
3615
 
@@ -3987,6 +3992,7 @@ exports.isSortable = isSortable;
3987
3992
  exports.isStateCompleteOrWarning = isStateCompleteOrWarning;
3988
3993
  exports.isUrl = isUrl;
3989
3994
  exports.labelId = labelId;
3995
+ exports.mergeInputNativeAria = mergeInputNativeAria;
3990
3996
  exports.observedNodesMap = observedNodesMap;
3991
3997
  exports.parseAndGetAriaAttributes = parseAndGetAriaAttributes;
3992
3998
  exports.parseJSONAttribute = parseJSONAttribute;
@@ -7,23 +7,23 @@ var hooks = require('../../hooks.cjs');
7
7
  var utils = require('../../utils.cjs');
8
8
  var inputSearch = require('../dsr-components/input-search.cjs');
9
9
 
10
- const PInputSearch = /*#__PURE__*/ react.forwardRef(({ autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, children, ...rest }, ref) => {
10
+ const PInputSearch = /*#__PURE__*/ react.forwardRef(({ aria, autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, children, ...rest }, ref) => {
11
11
  const elementRef = react.useRef(undefined);
12
12
  hooks.useEventCallback(elementRef, 'blur', onBlur);
13
13
  hooks.useEventCallback(elementRef, 'change', onChange);
14
14
  hooks.useEventCallback(elementRef, 'input', onInput);
15
15
  const WebComponentTag = hooks.usePrefix('p-input-search');
16
- const propsToSync = [autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
16
+ const propsToSync = [aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
17
17
  hooks.useBrowserLayoutEffect(() => {
18
18
  const { current } = elementRef;
19
- ['autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
19
+ ['aria', 'autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
20
20
  }, propsToSync);
21
21
  const props = {
22
22
  ...rest,
23
23
  // @ts-ignore
24
24
  ...(!process.browser
25
25
  ? {
26
- children: (jsxRuntime.jsx(inputSearch.DSRInputSearch, { autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value, children })),
26
+ children: (jsxRuntime.jsx(inputSearch.DSRInputSearch, { aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value, children })),
27
27
  }
28
28
  : {
29
29
  children,
@@ -19,11 +19,17 @@ id, label: label$1, description, loading, initialLoading, required, disabled, st
19
19
  // onBlur,
20
20
  // onKeyDown,
21
21
  // refElement,
22
- start, end, }) => {
22
+ start, end, aria, }) => {
23
23
  const { namedSlotChildren } = splitChildren.splitChildren(children);
24
24
  const inputDescriptionId = (description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0) ? utilsEntry.descriptionId : undefined;
25
25
  const inputMessageId = (message || namedSlotChildren.filter(({ props: { slot } }) => slot === 'message').length > 0) && ['success', 'error'].includes(state) ? stateMessage.messageId : undefined;
26
- return (jsxRuntime.jsxs("div", { className: "root", children: [jsxRuntime.jsx(label.Label, { hasLabel: !!label$1 || namedSlotChildren.filter(({ props: { slot } }) => slot === 'label').length > 0, hasDescription: !!description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0, host: null, label: label$1, description: description, htmlFor: id, isRequired: required, isLoading: loading, isDisabled: disabled }), jsxRuntime.jsxs("div", { className: "wrapper", children: [jsxRuntime.jsx("slot", { name: "start" }), start, jsxRuntime.jsx("input", { "aria-describedby": utilsEntry.setAriaIDREF(loading && loadingMessage.loadingId, inputMessageId, inputDescriptionId), "aria-invalid": state === 'error' ? 'true' : null, "aria-disabled": disabled || loading ? 'true' : null, "aria-readonly": readOnly ? 'true' : null, id: id, name: name, form: form, type: type, required: required, placeholder: placeholder || null, maxLength: maxLength, minLength: minLength, spellCheck: spellCheck, max: max, min: min, step: step, defaultValue: value, readOnly: readOnly, autoComplete: autoComplete, disabled: disabled, pattern: pattern, multiple: multiple, dir: "auto" // This overwrites the default: let the browser now decide in which direction the value should be placed.
26
+ const inputAria = utilsEntry.mergeInputNativeAria(aria, {
27
+ 'aria-describedby': utilsEntry.setAriaIDREF(loading && loadingMessage.loadingId, inputMessageId, inputDescriptionId),
28
+ 'aria-invalid': state === 'error' ? 'true' : null,
29
+ 'aria-disabled': disabled || loading ? 'true' : null,
30
+ 'aria-readonly': readOnly ? 'true' : null,
31
+ });
32
+ return (jsxRuntime.jsxs("div", { className: "root", children: [jsxRuntime.jsx(label.Label, { hasLabel: !!label$1 || namedSlotChildren.filter(({ props: { slot } }) => slot === 'label').length > 0, hasDescription: !!description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0, host: null, label: label$1, description: description, htmlFor: id, isRequired: required, isLoading: loading, isDisabled: disabled }), jsxRuntime.jsxs("div", { className: "wrapper", children: [jsxRuntime.jsx("slot", { name: "start" }), start, jsxRuntime.jsx("input", { ...inputAria, id: id, name: name, form: form, type: type, required: required, placeholder: placeholder || null, maxLength: maxLength, minLength: minLength, spellCheck: spellCheck, max: max, min: min, step: step, defaultValue: value, readOnly: readOnly, autoComplete: autoComplete, disabled: disabled, pattern: pattern, multiple: multiple, dir: "auto" // This overwrites the default: let the browser now decide in which direction the value should be placed.
27
33
  }), end, jsxRuntime.jsx("slot", { name: "end" }), loading && jsxRuntime.jsx(spinner_wrapper.PSpinner, { "aria-hidden": "true" })] }), jsxRuntime.jsx(stateMessage.StateMessage, { hasMessage: (message || namedSlotChildren.filter(({ props: { slot } }) => slot === 'message').length > 0) && ['success', 'error'].includes(state), state: state, message: message, host: null }), jsxRuntime.jsx(loadingMessage.LoadingMessage, { loading: loading, initialLoading: initialLoading })] }));
28
34
  };
29
35
 
@@ -38,7 +38,7 @@ class DSRInputSearch extends react.Component {
38
38
  render() {
39
39
  splitChildren.splitChildren(this.props.children);
40
40
  const style = minifyCss.minifyCss(stylesEntry.getInputSearchCss(this.props.disabled, this.props.loading, this.props.hideLabel, this.props.state, this.props.compact, this.props.readOnly, this.props.clear));
41
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", shadowrootdelegatesfocus: "true", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxRuntime.jsx(inputBase.InputBase, { children: this.props.children, host: null, label: this.props.label, description: this.props.description, id: "input-search", name: this.props.name, form: this.props.form, type: "search", required: this.props.required, placeholder: this.props.placeholder, maxLength: this.props.maxLength, minLength: this.props.minLength, value: this.props.value, readOnly: this.props.readOnly, autoComplete: this.props.autoComplete, disabled: this.props.disabled, state: this.props.state, message: this.props.message, loading: this.props.loading, initialLoading: this.props.initialLoading, ...(this.props.indicator && {
41
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("template", { shadowroot: "open", shadowrootmode: "open", shadowrootdelegatesfocus: "true", children: [jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsxRuntime.jsx(inputBase.InputBase, { children: this.props.children, host: null, label: this.props.label, description: this.props.description, id: "input-search", name: this.props.name, form: this.props.form, type: "search", required: this.props.required, placeholder: this.props.placeholder, maxLength: this.props.maxLength, minLength: this.props.minLength, value: this.props.value, readOnly: this.props.readOnly, autoComplete: this.props.autoComplete, disabled: this.props.disabled, state: this.props.state, message: this.props.message, loading: this.props.loading, initialLoading: this.props.initialLoading, aria: this.props.aria, ...(this.props.indicator && {
42
42
  start: jsxRuntime.jsx(icon_wrapper.PIcon, { "aria-hidden": "true", name: "search", color: "contrast-medium" }),
43
43
  }), ...(this.props.clear && {
44
44
  end: (jsxRuntime.jsx(buttonPure_wrapper.PButtonPure, { tabIndex: -1, hideLabel: true, className: "button", type: "button", icon: "close", hidden: !this.props.isClearable, disabled: this.props.readOnly || this.props.disabled, children: "Clear field" })),
@@ -3773,7 +3773,7 @@ const OPTION_LIST_SAFE_ZONE = 6;
3773
3773
 
3774
3774
  const getCDNBaseURL = () => global.PORSCHE_DESIGN_SYSTEM_CDN_URL + "/porsche-design-system";
3775
3775
 
3776
- const prefix = `[Porsche Design System v${"4.0.0"}]` // this part isn't covered by unit tests
3776
+ const prefix = `[Porsche Design System v${"4.1.0"}]` // this part isn't covered by unit tests
3777
3777
  ;
3778
3778
  const consoleError = (...messages) => {
3779
3779
  console.error(prefix, ...messages);
@@ -3452,7 +3452,7 @@ const hasShowPickerSupport = () => (hasDocument &&
3452
3452
  'showPicker' in HTMLInputElement.prototype &&
3453
3453
  CSS.supports('selector(::-webkit-calendar-picker-indicator)'));
3454
3454
 
3455
- const prefix = `[Porsche Design System v${"4.0.0"}]` // this part isn't covered by unit tests
3455
+ const prefix = `[Porsche Design System v${"4.1.0"}]` // this part isn't covered by unit tests
3456
3456
  ;
3457
3457
  const consoleError$1 = (...messages) => {
3458
3458
  console.error(prefix, ...messages);
@@ -3603,6 +3603,11 @@ const isInfinitePagination = (amountOfPages) => {
3603
3603
  return amountOfPages > INFINITE_BULLET_THRESHOLD;
3604
3604
  };
3605
3605
 
3606
+ const mergeInputNativeAria = (passthrough, builtIn) => ({
3607
+ ...(parseAndGetAriaAttributes(passthrough) ?? {}),
3608
+ ...builtIn,
3609
+ });
3610
+
3606
3611
  const labelId = 'label';
3607
3612
  const descriptionId = 'description';
3608
3613
 
@@ -3930,4 +3935,4 @@ const getTextTagType = (host, tag) => {
3930
3935
  return tag;
3931
3936
  };
3932
3937
 
3933
- export { DISPLAY_TAGS, HEADING_TAGS, ItemType, TEXT_TAGS, anchorSlot, attributeMutationMap, buildCrestImgSrc, buildCrestSrcSet, buildFlagUrl, buildIconUrl, consoleError$1 as consoleError, createPaginationItems, createRange, crestSize, descriptionId, displaySizeToTagMap, getBannerAriaAttributes, getButtonAriaAttributes, getButtonBaseAriaAttributes, getButtonPureAriaAttributes, getCDNBaseURL, getComboboxAriaAttributes, getCurrentActivePage, getDirectChildHTMLElement, getDisplayTagType, getFieldsetAriaAttributes, getHTMLElement, getHasNativePopoverSupport, getHeadingTagType, getInlineNotificationAriaAttributes, getListboxAriaAttributes, getSanitizedActiveTabIndex, getSegmentedControlItemAriaAttributes, getStepperHorizontalIconName, getSvgUrl, getSwitchButtonAriaAttributes, getTagName, getTagNameWithoutPrefix, getTextTagType, getTotalPages, hasDocument, hasShowPickerSupport, hasSpecificDirectChildTag, hasVisibleIcon, hasWindow$1 as hasWindow, headerSlot, internalDrilldown, isCurrentInput, isDisabledOrLoading, isElementOfKind, isInfinitePagination, isListTypeOrdered, isSortable, isStateCompleteOrWarning, isUrl, labelId, observedNodesMap, parseAndGetAriaAttributes, parseJSONAttribute, setAriaIDREF, supportsConstructableStylesheets, supportsNativePopover, tempDiv, tempIcon, tempLabel, traverseTreeAndUpdateState, updateDrilldownItemState };
3938
+ export { DISPLAY_TAGS, HEADING_TAGS, ItemType, TEXT_TAGS, anchorSlot, attributeMutationMap, buildCrestImgSrc, buildCrestSrcSet, buildFlagUrl, buildIconUrl, consoleError$1 as consoleError, createPaginationItems, createRange, crestSize, descriptionId, displaySizeToTagMap, getBannerAriaAttributes, getButtonAriaAttributes, getButtonBaseAriaAttributes, getButtonPureAriaAttributes, getCDNBaseURL, getComboboxAriaAttributes, getCurrentActivePage, getDirectChildHTMLElement, getDisplayTagType, getFieldsetAriaAttributes, getHTMLElement, getHasNativePopoverSupport, getHeadingTagType, getInlineNotificationAriaAttributes, getListboxAriaAttributes, getSanitizedActiveTabIndex, getSegmentedControlItemAriaAttributes, getStepperHorizontalIconName, getSvgUrl, getSwitchButtonAriaAttributes, getTagName, getTagNameWithoutPrefix, getTextTagType, getTotalPages, hasDocument, hasShowPickerSupport, hasSpecificDirectChildTag, hasVisibleIcon, hasWindow$1 as hasWindow, headerSlot, internalDrilldown, isCurrentInput, isDisabledOrLoading, isElementOfKind, isInfinitePagination, isListTypeOrdered, isSortable, isStateCompleteOrWarning, isUrl, labelId, mergeInputNativeAria, observedNodesMap, parseAndGetAriaAttributes, parseJSONAttribute, setAriaIDREF, supportsConstructableStylesheets, supportsNativePopover, tempDiv, tempIcon, tempLabel, traverseTreeAndUpdateState, updateDrilldownItemState };
@@ -5,23 +5,23 @@ import { useEventCallback, usePrefix, useBrowserLayoutEffect, useMergedClass } f
5
5
  import { syncRef } from '../../utils.mjs';
6
6
  import { DSRInputSearch } from '../dsr-components/input-search.mjs';
7
7
 
8
- const PInputSearch = /*#__PURE__*/ forwardRef(({ autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, children, ...rest }, ref) => {
8
+ const PInputSearch = /*#__PURE__*/ forwardRef(({ aria, autoComplete, clear = false, compact = false, description = '', disabled = false, form, hideLabel = false, indicator = false, label = '', loading = false, maxLength, message = '', minLength, name, onBlur, onChange, onInput, placeholder = '', readOnly = false, required = false, state = 'none', value = '', className, children, ...rest }, ref) => {
9
9
  const elementRef = useRef(undefined);
10
10
  useEventCallback(elementRef, 'blur', onBlur);
11
11
  useEventCallback(elementRef, 'change', onChange);
12
12
  useEventCallback(elementRef, 'input', onInput);
13
13
  const WebComponentTag = usePrefix('p-input-search');
14
- const propsToSync = [autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
14
+ const propsToSync = [aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value];
15
15
  useBrowserLayoutEffect(() => {
16
16
  const { current } = elementRef;
17
- ['autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
17
+ ['aria', 'autoComplete', 'clear', 'compact', 'description', 'disabled', 'form', 'hideLabel', 'indicator', 'label', 'loading', 'maxLength', 'message', 'minLength', 'name', 'placeholder', 'readOnly', 'required', 'state', 'value'].forEach((propName, i) => (current[propName] = propsToSync[i]));
18
18
  }, propsToSync);
19
19
  const props = {
20
20
  ...rest,
21
21
  // @ts-ignore
22
22
  ...(!process.browser
23
23
  ? {
24
- children: (jsx(DSRInputSearch, { autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value, children })),
24
+ children: (jsx(DSRInputSearch, { aria, autoComplete, clear, compact, description, disabled, form, hideLabel, indicator, label, loading, maxLength, message, minLength, name, placeholder, readOnly, required, state, value, children })),
25
25
  }
26
26
  : {
27
27
  children,
@@ -2,7 +2,7 @@ import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { splitChildren } from '../../splitChildren.mjs';
3
3
  import 'react';
4
4
  import '../../provider.mjs';
5
- import { descriptionId, setAriaIDREF } from '../../../../../../components/dist/utils/esm/utils-entry.mjs';
5
+ import { descriptionId, mergeInputNativeAria, setAriaIDREF } from '../../../../../../components/dist/utils/esm/utils-entry.mjs';
6
6
  import { loadingId, LoadingMessage } from './loading-message.mjs';
7
7
  import { Label } from './label.mjs';
8
8
  import { messageId, StateMessage } from './state-message.mjs';
@@ -17,11 +17,17 @@ id, label, description, loading, initialLoading, required, disabled, state, mess
17
17
  // onBlur,
18
18
  // onKeyDown,
19
19
  // refElement,
20
- start, end, }) => {
20
+ start, end, aria, }) => {
21
21
  const { namedSlotChildren } = splitChildren(children);
22
22
  const inputDescriptionId = (description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0) ? descriptionId : undefined;
23
23
  const inputMessageId = (message || namedSlotChildren.filter(({ props: { slot } }) => slot === 'message').length > 0) && ['success', 'error'].includes(state) ? messageId : undefined;
24
- return (jsxs("div", { className: "root", children: [jsx(Label, { hasLabel: !!label || namedSlotChildren.filter(({ props: { slot } }) => slot === 'label').length > 0, hasDescription: !!description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0, host: null, label: label, description: description, htmlFor: id, isRequired: required, isLoading: loading, isDisabled: disabled }), jsxs("div", { className: "wrapper", children: [jsx("slot", { name: "start" }), start, jsx("input", { "aria-describedby": setAriaIDREF(loading && loadingId, inputMessageId, inputDescriptionId), "aria-invalid": state === 'error' ? 'true' : null, "aria-disabled": disabled || loading ? 'true' : null, "aria-readonly": readOnly ? 'true' : null, id: id, name: name, form: form, type: type, required: required, placeholder: placeholder || null, maxLength: maxLength, minLength: minLength, spellCheck: spellCheck, max: max, min: min, step: step, defaultValue: value, readOnly: readOnly, autoComplete: autoComplete, disabled: disabled, pattern: pattern, multiple: multiple, dir: "auto" // This overwrites the default: let the browser now decide in which direction the value should be placed.
24
+ const inputAria = mergeInputNativeAria(aria, {
25
+ 'aria-describedby': setAriaIDREF(loading && loadingId, inputMessageId, inputDescriptionId),
26
+ 'aria-invalid': state === 'error' ? 'true' : null,
27
+ 'aria-disabled': disabled || loading ? 'true' : null,
28
+ 'aria-readonly': readOnly ? 'true' : null,
29
+ });
30
+ return (jsxs("div", { className: "root", children: [jsx(Label, { hasLabel: !!label || namedSlotChildren.filter(({ props: { slot } }) => slot === 'label').length > 0, hasDescription: !!description || namedSlotChildren.filter(({ props: { slot } }) => slot === 'description').length > 0, host: null, label: label, description: description, htmlFor: id, isRequired: required, isLoading: loading, isDisabled: disabled }), jsxs("div", { className: "wrapper", children: [jsx("slot", { name: "start" }), start, jsx("input", { ...inputAria, id: id, name: name, form: form, type: type, required: required, placeholder: placeholder || null, maxLength: maxLength, minLength: minLength, spellCheck: spellCheck, max: max, min: min, step: step, defaultValue: value, readOnly: readOnly, autoComplete: autoComplete, disabled: disabled, pattern: pattern, multiple: multiple, dir: "auto" // This overwrites the default: let the browser now decide in which direction the value should be placed.
25
31
  }), end, jsx("slot", { name: "end" }), loading && jsx(PSpinner, { "aria-hidden": "true" })] }), jsx(StateMessage, { hasMessage: (message || namedSlotChildren.filter(({ props: { slot } }) => slot === 'message').length > 0) && ['success', 'error'].includes(state), state: state, message: message, host: null }), jsx(LoadingMessage, { loading: loading, initialLoading: initialLoading })] }));
26
32
  };
27
33
 
@@ -36,7 +36,7 @@ class DSRInputSearch extends Component {
36
36
  render() {
37
37
  splitChildren(this.props.children);
38
38
  const style = minifyCss(getComponentCss$M(this.props.disabled, this.props.loading, this.props.hideLabel, this.props.state, this.props.compact, this.props.readOnly, this.props.clear));
39
- return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", shadowrootdelegatesfocus: "true", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsx(InputBase, { children: this.props.children, host: null, label: this.props.label, description: this.props.description, id: "input-search", name: this.props.name, form: this.props.form, type: "search", required: this.props.required, placeholder: this.props.placeholder, maxLength: this.props.maxLength, minLength: this.props.minLength, value: this.props.value, readOnly: this.props.readOnly, autoComplete: this.props.autoComplete, disabled: this.props.disabled, state: this.props.state, message: this.props.message, loading: this.props.loading, initialLoading: this.props.initialLoading, ...(this.props.indicator && {
39
+ return (jsxs(Fragment, { children: [jsxs("template", { shadowroot: "open", shadowrootmode: "open", shadowrootdelegatesfocus: "true", children: [jsx("style", { dangerouslySetInnerHTML: { __html: style } }), jsx(InputBase, { children: this.props.children, host: null, label: this.props.label, description: this.props.description, id: "input-search", name: this.props.name, form: this.props.form, type: "search", required: this.props.required, placeholder: this.props.placeholder, maxLength: this.props.maxLength, minLength: this.props.minLength, value: this.props.value, readOnly: this.props.readOnly, autoComplete: this.props.autoComplete, disabled: this.props.disabled, state: this.props.state, message: this.props.message, loading: this.props.loading, initialLoading: this.props.initialLoading, aria: this.props.aria, ...(this.props.indicator && {
40
40
  start: jsx(PIcon, { "aria-hidden": "true", name: "search", color: "contrast-medium" }),
41
41
  }), ...(this.props.clear && {
42
42
  end: (jsx(PButtonPure, { tabIndex: -1, hideLabel: true, className: "button", type: "button", icon: "close", hidden: !this.props.isClearable, disabled: this.props.readOnly || this.props.disabled, children: "Clear field" })),
@@ -1,6 +1,10 @@
1
1
  import type { BaseProps } from '../../BaseProps';
2
- import type { BreakpointCustomizable, InputSearchBlurEventDetail, InputSearchChangeEventDetail, InputSearchInputEventDetail, InputSearchState } from '../types';
2
+ import type { SelectedAriaAttributes, InputSearchAriaAttribute, BreakpointCustomizable, InputSearchBlurEventDetail, InputSearchChangeEventDetail, InputSearchInputEventDetail, InputSearchState } from '../types';
3
3
  export type PInputSearchProps = BaseProps & {
4
+ /**
5
+ * Additional ARIA attributes for the native search input (e.g. `role="combobox"`, `aria-expanded`).
6
+ */
7
+ aria?: SelectedAriaAttributes<InputSearchAriaAttribute>;
4
8
  /**
5
9
  * Provides a hint to the browser about what type of data the field expects, which can assist with autofill features (e.g., autocomplete='on').
6
10
  */
@@ -105,6 +109,10 @@ export type PInputSearchProps = BaseProps & {
105
109
  value?: string;
106
110
  };
107
111
  export declare const PInputSearch: import("react").ForwardRefExoticComponent<Omit<import("react").DOMAttributes<{}>, "onChange" | "onInput" | "onToggle"> & Pick<import("react").HTMLAttributes<{}>, "suppressHydrationWarning" | "autoFocus" | "className" | "dir" | "hidden" | "id" | "inert" | "lang" | "slot" | "style" | "tabIndex" | "title" | "translate" | "role"> & {
112
+ /**
113
+ * Additional ARIA attributes for the native search input (e.g. `role="combobox"`, `aria-expanded`).
114
+ */
115
+ aria?: SelectedAriaAttributes<InputSearchAriaAttribute>;
108
116
  /**
109
117
  * Provides a hint to the browser about what type of data the field expects, which can assist with autofill features (e.g., autocomplete='on').
110
118
  */
@@ -1,6 +1,7 @@
1
+ import type { AriaAttributes } from '../types';
1
2
  import type { FC } from 'react';
2
3
  import type { JSX } from 'react';
3
- import type { InputBaseState } from '@porsche-design-system/components/dist/utils';
4
+ import { type InputBaseState } from '@porsche-design-system/components/dist/utils';
4
5
  type InputBaseProps = {
5
6
  children?: JSX.Element;
6
7
  host: HTMLElement;
@@ -30,6 +31,7 @@ type InputBaseProps = {
30
31
  spellCheck?: boolean;
31
32
  start?: JSX.Element;
32
33
  end?: JSX.Element;
34
+ aria?: AriaAttributes | string;
33
35
  };
34
36
  export declare const InputBase: FC<InputBaseProps>;
35
37
  export {};
@@ -1137,6 +1137,15 @@ export type InputSearchState = FormState;
1137
1137
  export type InputSearchChangeEventDetail = Event;
1138
1138
  export type InputSearchBlurEventDetail = Event;
1139
1139
  export type InputSearchInputEventDetail = InputEvent;
1140
+ declare const INPUT_SEARCH_ARIA_ATTRIBUTES: readonly [
1141
+ "role",
1142
+ "aria-autocomplete",
1143
+ "aria-controls",
1144
+ "aria-expanded",
1145
+ "aria-haspopup",
1146
+ "aria-label"
1147
+ ];
1148
+ export type InputSearchAriaAttribute = (typeof INPUT_SEARCH_ARIA_ATTRIBUTES)[number];
1140
1149
  export type InputTelState = FormState;
1141
1150
  export type InputTelChangeEventDetail = Event;
1142
1151
  export type InputTelBlurEventDetail = Event;