@porsche-design-system/components-react 4.2.0-rc.3 → 4.2.0-rc.4

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,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0),
14
14
 
15
15
  ## [Unreleased]
16
16
 
17
+ ## [4.2.0-rc.4] - 2026-06-10
18
+
19
+ ### Fixed
20
+
21
+ - `Radio Group`: Prevent clicks on `label-after` slot from bubbling to the host element
22
+ ([#4477](https://github.com/porsche-design-system/porsche-design-system/pull/4477))
23
+ - `Flyout`, `Modal`, `Sheet`: Prevent dialog elements from reserving layout space in closed state when placed within a
24
+ new stacking context ([#4494](https://github.com/porsche-design-system/porsche-design-system/pull/4494))
25
+
17
26
  ## [4.2.0-rc.3] - 2026-06-09
18
27
 
19
28
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@porsche-design-system/components-react",
3
- "version": "4.2.0-rc.3",
3
+ "version": "4.2.0-rc.4",
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.2.0-rc.3"
24
+ "@porsche-design-system/components-js": "4.2.0-rc.4"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "ag-grid-community": ">= 35.0.0 <36.0.0",
@@ -4132,7 +4132,7 @@ const OPTION_LIST_SAFE_ZONE = 6;
4132
4132
 
4133
4133
  const getCDNBaseURL = () => global.PORSCHE_DESIGN_SYSTEM_CDN_URL + "/porsche-design-system";
4134
4134
 
4135
- const prefix = `[Porsche Design System v${"4.2.0-rc.3"}]` // this part isn't covered by unit tests
4135
+ const prefix = `[Porsche Design System v${"4.2.0-rc.4"}]` // this part isn't covered by unit tests
4136
4136
  ;
4137
4137
  const consoleError = (...messages) => {
4138
4138
  console.error(prefix, ...messages);
@@ -5899,15 +5899,20 @@ const getFunctionalComponentRequiredStyles = () => {
5899
5899
  };
5900
5900
 
5901
5901
  const getFunctionalComponentLabelAfterStyles = () => {
5902
+ const labelAfterStyles = {
5903
+ display: 'inline-block',
5904
+ verticalAlign: 'top',
5905
+ '&::slotted(*)': {
5906
+ ...addImportantToEachRule({
5907
+ marginInlineStart: ref(spacingStaticXs),
5908
+ }),
5909
+ },
5910
+ };
5902
5911
  return {
5903
- 'slot[name="label-after"]': {
5904
- display: 'inline-block',
5905
- verticalAlign: 'top',
5906
- '&::slotted(*)': {
5907
- ...addImportantToEachRule({
5908
- marginInlineStart: ref(spacingStaticXs),
5909
- }),
5910
- },
5912
+ 'slot[name="label-after"]': labelAfterStyles,
5913
+ '.label-after': {
5914
+ display: labelAfterStyles.display,
5915
+ verticalAlign: labelAfterStyles.verticalAlign,
5911
5916
  },
5912
5917
  };
5913
5918
  };
@@ -6061,18 +6066,14 @@ const getFunctionalComponentDialogBaseStyles = (isVisible, backdrop = 'blur') =>
6061
6066
  };
6062
6067
  };
6063
6068
  const dialogBackdropResetJssStyle = {
6064
- position: 'fixed', // ua-style
6065
- inset: 0, // ua-style
6066
- margin: 0, // ua-style
6067
- padding: 0, // ua-style
6068
- border: 0, // ua-style
6069
- width: '100dvw', // ua-style
6070
- height: '100dvh', // ua-style
6071
- maxWidth: '100dvw', // ua-style
6072
- maxHeight: '100dvh', // ua-style
6073
- overflow: 'hidden', // ua-style
6074
- display: 'block', // ua-style
6075
- outline: 0, // ua-style (we always expect a focusable element to be within the dialog)
6069
+ all: 'unset',
6070
+ position: 'fixed',
6071
+ inset: 0,
6072
+ maxWidth: '100dvw',
6073
+ maxHeight: '100dvh',
6074
+ overflow: 'hidden',
6075
+ display: 'block',
6076
+ outline: 0, // we always expect a focusable element to be within the dialog
6076
6077
  '&::backdrop': {
6077
6078
  display: 'none', // ua-style (we can't use it atm because it's not animatable in all browsers)
6078
6079
  },
@@ -6081,12 +6082,15 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
6081
6082
  const isBackdropBlur = backdrop === 'blur';
6082
6083
  const duration = isVisible ? 'long' : 'moderate';
6083
6084
  const easing = isVisible ? 'in' : 'out';
6085
+ const delay = ref(cssVariableTransitionDuration, isVisible ? '0s' : motionDurationMap[duration]);
6084
6086
  // as soon as all browsers are supporting `allow-discrete`, visibility transition shouldn't be necessary anymore
6085
- const transition = `visibility 0s linear ${ref(cssVariableTransitionDuration, isVisible ? '0s' : motionDurationMap[duration])}, ${getTransition('background-color', duration, easing)}, ${getTransition('-webkit-backdrop-filter', duration, easing)}, ${getTransition('backdrop-filter', duration, easing)}`;
6087
+ const transition = `visibility 0s linear ${delay}, width 0s linear ${delay}, height 0s linear ${delay}, ${getTransition('background-color', duration, easing)}, ${getTransition('-webkit-backdrop-filter', duration, easing)}, ${getTransition('backdrop-filter', duration, easing)}`;
6086
6088
  return {
6087
6089
  zIndex: 9999999, // fallback for fade out stacking until `overlay` + `allow-discrete` is supported in all browsers. It tries to mimic #top-layer positioning hierarchy.
6088
6090
  ...(isVisible
6089
6091
  ? {
6092
+ width: '100dvw',
6093
+ height: '100dvh',
6090
6094
  visibility: 'inherit',
6091
6095
  pointerEvents: 'auto',
6092
6096
  background: ref(colorBackdrop),
@@ -6096,6 +6100,12 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
6096
6100
  }),
6097
6101
  }
6098
6102
  : {
6103
+ // When the dialog lives inside a new stacking context, it is no longer promoted to the #top-layer while closed.
6104
+ // In that case it can reserve additional space and break the surrounding layout, so width and height are collapsed to zero.
6105
+ // A future improvement could use `transition-behavior: allow-discrete` to toggle between `display: none` and `block`,
6106
+ // but browser support is still insufficient and behaves inconsistently in Safari and Firefox.
6107
+ width: '0px',
6108
+ height: '0px',
6099
6109
  visibility: 'hidden', // element shall not be tabbable with keyboard after fade out transition has finished
6100
6110
  pointerEvents: 'none', // element can't be interacted with mouse
6101
6111
  background: 'transparent',
@@ -3465,7 +3465,7 @@ const hasShowPickerSupport = () => (hasDocument &&
3465
3465
  'showPicker' in HTMLInputElement.prototype &&
3466
3466
  CSS.supports('selector(::-webkit-calendar-picker-indicator)'));
3467
3467
 
3468
- const prefix = `[Porsche Design System v${"4.2.0-rc.3"}]` // this part isn't covered by unit tests
3468
+ const prefix = `[Porsche Design System v${"4.2.0-rc.4"}]` // this part isn't covered by unit tests
3469
3469
  ;
3470
3470
  const consoleError$1 = (...messages) => {
3471
3471
  console.error(prefix, ...messages);
@@ -10,7 +10,7 @@ const Label = ({ hasLabel, hasDescription, children,
10
10
  label, tag, description, htmlFor, isRequired, isLoading, isDisabled, stopClickPropagation, }) => {
11
11
  splitChildren.splitChildren(children);
12
12
  const TagType = tag || 'label';
13
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasLabel && (jsxRuntime.jsxs("div", { className: "label-wrapper", children: [jsxRuntime.jsx(TagType, { className: "label", id: utilsEntry.labelId, "aria-disabled": isLoading || isDisabled ? 'true' : null, htmlFor: htmlFor, children: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label || jsxRuntime.jsx("slot", { name: "label" }), isRequired /* && !isParentFieldsetRequired(host) */ && jsxRuntime.jsx(required.Required, {})] }) }), jsxRuntime.jsx("slot", { name: "label-after" })] })), hasDescription && (jsxRuntime.jsx("span", { className: "label", id: utilsEntry.descriptionId, "aria-disabled": isLoading || isDisabled ? 'true' : null, children: description || jsxRuntime.jsx("slot", { name: "description" }) }))] }));
13
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [hasLabel && (jsxRuntime.jsxs("div", { className: "label-wrapper", children: [jsxRuntime.jsx(TagType, { className: "label", id: utilsEntry.labelId, "aria-disabled": isLoading || isDisabled ? 'true' : null, htmlFor: htmlFor, children: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [label || jsxRuntime.jsx("slot", { name: "label" }), isRequired /* && !isParentFieldsetRequired(host) */ && jsxRuntime.jsx(required.Required, {})] }) }), stopClickPropagation ? (jsxRuntime.jsx("span", { className: "label-after", children: jsxRuntime.jsx("slot", { name: "label-after" }) })) : (jsxRuntime.jsx("slot", { name: "label-after" }))] })), hasDescription && (jsxRuntime.jsx("span", { className: "label", id: utilsEntry.descriptionId, "aria-disabled": isLoading || isDisabled ? 'true' : null, children: description || jsxRuntime.jsx("slot", { name: "description" }) }))] }));
14
14
  };
