Skip to content
This repository was archived by the owner on Dec 1, 2025. It is now read-only.

Commit 5724813

Browse files
authored
Merge pull request #134 from thepensionsregulator/feature-in-house-admin-card
Feature in house admin card
2 parents 0502d5b + 97896dc commit 5724813

32 files changed

Lines changed: 3208 additions & 2066 deletions

CONTRIBUTING.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ series [How to Contribute to an Open Source Project on GitHub][egghead]
1111
2. `npm yarn install` to setup and validate your clone of the project
1212
3. `npm yarn build` to lerna link dependencies
1313
4. Create a branch for your PR
14+
5. `yarn docz:dev` to start gatsby components site for components development
1415

1516
> Tip: Keep your `master` branch pointing at the original repository and make
1617
> pull requests from branches on your fork. To do this, run:
@@ -34,3 +35,19 @@ Please make sure to run the tests before you commit your changes. You can run
3435
3536
[issues]: https://github.com/thepensionsregulator/react-components/issues
3637
[egghead]: https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github
38+
39+
## Deployment to NPM
40+
41+
Automatic deployment:
42+
43+
- Branch names that starts with `rc`, `feature` or `bug` will be automatically published to NPM with a `next` tag on a `patch` version, only if they pass build and tests on CircleCI. The status of deployment will be visible on a pull request.
44+
45+
Manual deployment:
46+
47+
- When you're ready to deploy `minor` or `major` versions you can do so from updated `develop` branch in your terminal running command `yarn deploy` and follow the instructions in your terminal. `Lerna` will bump all versions to chosen ones and will push git refs to github and new packages to npm registry.
48+
49+
## Deployment to Netlify
50+
51+
Automatic deployment:
52+
53+
- Create a PR to master branch and Netlify will build Gatsby site. If build is successful merge it and production site will be updated.

packages/layout/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ Layout components for TPR apps.
88
npm install @tpr/layout
99

