Skip to content

Commit 48fd5dc

Browse files
Widen support for ref, as and asElement props
1 parent 82f03d7 commit 48fd5dc

File tree

74 files changed

+2013
-1180
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+2013
-1180
lines changed
Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
1-
import React, { ComponentPropsWithoutRef, FC } from 'react';
1+
import React, { ComponentPropsWithoutRef, FC, forwardRef } from 'react';
22
import classNames from 'classnames';
33

44
interface DetailsProps extends ComponentPropsWithoutRef<'details'> {
55
expander?: boolean;
66
}
77

8-
const DetailsComponent: FC<DetailsProps> = ({ className, expander, ...rest }) => (
9-
<details
10-
className={classNames('nhsuk-details', { 'nhsuk-expander': expander }, className)}
11-
{...rest}
12-
/>
8+
const DetailsComponent = forwardRef<HTMLDetailsElement, DetailsProps>(
9+
({ className, expander, ...rest }, forwardedRef) => (
10+
<details
11+
className={classNames('nhsuk-details', { 'nhsuk-expander': expander }, className)}
12+
ref={forwardedRef}
13+
{...rest}
14+
/>
15+
),
1316
);
1417

15-
const DetailsSummary: FC<ComponentPropsWithoutRef<'div'>> = ({ className, children, ...rest }) => (
16-
<summary className={classNames('nhsuk-details__summary', className)} {...rest}>
17-
<span className="nhsuk-details__summary-text">{children}</span>
18-
</summary>
18+
const DetailsSummary = forwardRef<HTMLElement, ComponentPropsWithoutRef<'div'>>(
19+
({ children, className, ...rest }, forwardedRef) => (
20+
<summary
21+
className={classNames('nhsuk-details__summary', className)}
22+
ref={forwardedRef}
23+
{...rest}
24+
>
25+
<span className="nhsuk-details__summary-text">{children}</span>
26+
</summary>
27+
),
1928
);
2029

2130
const DetailsText: FC<ComponentPropsWithoutRef<'div'>> = ({ className, ...rest }) => (
@@ -34,5 +43,5 @@ ExpanderGroup.displayName = 'Details.ExpanderGroup';
3443
export default Object.assign(DetailsComponent, {
3544
Summary: DetailsSummary,
3645
Text: DetailsText,
37-
ExpanderGroup: ExpanderGroup,
46+
ExpanderGroup,
3847
});

src/components/content-presentation/details/__tests__/Details.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { createRef } from 'react';
22
import { render } from '@testing-library/react';
33
import Details from '../';
44

@@ -21,6 +21,17 @@ describe('Details', () => {
2121
expect(container.querySelector('.nhsuk-expander')).toBeTruthy();
2222
});
2323

24+
it('forwards refs', () => {
25+
const ref = createRef<HTMLDetailsElement>();
26+
27+
const { container } = render(<Details ref={ref} />);
28+
29+
const detailsEl = container.querySelector('details');
30+
31+
expect(ref.current).toBe(detailsEl);
32+
expect(ref.current).toHaveClass('nhsuk-details');
33+
});
34+
2435
describe('Details.Summary', () => {
2536
it('matches snapshot', () => {
2637
const { container } = render(<Details.Summary>Content</Details.Summary>);

src/components/content-presentation/do-and-dont-list/DoAndDontList.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
'use client';
2-
import React, { ComponentPropsWithoutRef, FC, createContext, useContext, ReactNode } from 'react';
2+
import React, {
3+
ComponentPropsWithoutRef,
4+
FC,
5+
createContext,
6+
useContext,
7+
ReactNode,
8+
forwardRef,
9+
} from 'react';
310
import classNames from 'classnames';
411
import { Tick, Cross } from '@components/content-presentation/icons';
512
import HeadingLevel, { HeadingLevelProps } from '@components/utils/HeadingLevel';
@@ -15,16 +22,9 @@ interface DoAndDontListProps
1522

1623
const DoAndDontListContext = createContext<ListType>('do');
1724

18-
const DoAndDontListComponent: FC<DoAndDontListProps> = ({
19-
className,
20-
listType,
21-
children,
22-
heading,
23-
headingLevel,
24-
...rest
25-
}) => {
26-
return (
27-
<div className={classNames('nhsuk-do-dont-list', className)} {...rest}>
25+
const DoAndDontListComponent = forwardRef<HTMLDivElement, DoAndDontListProps>(
26+
({ className, listType, children, heading, headingLevel, ...rest }, forwardedRef) => (
27+
<div className={classNames('nhsuk-do-dont-list', className)} ref={forwardedRef} {...rest}>
2828
<HeadingLevel className="nhsuk-do-dont-list__label" headingLevel={headingLevel}>
2929
{heading || (listType === 'do' ? 'Do' : "Don't")}
3030
</HeadingLevel>
@@ -38,8 +38,8 @@ const DoAndDontListComponent: FC<DoAndDontListProps> = ({
3838
<DoAndDontListContext.Provider value={listType}>{children}</DoAndDontListContext.Provider>
3939
</ul>
4040
</div>
41-
);
42-
};
41+
),
42+
);
4343

4444
interface DoAndDontItemProps extends ComponentPropsWithoutRef<'li'> {
4545
listItemType?: ListType;

src/components/content-presentation/do-and-dont-list/__tests__/DoAndDontList.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { createRef } from 'react';
22
import { render } from '@testing-library/react';
33
import DoAndDontList from '../';
44

@@ -10,6 +10,17 @@ describe('DoAndDontList', () => {
1010
expect(container).toMatchSnapshot('DoDontList-Do');
1111
});
1212

13+
it('forwards refs', () => {
14+
const ref = createRef<HTMLDivElement>();
15+
16+
const { container } = render(<DoAndDontList listType="do" ref={ref} />);
17+
18+
const listEl = container.querySelector('div');
19+
20+
expect(ref.current).toBe(listEl);
21+
expect(ref.current).toHaveClass('nhsuk-do-dont-list');
22+
});
23+
1324
it('adds the correct headings', () => {
1425
const { container } = render(<DoAndDontList listType="do" />);
1526

src/components/content-presentation/hero/Hero.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { ComponentPropsWithoutRef, FC } from 'react';
1+
import React, { ComponentPropsWithoutRef, FC, forwardRef } from 'react';
22
import classNames from 'classnames';
33
import { Container, Row, Col } from '../../layout';
44
import HeadingLevel, { HeadingLevelProps } from '@components/utils/HeadingLevel';
@@ -45,25 +45,28 @@ interface HeroProps extends ComponentPropsWithoutRef<'div'> {
4545
imageSrc?: string;
4646
}
4747

48-
const HeroComponent: FC<HeroProps> = ({ className, children, imageSrc, ...rest }) => (
49-
<section
50-
className={classNames(
51-
'nhsuk-hero',
52-
{ 'nhsuk-hero--image': imageSrc },
53-
{ 'nhsuk-hero--image-description': imageSrc && children },
54-
className,
55-
)}
56-
style={imageSrc ? { backgroundImage: `url('${imageSrc}')` } : undefined}
57-
{...rest}
58-
>
59-
{imageSrc ? (
60-
<div className="nhsuk-hero__overlay">
48+
const HeroComponent = forwardRef<HTMLElement, HeroProps>(
49+
({ children, className, imageSrc, ...rest }, forwardedRef) => (
50+
<section
51+
className={classNames(
52+
'nhsuk-hero',
53+
{ 'nhsuk-hero--image': imageSrc },
54+
{ 'nhsuk-hero--image-description': imageSrc && children },
55+
className,
56+
)}
57+
style={imageSrc ? { backgroundImage: `url('${imageSrc}')` } : undefined}
58+
ref={forwardedRef}
59+
{...rest}
60+
>
61+
{imageSrc ? (
62+
<div className="nhsuk-hero__overlay">
63+
<HeroContent hasImage={Boolean(imageSrc)}>{children}</HeroContent>
64+
</div>
65+
) : (
6166
<HeroContent hasImage={Boolean(imageSrc)}>{children}</HeroContent>
62-
</div>
63-
) : (
64-
<HeroContent hasImage={Boolean(imageSrc)}>{children}</HeroContent>
65-
)}
66-
</section>
67+
)}
68+
</section>
69+
),
6770
);
6871

6972
HeroComponent.displayName = 'Hero';

src/components/content-presentation/hero/__tests__/Hero.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { createRef } from 'react';
22
import { render } from '@testing-library/react';
33
import Hero from '..';
44

@@ -10,6 +10,17 @@ describe('Hero', () => {
1010
expect(container).toMatchSnapshot('Hero');
1111
});
1212

13+
it('forwards refs', () => {
14+
const ref = createRef<HTMLElement>();
15+
16+
const { container } = render(<Hero ref={ref} />);
17+
18+
const heroEl = container.querySelector('section');
19+
20+
expect(ref.current).toBe(heroEl);
21+
expect(ref.current).toHaveClass('nhsuk-hero');
22+
});
23+
1324
it('adds correct attributes when imageSrc is provided', () => {
1425
const { container } = render(<Hero imageSrc="image.png" />);
1526

src/components/content-presentation/images/Images.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { ComponentPropsWithoutRef, FC } from 'react';
1+
import React, { ComponentPropsWithoutRef, forwardRef } from 'react';
22
import classNames from 'classnames';
33

44
interface ImageProps extends ComponentPropsWithoutRef<'img'> {
55
caption?: string;
66
}
77

8-
const ImagesComponent: FC<ImageProps> = ({ alt = '', className, caption, ...rest }) => (
9-
<figure className="nhsuk-image">
10-
<img className={classNames('nhsuk-image__img', className)} alt={alt} {...rest} />
11-
{caption ? <figcaption className="nhsuk-image__caption">{caption}</figcaption> : null}
12-
</figure>
8+
const ImagesComponent = forwardRef<HTMLElement, ImageProps>(
9+
({ alt = '', className, caption, ...rest }, forwardedRef) => (
10+
<figure className="nhsuk-image" ref={forwardedRef}>
11+
<img className={classNames('nhsuk-image__img', className)} alt={alt} {...rest} />
12+
{caption ? <figcaption className="nhsuk-image__caption">{caption}</figcaption> : null}
13+
</figure>
14+
),
1315
);
1416

1517
ImagesComponent.displayName = 'Images';

src/components/content-presentation/images/__tests__/Images.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { createRef } from 'react';
22
import { render } from '@testing-library/react';
33
import Images from '../';
44

@@ -10,6 +10,17 @@ describe('Images', () => {
1010
expect(container).toMatchSnapshot('Images');
1111
});
1212

13+
it('forwards refs', () => {
14+
const ref = createRef<HTMLElement>();
15+
16+
const { container } = render(<Images ref={ref} />);
17+
18+
const figureEl = container.querySelector('figure');
19+
20+
expect(ref.current).toBe(figureEl);
21+
expect(ref.current).toHaveClass('nhsuk-image');
22+
});
23+
1324
it('renders caption', () => {
1425
const { container } = render(<Images caption="Caption" />);
1526

src/components/content-presentation/inset-text/InsetText.tsx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import React, { ComponentPropsWithoutRef, FC } from 'react';
1+
import React, { ComponentPropsWithoutRef, forwardRef } from 'react';
22
import classNames from 'classnames';
33

4-
const InsetTextComponent: FC<ComponentPropsWithoutRef<'div'>> = ({
5-
className,
6-
children,
7-
...rest
8-
}) => (
9-
<div className={classNames('nhsuk-inset-text', className)} {...rest}>
10-
<span className="nhsuk-u-visually-hidden">Information: </span>
11-
{children}
12-
</div>
4+
type InsetTextProps = ComponentPropsWithoutRef<'div'>;
5+
6+
const InsetTextComponent = forwardRef<HTMLDivElement, InsetTextProps>(
7+
({ className, children, ...rest }, forwardedRef) => (
8+
<div className={classNames('nhsuk-inset-text', className)} ref={forwardedRef} {...rest}>
9+
<span className="nhsuk-u-visually-hidden">Information: </span>
10+
{children}
11+
</div>
12+
),
1313
);
1414

1515
InsetTextComponent.displayName = 'InsetText';

src/components/content-presentation/inset-text/__tests__/InsetText.test.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { createRef } from 'react';
22
import { render } from '@testing-library/react';
33
import InsetText from '../';
44

@@ -9,6 +9,17 @@ describe('InsetText', () => {
99
expect(container).toMatchSnapshot('InsetText');
1010
});
1111

12+
it('forwards refs', () => {
13+
const ref = createRef<HTMLDivElement>();
14+
15+
const { container } = render(<InsetText ref={ref} />);
16+
17+
const insetTextEl = container.querySelector('div');
18+
19+
expect(ref.current).toBe(insetTextEl);
20+
expect(ref.current).toHaveClass('nhsuk-inset-text');
21+
});
22+
1223
it('has default visually hidden text', () => {
1324
const { container } = render(<InsetText />);
1425

0 commit comments

Comments
 (0)