Skip to content

Commit

Permalink
Convert class components into functional components with hooks.
Browse files Browse the repository at this point in the history
  • Loading branch information
the-nathan-smith authored and Joshua Bates committed Apr 8, 2024
1 parent 3d8fe18 commit f47127b
Show file tree
Hide file tree
Showing 106 changed files with 1,465 additions and 1,669 deletions.
2 changes: 1 addition & 1 deletion bundle-base.tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@form-elements/*": ["src/components/form-elements/*"],
"@navigation/*": ["src/components/navigation/*"],
"@typography/*": ["src/components/typography/*"],
"@utils/*": ["src/utils/*"],
"@util/*": ["src/util/*"],
"@patterns/*": ["src/patterns/*"]
}
},
Expand Down
13 changes: 10 additions & 3 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import terser from '@rollup/plugin-terser';
import external from 'rollup-plugin-peer-deps-external';
import dts from 'rollup-plugin-dts';
import { dts } from 'rollup-plugin-dts';
import preserveDirectives from 'rollup-plugin-preserve-directives';

import tsBuildConfig from './bundle-base.tsconfig.json' assert { type: 'json' };
import packageJson from './package.json' assert { type: 'json' };

// suppresses warnings printed to console as part of bundling components with directives present.
Expand Down Expand Up @@ -72,9 +73,15 @@ export default [
},
// type bundling
{
input: 'dist/esm/index.d.ts',
input: 'src/index.ts',
output: [{ file: 'dist/index.d.ts', format: 'esm' }],
external: [],
plugins: [dts()],
plugins: [
dts({
compilerOptions: {
paths: tsBuildConfig.compilerOptions.paths,
},
}),
],
},
];
16 changes: 8 additions & 8 deletions src/components/content-presentation/details/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';

interface DetailsProps extends HTMLProps<HTMLDetailsElement> {
expander?: boolean;
}

interface Details extends React.FC<DetailsProps> {
Summary: React.FC<HTMLProps<HTMLDivElement>>;
Text: React.FC<HTMLProps<HTMLDivElement>>;
ExpanderGroup: React.FC<HTMLProps<HTMLDivElement>>;
interface Details extends FC<DetailsProps> {
Summary: FC<HTMLProps<HTMLDivElement>>;
Text: FC<HTMLProps<HTMLDivElement>>;
ExpanderGroup: FC<HTMLProps<HTMLDivElement>>;
}

// TODO: Check if standard NHS.UK polyfill "details.polyfill.js" is required
Expand All @@ -19,17 +19,17 @@ const Details: Details = ({ className, expander, ...rest }) => (
/>
);

const DetailsSummary: React.FC<HTMLProps<HTMLDivElement>> = ({ className, children, ...rest }) => (
const DetailsSummary: FC<HTMLProps<HTMLDivElement>> = ({ className, children, ...rest }) => (
<summary className={classNames('nhsuk-details__summary', className)} {...rest}>
<span className="nhsuk-details__summary-text">{children}</span>
</summary>
);

const DetailsText: React.FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
const DetailsText: FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
<div className={classNames('nhsuk-details__text', className)} {...rest} />
);

const ExpanderGroup: React.FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
const ExpanderGroup: FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
<div className={classNames('nhsuk-expander-group', className)} {...rest} />
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import React, { HTMLProps, createContext, useContext, ReactNode } from 'react';
import React, { FC, HTMLProps, createContext, useContext, ReactNode } from 'react';
import classNames from 'classnames';
import { Tick, Cross } from '@components/icons';
import HeadingLevel, { HeadingLevelType } from '@util/HeadingLevel';
Expand All @@ -12,8 +12,8 @@ interface DoAndDontListProps extends HTMLProps<HTMLDivElement> {
headingLevel?: HeadingLevelType;
}

interface DoAndDontList extends React.FC<DoAndDontListProps> {
Item: React.FC<DoAndDontItemProps>;
interface DoAndDontList extends FC<DoAndDontListProps> {
Item: FC<DoAndDontItemProps>;
}

const DoAndDontListContext = createContext<ListType>('do');
Expand Down Expand Up @@ -49,12 +49,7 @@ interface DoAndDontItemProps extends HTMLProps<HTMLLIElement> {
prefixText?: ReactNode;
}

const DoAndDontItem: React.FC<DoAndDontItemProps> = ({
prefixText,
listItemType,
children,
...rest
}) => {
const DoAndDontItem: FC<DoAndDontItemProps> = ({ prefixText, listItemType, children, ...rest }) => {
const listItem = useContext(DoAndDontListContext);
const defaultPrefix = (listItemType || listItem) === 'do' ? null : 'do not ';
const actualPrefix = prefixText === undefined ? defaultPrefix : prefixText;
Expand Down
4 changes: 2 additions & 2 deletions src/components/content-presentation/images/Images.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';

interface ImageProps extends HTMLProps<HTMLImageElement> {
Expand All @@ -8,7 +8,7 @@ interface ImageProps extends HTMLProps<HTMLImageElement> {
caption?: string;
}

const Images: React.FC<ImageProps> = ({ className, caption, ...rest }) => (
const Images: FC<ImageProps> = ({ className, caption, ...rest }) => (
<figure className="nhsuk-image">
{/* eslint-disable-next-line jsx-a11y/alt-text */}
<img className={classNames('nhsuk-image__img', className)} {...rest} />
Expand Down
4 changes: 2 additions & 2 deletions src/components/content-presentation/inset-text/InsetText.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';

