Skip to content

Commit d3f1b74

Browse files
authored
docs(eds-core-react): 📝 Banner - clarify complex content usage patterns and improve examples (#3961)
1 parent 93d493e commit d3f1b74

File tree

4 files changed

+330
-42
lines changed

4 files changed

+330
-42
lines changed

‎packages/eds-core-react/src/components/Banner/Banner.docs.mdx‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ Banners can supplement their message using a supporting icon.
6767

6868
<Canvas of={ComponentStories.TextAndIconAndAction} />
6969

70-
### Complex Banner Message (special cases)
70+
### Complex Content (special cases)
7171

72-
While using string content for Banner.Message is the recommended approach for most use cases, there are situations where more complex content might be necessary. For these special cases, Banner.Message now supports ReactNode children.
72+
While using string content for Banner.Message is the recommended approach for most use cases, there are situations where more complex content might be necessary. For these special cases, Banner support ReactNode as children, so you can nest them directly.
7373

7474
<ul>
7575
<li>Use string content for Banner.Message whenever possible to maintain consistency and proper styling.</li>
76-
<li>Only use ReactNode when you have specific requirements that cannot be met with string content.</li>
77-
<li>When using ReactNode content, be mindful of accessibility, responsive behavior, and Equinor design guidelines.</li>
76+
<li>Only use complex content inside Banner when you have specific requirements that cannot be met with string content.</li>
77+
<li>When using complex content in Banner, be mindful of accessibility, responsive behavior, and Equinor design guidelines.</li>
7878
</ul>
7979

80-
Some examples where ReactNode might be appropriate:
80+
Some examples where complex content inside Banner might be appropriate:
8181
- Content requiring specific formatting (bold text, code elements)
8282
- Content with structured information that benefits from hierarchical display
8383
- Content with links
8484

85-
<Canvas of={ComponentStories.ComplexBannerMessage} />
85+
<Canvas of={ComponentStories.ComplexBannerContent} />

‎packages/eds-core-react/src/components/Banner/Banner.stories.tsx‎

Lines changed: 58 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { StoryFn, Meta } from '@storybook/react'
22
import { Stack } from '../../../.storybook/components/'
3-
import { Banner, Icon, Button, BannerProps } from '../..'
3+
import { Banner, Icon, Button, BannerProps, Typography } from '../..'
44
import { save, thumbs_up, thumbs_down, mood_sad } from '@equinor/eds-icons'
55
import page from './Banner.docs.mdx'
66

@@ -134,33 +134,34 @@ export const TextAndIconAndAction: StoryFn<BannerProps> = () => (
134134
</>
135135
)
136136
TextAndIconAndAction.storyName = 'Text and icon and actions'
137-
138-
export const ComplexBannerMessage: StoryFn<BannerProps> = () => (
137+
export const ComplexBannerContent: StoryFn<BannerProps> = () => (
139138
<>
140139
<Banner>
141140
<Banner.Icon variant="warning">
142141
<Icon name="thumbs_down" />
143142
</Banner.Icon>
144-
<Banner.Message>
145-
<div>
146-
<strong>Important update required</strong>
147-
<p style={{ margin: '4px 0' }}>
148-
Your project contains{' '}
149-
<a href="#deprecated">3 deprecated components</a> that need to be
150-
updated before June 2025.
151-
</p>
152-
<code
153-
style={{
154-
background: '#f5f5f5',
155-
padding: '2px 4px',
156-
borderRadius: '2px',
157-
fontSize: '0.9em',
158-
}}
159-
>
160-
ComponentA, ComponentB, ComponentC
161-
</code>
162-
</div>
163-
</Banner.Message>
143+
<div>
144+
<Typography variant="body_short" style={{ fontWeight: 'bold' }}>
145+
Important update required
146+
</Typography>
147+
<Typography variant="body_long" style={{ margin: '4px 0' }}>
148+
Your project contains{' '}
149+
<a href="#deprecated">3 deprecated components</a> that need to be
150+
updated before June 2025.
151+
</Typography>
152+
<Typography
153+
variant="caption"
154+
style={{
155+
background: '#f5f5f5',
156+
padding: '2px 4px',
157+
borderRadius: '2px',
158+
display: 'inline-block',
159+
fontFamily: 'monospace',
160+
}}
161+
>
162+
ComponentA, ComponentB, ComponentC
163+
</Typography>
164+
</div>
164165
<Banner.Actions>
165166
<Button>View details</Button>
166167
</Banner.Actions>
@@ -170,24 +171,45 @@ export const ComplexBannerMessage: StoryFn<BannerProps> = () => (
170171
<Banner.Icon>
171172
<Icon name="thumbs_up" />
172173
</Banner.Icon>
173-
<Banner.Message>
174-
<div>
174+
<div>
175+
<Typography variant="body_long">
175176
Project status:{' '}
176-
<span style={{ color: 'green', fontWeight: 'bold' }}>Active</span>
177-
<ul style={{ margin: '4px 0', paddingLeft: '20px' }}>
178-
<li>Last updated: May 15, 2025</li>
179-
<li>Contributors: 8</li>
180-
<li>
181-
Health check: <span style={{ color: 'green' }}>Passing</span>
182-
</li>
183-
</ul>
184-
</div>
185-
</Banner.Message>
177+
<Typography
178+
variant="body_long"
179+
as="span"
180+
style={{ color: 'green', fontWeight: 'bold' }}
181+
>
182+
Active
183+
</Typography>
184+
</Typography>
185+
<Typography
186+
variant="body_long"
187+
as="ul"
188+
style={{ margin: '4px 0', paddingLeft: '20px' }}
189+
>
190+
<Typography variant="body_long" as="li">
191+
Last updated: May 15, 2025
192+
</Typography>
193+
<Typography variant="body_long" as="li">
194+
Contributors: 8
195+
</Typography>
196+
<Typography variant="body_long" as="li">
197+
Health check:{' '}
198+
<Typography
199+
variant="body_long"
200+
as="span"
201+
style={{ color: 'green' }}
202+
>
203+
Passing
204+
</Typography>
205+
</Typography>
206+
</Typography>
207+
</div>
186208
<Banner.Actions>
187209
<Button>View dashboard</Button>
188210
<Button variant="outlined">Export report</Button>
189211
</Banner.Actions>
190212
</Banner>
191213
</>
192214
)
193-
ComplexBannerMessage.storyName = 'Complex Banner Message'
215+
ComplexBannerContent.storyName = 'Complex Banner Content'

‎packages/eds-core-react/src/components/Banner/Banner.test.tsx‎

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { add } from '@equinor/eds-icons'
66
import { Banner } from '.'
77
import { Icon } from '../Icon'
88
import * as tokens from './Banner.tokens'
9+
import { Typography } from '../Typography'
910

1011
Icon.add({ add })
1112

@@ -145,3 +146,177 @@ describe('Banner', () => {
145146
)
146147
})
147148
})
149+
describe('Banner with complex content', () => {
150+
it('Matches snapshot with complex content', () => {
151+
const { asFragment } = render(
152+
<Banner>
153+
<Banner.Icon>
154+
<Icon data={add} />
155+
</Banner.Icon>
156+
<div>
157+
<strong>Important update</strong>
158+
<p>Complex content with HTML elements</p>
159+
</div>
160+
</Banner>,
161+
)
162+
expect(asFragment()).toMatchSnapshot()
163+
})
164+
165+
it('Should pass a11y test with complex content', async () => {
166+
const { container } = render(
167+
<StyledBanner>
168+
<Banner.Icon variant="warning">
169+
<Icon name="add" />
170+
</Banner.Icon>
171+
<div>
172+
<h3>Important update required</h3>
173+
<p>
174+
Your project contains{' '}
175+
<a href="#deprecated">3 deprecated components</a> that need to be
176+
updated.
177+
</p>
178+
<ul>
179+
<li>ComponentA</li>
180+
<li>ComponentB</li>
181+
</ul>
182+
</div>
183+
<Banner.Actions>
184+
<button type="button">View details</button>
185+
</Banner.Actions>
186+
</StyledBanner>,
187+
)
188+
expect(await axe(container)).toHaveNoViolations()
189+
})
190+
191+
it('Has provided complex HTML content', () => {
192+
const bannerHeading = 'Important Update'
193+
const bannerText = 'Your project needs attention'
194+
render(
195+
<Banner>
196+
<div>
197+
<h3>{bannerHeading}</h3>
198+
<p>{bannerText}</p>
199+
<ul>
200+
<li>Item 1</li>
201+
<li>Item 2</li>
202+
</ul>
203+
</div>
204+
</Banner>,
205+
)
206+
expect(screen.getByText(bannerHeading)).toBeDefined()
207+
expect(screen.getByText(bannerText)).toBeDefined()
208+
expect(screen.getByRole('list')).toBeDefined()
209+
expect(screen.getAllByRole('listitem')).toHaveLength(2)
210+
})
211+
212+
it('Has complex content with Icon and Actions', () => {
213+
const bannerText = 'Complex banner content'
214+
const actionButtonText = 'Action button'
215+
const iconTestId = 'banner-icon-test'
216+
render(
217+
<Banner>
218+
<Banner.Icon>
219+
<Icon name="add" data-testid={iconTestId} />
220+
</Banner.Icon>
221+
<div>
222+
<strong>{bannerText}</strong>
223+
<p>Additional paragraph content</p>
224+
</div>
225+
<Banner.Actions>
226+
<button type="button">{actionButtonText}</button>
227+
</Banner.Actions>
228+
</Banner>,
229+
)
230+
expect(screen.getByText(bannerText)).toBeDefined()
231+
expect(screen.getByText('Additional paragraph content')).toBeDefined()
232+
expect(screen.getByText(actionButtonText)).toBeDefined()
233+
expect(screen.getByTestId(iconTestId)).toBeDefined()
234+
})
235+
236+
it('Allows nested HTML without DOM nesting warnings', () => {
237+
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
238+
239+
render(
240+
<Banner>
241+
<div>
242+
<h4>Nested content test</h4>
243+
<p>
244+
This paragraph contains <a href="/link">a link</a> and{' '}
245+
<code>code elements</code>.
246+
</p>
247+
<ul>
248+
<li>
249+
List item with <strong>bold text</strong>
250+
</li>
251+
</ul>
252+
</div>
253+
</Banner>,
254+
)
255+
256+
// Verify no validateDOMNesting warnings
257+
const domNestingErrors = consoleSpy.mock.calls.filter((call) => {
258+
return (
259+
call.length > 0 &&
260+
typeof call[0] === 'string' &&
261+
call[0].includes('validateDOMNesting')
262+
)
263+
})
264+
expect(domNestingErrors).toHaveLength(0)
265+
266+
consoleSpy.mockRestore()
267+
})
268+
269+
it('Supports both simple text and complex content', () => {
270+
render(
271+
<div>
272+
<Banner data-testid="simple-banner">
273+
<Banner.Message>Simple text message</Banner.Message>
274+
</Banner>
275+
276+
<Banner data-testid="complex-banner">
277+
<div>
278+
<h3>Complex HTML content</h3>
279+
<p>With multiple elements</p>
280+
</div>
281+
</Banner>
282+
283+
<Banner data-testid="direct-text-banner">
284+
Just plain text without wrapper
285+
</Banner>
286+
</div>,
287+
)
288+
289+
expect(screen.getByText('Simple text message')).toBeDefined()
290+
expect(screen.getByText('Complex HTML content')).toBeDefined()
291+
expect(screen.getByText('With multiple elements')).toBeDefined()
292+
expect(screen.getByText('Just plain text without wrapper')).toBeDefined()
293+
})
294+
it('Supports Typography components for styled text', () => {
295+
render(
296+
<Banner>
297+
<Banner.Icon variant="warning">
298+
<Icon data={add} />
299+
</Banner.Icon>
300+
<div>
301+
<Typography variant="body_short" style={{ fontWeight: 'bold' }}>
302+
Styled heading with Typography
303+
</Typography>
304+
<Typography variant="body_long">
305+
Descriptive text using Typography component
306+
</Typography>
307+
</div>
308+
<Banner.Actions>
309+
<button type="button">View details</button>
310+
</Banner.Actions>
311+
</Banner>,
312+
)
313+
314+
expect(
315+
screen.getByText('Styled heading with Typography'),
316+
).toBeInTheDocument()
317+
expect(
318+
screen.getByText('Descriptive text using Typography component'),
319+
).toBeInTheDocument()
320+
expect(screen.getByText('View details')).toBeInTheDocument()
321+
})
322+
})

0 commit comments

Comments
 (0)