Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add horizontal and vertical alignment options to Row #1330

Merged
merged 12 commits into from
Jul 19, 2024
Merged
44 changes: 44 additions & 0 deletions packages/css/src/components/row/row.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,47 @@
.ams-row--wrap {
flex-wrap: wrap;
}

.ams-row--align-around {
justify-content: space-around;
}

.ams-row--align-between {
justify-content: space-between;
}

.ams-row--align-center {
justify-content: center;
}

.ams-row--align-end {
justify-content: flex-end;
}

.ams-row--align-evenly {
justify-content: space-evenly;
}

.ams-row--align-start {
justify-content: flex-start;
}
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

.ams-row--align-vertical-bottom {
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
align-items: flex-end;
}

.ams-row--align-vertical-baseline {
align-items: baseline;
}

.ams-row--align-vertical-center {
align-items: center;
}

.ams-row--align-vertical-stretch {
align-items: stretch;
}
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

.ams-row--align-vertical-top {
align-items: flex-start;
}
23 changes: 23 additions & 0 deletions packages/react/src/Row/Row.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render, screen } from '@testing-library/react'
import { createRef } from 'react'
import { Row, rowGapSizes } from './Row'
import { crossAlignOptions, mainAlignOptions } from '../common/layout'
import '@testing-library/jest-dom'

describe('Row', () => {
Expand Down Expand Up @@ -64,4 +65,26 @@ describe('Row', () => {

expect(ref.current).toBe(component)
})

describe('Alignment', () => {
mainAlignOptions.map((align) =>
it(`sets the ‘${align}’ alignment`, () => {
const { container } = render(<Row align={align} />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass(`ams-row--align-${align}`)
}),
)

crossAlignOptions.map((align) =>
it(`sets the ‘${align}’ vertical alignment`, () => {
const { container } = render(<Row alignVertical={align} />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass(`ams-row--align-vertical-${align}`)
}),
)
})
})
27 changes: 23 additions & 4 deletions packages/react/src/Row/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,43 @@
import clsx from 'clsx'
import { forwardRef } from 'react'
import type { HTMLAttributes, PropsWithChildren } from 'react'
import type { CrossAlign, MainAlign } from '../common/layout'

export const rowGapSizes: Array<string> = ['extra-small', 'small', 'medium', 'large', 'extra-large']

type RowTag = 'article' | 'div' | 'section'
type RowGap = (typeof rowGapSizes)[number]
type RowTag = 'article' | 'div' | 'section'

export type RowProps = {
/** The horizontal alignment of the items in the row. */
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
align?: MainAlign
/** The vertical alignment of the items in the row. */
alignVertical?: CrossAlign
/** The HTML element to use. */
as?: RowTag
/** The amount of vertical space between items. */
gap?: RowGap
/** Whether items of the row can wrap onto multiple lines. */
wrap?: Boolean
wrap?: boolean
} & PropsWithChildren<HTMLAttributes<HTMLElement>>

export const Row = forwardRef(
({ as: Tag = 'div', children, className, gap = 'medium', wrap = false, ...restProps }: RowProps, ref: any) => (
<Tag {...restProps} ref={ref} className={clsx('ams-row', `ams-row--${gap}`, wrap && 'ams-row--wrap', className)}>
(
{ align, alignVertical, as: Tag = 'div', children, className, gap = 'medium', wrap, ...restProps }: RowProps,
ref: any,
) => (
<Tag
{...restProps}
ref={ref}
className={clsx(
'ams-row',
`ams-row--${gap}`,
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
align && `ams-row--align-${align}`,
alignVertical && `ams-row--align-vertical-${alignVertical}`,
wrap && 'ams-row--wrap',
className,
)}
>
{children}
</Tag>
),
Expand Down
5 changes: 5 additions & 0 deletions packages/react/src/common/layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const crossAlignOptions: Array<string> = ['baseline', 'bottom', 'center', 'stretch', 'top']
export type CrossAlign = (typeof crossAlignOptions)[number]

export const mainAlignOptions: Array<string> = ['around', 'between', 'center', 'end', 'evenly', 'start']
export type MainAlign = (typeof mainAlignOptions)[number]
1 change: 1 addition & 0 deletions packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

/* Append here */
export * from './common/layout'
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
export * from './FormErrorList'
export * from './TableOfContents'
export * from './ErrorMessage'
Expand Down
15 changes: 15 additions & 0 deletions storybook/src/components/Row/Row.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,21 @@ Wrap a Row around a set of components that need the same amount of white space b

<Controls />

### Horizontal Alignment

Items can be aligned horizontally to the start, center or end of the row.
Or whitespace can be distributed between, around or evenly around them.
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

<Canvas of={RowStories.HorizontalAlignment} />
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved

### Vertical Alignment

Items can be aligned vertically to the top, middle, or bottom of the row.
They can also align to their common text baseline.
By default, they stretch to fill the height of the row.

<Canvas of={RowStories.VerticalAlignment} />

### Wrapping

Items that may not fit together on a single line should be allowed to wrap onto multiple lines.
Expand Down
47 changes: 39 additions & 8 deletions storybook/src/components/Row/Row.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,23 @@
* Copyright Gemeente Amsterdam
*/

import { Button, Paragraph, Row } from '@amsterdam/design-system-react/src'
import { Button, crossAlignOptions, mainAlignOptions, Paragraph, Row } from '@amsterdam/design-system-react/src'
import { Meta, StoryObj } from '@storybook/react'

const ThreeButtons = Array.from(Array(3).keys()).map((i) => <Button key={i}>Button {i + 1}</Button>)

const FourBoxes = Array.from(Array(4).keys()).map((i) => (
<Paragraph className="ams-docs-pink-box" key={i}>
Wrapping row item {i + 1}
</Paragraph>
))
const ThreeParagraphs = [
<Paragraph className="ams-docs-pink-box" key={1} style={{ inlineSize: '8rem' }}>
One line
</Paragraph>,
<Paragraph className="ams-docs-pink-box" key={2} style={{ inlineSize: '8rem' }}>
Two
<br />
lines
</Paragraph>,
<Paragraph className="ams-docs-pink-box" key={3} style={{ inlineSize: '8rem' }}>
One line
</Paragraph>,
]

const meta = {
title: 'Components/Layout/Row',
Expand All @@ -21,6 +28,14 @@ const meta = {
children: ThreeButtons,
},
argTypes: {
align: {
control: 'radio',
options: mainAlignOptions,
},
alignVertical: {
control: 'radio',
options: crossAlignOptions,
},
gap: {
control: 'radio',
options: ['extra-small', 'small', 'medium', 'large', 'extra-large'],
Expand All @@ -34,9 +49,25 @@ type Story = StoryObj<typeof meta>

export const Default: Story = {}

export const HorizontalAlignment: Story = {
args: {
align: 'evenly',
children: ThreeParagraphs,
},
}

export const VerticalAlignment: Story = {
args: {
alignVertical: 'center',
children: ThreeParagraphs,
},
}

export const Wrapping: Story = {
args: {
children: FourBoxes,
children: Array.from(Array(4).keys()).map((i) => (
<Paragraph className="ams-docs-pink-box" key={i} style={{ inlineSize: '16rem' }} />
VincentSmedinga marked this conversation as resolved.
Show resolved Hide resolved
)),
wrap: true,
},
}