15
15
 
16
16
  exports.Label = Label;
@@ -4130,7 +4130,7 @@ const OPTION_LIST_SAFE_ZONE = 6;
4130
4130
 
4131
4131
  const getCDNBaseURL = () => global.PORSCHE_DESIGN_SYSTEM_CDN_URL + "/porsche-design-system";
4132
4132
 
4133
- const prefix = `[Porsche Design System v${"4.2.0-rc.3"}]` // this part isn't covered by unit tests
4133
+ const prefix = `[Porsche Design System v${"4.2.0-rc.4"}]` // this part isn't covered by unit tests
4134
4134
  ;
4135
4135
  const consoleError = (...messages) => {
4136
4136
  console.error(prefix, ...messages);
@@ -5897,15 +5897,20 @@ const getFunctionalComponentRequiredStyles = () => {
5897
5897
  };
5898
5898
 
5899
5899
  const getFunctionalComponentLabelAfterStyles = () => {
5900
+ const labelAfterStyles = {
5901
+ display: 'inline-block',
5902
+ verticalAlign: 'top',
5903
+ '&::slotted(*)': {
5904
+ ...addImportantToEachRule({
5905
+ marginInlineStart: ref(spacingStaticXs),
5906
+ }),
5907
+ },
5908
+ };
5900
5909
  return {
5901
- 'slot[name="label-after"]': {
5902
- display: 'inline-block',
5903
- verticalAlign: 'top',
5904
- '&::slotted(*)': {
5905
- ...addImportantToEachRule({
5906
- marginInlineStart: ref(spacingStaticXs),
5907
- }),
5908
- },
5910
+ 'slot[name="label-after"]': labelAfterStyles,
5911
+ '.label-after': {
5912
+ display: labelAfterStyles.display,
5913
+ verticalAlign: labelAfterStyles.verticalAlign,
5909
5914
  },
5910
5915
  };
5911
5916
  };
@@ -6059,18 +6064,14 @@ const getFunctionalComponentDialogBaseStyles = (isVisible, backdrop = 'blur') =>
6059
6064
  };
6060
6065
  };
