Skip to content

Commit

Permalink
add Label child support to Bento and River components
Browse files Browse the repository at this point in the history
  • Loading branch information
rezrah committed Dec 19, 2023
1 parent 419555e commit 7134054
Show file tree
Hide file tree
Showing 19 changed files with 117 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/tiny-laws-remember.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react-brand': patch
---

Added Label child support for River and Bento component
3 changes: 2 additions & 1 deletion apps/docs/content/components/River/react.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {River, RiverBreakout} from '@primer/react-brand'
/>
</River.Visual>
<River.Content>
<Label>Label</Label>
<Heading>Heading</Heading>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In sapien sit
Expand Down Expand Up @@ -230,7 +231,7 @@ import {River, RiverBreakout} from '@primer/react-brand'

### River.Content and RiverBreakout.Content <Label>Required</Label>

[`Text`](https://primer.style/brand/components/Text),[`Heading`](https://primer.style/brand/components/Heading), [`Link`](https://primer.style/brand/components/Link) are the only `children` accepted. They can be composed in any order, but their rendered output will always be in a predetermined order.
[`Label`](https://primer.style/brand/components/Label), [`Text`](https://primer.style/brand/components/Text),[`Heading`](https://primer.style/brand/components/Heading), [`Link`](https://primer.style/brand/components/Link) are the only `children` accepted. They can be composed in any order, but their rendered output will always be in a predetermined order.

| Name | Type | Default | Description |
| :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----: | :----------------------------------------------- |
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/scripts/components-with-animation.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* This file is generated by a script. Do not modify. */

export const supportedComponents = [
'Box',
'Button',
'Box',
'ComparisonTable',
'FAQ',
'Heading',
Expand Down
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"value": "var(--base-size-16)"
}
},
"label": {
"margin": {
"value": "var(--base-size-16)"
}
},
"visual": {
"shadow": {
"value": "0px 100px 80px rgba(0, 0, 0, 0.0174624), 0px 41.7776px 33.4221px rgba(0, 0, 0, 0.0235573), 0px 22.3363px 17.869px rgba(0, 0, 0, 0.0282784), 0px 12.5216px 10.0172px rgba(0, 0, 0, 0.0339075), 0px 6.6501px 5.32008px rgba(0, 0, 0, 0.04317), 0px 2.76726px 2.21381px rgba(0, 0, 0, 0.07)"
Expand Down
4 changes: 4 additions & 0 deletions packages/react/src/Bento/Bento.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@
margin-bottom: var(--base-size-24);
}

.Bento__Content-label {
margin-block-end: var(--base-size-16);
}

/* Column Span */
.Bento__Item--column-span-1 {
grid-column: auto/span 1;
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/Bento/Bento.module.css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ declare const styles: {
readonly "Bento__Item--flow-row": string;
readonly "Bento__call-to-action": string;
readonly "Bento__Content-text": string;
readonly "Bento__Content-label": string;
readonly "Bento__Item--column-span-1": string;
readonly "Bento__Item--column-span-2": string;
readonly "Bento__Item--column-span-3": string;
Expand Down
5 changes: 4 additions & 1 deletion packages/react/src/Bento/Bento.test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {render, cleanup} from '@testing-library/react'
import '@testing-library/jest-dom'
import {Bento, ColumnIndex} from './Bento'
import {Text, Link, ColorModesEnum} from '../'
import {Text, Link, ColorModesEnum, Label} from '../'
import {axe, toHaveNoViolations} from 'jest-axe'

expect.extend(toHaveNoViolations)
Expand Down Expand Up @@ -178,8 +178,10 @@ describe('Bento.Content', () => {
const headingText = 'Allowed'
const textText = 'Allowed 2'
const linkText = 'Allowed 3'
const labelText = 'Allowed 4'
const {getByText} = render(
<Bento.Content>
<Label>{labelText}</Label>
<Bento.Heading>{headingText}</Bento.Heading>
<Text>{textText}</Text>
<Link href="#">{linkText}</Link>
Expand All @@ -189,6 +191,7 @@ describe('Bento.Content', () => {
expect(getByText(headingText)).toBeInTheDocument()
expect(getByText(textText)).toBeInTheDocument()
expect(getByText(linkText)).toBeInTheDocument()
expect(getByText(labelText)).toBeInTheDocument()
})

it('adds the class for padding', () => {
Expand Down
12 changes: 11 additions & 1 deletion packages/react/src/Bento/Bento.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {useWindowSize, BreakpointSize} from '../hooks/useWindowSize'
import type {BaseProps} from '../component-helpers'
import findElementInChildren from '../findElementInChildren'

import {Heading, Text, Link, HeadingProps, TextProps, LinkProps, ColorMode as FullColorMode, Image} from '../'
import {Heading, Text, Link, HeadingProps, TextProps, LinkProps, ColorMode as FullColorMode, Image, Label} from '../'

import '@primer/brand-primitives/lib/design-tokens/css/tokens/functional/components/bento/base.css'
import styles from './Bento.module.css'
Expand Down Expand Up @@ -195,6 +195,8 @@ const Content = ({
const memoizedChildren = useMemo(() => React.Children.toArray(children), [children])

const HeadingChildren = memoizedChildren.filter(child => React.isValidElement(child) && child.type === _Heading)

const LabelChild = memoizedChildren.find(child => React.isValidElement(child) && child.type === Label)
const TextChild = memoizedChildren.find(child => React.isValidElement(child) && child.type === Text)
const LinkChild = memoizedChildren.find(child => React.isValidElement(child) && child.type === Link)

Expand All @@ -205,6 +207,12 @@ const Content = ({
className: styles['Bento__Content-icon'],
size: LeadingVisual['size'] || 44,
})}

{React.isValidElement(LabelChild) &&
React.cloneElement(LabelChild as React.ReactElement<TextProps>, {
className: clsx(styles['Bento__Content-label'], LabelChild.props.className),
})}

{HeadingChildren.map(
HeadingChild =>
React.isValidElement(HeadingChild) &&
Expand All @@ -216,13 +224,15 @@ const Content = ({
),
}),
)}

{React.isValidElement(TextChild) &&
React.cloneElement(TextChild as React.ReactElement<TextProps>, {
variant: TextChild.props.variant || 'muted',
as: TextChild.props.as || 'p',
size: TextChild.props.size || '300',
className: clsx(styles['Bento__Content-text'], TextChild.props.className),
})}

{React.isValidElement(LinkChild) &&
React.cloneElement(LinkChild as React.ReactElement<LinkProps>, {
variant: LinkChild.props.variant || 'accent',
Expand Down
7 changes: 7 additions & 0 deletions packages/react/src/Bento/Bento.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ test.describe('Visual Comparison: Bento', () => {
expect(await page.screenshot()).toMatchSnapshot()
})

test('Bento / With Label', async ({page}) => {
await page.goto('http://localhost:6006/iframe.html?args=&id=components-bento-item--with-label&viewMode=story')

await page.waitForTimeout(500)
expect(await page.screenshot()).toMatchSnapshot()
})

test('Bento / Visual Position Bottom', async ({page}) => {
await page.goto(
'http://localhost:6006/iframe.html?args=&id=components-bento-item--visual-position-bottom&viewMode=story',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 18 additions & 1 deletion packages/react/src/Bento/BentoItem.features.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import {StoryFn, Meta} from '@storybook/react'
import {INITIAL_VIEWPORTS} from '@storybook/addon-viewport'
import {Bento} from '.'
import {Link} from '../'
import {Label, Link} from '../'
import placeholderImage from '../fixtures/images/placeholder-600x400.png'
import styles from './Bento.features.stories.module.css'

Expand Down Expand Up @@ -40,6 +40,23 @@ export const HeadingWithEmphasizedText: StoryFn<typeof Bento> = () => (
</Bento>
)

export const WithLabel: StoryFn<typeof Bento> = () => (
<Bento>
<Bento.Item rowSpan={5} flow="column">
<Bento.Content>
<Label color="red">Label</Label>
<Bento.Heading size="3">
<em>This is my super-sweet</em> bento heading
</Bento.Heading>
<Link href="#">Call to action</Link>
</Bento.Content>
<Bento.Visual position="50% 100%">
<img alt="placeholder, blank area with an gray background color" src={placeholderImage} />
</Bento.Visual>
</Bento.Item>
</Bento>
)

export const VisualPositionBottom: StoryFn<typeof Bento> = () => (
<Bento>
<Bento.Item rowSpan={4}>
Expand Down
17 changes: 16 additions & 1 deletion packages/react/src/river/River/River.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import {Meta, StoryFn} from '@storybook/react'
import {River} from '.'
import {Heading, Link, Stack, Text, Timeline} from '../../'
import {Heading, Label, Link, Stack, Text, Timeline} from '../../'
import {Container} from '../../component-helpers'
import placeholderImage from '../../fixtures/images/placeholder-600x400.png'

Expand Down Expand Up @@ -134,6 +134,21 @@ export const AlternativeHeadingSize: StoryFn<typeof River> = () => (
</Container>
)

export const WithLabel: StoryFn<typeof River> = () => (
<Container>
<River>
<River.Visual>
<PlaceholderImage />
</River.Visual>
<River.Content>
<Label color="green">Label</Label>
<Heading size="1">Heading</Heading>
<Text>Use alternative heading sizes, while maintaining the default heading level.</Text>
</River.Content>
</River>
</Container>
)

const ExampleTrailingComponent = () => (
<Stack direction="vertical" padding="none" gap="spacious" alignItems="flex-start">
<Timeline>
Expand Down
9 changes: 8 additions & 1 deletion packages/react/src/river/River/River.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import React, {render, cleanup} from '@testing-library/react'
import '@testing-library/jest-dom'

import {River} from '../'
import {Text, Link, Heading} from '../../'
import {Text, Link, Heading, Label} from '../../'
import {axe, toHaveNoViolations} from 'jest-axe'

expect.extend(toHaveNoViolations)

describe('River', () => {
const mockLabel = 'Label'
const mockText = 'Minimal description'
const mockHeading = 'Mock heading'
const mockLinkText = 'call to action'
Expand All @@ -25,14 +26,20 @@ describe('River', () => {
<MockImage />
</River.Visual>
<River.Content>
<Label>{mockLabel}</Label>
<Text>{mockText}</Text>
<Link href="#">{mockLinkText}</Link>
</River.Content>
</River>,
)

const textEl = getByText(mockText)
const labelEl = getByText(mockLabel)
const linkEl = getByText(mockLinkText)

expect(textEl).toBeInTheDocument()
expect(labelEl).toBeInTheDocument()
expect(linkEl).toBeInTheDocument()
})

it('renders in 50:50 image ratio mode by default', () => {
Expand Down
25 changes: 17 additions & 8 deletions packages/react/src/river/River/River.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, {forwardRef, PropsWithChildren, type Ref} from 'react'
import React, {forwardRef, PropsWithChildren, useMemo, type Ref} from 'react'
import clsx from 'clsx'
import {Heading, LinkProps, HeadingProps, TextProps, Text, Link, useAnimation} from '../../'
import {Heading, LinkProps, HeadingProps, TextProps, Text, Link, useAnimation, Label, LabelProps} from '../../'

import type {BaseProps} from '../../component-helpers'

Expand Down Expand Up @@ -136,13 +136,15 @@ const Content = forwardRef(
) => {
const {classes: animationClasses, styles: animationInlineStyles} = useAnimation(animate)

const HeadingChild = React.Children.toArray(children).find(
child => React.isValidElement(child) && child.type === Heading,
)
const Children = useMemo(() => React.Children.toArray(children), [children])

const HeadingChild = Children.find(child => React.isValidElement(child) && child.type === Heading)

const TextChild = React.Children.toArray(children).find(child => React.isValidElement(child) && child.type === Text)
const TextChild = Children.find(child => React.isValidElement(child) && child.type === Text)

const LinkChild = React.Children.toArray(children).find(child => React.isValidElement(child) && child.type === Link)
const LinkChild = Children.find(child => React.isValidElement(child) && child.type === Link)

const LabelChild = Children.find(child => React.isValidElement(child) && child.type === Label)

return (
<div
Expand All @@ -151,11 +153,18 @@ const Content = forwardRef(
{...rest}
ref={ref}
>
{LeadingComponent && (
{React.isValidElement(LabelChild) && !LeadingComponent && (
<div className={styles.River__label}>
{React.cloneElement(LabelChild as React.ReactElement<LabelProps>, {})}
</div>
)}

{!LabelChild && LeadingComponent && (
<div>
<LeadingComponent />
</div>
)}

{React.isValidElement(HeadingChild) && (
<div className={styles.River__heading}>
{React.cloneElement(HeadingChild as React.ReactElement<HeadingProps>, {
Expand Down
7 changes: 7 additions & 0 deletions packages/react/src/river/River/River.visual.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ test.describe('Visual Comparison: River', () => {
expect(await page.screenshot()).toMatchSnapshot()
})

test('River / With Label', async ({page}) => {
await page.goto('http://localhost:6006/iframe.html?args=&id=components-river-features--with-label&viewMode=story')

await page.waitForTimeout(500)
expect(await page.screenshot()).toMatchSnapshot()
})

test('River / Custom trailing content', async ({page}) => {
await page.goto(
'http://localhost:6006/iframe.html?args=&id=components-river-features--custom-trailing-content&viewMode=story',
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions packages/react/src/river/river-shared.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
border-top: var(--brand-borderWidth-thin) solid var(--brand-color-border-muted);
}

.River__label {
margin-block-end: var(--brand-River-label-margin);
}

/* Medium breakpoint and up */
@media screen and (min-width: 48rem) {
.River {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/river/river-shared.module.css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare const styles: {
readonly "River__text": string;
readonly "River__trailingComponent": string;
readonly "River__trailingComponent--divider": string;
readonly "River__label": string;
readonly "River--50-50": string;
readonly "River--60-40": string;
readonly "River--align-start": string;
Expand Down

0 comments on commit 7134054

Please sign in to comment.