diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 938c815d9522b64d648df581cd39f0f7d6cb1e41..dccb7cad7894b84b4997d6250f4d6c380307358f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "greg", "version": "0.1.0", "dependencies": { + "@navikt/fnrvalidator": "^1.1.4", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", @@ -2805,6 +2806,11 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/@navikt/fnrvalidator": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@navikt/fnrvalidator/-/fnrvalidator-1.1.4.tgz", + "integrity": "sha512-IroYFqa8PpzVxtIk6ifeEQBFzsI2QJX/FSIQyBtJvpp2D8Cb5Z9+hGNFppCILWR+QYbY8Dkk48la7K5fPGXeWQ==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -5155,15 +5161,6 @@ "node": ">=8" } }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -8791,12 +8788,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "node_modules/filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -9297,19 +9288,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -13664,12 +13642,6 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, - "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "optional": true - }, "node_modules/nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -20426,24 +20398,6 @@ "node": ">=0.10.0" } }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/watchpack-chokidar2/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -20916,24 +20870,6 @@ "node": ">=6" } }, - "node_modules/webpack-dev-server/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, "node_modules/webpack-dev-server/node_modules/glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -24205,6 +24141,11 @@ "chalk": "^4.0.0" } }, + "@navikt/fnrvalidator": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@navikt/fnrvalidator/-/fnrvalidator-1.1.4.tgz", + "integrity": "sha512-IroYFqa8PpzVxtIk6ifeEQBFzsI2QJX/FSIQyBtJvpp2D8Cb5Z9+hGNFppCILWR+QYbY8Dkk48la7K5fPGXeWQ==" + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -26057,15 +25998,6 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "optional": true }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "requires": { - "file-uri-to-path": "1.0.0" - } - }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -28906,12 +28838,6 @@ } } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -29300,12 +29226,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "optional": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -32658,12 +32578,6 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, - "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", - "optional": true - }, "nanoid": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", @@ -38010,16 +37924,6 @@ } } }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", @@ -38637,16 +38541,6 @@ "locate-path": "^3.0.0" } }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, "glob-parent": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8347e208a8cde6a655775345a315f2a3458eff85..307f95e4064e6b04df59f8675e9207bc40f0ea21 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@navikt/fnrvalidator": "^1.1.4", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^11.2.7", "@testing-library/user-event": "^12.8.3", diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 3e446301e09b80fec0560fad31d727e4f0e7d106..23b623dc27804e6390f5ce5422ae88d408e85172 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -6,6 +6,7 @@ "language": { "change": "Change language to {{lang}}" }, + "fnr": "National identity number", "header": { "applicationTitleShort": "GREG", "applicationTitleLong": "Guest Registration", diff --git a/frontend/public/locales/nb/common.json b/frontend/public/locales/nb/common.json index 887180ac7c0f29a071985584d002416fa659c419..d916c4911609a188677a2bbf3dffcdf27761dbc4 100644 --- a/frontend/public/locales/nb/common.json +++ b/frontend/public/locales/nb/common.json @@ -6,6 +6,7 @@ "language": { "change": "Bytt språk til {{lang}}" }, + "fnr": "Fødselsnummer", "header":{ "applicationTitleShort": "GREG", "applicationTitleLong": "Gjesteregistrering", diff --git a/frontend/public/locales/nn/common.json b/frontend/public/locales/nn/common.json index 92c11d0febbfabda6240752d39f1573cbd218a8e..db11f21eacd26a8402d7d1244315094909efbe62 100644 --- a/frontend/public/locales/nn/common.json +++ b/frontend/public/locales/nn/common.json @@ -7,6 +7,7 @@ "languageName": "Språk", "change": "Bytt språk til {{lang}}" }, + "fnr": "National identity number", "header":{ "applicationTitleShort": "GREG", "applicationTitleLong": "Gjesteregistrering", diff --git a/frontend/src/components/form/fnr.tsx b/frontend/src/components/form/fnr.tsx new file mode 100644 index 0000000000000000000000000000000000000000..16e356125bdef88dfbae3e90d18164ef31e6b318 --- /dev/null +++ b/frontend/src/components/form/fnr.tsx @@ -0,0 +1,43 @@ +import React from 'react' +import validator from '@navikt/fnrvalidator' +import { UseFormReturn } from 'react-hook-form' + +export function isValidIdnr(data: string) { + const validationResult = validator.idnr(data) + return validationResult.status === 'valid' +} + +interface FnrProps extends Partial<Pick<UseFormReturn, 'register'>> { + name: string + errors?: any +} + +function Fnr(props: FnrProps) { + const { register, name, errors } = props + if (register === undefined) { + return <></> + } + + return ( + <> + <input + type="text" + placeholder={name} + // eslint-disable-next-line react/jsx-props-no-spreading + {...register(name, { + required: 'Fnr is required', + validate: isValidIdnr, + })} + id="fnr" + /> + {errors.fnr && errors.fnr.message} + {errors.fnr && errors.fnr.type === 'validate' && 'Invalid fnr'} + </> + ) +} + +Fnr.defaultProps = { + errors: {}, +} + +export default Fnr diff --git a/frontend/src/components/form/form.tsx b/frontend/src/components/form/form.tsx new file mode 100644 index 0000000000000000000000000000000000000000..305eb2d302e1eb138480cff9399447c69d1c0e92 --- /dev/null +++ b/frontend/src/components/form/form.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import { useForm } from 'react-hook-form' + +interface FormProps { + children: any + onSubmit: any +} + +export default function Form(props: FormProps) { + const { children, onSubmit } = props + const methods = useForm() + const { + handleSubmit, + formState: { errors }, + } = methods + return ( + <form onSubmit={handleSubmit(onSubmit)}> + {React.Children.map(children, (child) => + child.props.name + ? React.createElement(child.type, { + // eslint-disable-next-line react/jsx-props-no-spreading + ...{ + // eslint-disable-next-line react/jsx-props-no-spreading + ...child.props, + register: methods.register, + errors, + key: child.props.name, + }, + }) + : child + )} + </form> + ) +} diff --git a/frontend/src/components/form/index.tsx b/frontend/src/components/form/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1d3d7430ae22ba5e53a288393dc725ec272d84da --- /dev/null +++ b/frontend/src/components/form/index.tsx @@ -0,0 +1,6 @@ +import Form from './form' +import Input from './input' +import Select from './select' +import Fnr from './fnr' + +export { Fnr, Form, Input, Select } diff --git a/frontend/src/components/form/input.tsx b/frontend/src/components/form/input.tsx new file mode 100644 index 0000000000000000000000000000000000000000..231a731c4b4be4fd8e98e684373dbba1bbca134f --- /dev/null +++ b/frontend/src/components/form/input.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import { UseFormReturn } from 'react-hook-form' + +interface InputProps extends Partial<Pick<UseFormReturn, 'register'>> { + name: string + errors?: any + type?: 'text' | 'email' | 'number' +} + +function Input(props: InputProps) { + // eslint-disable-next-line react/jsx-props-no-spreading + const { register, name, errors, type, ...rest } = props + if (register === undefined) { + return <></> + } + // eslint-disable-next-line react/jsx-props-no-spreading + return <input {...register(name)} {...rest} /> +} + +Input.defaultProps = { + type: 'text', + errors: {}, +} + +export default Input diff --git a/frontend/src/components/form/select.tsx b/frontend/src/components/form/select.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e5eadbd4a9ce5ad91738f41946c79404eb99132a --- /dev/null +++ b/frontend/src/components/form/select.tsx @@ -0,0 +1,31 @@ +import React from 'react' +import { UseFormReturn } from 'react-hook-form' + +interface SelectProps extends Partial<Pick<UseFormReturn, 'register'>> { + options: Array<string> + name: string + type?: 'text' | 'email' | 'number' +} + +function Select(props: SelectProps) { + const { register, options, name, ...rest } = props + if (register === undefined) { + return <></> + } + return ( + // eslint-disable-next-line react/jsx-props-no-spreading + <select {...register(name)} {...rest}> + {options.map((value) => ( + <option key={value} value={value}> + {value} + </option> + ))} + </select> + ) +} + +Select.defaultProps = { + type: 'text', +} + +export default Select diff --git a/frontend/src/routes/frontpage/index.tsx b/frontend/src/routes/frontpage/index.tsx index 29b2159c657bc51e985c5d121d72544940264c10..660d8d828220ff9678375c3dba128f579f73b4c4 100644 --- a/frontend/src/routes/frontpage/index.tsx +++ b/frontend/src/routes/frontpage/index.tsx @@ -2,10 +2,13 @@ import React, { useState } from 'react' import Page from 'components/page' import { appTimezone, appVersion, appTheme } from 'appConfig' +import { Fnr, Form, Select, Input } from 'components/form' +import { useTranslation } from 'react-i18next' export default function FrontPage() { const [apiHealth, setApiHealth] = useState('not yet') const [didContactApi, setDidContactApi] = useState(false) + const { t } = useTranslation(['common', 'footer']) if (!didContactApi) { setDidContactApi(true) @@ -18,13 +21,22 @@ export default function FrontPage() { setApiHealth(result) } }) - // eslint-disable-next-line @typescript-eslint/no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars .catch((error) => { setApiHealth('error') }) } + return ( <Page header="Greg main page"> + <Form onSubmit={() => {}}> + <Input name="firstName" /> + <Input name="lastName" /> + <Select name="gender" options={['female', 'male', 'other']} /> + <Fnr name={t('common:fnr')} /> + <button type="submit">Submit</button> + </Form> + <br /> Version {appVersion} <br /> Timezone {appTimezone}