6061
6066
  const dialogBackdropResetJssStyle = {
6062
- position: 'fixed', // ua-style
6063
- inset: 0, // ua-style
6064
- margin: 0, // ua-style
6065
- padding: 0, // ua-style
6066
- border: 0, // ua-style
6067
- width: '100dvw', // ua-style
6068
- height: '100dvh', // ua-style
6069
- maxWidth: '100dvw', // ua-style
6070
- maxHeight: '100dvh', // ua-style
6071
- overflow: 'hidden', // ua-style
6072
- display: 'block', // ua-style
6073
- outline: 0, // ua-style (we always expect a focusable element to be within the dialog)
6067
+ all: 'unset',
6068
+ position: 'fixed',
6069
+ inset: 0,
6070
+ maxWidth: '100dvw',
6071
+ maxHeight: '100dvh',
6072
+ overflow: 'hidden',
6073
+ display: 'block',
6074
+ outline: 0, // we always expect a focusable element to be within the dialog
6074
6075
  '&::backdrop': {
6075
6076
  display: 'none', // ua-style (we can't use it atm because it's not animatable in all browsers)
6076
6077
  },
@@ -6079,12 +6080,15 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
6079
6080
  const isBackdropBlur = backdrop === 'blur';
6080
6081
  const duration = isVisible ? 'long' : 'moderate';
6081
6082
  const easing = isVisible ? 'in' : 'out';
6083
+ const delay = ref(cssVariableTransitionDuration, isVisible ? '0s' : motionDurationMap[duration]);
6082
6084
  // as soon as all browsers are supporting `allow-discrete`, visibility transition shouldn't be necessary anymore
6083
- const transition = `visibility 0s linear ${ref(cssVariableTransitionDuration, isVisible ? '0s' : motionDurationMap[duration])}, ${getTransition('background-color', duration, easing)}, ${getTransition('-webkit-backdrop-filter', duration, easing)}, ${getTransition('backdrop-filter', duration, easing)}`;
6085
+ const transition = `visibility 0s linear ${delay}, width 0s linear ${delay}, height 0s linear ${delay}, ${getTransition('background-color', duration, easing)}, ${getTransition('-webkit-backdrop-filter', duration, easing)}, ${getTransition('backdrop-filter', duration, easing)}`;
6084
6086
  return {
6085
6087
  zIndex: 9999999, // fallback for fade out stacking until `overlay` + `allow-discrete` is supported in all browsers. It tries to mimic #top-layer positioning hierarchy.
6086
6088
  ...(isVisible
6087
6089
  ? {
6090
+ width: '100dvw',
6091
+ height: '100dvh',
6088
6092
  visibility: 'inherit',
6089
6093
  pointerEvents: 'auto',
6090
6094
  background: ref(colorBackdrop),
@@ -6094,6 +6098,12 @@ const getDialogBackdropTransitionJssStyle = (isVisible, backdrop = 'blur') => {
6094
6098
  }),
6095
6099
  }
6096
6100
  : {
6101
+ // When the dialog lives inside a new stacking context, it is no longer promoted to the #top-layer while closed.
6102
+ // In that case it can reserve additional space and break the surrounding layout, so width and height are collapsed to zero.
6103
+ // A future improvement could use `transition-behavior: allow-discrete` to toggle between `display: none` and `block`,
6104
+ // but browser support is still insufficient and behaves inconsistently in Safari and Firefox.
6105
+ width: '0px',
6106
+ height: '0px',
6097
6107
  visibility: 'hidden', // element shall not be tabbable with keyboard after fade out transition has finished
6098
6108
  pointerEvents: 'none', // element can't be interacted with mouse
6099
6109
  background: 'transparent',
@@ -3463,7 +3463,7 @@ const hasShowPickerSupport = () => (hasDocument &&
3463
3463
  'showPicker' in HTMLInputElement.prototype &&
3464
3464
  CSS.supports('selector(::-webkit-calendar-picker-indicator)'));
3465
3465
 
3466
- const prefix = `[Porsche Design System v${"4.2.0-rc.3"}]` // this part isn't covered by unit tests
3466
+ const prefix = `[Porsche Design System v${"4.2.0-rc.4"}]` // this part isn't covered by unit tests
3467
3467
  ;
3468
3468
  const consoleError$1 = (...messages) => {
3469
3469
  console.error(prefix, ...messages);
@@ -8,7 +8,7 @@ const Label = ({ hasLabel, hasDescription, children,
8
8
  label, tag, description, htmlFor, isRequired, isLoading, isDisabled, stopClickPropagation, }) => {
9
9
  splitChildren(children);
10
10
  const TagType = tag || 'label';
11
- return (jsxs(Fragment, { children: [hasLabel && (jsxs("div", { className: "label-wrapper", children: [jsx(TagType, { className: "label", id: labelId, "aria-disabled": isLoading || isDisabled ? 'true' : null, htmlFor: htmlFor, children: jsxs(Fragment, { children: [label || jsx("slot", { name: "label" }), isRequired /* && !isParentFieldsetRequired(host) */ && jsx(Required, {})] }) }), jsx("slot", { name: "label-after" })] })), hasDescription && (jsx("span", { className: "label", id: descriptionId, "aria-disabled": isLoading || isDisabled ? 'true' : null, children: description || jsx("slot", { name: "description" }) }))] }));
11
+ return (jsxs(Fragment, { children: [hasLabel && (jsxs("div", { className: "label-wrapper", children: [jsx(TagType, { className: "label", id: labelId, "aria-disabled": isLoading || isDisabled ? 'true' : null, htmlFor: htmlFor, children: jsxs(Fragment, { children: [label || jsx("slot", { name: "label" }), isRequired /* && !isParentFieldsetRequired(host) */ && jsx(Required, {})] }) }), stopClickPropagation ? (jsx("span", { className: "label-after", children: jsx("slot", { name: "label-after" }) })) : (jsx("slot", { name: "label-after" }))] })), hasDescription && (jsx("span", { className: "label", id: descriptionId, "aria-disabled": isLoading || isDisabled ? 'true' : null, children: description || jsx("slot", { name: "description" }) }))] }));
12
12
  };
13
13
 
14
14
  export { Label };
@@ -14,8 +14,8 @@ type LabelProps = {
14
14
  isLoading?: boolean;
15
15
  isDisabled?: boolean;
16
16
  /**
17
- * If true, clicking the label will not bubble to the host element,
18
- * preventing duplicate handling on host click listeners.
17
+ * If true, clicks on the label use `htmlFor` only and do not bubble to the host.
18
+ * Clicks on the `label-after` slot are also stopped so they do not trigger host handlers (e.g. radio selection).
19
19
  */
20
20
  stopClickPropagation?: boolean;
21
21
  };