1010
# Peer Dependencies - Also Required
11-
npm install react react-dom @tpr/theming
11+
npm install react react-dom @tpr/core @tpr/forms @tpr/icons @tpr/theming
1212
```

packages/layout/package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@
3434
},
3535
"peerDependencies": {
3636
"react": "^16.12.0",
37-
"react-dom": "^16.12.0"
37+
"react-dom": "^16.12.0",
38+
"@tpr/core": "~2.1.7",
39+
"@tpr/forms": "~2.2.0",
40+
"@tpr/icons": "~2.1.7",
41+
"@tpr/theming": "~2.1.5"
3842
},
3943
"dependencies": {
40-
"@tpr/core": "file:../core",
41-
"@tpr/forms": "file:../forms",
42-
"@tpr/icons": "file:../icons",
43-
"@tpr/theming": "file:../theming",
4444
"@types/lodash": "^4.14.157",
4545
"@xstate/react": "^0.8.1",
4646
"date-fns": "^2.14.0",
@@ -52,7 +52,11 @@
5252
"xstate": "^4.10.0"
5353
},
5454
"devDependencies": {
55-
"cross-env": "^7.0.2"
55+
"cross-env": "^7.0.2",
56+
"@tpr/core": "file:../core",
57+
"@tpr/forms": "file:../forms",
58+
"@tpr/icons": "file:../icons",
59+
"@tpr/theming": "file:../theming"
5660
},
5761
"publishConfig": {
5862
"access": "public"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { Employer } from '../cards/employer/employer';
4+
import { axe } from 'jest-axe';
5+
6+
const noop = () => Promise.resolve();
7+
8+
const employer = {
9+
schemeRoleId: 123,
10+
employerType: 'principal-and-participating' as 'principal-and-participating',
11+
organisationReference: 0,
12+
companiesHouseNumber: 'AB123456',
13+
registeredCharityNumber: '123',
14+
organisationName: 'The Pensions Regulator',
15+
addressLine1: 'Napier House',
16+
addressLine2: 'Trafalgar Pl',
17+
postTown: 'Brighton',
18+
postCode: 'BN1 4DW',
19+
county: 'West Sussex',
20+
countryId: '',
21+
effectiveDate: '1997-04-01T00:00:00',
22+
};
23+
24+
describe('Employer Preview', () => {
25+
test('is accessible', async () => {
26+
const { container } = render(
27+
<Employer
28+
onSaveType={noop}
29+
onRemove={noop}
30+
onCorrect={(_value) => {}}
31+
complete={true}
32+
employer={employer}
33+
/>,
34+
);
35+
36+
const results = await axe(container);
37+
expect(results).toHaveNoViolations();
38+
});
39+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import { render } from '@testing-library/react';
3+
import { InHouseAdmin } from '../cards/inHouseAdmin/inHouseAdmin';
4+
import { axe } from 'jest-axe';
5+
6+
const noop = () => Promise.resolve();
7+
8+
const inHouseAdmin = {
9+
schemeRoleId: '123',
10+
title: 'Mr',
11+
firstname: 'John',
12+
lastname: 'Smoth',
13+
effectiveDate: '1997-04-01T00:00:00',
14+
addressLine1: 'Napier House',
15+
addressLine2: 'Trafalgar Pl',
16+
addressLine3: '',
17+
postTown: 'Brighton',
18+
postCode: 'BN1 4DW',
19+
county: 'West Sussex',
20+
countryId: '',
21+
telephoneNumber: '01273 222 111',
22+
emailAddress: 'john.wick@warnerbros.com',
23+
};
24+
25+
describe('InHouseAdmin Preview', () => {
26+
test('is accessible', async () => {
27+
const { container } = render(
28+
<InHouseAdmin
29+
onSaveContacts={noop}
30+
onSaveAddress={noop}
31+
onSaveName={noop}
32+
onRemove={noop}
33+
onCorrect={(_value) => {}}
34+
complete={true}
35+
addressAPI={{
36+
get: (_endpont) => Promise.resolve(),
37+
limit: 100,
38+
}}
39+
inHouseAdmin={inHouseAdmin}
40+
/>,
41+
);
42+
43+
const results = await axe(container);
44+
expect(results).toHaveNoViolations();
45+
});
46+
});

packages/layout/src/components/cards/components/card.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ export const StyledCardToolbar: React.FC = ({ children }) => {
2424
};
2525

2626
type ToolbarProps = {
27-
type: 'trustee' | 'employer' | 'insurer';
27+
type: 'trustee' | 'employer' | 'insurer' | 'inHouseAdmin';
28+
typeName?: string;
2829
title: string;
2930
subtitle?: string;
3031
};
3132
export const Toolbar: React.FC<ToolbarProps> = ({
3233
type = 'trustee',
34+
typeName,
3335
title,
3436
subtitle,
3537
}) => {
@@ -39,7 +41,9 @@ export const Toolbar: React.FC<ToolbarProps> = ({
3941
cfg={{ flexDirection: 'column', pb: 2 }}
4042
className={styles.toolbarBottomBorder}
4143
>
42-
<P cfg={{ color: 'neutral.6', fontSize: 3 }}>Edit {type}</P>
44+
<P cfg={{ color: 'neutral.6', fontSize: 3 }}>
45+
Edit {typeName ? typeName : type}
46+
</P>
4347
<H3 cfg={{ fontWeight: 3 }}>{title}</H3>
4448
</Flex>
4549
{subtitle && (

packages/layout/src/components/cards/components/content.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import styles from './content.module.scss';
66
export const Loading = () => <div className={styles.loading} />;
77

88
type ContentProps = {
9-
type: 'trustee' | 'employer' | 'insurer';
9+
type: 'trustee' | 'employer' | 'insurer' | 'inHouseAdmin';
10+
typeName?: string;
1011
title?: string;
1112
loading?: boolean;
1213
breadcrumbs?: any;
1314
subtitle?: string;
1415
};
1516
export const Content: React.FC<ContentProps> = ({
1617
type,
18+
typeName,
1719
children,
1820
title,
1921
loading = false,
@@ -29,7 +31,14 @@ export const Content: React.FC<ContentProps> = ({
2931
>
3032
{loading && <Loading />}
3133
{Breadcrumbs && <Breadcrumbs />}
32-
{title && <Toolbar type={type} title={title} subtitle={subtitle} />}
34+
{title && (
35+
<Toolbar
36+
type={type}
37+
typeName={typeName}
38+
title={title}
39+
subtitle={subtitle}
40+
/>
41+
)}
3342
{children}
3443
</div>
3544
);

packages/layout/src/components/cards/employer/context.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface EmployerProviderProps {
4040
}
4141

4242
export type EmployerProps = {
43-
schemeRoleId: number;
43+
schemeRoleId: string | number;
4444
employerType:
4545
| 'participating-employer'
4646
| 'principal-employer'
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React, { createContext, useContext, ReactElement } from 'react';
2+
import { useMachine } from '@xstate/react';
3+
import inHouseAdminMachine, {
4+
InHouseAdminContext as IHAC,
5+
} from './inHouseAdminMachine';
6+
import { State, EventData } from 'xstate';
7+
import { SpaceProps } from '@tpr/core';
8+
import { i18n as i18nDefaults, InHouseAdminI18nProps } from './i18n';
9+
import { useI18n } from '../hooks/use-i18n';
10+
import { splitObjectIntoTwo } from '../../../utils';
11+
12+
export const InHouseAdminContext = createContext<InHouseAdminContextProps>({
13+
current: {},
14+
send: (_, __) => ({}),
15+
onCorrect: () => {},
16+
onRemove: Promise.resolve,
17+
onSaveContacts: Promise.resolve,
18+
onSaveAddress: Promise.resolve,
19+
onSaveName: Promise.resolve,
20+
i18n: i18nDefaults,
21+
addressAPI: {
22+
get: (endpoint) => Promise.resolve(endpoint),
23+
limit: 50,
24+
},
25+
});
26+
27+
type AddressAPIType = {
28+
/** API instance with auth to get a list of addresses */
29+
get: (endpoint: string) => Promise<any>;
30+
/** limit of items to display per search */
31+
limit: number;
32+
};
33+
34+
type RenderProps = (_props: InHouseAdminContextProps) => ReactElement;
35+
36+
export interface InHouseAdminContextProps
37+
extends Omit<InHouseAdminProviderProps, 'inHouseAdmin'> {
38+
send: (
39+
event: any,
40+
payload?: EventData,
41+
) => Partial<State<IHAC, any, any, any>>;
42+
current: Partial<State<IHAC, any, any, any>>;
43+
}
44+
45+
export type RecursivePartial<T> = {
46+
[P in keyof T]?: RecursivePartial<T[P]>;
47+
};
48+
49+
interface InHouseAdmin {
50+
id: string;
51+
schemeRoleId: string | number;
52+
title: string;
53+
firstname: string;
54+
lastname: string;
55+
effectiveDate: string;
56+
countryId: string;
57+
telephoneNumber: string;
58+
emailAddress: string;
59+
addressAPI: AddressAPIType;
60+
}
61+
62+
export interface InHouseAdminWithContactsProps extends InHouseAdmin {
63+
address: Partial<{
64+
addressLine1: string;
65+
addressLine2: string;
66+
addressLine3: string;
67+
postTown: string;
68+
county: string;
69+
postCode: string;
70+
country: string;
71+
}>;
72+
}
73+
74+
export interface InHouseAdminProps extends InHouseAdmin {
75+
addressLine1: string;
76+
addressLine2: string;
77+
addressLine3: string;
78+
postTown: string;
79+
county: string;
80+
postCode: string;
81+
country: string;
82+
}
83+
84+
export interface InHouseAdminProviderProps {
85+
complete?: boolean;
86+
onCorrect?: (...args: any[]) => void;
87+
onRemove?: (...args: any[]) => Promise<any>;
88+
onSaveContacts?: (...args: any[]) => Promise<any>;
89+
onSaveAddress?: (...args: any[]) => Promise<any>;
90+
onSaveName?: (...args: any[]) => Promise<any>;
91+
testId?: string;
92+
/** inHouseAdmin props from the API */
93+
inHouseAdmin: Partial<InHouseAdminProps>;
94+
children?: RenderProps | ReactElement;
95+
addressAPI: AddressAPIType;
96+
/** overwrite any text that you need */
97+
i18n?: RecursivePartial<InHouseAdminI18nProps>;
98+
/** cfg space props */
99+
cfg?: SpaceProps;
100+
}
101+
102+
const addressFields = [
103+
'addressLine1',
104+
'addressLine2',
105+
'addressLine3',
106+
'postTown',
107+
'county',
108+
'country',
109+
'postCode',
110+
'countryId',
111+
];
112+
113+
export const InHouseAdminProvider = ({
114+
complete,
115+
inHouseAdmin,
116+
children,
117+
i18n: i18nOverrides = {},
118+
...rest
119+
}: InHouseAdminProviderProps) => {
120+
const i18n = useI18n(i18nDefaults, i18nOverrides);
121+
const [modifiedAdmin, adminAddress] = splitObjectIntoTwo(
122+
inHouseAdmin,
123+
addressFields,
124+
);
125+
const [current, send] = useMachine(inHouseAdminMachine, {
126+
context: {
127+
complete,
128+
inHouseAdmin: {
129+
...modifiedAdmin,
130+
address: adminAddress,
131+
},
132+
},
133+
});
134+
135+
const ui =
136+
typeof children === 'function'
137+
? children({ current, send, i18n, ...rest })
138+
: children;
139+
return (
140+
<InHouseAdminContext.Provider value={{ current, send, i18n, ...rest }}>
141+
{ui}
142+
</InHouseAdminContext.Provider>
143+
);
144+
};
145+
146+
export const useInHouseAdminContext = (): InHouseAdminContextProps => {
147+
const inHouseAdminUtils = useContext(InHouseAdminContext);
148+
if (!inHouseAdminUtils) {
149+
throw new Error(
150+
`InHouseAdmin compound components cannot be rendered outside the InHouseAdminProvider component`,
151+
);
152+
}
153+
return inHouseAdminUtils;
154+
};

0 commit comments

Comments
 (0)