Skip to content

Commit 3beeb3c

Browse files
committed
overhaul AutoLayout Alignment property (incl. stories)
1 parent a7c1469 commit 3beeb3c

File tree

4 files changed

+265
-187
lines changed

4 files changed

+265
-187
lines changed
Lines changed: 184 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,35 @@
1-
import React, { ComponentProps } from 'react';
2-
import { Meta } from '@storybook/react';
1+
import React from 'react';
2+
import { Meta, StoryObj } from '@storybook/react';
33
import { BADGE } from '@geometricpanda/storybook-addon-badges';
44
import { RadiusAutoLayout } from './auto-layout';
5-
import { Stories, Title, Description } from '@storybook/addon-docs';
5+
import { AutoLayoutExtendedProps } from './auto-layout.types';
66

7+
const alignmentOptions = [
8+
'topLeft',
9+
'topCenter',
10+
'topRight',
11+
'left',
12+
'center',
13+
'right',
14+
'bottomLeft',
15+
'bottomCenter',
16+
'bottomRight',
17+
] as const;
18+
19+
/**
20+
* The RadiusAutoLayout component duplicates the behaviour of Figma AutoLayout's
21+
* Alignment property. This deviates from how CSS flexbox works in some cases -
22+
* for example when the `direction` is changed, the alignment of the children
23+
* remains the same instead of being dependent on the flex-direction. Also, when
24+
* the `space` property is set to `auto`, this is equivalent to writing
25+
* `justify-content: space-between`, which evenly spaces children regardless of
26+
* direction.
27+
*
28+
* Please see the examples of the different behaviours below.
29+
*
30+
* ## Resources
31+
* [How Figma Alignment Works](https://help.figma.com/hc/en-us/articles/360040451373-Explore-auto-layout-properties#alignment)
32+
*/
733
const meta: Meta<typeof RadiusAutoLayout> = {
834
component: RadiusAutoLayout,
935
title: 'Component Development Kit / Auto Layout/Alignment',
@@ -19,194 +45,180 @@ const meta: Meta<typeof RadiusAutoLayout> = {
1945
patch: process.env.COMPONENT_VERSION?.[2],
2046
},
2147
badges: [BADGE.BETA],
22-
docs: {
23-
page: () => (
24-
<>
25-
<Title>Alignment</Title>
26-
<Description children="We duplicate the way Figma aligns in Auto Layout components."></Description>
27-
<Stories includePrimary={true} />
28-
</>
29-
),
48+
controls: {
49+
// only show controls relevant to this story
50+
include: ['alignment', 'direction', 'space'],
3051
},
3152
},
32-
};
33-
34-
export default meta;
35-
// type Story = StoryObj<typeof RadiusAutoLayout>;
36-
// TODO: apply `Story` type to all stories - causes issues due to parent and children args not existing in original component
37-
38-
const ThreeBoxesTemplateAlignmentHor = {
39-
render: (args: {
40-
parent: ComponentProps<typeof RadiusAutoLayout>;
41-
children: ComponentProps<typeof RadiusAutoLayout>;
42-
}) => (
43-
<RadiusAutoLayout direction="vertical">
44-
<RadiusAutoLayout
45-
direction={args.parent.direction}
46-
space={args.parent.space}
47-
alignment={args.parent.alignment}
48-
width="fill-parent"
49-
height={
50-
args.parent.direction === 'horizontal' ? 'fill-parent' : '100px'
51-
}
52-
padding={{ css: '12px' }}
53-
>
54-
<RadiusAutoLayout
55-
width={args.children.width}
56-
height={20}
57-
fill={{ css: '#D44527' }}
58-
/>
59-
<RadiusAutoLayout
60-
width={args.children.width}
61-
height={40}
62-
fill={{ css: '#D44527' }}
63-
/>
64-
<RadiusAutoLayout
65-
width={args.children.width}
66-
height={60}
67-
fill={{ css: '#D44527' }}
68-
/>
69-
</RadiusAutoLayout>
70-
</RadiusAutoLayout>
71-
),
72-
};
73-
74-
export const AlignmentTop = {
75-
...ThreeBoxesTemplateAlignmentHor,
76-
args: {
77-
parent: {
78-
direction: 'horizontal',
79-
space: 'auto',
80-
alignment: 'top',
53+
argTypes: {
54+
alignment: {
55+
options: alignmentOptions,
8156
},
82-
children: {
83-
width: 100,
57+
direction: {
58+
options: ['horizontal', 'vertical'],
8459
},
85-
},
86-
parameters: {
87-
controls: {
88-
disable: true,
60+
space: {
61+
options: {
62+
auto: 'auto',
63+
fixed: { css: '10px' },
64+
},
65+
table: { defaultValue: { summary: '10px' } },
8966
},
9067
},
91-
};
92-
93-
export const AlignmentCenter = {
94-
...ThreeBoxesTemplateAlignmentHor,
9568
args: {
96-
parent: {
97-
direction: 'horizontal',
98-
space: 'auto',
99-
alignment: 'center',
100-
},
101-
children: {
102-
width: 100,
103-
},
104-
},
105-
parameters: {
106-
controls: {
107-
disable: true,
108-
},
69+
alignment: 'topLeft',
70+
direction: 'vertical',
10971
},
11072
};
11173

112-
export const AlignmentBottom = {
113-
...ThreeBoxesTemplateAlignmentHor,
114-
args: {
115-
parent: {
116-
direction: 'horizontal',
117-
space: 'auto',
118-
alignment: 'bottom',
119-
},
120-
children: {
121-
width: 100,
122-
},
123-
},
124-
parameters: {
125-
controls: {
126-
disable: true,
127-
},
128-
sidebar: { disabled: true },
129-
},
74+
export default meta;
75+
type Story = StoryObj<typeof RadiusAutoLayout>;
76+
77+
const PADDING = 24;
78+
const CIRCLE_SIZE = 12;
79+
80+
/** A circle to show the alignment of the child AutoLayouts */
81+
const Circle = ({
82+
top,
83+
left,
84+
bottom,
85+
right,
86+
}: {
87+
top?: number | string;
88+
left?: number | string;
89+
bottom?: number | string;
90+
right?: number | string;
91+
}) => {
92+
return (
93+
<div
94+
style={{
95+
backgroundColor: '#D9D9D9',
96+
height: CIRCLE_SIZE,
97+
width: CIRCLE_SIZE,
98+
borderRadius: '50%',
99+
position: 'absolute',
100+
top,
101+
left,
102+
bottom,
103+
right,
104+
}}
105+
></div>
106+
);
130107
};
131108

132-
const ThreeBoxesTemplateAlignmentVert = {
133-
render: (args: {
134-
parent: ComponentProps<typeof RadiusAutoLayout>;
135-
children: ComponentProps<typeof RadiusAutoLayout>;
136-
}) => (
137-
<RadiusAutoLayout direction="vertical">
109+
/** The positions of each of the circles */
110+
const circles = [
111+
{ top: PADDING, left: PADDING },
112+
{ top: PADDING, left: `calc(50% - ${CIRCLE_SIZE / 2}px)` },
113+
{ top: PADDING, right: PADDING },
114+
{ top: `calc(50% - ${CIRCLE_SIZE / 2}px)`, left: PADDING },
115+
{
116+
top: `calc(50% - ${CIRCLE_SIZE / 2}px)`,
117+
left: `calc(50% - ${CIRCLE_SIZE / 2}px)`,
118+
},
119+
{ top: `calc(50% - ${CIRCLE_SIZE / 2}px)`, right: PADDING },
120+
{ bottom: PADDING, left: PADDING },
121+
{ bottom: PADDING, left: `calc(50% - ${CIRCLE_SIZE / 2}px)` },
122+
{ bottom: PADDING, right: PADDING },
123+
];
124+
125+
const AlignmentDemo = ({
126+
alignment,
127+
direction,
128+
space,
129+
}: {
130+
alignment: AutoLayoutExtendedProps['alignment'];
131+
direction: AutoLayoutExtendedProps['direction'];
132+
space: AutoLayoutExtendedProps['space'];
133+
}) => {
134+
return (
135+
<RadiusAutoLayout
136+
width="284px"
137+
height="284px"
138+
stroke={{ css: '#A6A6A6' }}
139+
strokeWidth={{ css: '1px' }}
140+
padding={{ css: `${PADDING}px` }}
141+
alignment={alignment}
142+
direction={direction}
143+
space={space}
144+
isParent
145+
>
146+
<RadiusAutoLayout
147+
width={direction === 'vertical' ? '80px' : '12px'}
148+
height={direction === 'vertical' ? '12px' : '80px'}
149+
fill={{ css: '#F7856E' }}
150+
style={{ zIndex: 1 }}
151+
/>
138152
<RadiusAutoLayout
139-
direction={args.parent.direction}
140-
space={args.parent.space}
141-
alignment={args.parent.alignment}
142-
width="fill-parent"
143-
height={
144-
args.parent.direction === 'horizontal' ? 'fill-parent' : '100px'
145-
}
146-
padding={{ css: '12px' }}
147-
>
148-
<RadiusAutoLayout width="25%" height={10} fill={{ css: '#D44527' }} />
149-
<RadiusAutoLayout width="50%" height={10} fill={{ css: '#D44527' }} />
150-
<RadiusAutoLayout width="75%" height={10} fill={{ css: '#D44527' }} />
151-
</RadiusAutoLayout>
153+
width={direction === 'vertical' ? '113px' : '12px'}
154+
height={direction === 'vertical' ? '12px' : '113px'}
155+
fill={{ css: '#F7856E' }}
156+
style={{ zIndex: 1 }}
157+
/>
158+
<RadiusAutoLayout
159+
width={direction === 'vertical' ? '65px' : '12px'}
160+
height={direction === 'vertical' ? '12px' : '65px'}
161+
fill={{ css: '#F7856E' }}
162+
style={{ zIndex: 1 }}
163+
/>
164+
{circles.map((positionProps) => (
165+
<Circle {...positionProps} />
166+
))}
152167
</RadiusAutoLayout>
168+
);
169+
};
170+
171+
export const Alignment: Story = {
172+
// @ts-expect-error - bug with `args` type inference due to polymorphism
173+
render: ({ alignment, direction, space }: AutoLayoutExtendedProps) => (
174+
<AlignmentDemo alignment={alignment} direction={direction} space={space} />
153175
),
154176
};
155177

156-
export const AlignmentLeft = {
157-
...ThreeBoxesTemplateAlignmentVert,
158-
args: {
159-
parent: {
160-
direction: 'vertical',
161-
space: 'auto',
162-
alignment: 'left',
163-
},
164-
children: {
165-
width: 100,
166-
},
167-
},
168-
parameters: {
169-
controls: {
170-
disable: true,
171-
},
172-
},
178+
const GridTemplate = ({ direction, space }: AutoLayoutExtendedProps) => {
179+
return (
180+
<div
181+
style={{
182+
width: '100%',
183+
display: 'grid',
184+
gridTemplateColumns: 'repeat(3, 1fr)',
185+
gap: 20,
186+
}}
187+
>
188+
{alignmentOptions.map((alignmentOption) => (
189+
<div
190+
style={{
191+
display: 'flex',
192+
flexDirection: 'column',
193+
fontSize: 12,
194+
fontFamily: 'Riforma LL',
195+
textAlign: 'center',
196+
}}
197+
>
198+
<h3>Alignment {alignmentOption}</h3>
199+
<AlignmentDemo
200+
alignment={alignmentOption}
201+
direction={direction}
202+
space={space}
203+
/>
204+
</div>
205+
))}
206+
</div>
207+
);
173208
};
174209

175-
export const AlignmentCenterVertically = {
176-
...ThreeBoxesTemplateAlignmentVert,
177-
args: {
178-
parent: {
179-
direction: 'vertical',
180-
space: 'auto',
181-
alignment: 'center',
182-
},
183-
children: {
184-
width: 100,
185-
},
186-
},
187-
parameters: {
188-
controls: {
189-
disable: true,
190-
},
191-
},
210+
export const VerticalWithFixedSpacing: Story = {
211+
render: () => <GridTemplate direction="vertical" />,
192212
};
193213

194-
export const AlignmentRight = {
195-
...ThreeBoxesTemplateAlignmentVert,
196-
args: {
197-
parent: {
198-
direction: 'vertical',
199-
space: 'auto',
200-
alignment: 'right',
201-
},
202-
},
203-
parameters: {
204-
name: 'Right',
205-
Title: 'Right',
206-
namespace: 'Right',
207-
controls: {
208-
disable: true,
209-
},
210-
sidebar: { disabled: true },
211-
},
214+
export const HorizontalWithFixedSpacing: Story = {
215+
render: () => <GridTemplate direction="horizontal" />,
216+
};
217+
218+
export const VerticalWithAutoSpacing: Story = {
219+
render: () => <GridTemplate direction="vertical" space="auto" />,
220+
};
221+
222+
export const HorizontalWithAutoSpacing: Story = {
223+
render: () => <GridTemplate direction="horizontal" space="auto" />,
212224
};

0 commit comments

Comments
 (0)