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 Column #1428

Merged
merged 14 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/css/src/components/column/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

# Column

Stacks its children vertically and adds a vertical gap between them.
Stacks its children vertically with gaps between them and offers alignment options.
32 changes: 32 additions & 0 deletions packages/css/src/components/column/column.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,35 @@
}
}
}

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

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

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

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

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

.ams-column--align-horizontal-center {
align-items: center;
}

.ams-column--align-horizontal-end {
align-items: flex-end;
}

.ams-column--align-horizontal-start {
align-items: flex-start;
}
2 changes: 2 additions & 0 deletions packages/css/src/components/row/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
<!-- @license CC0-1.0 -->

# Row

Stacks its children horizontally with gaps between them and offers alignment options.
23 changes: 23 additions & 0 deletions packages/react/src/Column/Column.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 { Column, columnGapSizes } from './Column'
import { crossAlignOptionsForColumn, mainAlignOptions } from '../common/layout'
import '@testing-library/jest-dom'

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

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

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

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

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

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

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

expect(component).toHaveClass(`ams-column--align-horizontal-${align}`)
}),
)
})
})
39 changes: 33 additions & 6 deletions packages/react/src/Column/Column.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,49 @@
import clsx from 'clsx'
import { forwardRef } from 'react'
import type { HTMLAttributes, PropsWithChildren } from 'react'
import type { CrossAlignForColumn, MainAlign } from '../common/layout'

export const columnGapSizes: Array<string> = ['none', 'extra-small', 'small', 'large', 'extra-large'] as const
export const columnGapSizes = ['none', 'extra-small', 'small', 'large', 'extra-large'] as const

type ColumnTag = 'article' | 'div' | 'section'
type ColumnGap = (typeof columnGapSizes)[number]
type ColumnTag = 'article' | 'div' | 'section'

export type ColumnProps = {
/** The HTML element to use. */
/**
* The vertical alignment of the items in the column.
* @default start
*/
align?: MainAlign
alimpens marked this conversation as resolved.
Show resolved Hide resolved
/**
* The horizontal alignment of the items in the column.
* @default stretch
*/
alignHorizontal?: CrossAlignForColumn
/**
* The HTML element to use.
* @default div
*/
as?: ColumnTag
/** The amount of vertical space between items. */
/**
* The amount of space between items.
* @default medium
*/
gap?: ColumnGap
} & PropsWithChildren<HTMLAttributes<HTMLElement>>

