diff --git a/playwright/components/alert/index.ts b/playwright/components/alert/index.ts
index 2045f454b9..ddf73759d1 100644
--- a/playwright/components/alert/index.ts
+++ b/playwright/components/alert/index.ts
@@ -27,15 +27,4 @@ const alertDialog = (page: Page) => {
return page.locator(ALERT_DIALOG);
};
-const alertChildren = (page: Page) => {
- return page.locator('[data-component="alert"] div:nth-of-type(2) div');
-};
-
-export {
- alert,
- alertCrossIcon,
- alertTitle,
- alertSubtitle,
- alertDialog,
- alertChildren,
-};
+export { alert, alertCrossIcon, alertTitle, alertSubtitle, alertDialog };
diff --git a/playwright/components/select/index.ts b/playwright/components/select/index.ts
index 3b79b12fe3..c5f2f19bc4 100644
--- a/playwright/components/select/index.ts
+++ b/playwright/components/select/index.ts
@@ -14,7 +14,6 @@ import {
SELECT_LIST_SCROLLABLE_WRAPPER,
} from "./locators";
import { PILL_PREVIEW } from "../pill/locators";
-import { ALERT_DIALOG } from "../dialog/locators";
import { getDataElementByValue } from "..";
// component preview locators
@@ -102,8 +101,5 @@ export const filterableSelectAddElementButton = (page: Page) =>
export const filterableSelectButtonIcon = (page: Page) =>
filterableSelectAddElementButton(page).locator("span:nth-child(2)");
-export const filterableSelectAddNewButton = (page: Page) =>
- page.locator(ALERT_DIALOG).locator("div:nth-child(3) > div > button");
-
export const selectResetButton = (page: Page) =>
page.locator(SELECT_RESET_BUTTON);
diff --git a/src/components/advanced-color-picker/advanced-color-picker.style.ts b/src/components/advanced-color-picker/advanced-color-picker.style.ts
index dca9255175..2861f67ea9 100644
--- a/src/components/advanced-color-picker/advanced-color-picker.style.ts
+++ b/src/components/advanced-color-picker/advanced-color-picker.style.ts
@@ -3,10 +3,7 @@ import { margin } from "styled-system";
import StyledAdvancedColorPickerCell from "./advanced-color-picker-cell.style";
import { StyledColorOptions } from "../simple-color-picker/simple-color-picker.style";
import { StyledSimpleColor } from "../simple-color-picker/simple-color/simple-color.style";
-import {
- StyledDialogContent,
- StyledDialogInnerContent,
-} from "../dialog/dialog.style";
+import { StyledDialogContent } from "../dialog/dialog.style";
import Dialog from "../dialog/dialog.component";
import StyledIconButton from "../icon-button/icon-button.style";
import checkerBoardSvg from "../simple-color-picker/simple-color/checker-board.svg";
@@ -59,10 +56,6 @@ const DialogStyle = styled(Dialog)`
padding: var(--spacing200);
}
- ${StyledDialogInnerContent} {
- padding: 0;
- }
-
${StyledColorOptions} {
max-width: 285px;
${StyledSimpleColor} {
diff --git a/src/components/alert/alert.pw.tsx b/src/components/alert/alert.pw.tsx
index 63e4aa0520..71b68e6973 100644
--- a/src/components/alert/alert.pw.tsx
+++ b/src/components/alert/alert.pw.tsx
@@ -4,7 +4,6 @@ import {
alertCrossIcon,
alertTitle,
alertSubtitle,
- alertChildren,
alertDialog,
} from "../../../playwright/components/alert";
import {
@@ -50,9 +49,7 @@ test.describe("should render Alert component", () => {
test(`with ${text} as children`, async ({ mount, page }) => {
await mount({text});
- const children = alertChildren(page);
- const alertChildrenText = await children.textContent();
- expect(alertChildrenText).toEqual(text);
+ await expect(page.getByText(text)).toBeVisible();
});
});
diff --git a/src/components/confirm/confirm.pw.tsx b/src/components/confirm/confirm.pw.tsx
index 375a3f7869..5d36dcd14b 100644
--- a/src/components/confirm/confirm.pw.tsx
+++ b/src/components/confirm/confirm.pw.tsx
@@ -14,6 +14,7 @@ import {
assertCssValueIsApproximately,
checkAccessibility,
checkDialogIsInDOM,
+ getStyle,
waitForAnimationEnd,
} from "../../../playwright/support/helper";
import { SIZE, CHARACTERS } from "../../../playwright/support/constants";
@@ -105,28 +106,30 @@ test.describe("should render Confirm component", () => {
});
});
- heights.forEach(([heightnumber, heightstring]) => {
- test(`should check Confirm height is ${heightstring}px`, async ({
+ ["0px", "100px", "500px"].forEach((height) => {
+ test(`height of Confirm dialog is ${height} when height prop is ${height}`, async ({
mount,
page,
}) => {
- await mount();
+ await page.setViewportSize({ width: 600, height: 1000 });
+ await mount();
- const viewportHeight = 768;
+ await expect(page.getByRole("alertdialog")).toHaveCSS("height", height);
+ });
+ });
- let resultHeight: number;
- if (heightnumber >= viewportHeight - 20) {
- resultHeight = viewportHeight - 20;
- } else {
- resultHeight = heightnumber;
- }
+ test("Confirm dialog's height does not exceed the height of the viewport", async ({
+ mount,
+ page,
+ }) => {
+ await page.setViewportSize({ width: 600, height: 1000 });
+ await mount();
- await assertCssValueIsApproximately(
- page.getByRole("alertdialog"),
- "height",
- resultHeight
- );
- });
+ const actualDialogHeight = parseInt(
+ await getStyle(page.getByRole("alertdialog"), "height")
+ );
+
+ expect(actualDialogHeight).toBeLessThanOrEqual(1000);
});
([
diff --git a/src/components/date/date.pw.tsx b/src/components/date/date.pw.tsx
index 71b21b231c..34b8335059 100644
--- a/src/components/date/date.pw.tsx
+++ b/src/components/date/date.pw.tsx
@@ -1,7 +1,6 @@
import React from "react";
import { expect, test } from "@playwright/experimental-ct-react17";
import dayjs from "dayjs";
-import Confirm from "../confirm";
import {
DateInputCustom,
DateInputValidationNewDesign,
@@ -784,26 +783,48 @@ test.describe("Functionality tests", () => {
await expect(wrapper).toBeVisible();
});
- [true, false].forEach((state) => {
- test(`should render with disablePortal prop ${state}`, async ({
- mount,
- page,
- }) => {
- await mount(
- {}}>
-
-
- );
+ test("date picker does not float above the rest of the page, when disablePortal prop is true", async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+
+
+ );
- const input = getDataElementByValue(page, "input");
- await input.click();
- const wrapper = dayPickerWrapper(page);
- if (state) {
- await expect(wrapper).not.toBeInViewport();
- } else {
- await expect(wrapper).toBeInViewport();
- }
- });
+ const input = page.getByLabel("Date");
+ await input.click();
+ const datePicker = dayPickerWrapper(page);
+ await expect(datePicker).not.toBeInViewport();
+ });
+ test("date picker floats above the rest of the page, when disablePortal prop is false", async ({
+ mount,
+ page,
+ }) => {
+ await mount(
+
+
+
+ );
+ const input = page.getByLabel("Date");
+ await input.click();
+ const datePicker = dayPickerWrapper(page);
+ await expect(datePicker).toBeInViewport();
});
test(`should have the expected border radius styling`, async ({
diff --git a/src/components/dialog-full-screen/content.style.ts b/src/components/dialog-full-screen/content.style.ts
index 6aa4d72be0..87a4924542 100644
--- a/src/components/dialog-full-screen/content.style.ts
+++ b/src/components/dialog-full-screen/content.style.ts
@@ -1,92 +1,62 @@
import styled, { css } from "styled-components";
-
-import { StyledFormFooter, StyledFormContent } from "../form/form.style";
+import { StyledForm, StyledFormContent } from "../form/form.style";
type StyledContentProps = {
hasHeader: boolean;
disableContentPadding?: boolean;
};
+const generatePaddingVariables = (px: number) => css`
+ padding-top: 0;
+ padding-left: ${px}px;
+ padding-right: ${px}px;
+ padding-bottom: 0;
+`;
+
+const stickyFormOverrides = (px: number) => css`
+ ${StyledForm}.sticky {
+ margin-left: calc(-1 * ${px}px);
+ margin-right: calc(-1 * ${px}px);
+
+ ${StyledFormContent} {
+ ${generatePaddingVariables(px)};
+ }
+ }
+`;
+
const StyledContent = styled.div`
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
overflow-y: auto;
- padding: 0 16px;
+
flex: 1;
+ width: 100%;
- /* Delegate handling overflow to child form if it has a sticky footer */
- &:has(${StyledFormContent}.sticky) {
- overflow-y: inherit;
- }
+ ${generatePaddingVariables(16)}
+ ${stickyFormOverrides(16)}
${({ disableContentPadding }) => css`
${!disableContentPadding &&
css`
@media screen and (min-width: 600px) {
- padding: 0 24px;
+ ${generatePaddingVariables(24)}
+ ${stickyFormOverrides(24)}
}
@media screen and (min-width: 960px) {
- padding: 0 32px;
+ ${generatePaddingVariables(32)}
+ ${stickyFormOverrides(32)}
}
@media screen and (min-width: 1260px) {
- padding: 0 40px;
- }
-
- ${StyledFormContent}.sticky {
- padding-right: 16px;
- padding-left: 16px;
- margin-right: -16px;
- margin-left: -16px;
-
- @media screen and (min-width: 600px) {
- padding-right: 24px;
- padding-left: 24px;
- margin-right: -24px;
- margin-left: -24px;
- }
- @media screen and (min-width: 960px) {
- padding-right: 32px;
- padding-left: 32px;
- margin-right: -32px;
- margin-left: -32px;
- }
- @media screen and (min-width: 1260px) {
- padding-right: 40px;
- padding-left: 40px;
- margin-right: -40px;
- margin-left: -40px;
- }
- }
-
- ${StyledFormFooter}.sticky {
- padding: 16px;
-
- margin-right: -16px;
- margin-left: -16px;
- width: calc(100% + 32px);
-
- @media screen and (min-width: 600px) {
- padding: 16px 24px;
- margin-right: -24px;
- margin-left: -24px;
- width: calc(100% + 48px);
- }
- @media screen and (min-width: 960px) {
- padding: 16px 32px;
- margin-right: -32px;
- margin-left: -32px;
- width: calc(100% + 64px);
- }
- @media screen and (min-width: 1260px) {
- padding: 16px 40px;
- margin-right: -40px;
- margin-left: -40px;
- width: calc(100% + 80px);
- }
+ ${generatePaddingVariables(40)}
+ ${stickyFormOverrides(40)}
}
`}
${disableContentPadding &&
css`
- padding: 0;
+ ${generatePaddingVariables(0)}
+ ${stickyFormOverrides(0)}
`}
`}
diff --git a/src/components/dialog-full-screen/dialog-full-screen.style.ts b/src/components/dialog-full-screen/dialog-full-screen.style.ts
index 6f5e69d6b3..34e0b0ddf4 100644
--- a/src/components/dialog-full-screen/dialog-full-screen.style.ts
+++ b/src/components/dialog-full-screen/dialog-full-screen.style.ts
@@ -8,7 +8,6 @@ import {
StyledHeaderContent,
StyledHeading,
} from "../heading/heading.style";
-import { StyledForm } from "../form/form.style";
const StyledDialogFullScreen = styled.div<{ pagesStyling?: boolean }>`
:focus {
@@ -27,10 +26,6 @@ const StyledDialogFullScreen = styled.div<{ pagesStyling?: boolean }>`
display: flex;
flex-direction: column;
- ${StyledForm} {
- min-height: 100%;
- }
-
${StyledHeaderContent} {
align-items: baseline;
}
diff --git a/src/components/dialog-full-screen/dialog-full-screen.test.tsx b/src/components/dialog-full-screen/dialog-full-screen.test.tsx
index 3cbea9414b..f25f95a930 100644
--- a/src/components/dialog-full-screen/dialog-full-screen.test.tsx
+++ b/src/components/dialog-full-screen/dialog-full-screen.test.tsx
@@ -12,7 +12,6 @@ import StyledContent from "./content.style";
import StyledIconButton from "../icon-button/icon-button.style";
import { StyledHeader, StyledHeading } from "../heading/heading.style";
import Form from "../form";
-import { StyledFormContent } from "../form/form.style";
import CarbonProvider from "../carbon-provider";
const ControlledDialog = ({
@@ -244,10 +243,10 @@ test("padding is removed from the content when the `disableContentPadding` prop
);
- expect(screen.getByTestId("dialog-full-screen-content")).toHaveStyleRule(
- "padding",
- "0"
- );
+ const content = screen.getByTestId("dialog-full-screen-content");
+ expect(content).toHaveStyle({
+ padding: "0px 0px 0px 0px",
+ });
});
/** Remove this when after Pages is re-written */
@@ -317,22 +316,6 @@ test("when a Form child does not have a sticky footer, overflow styling is set o
);
});
-test("when a Form child has a sticky footer, no overflow styling is set", () => {
- render(
-
-
-
- );
-
- expect(screen.getByTestId("dialog-full-screen-content")).toHaveStyleRule(
- "overflow-y",
- "inherit",
- {
- modifier: `&:has(${StyledFormContent}.sticky)`,
- }
- );
-});
-
test("when the `title` prop is a string, this value is set as the dialog's accessible name", () => {
render();
diff --git a/src/components/dialog/dialog-test.stories.tsx b/src/components/dialog/dialog-test.stories.tsx
index 57c478132a..1c7ef73d3e 100644
--- a/src/components/dialog/dialog-test.stories.tsx
+++ b/src/components/dialog/dialog-test.stories.tsx
@@ -15,6 +15,15 @@ import { Checkbox } from "../checkbox";
import { Select, Option } from "../select";
import TextEditor from "../text-editor";
+import Box from "../box";
+import Typography from "../typography";
+import {
+ FlexTileCell,
+ FlexTileContainer,
+ FlexTileDivider,
+ Tile,
+} from "../tile";
+
export default {
title: "Dialog/Test",
component: Dialog,
@@ -293,3 +302,161 @@ MaxSizeTestNonOverflowedForm.parameters = {
chromatic: { disableSnapshot: false, viewports: [1200, 900] },
layout: "fullscreen",
};
+
+export const DialogWithLongHeaderContent: StoryType = {
+ render: ({ size, ...args }) => (
+
+ ),
+};
diff --git a/src/components/dialog/dialog.component.tsx b/src/components/dialog/dialog.component.tsx
index 8d727fcb1f..444cc1b719 100644
--- a/src/components/dialog/dialog.component.tsx
+++ b/src/components/dialog/dialog.component.tsx
@@ -18,7 +18,6 @@ import {
StyledDialog,
StyledDialogTitle,
StyledDialogContent,
- StyledDialogInnerContent,
} from "./dialog.style";
import { DialogSizes, TOP_MARGIN } from "./dialog.config";
@@ -195,7 +194,7 @@ export const Dialog = forwardRef(
containerRef.current.style.top = `${midPointY}px`;
containerRef.current.style.left = `${midPointX}px`;
- }, [size]);
+ }, [isDialogMaximised]);
useResizeObserver(innerContentRef, centerDialog, !open);
@@ -341,14 +340,9 @@ export const Dialog = forwardRef(
{...contentPadding}
data-role="dialog-content"
tabIndex={-1}
+ ref={innerContentRef}
>
-
- {children}
-
+ {children}
diff --git a/src/components/dialog/dialog.stories.tsx b/src/components/dialog/dialog.stories.tsx
index 69b0463950..be7a444bc1 100644
--- a/src/components/dialog/dialog.stories.tsx
+++ b/src/components/dialog/dialog.stories.tsx
@@ -20,13 +20,12 @@ import useMediaQuery from "../../hooks/useMediaQuery";
import type { DialogHandle } from ".";
import Dialog from ".";
-const defaultOpenState = isChromatic();
-
const meta: Meta = {
title: "Dialog",
component: Dialog,
parameters: {
themeProvider: { chromatic: { theme: "sage" } },
+ layout: isChromatic() ? "fullscreen" : "padded",
controls: { disable: true },
chromatic: {
modes: {
@@ -37,8 +36,8 @@ const meta: Meta = {
decorators: [
(Story) => (
<>
- {defaultOpenState ? (
-
+ {isChromatic() ? (
+
) : (
@@ -52,6 +51,8 @@ const meta: Meta = {
export default meta;
type Story = StoryObj;
+const defaultOpenState = isChromatic();
+
export const DefaultStory: Story = () => {
const [isOpen, setIsOpen] = useState(defaultOpenState);
return (
diff --git a/src/components/dialog/dialog.style.ts b/src/components/dialog/dialog.style.ts
index ea646ca7e9..e3a0090596 100644
--- a/src/components/dialog/dialog.style.ts
+++ b/src/components/dialog/dialog.style.ts
@@ -1,16 +1,5 @@
import styled, { css } from "styled-components";
-import { padding as paddingFn } from "styled-system";
-
import baseTheme from "../../style/themes/base";
-import {
- calculateFormSpacingValues,
- calculateWidthValue,
-} from "../../style/utils/form-style-utils";
-import {
- StyledForm,
- StyledFormFooter,
- StyledFormContent,
-} from "../form/form.style";
import {
StyledHeaderContent,
StyledHeading,
@@ -24,6 +13,12 @@ import {
DialogSizes,
} from "./dialog.config";
import { ContentPaddingInterface } from "./dialog.component";
+import resolvePaddingSides from "../../style/utils/resolve-padding-sides";
+import {
+ StyledFormContent,
+ StyledForm,
+ StyledFormFooter,
+} from "../form/form.style";
const dialogSizes = {
auto: "fit-content",
@@ -36,17 +31,6 @@ const dialogSizes = {
"extra-large": "1080px",
};
-const calculatePaddingTopInnerContent = ({
- py,
- p,
-}: {
- py?: ContentPaddingInterface["py"];
- p?: ContentPaddingInterface["p"];
-}) =>
- [py, p].some((padding) => padding !== undefined)
- ? 0
- : `${CONTENT_TOP_PADDING}px`;
-
type StyledDialogProps = {
topMargin: number;
size?: DialogSizes;
@@ -54,18 +38,12 @@ type StyledDialogProps = {
backgroundColor: string;
};
-const StyledDialog = styled.div.attrs(
- ({ topMargin, size }: StyledDialogProps) => {
- const isDialogMaximised = size === "maximise";
- const isDialogMaximisedSmallViewport = topMargin === 32;
- const isDialogMaximisedLargeViewport = topMargin === 64;
- return {
- isDialogMaximised,
- isDialogMaximisedSmallViewport,
- isDialogMaximisedLargeViewport,
- };
- }
-)`
+const StyledDialog = styled.div.attrs(({ size }: StyledDialogProps) => {
+ const isDialogMaximised = size === "maximise";
+ return {
+ isDialogMaximised,
+ };
+})`
box-shadow: var(--boxShadow300);
display: flex;
flex-direction: column;
@@ -74,6 +52,7 @@ const StyledDialog = styled.div.attrs(
top: 50%;
z-index: ${({ theme }) => theme.zIndex.modal};
max-height: ${({ topMargin }) => `calc(100vh - ${topMargin}px)`};
+
${({ isDialogMaximised }) => isDialogMaximised && "height: 100%"};
&:focus {
@@ -99,32 +78,7 @@ const StyledDialog = styled.div.attrs(
css`
height: ${dialogHeight}px;
`}
-
- /* We're overriding the max-height on the form content to account for a larger height when in a smaller viewport.
- TODO: Remove this upon the completion of FE-6643. */
- ${StyledForm} {
- ${({ isDialogMaximised, isDialogMaximisedSmallViewport }) =>
- isDialogMaximised &&
- css`
- ${isDialogMaximisedSmallViewport && "max-height: calc(100vh - 184px);"}
- height: 100%;
- `}
-
- padding-bottom: 0px;
- box-sizing: border-box;
- }
-
- ${StyledFormContent}.sticky {
- ${(props) => calculateFormSpacingValues(props, true)}
- }
-
- ${StyledFormFooter}.sticky {
- ${calculateWidthValue}
- ${(props) => calculateFormSpacingValues(props, false)}
- border-bottom-right-radius: var(--borderRadius200);
- border-bottom-left-radius: var(--borderRadius200);
- }
-
+
> ${StyledIconButton} {
margin: 0;
position: absolute;
@@ -168,28 +122,44 @@ const StyledDialogTitle = styled.div`
}
`;
-const StyledDialogContent = styled.div`
- box-sizing: border-box;
- display: flex;
- flex-direction: column;
- overflow-y: auto;
+const StyledDialogContent = styled.div((props) => {
+ const {
+ paddingTop = `${CONTENT_TOP_PADDING}px`,
+ paddingRight = `${HORIZONTAL_PADDING}px`,
+ paddingBottom = `${CONTENT_BOTTOM_PADDING}px`,
+ paddingLeft = `${HORIZONTAL_PADDING}px`,
+ } = resolvePaddingSides(props);
- /* Delegate handling overflow to child form if it has a sticky footer */
- &:has(${StyledFormContent}.sticky) {
- overflow-y: inherit;
- }
+ const negate = (value: string) => `calc(-1 * ${value})`;
- width: 100%;
- flex: 1;
- padding: 0px ${HORIZONTAL_PADDING}px ${CONTENT_BOTTOM_PADDING}px;
- ${paddingFn}
-`;
+ return css`
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ overflow-y: auto;
+
+ width: 100%;
+ flex: 1;
+
+ ${StyledForm}.sticky {
+ margin-top: ${negate(paddingTop)};
+ margin-right: ${negate(paddingRight)};
+ margin-bottom: ${negate(paddingBottom)};
+ margin-left: ${negate(paddingLeft)};
+
+ ${StyledFormContent} {
+ padding: ${paddingTop} ${paddingRight} ${paddingBottom} ${paddingLeft};
+ }
+
+ ${StyledFormFooter} {
+ border-bottom-right-radius: var(--borderRadius200);
+ border-bottom-left-radius: var(--borderRadius200);
+ }
+ }
-const StyledDialogInnerContent = styled.div`
- position: relative;
- flex: 1;
- padding-top: ${calculatePaddingTopInnerContent};
-`;
+ padding: ${paddingTop} ${paddingRight} ${paddingBottom} ${paddingLeft};
+ `;
+});
StyledDialog.defaultProps = {
theme: baseTheme,
@@ -199,9 +169,4 @@ StyledDialogContent.defaultProps = {
theme: baseTheme,
};
-export {
- StyledDialog,
- StyledDialogTitle,
- StyledDialogContent,
- StyledDialogInnerContent,
-};
+export { StyledDialog, StyledDialogTitle, StyledDialogContent };
diff --git a/src/components/dialog/dialog.test.tsx b/src/components/dialog/dialog.test.tsx
index c129fd587a..c55490e722 100644
--- a/src/components/dialog/dialog.test.tsx
+++ b/src/components/dialog/dialog.test.tsx
@@ -8,7 +8,6 @@ import {
import userEvent from "@testing-library/user-event";
import CarbonProvider from "../carbon-provider";
-import Form from "../form";
import Dialog, { DialogHandle, DialogProps } from ".";
beforeEach(() => jest.useFakeTimers());
@@ -306,19 +305,6 @@ test("renders with grey background when greyBackground prop is passed", () => {
});
});
-test("does not apply vertical overflow styling to the content container when it contains a Form with a sticky footer", () => {
- render(
-
- );
-
- const content = screen.getByTestId("dialog-content");
-
- expect(content).not.toHaveStyle("overflow-y: auto");
- expect(content).not.toHaveStyle("overflow-y: scroll");
-});
-
test("dialog is wrapped in a container, which has the correct class names set, when className prop is passed", () => {
render();
@@ -397,6 +383,15 @@ test("dialog does not position itself such that it goes off the left edge of the
jest.restoreAllMocks();
});
+test("prevents content from overflowing", () => {
+ render(
+
+ );
+ expect(screen.getByTestId("dialog-content")).toHaveStyle("overflow-y: auto");
+});
+
test("no padding is rendered around dialog content, when zero padding is specified via contentPadding prop", () => {
render(
);
};
diff --git a/src/components/textarea/textarea.pw.tsx b/src/components/textarea/textarea.pw.tsx
index b8ce4835cd..a60893eb4d 100644
--- a/src/components/textarea/textarea.pw.tsx
+++ b/src/components/textarea/textarea.pw.tsx
@@ -52,7 +52,6 @@ import {
ValidationStringExample,
ValidationStringPositionExample,
} from "./components.test-pw";
-import { FORM_CONTENT } from "../../../playwright/components/textarea/locators";
const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS];
@@ -1059,22 +1058,29 @@ test("should not have borders when hideBorders prop is passed", async ({
);
});
-test("should not change the scroll position of a scrollable container when typing", async ({
+test("typing in the textarea should not change scroll position of the parent container", async ({
mount,
page,
}) => {
await mount();
- const formContentElement = page.locator(FORM_CONTENT);
- await formContentElement.evaluate((element) => element.scrollTo(0, 2000));
- const textareaChildrenElement = textareaChildren(page);
- await textareaChildrenElement.click();
- await textareaChildrenElement.press("ArrowRight");
- await textareaChildrenElement.press("f");
+ const textareaElement = page.getByRole("textbox");
+ const scrollableBox = page.getByTestId("scrollable-box");
+
+ // Select all textarea text, then move cursor to the end, to ensure that the box is scrolled to the bottom
+ await textareaElement.selectText();
+ await textareaElement.press("ArrowRight");
- const scrollTop = await formContentElement.evaluate((el) => el.scrollTop);
+ const initialScrollPosition = await scrollableBox.evaluate(
+ (element) => element.scrollTop
+ );
- expect(scrollTop).toBeGreaterThan(1000);
+ await textareaElement.press("a");
+
+ const finalScrollPosition = await scrollableBox.evaluate(
+ (element) => element.scrollTop
+ );
+ expect(finalScrollPosition).toBeCloseTo(initialScrollPosition);
});
test("should set aria-live attribute on Character Count to `polite` when component is focused and then change back to `off` when component is blurred", async ({
diff --git a/src/components/toast/components.test-pw.tsx b/src/components/toast/components.test-pw.tsx
index 78cf2b15f6..5dba969100 100644
--- a/src/components/toast/components.test-pw.tsx
+++ b/src/components/toast/components.test-pw.tsx
@@ -5,6 +5,7 @@ import Dialog from "../dialog";
export const ToastComponent = ({
children = "Toast",
+ onDismiss,
...props
}: Partial) => {
const [isOpen, setIsOpen] = useState(true);
@@ -13,7 +14,10 @@ export const ToastComponent = ({
variant="info"
id="toast"
open={isOpen}
- onDismiss={() => setIsOpen(!isOpen)}
+ onDismiss={(ev) => {
+ onDismiss?.(ev);
+ setIsOpen(!isOpen);
+ }}
{...props}
>
{children}
diff --git a/src/components/toast/toast.pw.tsx b/src/components/toast/toast.pw.tsx
index 9c2f75ac5c..67876b6d02 100644
--- a/src/components/toast/toast.pw.tsx
+++ b/src/components/toast/toast.pw.tsx
@@ -274,6 +274,8 @@ test.describe("check events for Toast component", () => {
/>
);
await page.keyboard.press("Escape");
+
+ await expect(page.getByText("Toast")).not.toBeVisible();
expect(callbackCount).toBe(1);
});
});
diff --git a/src/style/utils/form-style-utils.spec.ts b/src/style/utils/form-style-utils.spec.ts
deleted file mode 100644
index 2e26849d2c..0000000000
--- a/src/style/utils/form-style-utils.spec.ts
+++ /dev/null
@@ -1,440 +0,0 @@
-import {
- calculateWidthValue,
- calculateFormSpacingValues,
-} from "./form-style-utils";
-import {
- HORIZONTAL_PADDING,
- CONTENT_TOP_PADDING,
- CONTENT_BOTTOM_PADDING,
-} from "../../components/dialog/dialog.config";
-import {
- SIDEBAR_TOP_SPACING,
- SIDEBAR_BOTTOM_SPACING,
- SIDEBAR_LEFT_PADDING,
- SIDEBAR_RIGHT_PADDING,
-} from "../../components/sidebar/sidebar.config";
-
-describe("calculateWidthValue", () => {
- it("adds padding-left and padding-right values to the width when both are supplied", () => {
- const padding = { pl: "20px", pr: "30px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (20px + 30px));");
- });
-
- it("adds padding-left and the default horizontal padding to the width when padding-left is supplied but not padding-right", () => {
- const padding = { pl: "20px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe(
- `width: calc(100% + (20px + ${HORIZONTAL_PADDING}px));`
- );
- });
-
- it("adds padding-right and the default horizontal padding to the width when padding-right is supplied but not padding-left", () => {
- const padding = { pr: "30px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe(
- `width: calc(100% + (${HORIZONTAL_PADDING}px + 30px));`
- );
- });
-
- it("adds the default horizontal padding to the width twice when neither padding-left or padding-right is supplied", () => {
- const padding = {};
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe(
- `width: calc(100% + (${HORIZONTAL_PADDING}px + ${HORIZONTAL_PADDING}px));`
- );
- });
-
- it("adds the appropriate value twice to the width when padding is given as a 1-value string", () => {
- const padding = { p: "20px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (20px + 20px));");
- });
-
- it("adds the appropriate value twice to the width when padding is given as a 2-value string", () => {
- const padding = { p: "20px 30px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (30px + 30px));");
- });
-
- it("adds the appropriate value twice to the width when padding is given as a 3-value string", () => {
- const padding = { p: "20px 30px 40px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (30px + 30px));");
- });
-
- it("adds the two appropriate values to the width when padding is given as a 4-value string", () => {
- const padding = { p: "20px 30px 40px 50px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (50px + 30px));");
- });
-
- it("gives priority to left/right padding values when these are supplied along with an overall padding string", () => {
- const padding = { p: "40px", pl: "20px", pr: "30px" };
- const computedWidth = calculateWidthValue(padding);
- expect(computedWidth).toBe("width: calc(100% + (20px + 30px));");
- });
-});
-
-describe("calculateFormSpacingValues", () => {
- describe("when used for form content in a dialog", () => {
- it("adds padding to all sides and negative margin to all except bottom", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": "40px",
- "padding-bottom": "50px",
- });
- });
-
- it("adds appropriate padding and margin when padding is specified as a complex CSS string", () => {
- const padding = { p: "40px 30px 50px 20px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": "40px",
- "padding-bottom": "50px",
- });
- });
-
- it("uses the default top spacing if no top padding is specified", () => {
- const padding = { pl: "20px", pr: "30px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": `calc(-1 * ${CONTENT_TOP_PADDING}px)`,
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": `${CONTENT_TOP_PADDING}px`,
- "padding-bottom": "50px",
- });
- });
-
- it("uses the default bottom spacing if no bottom padding is specified", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": "40px",
- "padding-bottom": `${CONTENT_BOTTOM_PADDING}px`,
- });
- });
-
- it("uses the default horizontal spacing if no left padding is specified", () => {
- const padding = { pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": `${HORIZONTAL_PADDING}px`,
- "padding-right": "30px",
- "padding-top": "40px",
- "padding-bottom": "50px",
- });
- });
-
- it("uses the default horizontal spacing if no right padding is specified", () => {
- const padding = { pl: "20px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": `${HORIZONTAL_PADDING}px`,
- "padding-top": "40px",
- "padding-bottom": "50px",
- });
- });
- });
-
- describe("when used for form content in a sidebar", () => {
- it("adds padding negative margin to all sides except bottom", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": "40px",
- });
- });
-
- it("adds appropriate padding and margin when padding is specified as a complex CSS string", () => {
- const padding = { p: "40px 30px 50px 20px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": "40px",
- });
- });
-
- it("uses the default top spacing if no top padding is specified", () => {
- const padding = { pl: "20px", pr: "30px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-top": `calc(-1 * ${SIDEBAR_TOP_SPACING})`,
- "padding-left": "20px",
- "padding-right": "30px",
- "padding-top": SIDEBAR_TOP_SPACING,
- });
- });
-
- it("uses the default horizontal spacing if no left padding is specified", () => {
- const padding = { pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-right": "calc(-1 * 30px)",
- "margin-top": "calc(-1 * 40px)",
- "padding-left": `${HORIZONTAL_PADDING}px`,
- "padding-right": "30px",
- "padding-top": "40px",
- });
- });
-
- it("uses the default horizontal spacing if no right padding is specified", () => {
- const padding = { pl: "20px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- true,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-top": "calc(-1 * 40px)",
- "padding-left": "20px",
- "padding-right": `${HORIZONTAL_PADDING}px`,
- "padding-top": "40px",
- });
- });
- });
-
- describe("when used for form footer in a dialog", () => {
- it("adds negative margin to all sides except top but no padding", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- });
- });
-
- it("adds appropriate padding and margin when padding is specified as a complex CSS string", () => {
- const padding = { p: "40px 30px 50px 20px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- });
- });
-
- it("uses the default bottom spacing if no bottom padding is specified", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": `calc(-1 * ${CONTENT_BOTTOM_PADDING}px)`,
- });
- });
-
- it("uses the default horizontal spacing if no left padding is specified", () => {
- const padding = { pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- });
- });
-
- it("uses the default horizontal spacing if no right padding is specified", () => {
- const padding = { pl: "20px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "dialog"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-bottom": "calc(-1 * 50px)",
- });
- });
- });
-
- describe("when used for form footer in a sidebar", () => {
- it("adds negative margin to left and bottom and the default horizontal padding", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- });
- });
-
- it("adds appropriate padding and margin when padding is specified as a complex CSS string", () => {
- const padding = { p: "40px 30px 50px 20px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- });
- });
-
- it("uses the default bottom spacing if no bottom padding is specified", () => {
- const padding = { pl: "20px", pr: "30px", pt: "40px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": `calc(-1 * ${SIDEBAR_BOTTOM_SPACING})`,
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- });
- });
-
- it("uses the default left margin if no left padding is specified", () => {
- const padding = { pr: "30px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-right": "calc(-1 * 30px)",
- "margin-bottom": "calc(-1 * 50px)",
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- });
- });
-
- it("uses the default right margin if no right padding is specified", () => {
- const padding = { pl: "20px", pt: "40px", pb: "50px" };
- const computedSpacing = calculateFormSpacingValues(
- padding,
- false,
- "sidebar"
- );
- expect(computedSpacing).toEqual({
- "margin-left": "calc(-1 * 20px)",
- "margin-right": `calc(-1 * ${HORIZONTAL_PADDING}px)`,
- "margin-bottom": "calc(-1 * 50px)",
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- });
- });
- });
-});
diff --git a/src/style/utils/form-style-utils.ts b/src/style/utils/form-style-utils.ts
deleted file mode 100644
index b88dab6178..0000000000
--- a/src/style/utils/form-style-utils.ts
+++ /dev/null
@@ -1,137 +0,0 @@
-import { padding as paddingFn, PaddingProps } from "styled-system";
-
-import {
- HORIZONTAL_PADDING,
- CONTENT_TOP_PADDING,
- CONTENT_BOTTOM_PADDING,
-} from "../../components/dialog/dialog.config";
-import {
- SIDEBAR_TOP_SPACING,
- SIDEBAR_BOTTOM_SPACING,
- SIDEBAR_LEFT_PADDING,
- SIDEBAR_RIGHT_PADDING,
-} from "../../components/sidebar/sidebar.config";
-
-interface PaddingValues {
- paddingTop: string;
- paddingBottom: string;
- paddingLeft: string;
- paddingRight: string;
- padding: string;
-}
-
-type ContainerComponent = "dialog" | "sidebar";
-
-const parsePadding = (paddingString: string) => {
- const paddingValues = paddingString.split(/\s+/);
- let paddingTop, paddingBottom, paddingLeft, paddingRight;
- switch (paddingValues.length) {
- case 1: {
- const [value] = paddingValues;
- [paddingTop, paddingRight, paddingBottom, paddingLeft] = [
- value,
- value,
- value,
- value,
- ];
- break;
- }
- case 2: {
- const [vertical, horizontal] = paddingValues;
- [paddingTop, paddingRight, paddingBottom, paddingLeft] = [
- vertical,
- horizontal,
- vertical,
- horizontal,
- ];
- break;
- }
- case 3: {
- const [top, horizontal, bottom] = paddingValues;
- [paddingTop, paddingRight, paddingBottom, paddingLeft] = [
- top,
- horizontal,
- bottom,
- horizontal,
- ];
- break;
- }
- case 4: {
- [paddingTop, paddingRight, paddingBottom, paddingLeft] = paddingValues;
- break;
- }
- /* istanbul ignore next */
- default:
- break;
- }
- return { paddingTop, paddingBottom, paddingLeft, paddingRight };
-};
-
-const calculatePadding = (props: PaddingProps): Partial => {
- const {
- padding,
- ...individualPaddingProperties
- }: Partial = paddingFn(props);
- const result = padding ? parsePadding(padding) : {};
- Object.assign(result, individualPaddingProperties);
- return result;
-};
-
-export const calculateWidthValue = (props: PaddingProps) => {
- const { paddingLeft, paddingRight } = calculatePadding(props);
- const paddingValue = `(${paddingLeft ?? `${HORIZONTAL_PADDING}px`} + ${
- paddingRight ?? `${HORIZONTAL_PADDING}px`
- })`;
-
- return `width: calc(100% + ${paddingValue});`;
-};
-
-export const calculateFormSpacingValues = (
- props: PaddingProps,
- isFormContent: boolean,
- containerComponent: ContainerComponent = "dialog"
-) => {
- const {
- paddingTop,
- paddingBottom,
- paddingLeft,
- paddingRight,
- } = calculatePadding(props);
-
- const isSidebar = containerComponent === "sidebar";
-
- const spacingTopValue =
- paddingTop ??
- (isSidebar ? SIDEBAR_TOP_SPACING : `${CONTENT_TOP_PADDING}px`);
- const spacingRightValue = paddingRight ?? `${HORIZONTAL_PADDING}px`;
- const spacingBottomValue =
- paddingBottom ??
- (isSidebar ? SIDEBAR_BOTTOM_SPACING : `${CONTENT_BOTTOM_PADDING}px`);
- const spacingLeftValue = paddingLeft ?? `${HORIZONTAL_PADDING}px`;
-
- const setNegativeValue = (value: string) => `calc(-1 * ${value})`;
-
- return {
- "margin-left": setNegativeValue(spacingLeftValue),
- "margin-right": setNegativeValue(spacingRightValue),
-
- ...(isFormContent
- ? {
- "margin-top": setNegativeValue(spacingTopValue),
- "padding-top": spacingTopValue,
- "padding-bottom": isSidebar ? undefined : spacingBottomValue,
- "padding-left": spacingLeftValue,
- "padding-right": spacingRightValue,
- }
- : {
- "margin-bottom": setNegativeValue(spacingBottomValue),
- ...(isSidebar && {
- // if footer already has custom padding do not set
- ":not(.padded)": {
- "padding-left": SIDEBAR_LEFT_PADDING,
- "padding-right": SIDEBAR_RIGHT_PADDING,
- },
- }),
- }),
- };
-};
diff --git a/src/style/utils/resolve-padding-sides.test.ts b/src/style/utils/resolve-padding-sides.test.ts
new file mode 100644
index 0000000000..f6dd11244b
--- /dev/null
+++ b/src/style/utils/resolve-padding-sides.test.ts
@@ -0,0 +1,31 @@
+import resolvePaddingSides from "./resolve-padding-sides";
+
+test.each([
+ ["10px 20px 30px 40px", "10px", "20px", "30px", "40px"],
+ ["10px 20px 30px", "10px", "20px", "30px", "20px"],
+ ["10px 20px", "10px", "20px", "10px", "20px"],
+ ["10px", "10px", "10px", "10px", "10px"],
+] as const)(
+ "parses shorthand padding prop, with value '%s', into individual padding values",
+ (padding, expectedTop, expectedRight, expectedBottom, expectedLeft) => {
+ const result = resolvePaddingSides({ padding });
+ expect(result).toMatchObject({
+ paddingTop: expectedTop,
+ paddingRight: expectedRight,
+ paddingBottom: expectedBottom,
+ paddingLeft: expectedLeft,
+ });
+ }
+);
+
+test("returns individual padding values, when the shorthand padding prop is not provided", () => {
+ const result = resolvePaddingSides({
+ paddingTop: "1px",
+ paddingX: "100px",
+ });
+ expect(result).toMatchObject({
+ paddingTop: "1px",
+ paddingRight: "100px",
+ paddingLeft: "100px",
+ });
+});
diff --git a/src/style/utils/resolve-padding-sides.ts b/src/style/utils/resolve-padding-sides.ts
new file mode 100644
index 0000000000..827a50485b
--- /dev/null
+++ b/src/style/utils/resolve-padding-sides.ts
@@ -0,0 +1,41 @@
+import {
+ padding as convertPaddingPropsToCSS,
+ PaddingProps,
+} from "styled-system";
+
+interface PaddingValues {
+ padding?: string;
+ paddingTop?: string;
+ paddingRight?: string;
+ paddingBottom?: string;
+ paddingLeft?: string;
+}
+
+function resolvePaddingSides(props: PaddingProps) {
+ const expandShorthand = (cssValue: string) => {
+ const sideValues = cssValue.split(/\s+/);
+ const [
+ paddingTop,
+ paddingRight = paddingTop,
+ paddingBottom = paddingTop,
+ paddingLeft = paddingRight,
+ ] = sideValues;
+
+ return { paddingTop, paddingBottom, paddingLeft, paddingRight };
+ };
+
+ const {
+ padding,
+ ...individualSides
+ }: PaddingValues = convertPaddingPropsToCSS(props);
+
+ if (padding)
+ return {
+ ...expandShorthand(padding),
+ ...individualSides,
+ };
+
+ return individualSides;
+}
+
+export default resolvePaddingSides;