Skip to content

Commit

Permalink
fix: support size tokens in layout components (#2132)
Browse files Browse the repository at this point in the history
  • Loading branch information
SamuelAlev authored Dec 11, 2024
1 parent 9e12e3f commit 25211f7
Show file tree
Hide file tree
Showing 11 changed files with 193 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-singers-dream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@frontify/fondue-components": patch
---

fix: support size tokens in layout components
12 changes: 0 additions & 12 deletions .github/pull_request_template.md

This file was deleted.

14 changes: 14 additions & 0 deletions packages/components/src/components/Box/Box.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,17 @@ export const Default: Story = {
);
},
};

export const WithSizeToken: Story = {
args: {
width: 25,
height: 25,
},
render: (args) => {
return (
<Box {...args}>
<DecorativeContent />
</Box>
);
},
};
32 changes: 31 additions & 1 deletion packages/components/src/components/Box/__tests__/Box.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@ test('should render with normal padding/margin', async ({ mount }) => {
await expect(component).toHaveCSS('margin', '80px');
});

test('should render with size tokens', async ({ mount }) => {
const component = await mount(
<Box
p={6}
m={20}
minWidth={120}
width={256}
minHeight={18}
height={20}
bottom={16}
top={15}
right={14}
left={13}
>
{BOX_TEXT}
</Box>,
);

await expect(component).toHaveCSS('padding', '24px');
await expect(component).toHaveCSS('margin', '80px');
await expect(component).toHaveCSS('min-width', '480px');
await expect(component).toHaveCSS('width', '1024px');
await expect(component).toHaveCSS('min-height', '72px');
await expect(component).toHaveCSS('height', '80px');
await expect(component).toHaveCSS('bottom', '64px');
await expect(component).toHaveCSS('top', '60px');
await expect(component).toHaveCSS('right', '56px');
await expect(component).toHaveCSS('left', '52px');
});