interface InsetTextProps extends HTMLProps<HTMLDivElement> {
visuallyHiddenText?: string | false;
}

const InsetText: React.FC<InsetTextProps> = ({
const InsetText: FC<InsetTextProps> = ({
className,
children,
visuallyHiddenText = 'Information: ',
Expand Down
20 changes: 10 additions & 10 deletions src/components/content-presentation/summary-list/SummaryList.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';

const SummaryListRow: React.FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
const SummaryListRow: FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
<div className={classNames('nhsuk-summary-list__row', className)} {...rest} />
);

const SummaryListKey: React.FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
const SummaryListKey: FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
<dt className={classNames('nhsuk-summary-list__key', className)} {...rest} />
);

const SummaryListValue: React.FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
const SummaryListValue: FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
<dd className={classNames('nhsuk-summary-list__value', className)} {...rest} />
);

const SummaryListActions: React.FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
const SummaryListActions: FC<HTMLProps<HTMLDListElement>> = ({ className, ...rest }) => (
<dd className={classNames('nhsuk-summary-list__actions', className)} {...rest} />
);

interface SummaryListProps extends HTMLProps<HTMLDListElement> {
noBorder?: boolean;
}

interface SummaryList extends React.FC<SummaryListProps> {
Row: React.FC<HTMLProps<HTMLDivElement>>;
Key: React.FC<HTMLProps<HTMLDListElement>>;
Value: React.FC<HTMLProps<HTMLDListElement>>;
Actions: React.FC<HTMLProps<HTMLDListElement>>;
interface SummaryList extends FC<SummaryListProps> {
Row: FC<HTMLProps<HTMLDivElement>>;
Key: FC<HTMLProps<HTMLDListElement>>;
Value: FC<HTMLProps<HTMLDListElement>>;
Actions: FC<HTMLProps<HTMLDListElement>>;
}

const SummaryList: SummaryList = ({ className, noBorder, ...rest }) => (
Expand Down
112 changes: 49 additions & 63 deletions src/components/content-presentation/table/Table.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { ComponentProps, HTMLProps, ReactNode } from 'react';
import React, { ComponentProps, FC, HTMLProps, ReactNode, useMemo, useState } from 'react';
import classNames from 'classnames';
import TableBody from './components/TableBody';
import TableCaption from './components/TableCaption';
import TableCell from './components/TableCell';
import TableCell, { TableCellProps } from './components/TableCell';
import TableContainer from './components/TableContainer';
import TableHead from './components/TableHead';
import TableRow from './components/TableRow';
import TablePanel from './components/TablePanel';
import TablePanel, { TablePanelProps } from './components/TablePanel';
import TableContext, { ITableContext } from './TableContext';

interface TableProps extends HTMLProps<HTMLTableElement> {
Expand All @@ -15,69 +15,55 @@ interface TableProps extends HTMLProps<HTMLTableElement> {
captionProps?: ComponentProps<typeof TableCaption>;
}

interface TableState {
headings: string[];
interface Table extends FC<TableProps> {
Body: FC<HTMLProps<HTMLTableSectionElement>>;
Cell: FC<TableCellProps>;
Container: FC<HTMLProps<HTMLDivElement>>;
Head: FC<HTMLProps<HTMLTableSectionElement>>;
Panel: FC<TablePanelProps>;
Row: FC<HTMLProps<HTMLTableRowElement>>;
}

// TODO - turn into functional component
class Table extends React.PureComponent<TableProps, TableState> {
static defaultProps = {
responsive: false,
};

static Container = TableContainer;

static Head = TableHead;

static Row = TableRow;

static Cell = TableCell;

static Body = TableBody;

static Panel = TablePanel;

constructor(props: TableProps) {
super(props);
this.state = {
headings: [],
};
}

setHeadings = (headings: string[]): void => {
const isEqual = headings.reduce(
(prevValue, heading, index) => prevValue && heading === this.state.headings[index],
true,
);

if (!isEqual) this.setState({ headings });
};

render(): JSX.Element {
const { className, responsive, children, caption, captionProps, ...rest } = this.props;

const contextValue: ITableContext = {
const Table = ({
caption,
captionProps,
children,
className,
responsive = false,
...rest
}: TableProps) => {
const [headings, setHeadings] = useState<string[]>([]);

const contextValue: ITableContext = useMemo(() => {
return {
isResponsive: Boolean(responsive),
headings: this.state.headings,
setHeadings: this.setHeadings,
headings,
setHeadings,
};

return (
<TableContext.Provider value={contextValue}>
<table
className={classNames(
{ 'nhsuk-table': !responsive },
{ 'nhsuk-table-responsive': responsive },
className,
)}
{...rest}
>
{caption && <TableCaption {...captionProps}>{caption}</TableCaption>}
{children}
</table>
</TableContext.Provider>
);
}
}
}, [responsive, headings, setHeadings]);

return (
<TableContext.Provider value={contextValue}>
<table
className={classNames(
{ 'nhsuk-table': !responsive },
{ 'nhsuk-table-responsive': responsive },
className,
)}
{...rest}
>
{caption && <TableCaption {...captionProps}>{caption}</TableCaption>}
{children}
</table>
</TableContext.Provider>
);
};

Table.Body = TableBody;
Table.Cell = TableCell;
Table.Container = TableContainer;
Table.Head = TableHead;
Table.Panel = TablePanel;
Table.Row = TableRow;

export default Table;
4 changes: 2 additions & 2 deletions src/components/content-presentation/table/TableHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { isValidElement, ReactElement, ReactNode } from 'react';
import { Children, isValidElement, ReactElement, ReactNode } from 'react';
import TableCell from './components/TableCell';

export const isTableCell = (child: ReactNode): child is ReactElement => {
Expand All @@ -7,7 +7,7 @@ export const isTableCell = (child: ReactNode): child is ReactElement => {

export const getHeadingsFromChildren = (children: ReactNode): string[] => {
const headings: string[] = [];
React.Children.map(children, (child) => {
Children.map(children, (child) => {
if (isTableCell(child)) {
headings.push(child.props.children.toString());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import classNames from 'classnames';
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import TableSectionContext, { TableSection } from '../TableSectionContext';

const TableBody: React.FC<HTMLProps<HTMLTableSectionElement>> = ({
className,
children,
...rest
}) => (
const TableBody: FC<HTMLProps<HTMLTableSectionElement>> = ({ className, children, ...rest }) => (
<tbody className={classNames('nhsuk-table__body', className)} {...rest}>
<TableSectionContext.Provider value={TableSection.BODY}>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { HTMLProps } from 'react';
import React, { FC, HTMLProps } from 'react';
import classNames from 'classnames';

const TableCaption: React.FC<HTMLProps<HTMLTableCaptionElement>> = ({ className, ...rest }) => (
const TableCaption: FC<HTMLProps<HTMLTableCaptionElement>> = ({ className, ...rest }) => (
<caption className={classNames('nhsuk-table__caption', className)} {...rest} />
);
TableCaption.displayName = 'Table.Caption';
Expand Down
Loading

0 comments on commit f47127b

Please sign in to comment.