export const Column = forwardRef(
({ as: Tag = 'div', children, className, gap, ...restProps }: ColumnProps, ref: any) => (
<Tag {...restProps} ref={ref} className={clsx('ams-column', gap && `ams-column--gap-${gap}`, className)}>
({ align, alignHorizontal, as: Tag = 'div', children, className, gap, ...restProps }: ColumnProps, ref: any) => (
<Tag
{...restProps}
ref={ref}
className={clsx(
'ams-column',
align && `ams-column--align-${align}`,
alignHorizontal && `ams-column--align-horizontal-${alignHorizontal}`,
gap && `ams-column--gap-${gap}`,
className,
)}
>
{children}
</Tag>
),
Expand Down
27 changes: 21 additions & 6 deletions packages/react/src/Row/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,36 @@ import { forwardRef } from 'react'
import type { HTMLAttributes, PropsWithChildren } from 'react'
import type { CrossAlign, MainAlign } from '../common/layout'

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

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

export type RowProps = {
/** The horizontal alignment of the items in the row. */
/**
* The horizontal alignment of the items in the row.
* @default start
*/
align?: MainAlign
/** The vertical alignment of the items in the row. */
/**
* The vertical alignment of the items in the row.
* @default stretch
*/
alignVertical?: CrossAlign
/** The HTML element to use. */
/**
* The HTML element to use.
* @default div
*/
as?: RowTag
/** The amount of vertical space between items. */
/**
* The amount of space between items.
* @default medium
*/
gap?: RowGap
/** Whether items of the row can wrap onto multiple lines. */
/**
* Whether items of the row can wrap onto multiple lines.
* @default false
*/
wrap?: boolean
} & PropsWithChildren<HTMLAttributes<HTMLElement>>

Expand Down
9 changes: 7 additions & 2 deletions packages/react/src/common/layout.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
export const crossAlignOptions: Array<string> = ['baseline', 'center', 'end', 'start'] as const
export const crossAlignOptions = ['start', 'center', 'baseline', 'end'] as const
export type CrossAlign = (typeof crossAlignOptions)[number]

export const mainAlignOptions: Array<string> = ['around', 'between', 'center', 'end', 'evenly'] as const
// Baseline alignment doesn’t make much sense in a column.
// This separate type for it clarifies its appearance in Storybook.
export const crossAlignOptionsForColumn = crossAlignOptions.filter((option) => option !== 'baseline')
export type CrossAlignForColumn = (typeof crossAlignOptionsForColumn)[number]

export const mainAlignOptions = ['center', 'end', 'between', 'around', 'evenly'] as const
export type MainAlign = (typeof mainAlignOptions)[number]
63 changes: 51 additions & 12 deletions storybook/config/preview-body.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
<style>
:root {
--ams-docs-grey: rgba(0, 0, 0, 0.0625);
--ams-docs-pink: rgba(229, 0, 130, 0.25);
}

.ams-docs-figure {
display: flex;
gap: 0.25rem;
Expand Down Expand Up @@ -33,18 +38,6 @@
width: fit-content;
}

.ams-docs-paragraph {
font-family: "Amsterdam Sans", sans-serif;
font-size: 1rem;
line-height: 1.5;
}

.ams-docs-pink-box {
background-color: rgba(229, 0, 130, 25%); /* stylelint-disable-line color-function-notation */
padding-block: 2rem;
text-align: center;
}

.ams-docs-card {
border: 0.0625rem solid hotpink;
display: flex;
Expand All @@ -60,6 +53,52 @@
padding: 1rem; /* stylelint-disable-line */
}

.ams-docs-column,
.ams-docs-row {
background: repeating-linear-gradient(
135deg,
var(--ams-docs-grey),
var(--ams-docs-grey) 0.5rem,
white 0.5rem,
white 1rem
);
border: thin solid var(--ams-docs-grey);
}

.ams-docs-column {
max-inline-size: 16rem;
min-block-size: 16rem;
}

.ams-docs-row {
min-block-size: 6rem;
}

.ams-docs-grid {
/* Todo columns background */
}

.ams-docs-item {
background-color: var(--ams-docs-pink);
border: thin dashed var(--ams-docs-grey);
font-family: "Amsterdam Sans", sans-serif;
font-size: var(--ams-paragraph-small-font-size);
line-height: var(--ams-paragraph-small-line-height);
margin-block: 0;
margin-inline: 0;
padding-block: 1.5rem;
text-align: center;
}

.ams-docs-row > .ams-docs-item {
flex-basis: 8rem;
padding-inline: 1.5rem;
}

.ams-docs-column > .ams-docs-item {
min-inline-size: 3rem;
}

[class*="ams-docs-token-preview--"] {
block-size: 1rem;
}
Expand Down
43 changes: 35 additions & 8 deletions storybook/src/components/Column/Column.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@ import README from "../../../../packages/css/src/components/column/README.md?raw

<Markdown>{README}</Markdown>

## Design

The five [space](/docs/foundation-design-tokens-space--docs) sizes are available for the size of the gap.

## How to Use

Wrap a Column around a set of components that need the same amount of white space between them.

To add white space below a single element, the [Margin Bottom](/docs/utilities-css-margin--docs) utility class can be used as well.
- Wrap a Column around a set of elements that need the same amount of white space between them.
- The five sizes of [Component Space](/docs/foundation-design-tokens-space--docs) are available for the width or height of the gap.
- To add white space below a single element, you can add a [Margin Bottom](/docs/utilities-css-margin--docs) instead.
- Align the elements horizontally or vertically through the alignment props.

## Examples

Expand All @@ -26,9 +23,39 @@ To add white space below a single element, the [Margin Bottom](/docs/utilities-c

<Controls />

### Use Another HTML Element
### Alignment

Items in the column can be aligned vertically in several ways:

- **Center**: center items within the column.
- **End**: align items to the bottom of the column – to the bottom.
- **Space Between**: distribute whitespace between items.
- **Space Around**: distribute whitespace around items.
- **Space Evenly**: distribute whitespace evenly around items.

By default, items align to the **start** of the column – at the top.
This option has no class name or prop value.

This example centers three items vertically.

<Canvas of={ColumnStories.Alignment} />

### Horizontal alignment

Items in the column can be aligned horizontally in several ways:

- **Start**: align items to the start of the column. This is the left side in left-to-right languages.
- **Center**: center items within the column.
- **End**: align items to the end of the column. This is the right side in left-to-right languages.

This example centers three items horizontally.

<Canvas of={ColumnStories.HorizontalAlignment} />

### Use another HTML element

By default, a Column renders a `<div>`.
Use the `as` prop to make your markup more semantic.
In this example, the Column uses a `<section>` element to group the heading and the cards.

<Canvas of={ColumnStories.CustomTagName} />
Loading