test('should not inherit parent props', async ({ mount }) => {
const component = await mount(
<Box p="20px" m="50px">
Expand All @@ -44,7 +74,7 @@ test('should not inherit parent props', async ({ mount }) => {
});

const ResponsiveComponent = (
<Box p={{ base: '20px', md: '40px', lg: '60px' }} m={{ base: '80px', md: '100px', lg: '120px' }}>
<Box p={{ base: '20px', md: '40px', lg: 15 }} m={{ base: '80px', md: '100px', lg: 30 }}>
{BOX_TEXT}
</Box>
);
Expand Down
20 changes: 20 additions & 0 deletions packages/components/src/components/Flex/Flex.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,23 @@ export const Responsive: Story = {
);
},
};

export const WithSizeToken: Story = {
render: (args) => {
return (
<Flex {...args}>
<Flex px={36} gapX={12}>
<DecorativeBox />
<DecorativeBox />
</Flex>
<Flex p={5}>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Flex>
</Flex>
);
},
};
38 changes: 33 additions & 5 deletions packages/components/src/components/Flex/__tests__/Flex.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,40 @@ test('should not inherit parent props', async ({ mount }) => {
await expect(child).not.toHaveCSS('gap', '50px');
});

test('should render with size tokens', async ({ mount }) => {
const component = await mount(
<Flex
p={8}
my={10}
gap={16}
minWidth={120}
width={256}
minHeight={18}
height={20}
bottom={16}
top={15}
right={14}
left={13}
>
{FLEX_TEXT}
</Flex>,
);

await expect(component).toHaveCSS('padding', '32px');
await expect(component).toHaveCSS('margin', '40px 0px');
await expect(component).toHaveCSS('gap', '64px');
await expect(component).toHaveCSS('min-width', '480px');
await expect(component).toHaveCSS('width', '1024px');
await expect(component).toHaveCSS('min-height', '72px');
await expect(component).toHaveCSS('height', '80px');
await expect(component).toHaveCSS('bottom', '64px');
await expect(component).toHaveCSS('top', '60px');
await expect(component).toHaveCSS('right', '56px');
await expect(component).toHaveCSS('left', '52px');
});

const ResponsiveComponent = (
<Flex
direction={{ base: 'row', md: 'column' }}
gap={{ base: '80px', md: '120px' }}
p={{ base: '10px', md: '100px' }}
>
<Flex direction={{ base: 'row', md: 'column' }} gap={{ base: '80px', md: 30 }} p={{ base: '10px', md: 25 }}>
{FLEX_TEXT}
</Flex>
);
Expand Down
33 changes: 33 additions & 0 deletions packages/components/src/components/Grid/__tests__/Grid.ct.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,39 @@ test('should not inherit parent props', async ({ mount }) => {
await expect(child).not.toHaveCSS('margin', '50px');
});

test('should render with size tokens', async ({ mount }) => {
const component = await mount(
<Grid
p={9}
m={12}
gapX={6}
gapY={10}
minWidth={120}
width={256}
minHeight={18}
height={20}
bottom={16}
top={15}
right={14}
left={13}
>
<Grid data-test-id="flex-child">{GRID_TEXT}</Grid>
</Grid>,
);

await expect(component).toHaveCSS('padding', '36px');
await expect(component).toHaveCSS('margin', '48px');
await expect(component).toHaveCSS('gap', '40px 24px');
await expect(component).toHaveCSS('min-width', '480px');
await expect(component).toHaveCSS('width', '1024px');
await expect(component).toHaveCSS('min-height', '72px');
await expect(component).toHaveCSS('height', '80px');
await expect(component).toHaveCSS('bottom', '64px');
await expect(component).toHaveCSS('top', '60px');
await expect(component).toHaveCSS('right', '56px');
await expect(component).toHaveCSS('left', '52px');
});

const ResponsiveComponent = (
<Grid
columns={{ base: 'repeat(1, 1fr)', md: 'repeat(2, 1fr)' }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,33 @@ test('should not inherit parent props', async ({ mount }) => {
await expect(child).not.toHaveCSS('padding', '20px');
await expect(child).not.toHaveCSS('margin', '50px');
});

test('should render with size tokens', async ({ mount }) => {
const component = await mount(
<Section
p={6}
m={20}
minWidth={120}
width={256}
minHeight={18}
height={20}
bottom={16}
top={15}
right={14}
left={13}
>
{SECTION_TEXT}
</Section>,
);

await expect(component).toHaveCSS('padding', '24px');
await expect(component).toHaveCSS('margin', '80px');
await expect(component).toHaveCSS('min-width', '480px');
await expect(component).toHaveCSS('width', '1024px');
await expect(component).toHaveCSS('min-height', '72px');
await expect(component).toHaveCSS('height', '80px');
await expect(component).toHaveCSS('bottom', '64px');
await expect(component).toHaveCSS('top', '60px');
await expect(component).toHaveCSS('right', '56px');
await expect(component).toHaveCSS('left', '52px');
});
19 changes: 9 additions & 10 deletions packages/components/src/helpers/layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import { type screens } from '../../tailwind.config';
export type Breakpoint = keyof typeof screens;
export type Responsive<TValue> = ({ [key in Breakpoint]?: TValue } & { base?: TValue }) | TValue;

// `(string & {})` allows for arbitrary strings to be passed in while keeping the suggestion of the union type
// eslint-disable-next-line @typescript-eslint/ban-types
export type SizeValue = (string & {}) | 'auto' | 'fit-content' | 'intrinsic' | 'max-content' | 'min-content';
export type SizeValue = SpacingValue | 'auto' | 'fit-content' | 'intrinsic' | 'max-content' | 'min-content';

// `(string & {})` allows for arbitrary strings to be passed in while keeping the suggestion of the union type
// eslint-disable-next-line @typescript-eslint/ban-types
type SpacingValue = (string & {}) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64;
type SpacingValue =
// `(string & {})` and `(number & {})` allows for arbitrary strings/numbers to be passed in while keeping the suggestion of the union type
// eslint-disable-next-line @typescript-eslint/ban-types
(string & {}) | (number & {}) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 8 | 10 | 12 | 16 | 20 | 24 | 32 | 40 | 48 | 56 | 64;

type OverflowValue = 'visible' | 'hidden' | 'clip' | 'scroll' | 'auto';
type PositionValue = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky';
Expand Down Expand Up @@ -121,17 +120,17 @@ export type LayoutComponentProps = {
/**
* The top property of the component.
*/
top?: Responsive<string>;
top?: Responsive<SpacingValue>;
/**
* The right property of the component.
*/
right?: Responsive<string>;
right?: Responsive<SpacingValue>;
/**
* The bottom property of the component.
*/
bottom?: Responsive<string>;
bottom?: Responsive<SpacingValue>;
/**
* The left property of the component.
*/
left?: Responsive<string>;
left?: Responsive<SpacingValue>;
};
24 changes: 12 additions & 12 deletions packages/components/src/helpers/propsToCssVariables.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ describe('propsToCssVariables', () => {
it('should convert basic props to CSS variables', () => {
const props = { m: 10, p: 20 };
const expected = {
'--margin': 10,
'--padding': 20,
'--margin': '2.5rem',
'--padding': '5rem',
};
expect(propsToCssVariables(props)).toStrictEqual(expected);
});
Expand All @@ -18,7 +18,7 @@ describe('propsToCssVariables', () => {
const props = { x: 5 };
const extraAbbreviationToCssProperty = { x: 'custom-x' };
const expected = {
'--custom-x': 5,
'--custom-x': '1.25rem',
};
expect(propsToCssVariables(props, extraAbbreviationToCssProperty)).toStrictEqual(expected);
});
Expand All @@ -38,11 +38,11 @@ describe('propsToCssVariables', () => {
p: { sm: 5, lg: 15 },
};
const expected = {
'--sm-margin': 10,
'--md-margin': 20,
'--lg-margin': 30,
'--sm-padding': 5,
'--lg-padding': 15,
'--sm-margin': '2.5rem',
'--md-margin': '5rem',
'--lg-margin': '7.5rem',
'--sm-padding': '1.25rem',
'--lg-padding': '3.75rem',
};
expect(propsToCssVariables(props)).toStrictEqual(expected);
});
Expand All @@ -53,7 +53,7 @@ describe('propsToCssVariables', () => {
columns: { sm: 2, md: 'repeat(4, 1fr)' },
};
const expected = {
'--margin': 10,
'--margin': '2.5rem',
'--sm-grid-template-columns': 'repeat(2, 1fr)',
'--md-grid-template-columns': 'repeat(4, 1fr)',
};
Expand All @@ -64,8 +64,8 @@ describe('propsToCssVariables', () => {
const props = { m: 10, x: 5 };
const extraAbbreviationToCssProperty = { m: 'margin-custom', x: 'padding-custom' };
const expected = {
'--margin-custom': 10,
'--padding-custom': 5,
'--margin-custom': '2.5rem',
'--padding-custom': '1.25rem',
};
expect(propsToCssVariables(props, extraAbbreviationToCssProperty)).toStrictEqual(expected);
});
Expand All @@ -81,7 +81,7 @@ describe('propsToCssVariables', () => {
it('should not add blocklisted attributes as CSS variables', () => {
const props = { m: 10, 'aria-label': 'foo', fooBar: 'fooBar', role: 'foo' };
const expected = {
'--margin': 10,
'--margin': '2.5rem',
'--foo-bar': 'fooBar',
};
expect(propsToCssVariables(props)).toStrictEqual(expected);
Expand Down
6 changes: 6 additions & 0 deletions packages/components/src/helpers/propsToCssVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ const transformValueBasedOnKey = (key: string, value: string | number): string |
}
return value;
}

// Spacing tokens
if (typeof value === 'number') {
return `${value * 0.25}rem`;
}

return value;
};

Expand Down

0 comments on commit 25211f7

Please sign in to comment.