diff --git a/.changeset/small-gifts-hope.md b/.changeset/small-gifts-hope.md new file mode 100644 index 00000000000..a4c621de259 --- /dev/null +++ b/.changeset/small-gifts-hope.md @@ -0,0 +1,5 @@ +--- +'@shopify/polaris': minor +--- + +Added support for responsive padding to `Box` diff --git a/polaris-react/src/components/Box/Box.scss b/polaris-react/src/components/Box/Box.scss index 7e839dcd6fe..49f5d6fbb66 100644 --- a/polaris-react/src/components/Box/Box.scss +++ b/polaris-react/src/components/Box/Box.scss @@ -1,4 +1,32 @@ +@import '../../styles/common'; + .Box { + @include responsive-props('box', 'padding', 'padding'); + @include responsive-props( + 'box', + 'padding-block-end', + 'padding-block-end', + 'padding' + ); + @include responsive-props( + 'box', + 'padding-block-start', + 'padding-block-start', + 'padding' + ); + @include responsive-props( + 'box', + 'padding-inline-start', + 'padding-inline-start', + 'padding' + ); + @include responsive-props( + 'box', + 'padding-inline-end', + 'padding-inline-end', + 'padding' + ); + --pc-box-shadow: initial; --pc-box-background: initial; --pc-box-border-radius: initial; @@ -69,13 +97,6 @@ max-width: var(--pc-box-max-width); overflow-x: var(--pc-box-overflow-x); overflow-y: var(--pc-box-overflow-y); - padding-block-end: var(--pc-box-padding-block-end, var(--pc-box-padding)); - padding-inline-start: var( - --pc-box-padding-inline-start, - var(--pc-box-padding) - ); - padding-inline-end: var(--pc-box-padding-inline-end, var(--pc-box-padding)); - padding-block-start: var(--pc-box-padding-block-start, var(--pc-box-padding)); width: var(--pc-box-width); -webkit-overflow-scrolling: touch; } diff --git a/polaris-react/src/components/Box/Box.stories.tsx b/polaris-react/src/components/Box/Box.stories.tsx index d35c249f85a..bad0fccc80a 100644 --- a/polaris-react/src/components/Box/Box.stories.tsx +++ b/polaris-react/src/components/Box/Box.stories.tsx @@ -1,6 +1,6 @@ import React from 'react'; import type {ComponentMeta} from '@storybook/react'; -import {Box, Icon} from '@shopify/polaris'; +import {AlphaStack, Box, Icon} from '@shopify/polaris'; import {PaintBrushMajor} from '@shopify/polaris-icons'; export default { @@ -30,3 +30,44 @@ export function BoxWithBorderRadius() { ); } + +export function BoxWithResponsivePadding() { + return ( + + + + + + + + + + + + + + + + + + ); +} diff --git a/polaris-react/src/components/Box/Box.tsx b/polaris-react/src/components/Box/Box.tsx index 09ffbc4feab..d4340faadad 100644 --- a/polaris-react/src/components/Box/Box.tsx +++ b/polaris-react/src/components/Box/Box.tsx @@ -10,7 +10,12 @@ import type { SpacingSpaceScale, } from '@shopify/polaris-tokens'; -import {classNames, sanitizeCustomProperties} from '../../utilities/css'; +import { + getResponsiveProps, + ResponsiveProp, + classNames, + sanitizeCustomProperties, +} from '../../utilities/css'; import styles from './Box.scss'; @@ -42,6 +47,8 @@ export type BorderTokenAlias = | 'divider-on-dark' | 'transparent'; +type Spacing = ResponsiveProp; + interface Border { blockStart: BorderTokenAlias; blockEnd: BorderTokenAlias; @@ -75,13 +82,6 @@ interface BorderRadius { endEnd: BorderRadiusTokenScale; } -interface Spacing { - blockStart: SpacingSpaceScale; - blockEnd: SpacingSpaceScale; - inlineStart: SpacingSpaceScale; - inlineEnd: SpacingSpaceScale; -} - interface BorderWidth { blockStart: ShapeBorderWidthScale; blockEnd: ShapeBorderWidthScale; @@ -139,15 +139,15 @@ export interface BoxProps { /** Clip vertical content of children */ overflowY?: Overflow; /** Spacing around children */ - padding?: SpacingSpaceScale; + padding?: Spacing; /** Vertical start spacing around children */ - paddingBlockStart?: SpacingSpaceScale; + paddingBlockStart?: Spacing; /** Vertical end spacing around children */ - paddingBlockEnd?: SpacingSpaceScale; + paddingBlockEnd?: Spacing; /** Horizontal start spacing around children */ - paddingInlineStart?: SpacingSpaceScale; + paddingInlineStart?: Spacing; /** Horizontal end spacing around children */ - paddingInlineEnd?: SpacingSpaceScale; + paddingInlineEnd?: Spacing; /** Shadow */ shadow?: DepthShadowAlias; /** Set width of container */ @@ -215,13 +215,6 @@ export const Box = forwardRef( inlineEnd: borderInlineEndWidth, } as BorderWidth; - const paddings = { - blockEnd: paddingBlockEnd, - inlineStart: paddingInlineStart, - inlineEnd: paddingInlineEnd, - blockStart: paddingBlockStart, - } as Spacing; - const style = { '--pc-box-color': color ? `var(--p-${color})` : undefined, '--pc-box-background': background ? `var(--p-${background})` : undefined, @@ -273,19 +266,31 @@ export const Box = forwardRef( '--pc-box-max-width': maxWidth, '--pc-box-overflow-x': overflowX, '--pc-box-overflow-y': overflowY, - '--pc-box-padding': padding ? `var(--p-space-${padding})` : undefined, - '--pc-box-padding-block-end': paddings.blockEnd - ? `var(--p-space-${paddings.blockEnd})` - : undefined, - '--pc-box-padding-inline-start': paddings.inlineStart - ? `var(--p-space-${paddings.inlineStart})` - : undefined, - '--pc-box-padding-inline-end': paddings.inlineEnd - ? `var(--p-space-${paddings.inlineEnd})` - : undefined, - '--pc-box-padding-block-start': paddings.blockStart - ? `var(--p-space-${paddings.blockStart})` - : undefined, + ...getResponsiveProps('box', 'padding', 'space', padding), + ...getResponsiveProps( + 'box', + 'padding-block-end', + 'space', + paddingBlockEnd, + ), + ...getResponsiveProps( + 'box', + 'padding-block-start', + 'space', + paddingBlockStart, + ), + ...getResponsiveProps( + 'box', + 'padding-inline-start', + 'space', + paddingInlineStart, + ), + ...getResponsiveProps( + 'box', + 'padding-inline-end', + 'space', + paddingInlineEnd, + ), '--pc-box-shadow': shadow ? `var(--p-shadow-${shadow})` : undefined, '--pc-box-width': width, } as React.CSSProperties; diff --git a/polaris-react/src/components/Box/tests/Box.test.tsx b/polaris-react/src/components/Box/tests/Box.test.tsx index 1c788fe259c..a13be52bcae 100644 --- a/polaris-react/src/components/Box/tests/Box.test.tsx +++ b/polaris-react/src/components/Box/tests/Box.test.tsx @@ -79,7 +79,7 @@ describe('Box', () => { expect(box).toContainReactComponent('div', { style: { - '--pc-box-padding-inline-start': 'var(--p-space-2)', + '--pc-box-padding-inline-start-xs': 'var(--p-space-2)', } as React.CSSProperties, }); }); @@ -93,9 +93,22 @@ describe('Box', () => { expect(box).toContainReactComponent('div', { style: { - '--pc-box-padding': 'var(--p-space-1)', - '--pc-box-padding-inline-start': 'var(--p-space-2)', + '--pc-box-padding-xs': 'var(--p-space-1)', + '--pc-box-padding-inline-start-xs': 'var(--p-space-2)', } as React.CSSProperties, }); }); + + it('accepts padding based on breakpoints', () => { + const box = mountWithApp( + {children}, + ); + + expect(box).toContainReactComponent('div', { + style: expect.objectContaining({ + '--pc-box-padding-md': 'var(--p-space-8)', + '--pc-box-padding-xs': 'var(--p-space-2)', + }) as React.CSSProperties, + }); + }); }); diff --git a/polaris-react/src/styles/_common.scss b/polaris-react/src/styles/_common.scss index 35afcd48810..c1e6f1b2d5d 100644 --- a/polaris-react/src/styles/_common.scss +++ b/polaris-react/src/styles/_common.scss @@ -15,6 +15,7 @@ @import './shared/icons'; @import './shared/layout'; @import './shared/page'; +@import './shared/responsive-props'; @import './shared/typography'; @import './shared/interaction-state'; diff --git a/polaris-react/src/styles/shared/_responsive-props.scss b/polaris-react/src/styles/shared/_responsive-props.scss new file mode 100644 index 00000000000..c2534b1052f --- /dev/null +++ b/polaris-react/src/styles/shared/_responsive-props.scss @@ -0,0 +1,72 @@ +@mixin responsive-props( + $componentName, + $componentProp, + $declartionProp, + $shorthandFallback: null +) { + --pc-#{$componentName}-#{$componentProp}-xs: initial; + --pc-#{$componentName}-#{$componentProp}-sm: var( + --pc-#{$componentName}-#{$componentProp}-xs + ); + --pc-#{$componentName}-#{$componentProp}-md: var( + --pc-#{$componentName}-#{$componentProp}-sm + ); + --pc-#{$componentName}-#{$componentProp}-lg: var( + --pc-#{$componentName}-#{$componentProp}-md + ); + --pc-#{$componentName}-#{$componentProp}-xl: var( + --pc-#{$componentName}-#{$componentProp}-lg + ); + @if $shorthandFallback { + #{$declartionProp}: var( + --pc-#{$componentName}-#{$componentProp}-xs, + var(--pc-#{$componentName}-#{$shorthandFallback}-xs) + ); + + @media #{$p-breakpoints-sm-up} { + #{$declartionProp}: var( + --pc-#{$componentName}-#{$componentProp}-sm, + var(--pc-#{$componentName}-#{$shorthandFallback}-sm) + ); + } + + @media #{$p-breakpoints-md-up} { + #{$declartionProp}: var( + --pc-#{$componentName}-#{$componentProp}-md, + var(--pc-#{$componentName}-#{$shorthandFallback}-md) + ); + } + + @media #{$p-breakpoints-lg-up} { + #{$declartionProp}: var( + --pc-#{$componentName}-#{$componentProp}-lg, + var(--pc-#{$componentName}-#{$shorthandFallback}-lg) + ); + } + + @media #{$p-breakpoints-xl-up} { + #{$declartionProp}: var( + --pc-#{$componentName}-#{$componentProp}-xl, + var(--pc-#{$componentName}-#{$shorthandFallback}-xl) + ); + } + } @else { + #{$declartionProp}: var(--pc-#{$componentName}-#{$componentProp}-xs); + + @media #{$p-breakpoints-sm-up} { + #{$declartionProp}: var(--pc-#{$componentName}-#{$componentProp}-sm); + } + + @media #{$p-breakpoints-md-up} { + #{$declartionProp}: var(--pc-#{$componentName}-#{$componentProp}-md); + } + + @media #{$p-breakpoints-lg-up} { + #{$declartionProp}: var(--pc-#{$componentName}-#{$componentProp}-lg); + } + + @media #{$p-breakpoints-xl-up} { + #{$declartionProp}: var(--pc-#{$componentName}-#{$componentProp}-xl); + } + } +} diff --git a/polaris-react/src/utilities/css.ts b/polaris-react/src/utilities/css.ts index fc1f112923b..74c8e02b9f5 100644 --- a/polaris-react/src/utilities/css.ts +++ b/polaris-react/src/utilities/css.ts @@ -30,12 +30,14 @@ export function getResponsiveProps( componentName: string, componentProp: string, tokenSubgroup: string, - responsiveProp: + responsiveProp?: | string | { [Breakpoint in BreakpointsAlias]?: string; }, ) { + if (!responsiveProp) return {}; + if (typeof responsiveProp === 'string') { return { [`--pc-${componentName}-${componentProp}-xs`]: `var(--p-${tokenSubgroup}-${responsiveProp})`,