Skip to content

Commit d918379

Browse files
authored
Add leadingComponent slot to section intro component (#878)
1 parent bfe4061 commit d918379

6 files changed

+77
-1
lines changed

.changeset/early-roses-pump.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@primer/react-brand': patch
3+
---
4+
5+
Added `leadingComponent` slot to `SectionIntro` for inserting arbitrary visuals and JSX above the heading.
6+
7+
```jsx
8+
<SectionIntro leadingComponent={() => <img src="leading-visual.png" alt="description of your leading visual" />}>
9+
<SectionIntro.Heading>...</SectionIntro.Heading>
10+
<SectionIntro.Description>...</SectionIntro.Description>
11+
<SectionIntro.Link>...</SectionIntro.Link>
12+
</SectionIntro>
13+
```
14+
15+
🔗 [See Storybook example](https://primer.style/brand/storybook?path=/story/components-sectionintro-features--leading-component)

packages/react/src/SectionIntro/SectionIntro.features.stories.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react'
22
import {Meta} from '@storybook/react'
33
import {SectionIntro} from '.'
4+
import {CopilotIcon} from '@primer/octicons-react'
45

56
export default {
67
title: 'Components/SectionIntro/Features',
@@ -84,3 +85,15 @@ export const FullWidth = () => (
8485
</SectionIntro.Heading>
8586
</SectionIntro>
8687
)
88+
89+
export const LeadingComponent = () => {
90+
const Image = () => <CopilotIcon size={40} />
91+
92+
return (
93+
<SectionIntro leadingComponent={Image} align="center">
94+
<SectionIntro.Heading>
95+
<b>Expressive headline</b> about an exclusive set of features.
96+
</SectionIntro.Heading>
97+
</SectionIntro>
98+
)
99+
}

packages/react/src/SectionIntro/SectionIntro.test.tsx

+19
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,23 @@ describe('SectionIntro', () => {
214214

215215
expect(results).toHaveNoViolations()
216216
})
217+
218+
it('accepts a leading component that renders in the correct location', () => {
219+
const mockImage = 'mockImage.png'
220+
221+
const {getByRole, container} = render(
222+
<SectionIntro leadingComponent={() => <img src={mockImage} alt="mock" />}>
223+
<SectionIntro.Heading>{mockHeading}</SectionIntro.Heading>
224+
<SectionIntro.Description>{mockDescription}</SectionIntro.Description>
225+
<SectionIntro.Link href="#">{mockLinkText}</SectionIntro.Link>
226+
</SectionIntro>,
227+
)
228+
229+
const imgEl = getByRole('img')
230+
expect(imgEl).toBeInTheDocument()
231+
expect(imgEl).toHaveAttribute('src', mockImage)
232+
233+
const sectionIntroEl = container.firstChild
234+
expect(sectionIntroEl?.firstChild).toBe(imgEl)
235+
})
217236
})

packages/react/src/SectionIntro/SectionIntro.tsx

+21-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,30 @@ import type {BaseProps} from '../component-helpers'
1313
export type SectionIntroProps = {
1414
align?: 'start' | 'center'
1515
fullWidth?: boolean
16+
/**
17+
* Escape-hatch for inserting custom React components.
18+
* Warning:
19+
* This prop isn't advertised in our docs but remains part of the public API for edge-cases.
20+
* Need to use this prop? Please check in with #primer-brand first to confirm correct usage.
21+
*/
22+
leadingComponent?: React.FunctionComponent
1623
} & React.HTMLAttributes<HTMLHeadingElement> &
1724
BaseProps<HTMLHeadingElement>
1825

1926
const Root = forwardRef<HTMLHeadingElement, PropsWithChildren<SectionIntroProps>>(
20-
({align = 'start', animate, className, children, fullWidth = false, style, ...props}, ref) => {
27+
(
28+
{
29+
align = 'start',
30+
animate,
31+
className,
32+
children,
33+
fullWidth = false,
34+
leadingComponent: LeadingComponent,
35+
style,
36+
...props
37+
},
38+
ref,
39+
) => {
2140
const {classes: animationClasses, styles: animationInlineStyles} = useAnimation(animate)
2241

2342
return (
@@ -33,6 +52,7 @@ const Root = forwardRef<HTMLHeadingElement, PropsWithChildren<SectionIntroProps>
3352
{...props}
3453
style={{...animationInlineStyles, ...style}}
3554
>
55+
{LeadingComponent && <LeadingComponent />}
3656
{children}
3757
</header>
3858
)

packages/react/src/SectionIntro/SectionIntro.visual.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,13 @@ test.describe('Visual Comparison: SectionIntro', () => {
9292
await page.waitForTimeout(500)
9393
expect(await page.screenshot({fullPage: true})).toMatchSnapshot()
9494
})
95+
96+
test('SectionIntro / Leading Component', async ({page}) => {
97+
await page.goto(
98+
'http://localhost:6006/iframe.html?args=&id=components-sectionintro-features--leading-component&viewMode=story',
99+
)
100+
101+
await page.waitForTimeout(500)
102+
expect(await page.screenshot({fullPage: true})).toMatchSnapshot()
103+
})
95104
})

0 commit comments

Comments
 (0)