From 061b6bf27422b38b1165f664da2c66b5f1c2d9a7 Mon Sep 17 00:00:00 2001 From: mheggelund Date: Wed, 25 Oct 2023 14:41:33 +0200 Subject: [PATCH] feat: Add simple input validation --- package.json | 7 +- .../AreaCoordinates.styled.tsx | 6 + .../AreaCoordinates/AreaCoordinates.tsx | 359 +++++++----------- yarn.lock | 15 + 4 files changed, 164 insertions(+), 223 deletions(-) diff --git a/package.json b/package.json index cc30b246..d843901e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "@equinor/eds-data-grid-react": "^0.1.0-beta.2", "@equinor/eds-icons": "^0.19.1", "@equinor/eds-tokens": "^0.9.0", + "@hookform/resolvers": "^3.3.2", "@tanstack/react-query": "^4.32.6", "@tanstack/react-query-devtools": "^4.36.1", "@tanstack/react-table": "^8.9.1", @@ -24,11 +25,13 @@ "openapi-typescript-codegen": "^0.25.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.47.0", "react-router-dom": "^6.8.0", "react-scripts": "5.0.1", "styled-components": "^5.3.9", "typescript": "^4.9.4", - "web-vitals": "^2.1.0" + "web-vitals": "^2.1.0", + "zod": "^3.22.4" }, "scripts": { "start": "react-scripts start", @@ -70,4 +73,4 @@ "openapi-typescript": "^6.3.9", "prettier": "^2.8.2" } -} \ No newline at end of file +} diff --git a/src/components/AreaCoordinates/AreaCoordinates.styled.tsx b/src/components/AreaCoordinates/AreaCoordinates.styled.tsx index 8f5b4567..7d750731 100644 --- a/src/components/AreaCoordinates/AreaCoordinates.styled.tsx +++ b/src/components/AreaCoordinates/AreaCoordinates.styled.tsx @@ -19,6 +19,10 @@ export const CoordinateGroup = styled.div` display: flex; flex-direction: column; + > .autocomplete-error { + color: red; + } + > .autocomplete-error { > div { > div { @@ -33,6 +37,8 @@ export const CoordinateGroup = styled.div` border: solid 2px red; } } + + color: red; } `; diff --git a/src/components/AreaCoordinates/AreaCoordinates.tsx b/src/components/AreaCoordinates/AreaCoordinates.tsx index 39f15761..aab0efd1 100644 --- a/src/components/AreaCoordinates/AreaCoordinates.tsx +++ b/src/components/AreaCoordinates/AreaCoordinates.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react/prop-types */ /* eslint-disable max-lines-per-function */ import { Autocomplete, @@ -5,259 +6,175 @@ import { Button, Input, Label, + Snackbar, Typography, } from '@equinor/eds-core-react'; +import { zodResolver } from '@hookform/resolvers/zod'; import { useQuery } from '@tanstack/react-query'; -import { useEffect, useState } from 'react'; +import { useState } from 'react'; +import { FieldValues, useController, useForm } from 'react-hook-form'; +import { z } from 'zod'; import { AnalogueModelsService } from '../../api/generated/services/AnalogueModelsService'; import optionTypes from '../../features/Compute/ComputeVariogram/CaseCard/CaseCard'; import * as Styled from './AreaCoordinates.styled'; -interface CoordinateType { - top: { - X?: string; - Y?: string; - }; - bottom: { - X?: string; - Y?: string; - }; -} +// const areaSchema = z.object({ +// id: z.number(), +// name: z.string().min(1, { message: 'Ares is required' }), +// }); + +const schema = z.object({ + // area: z.array( + // z.object({ + // id: z.number(), + // name: z.string().min(1, { message: 'Ares is required' }), + // }), + // ), + area: z.string().min(1, { message: 'Ares is required' }), + + topX: z + .string() + .min(1, { message: 'Coordinate is required' }) + .max(12, { message: 'Coordinate is too long, max 12 characters' }), + topY: z + .string() + .min(1, { message: 'Coordinate is required' }) + .max(12, { message: 'Coordinate is too long, max 12 characters' }), + bottomX: z + .string() + .min(1, { message: 'Coordinate is required' }) + .max(12, { message: 'Coordinate is too long, max 12 characters' }), + bottomY: z + .string() + .min(1, { message: 'Coordinate is required' }) + .max(12, { message: 'Coordinate is too long, max 12 characters' }), +}); + +// type innput = z.input; +// type output = z.infer; -type AreaErrorType = { - area?: string; - coordinateTop?: string; - coordinateBottom?: string; -}; export const AreaCoordinates = ({ modelId }: { modelId: string }) => { - const [selectedArea, setSelectedArea] = useState(); - const [areaCoordinats, setAreaCoordinats] = useState({ - top: { - X: undefined, - Y: undefined, - }, - bottom: { - X: undefined, - Y: undefined, - }, - }); - const [errors, setErrors] = useState({}); - const [submitting, setSubmitting] = useState(false); - + const [showSaveAlert, setSaveAlert] = useState(false); const { data, isLoading } = useQuery({ queryKey: ['analogue-models', modelId], queryFn: () => AnalogueModelsService.getApiAnalogueModels1(modelId), }); + function clearStatus() { + setSaveAlert(false); + } + const modelAreas: optionTypes[] = [ { id: 10, name: 'Proximal' }, { id: 11, name: 'Left' }, { id: 12, name: 'Distal' }, ]; - const validateValues = ( - selectedArea?: optionTypes, - areaCoordinats?: CoordinateType, - ) => { - const errors: AreaErrorType = {}; - if (selectedArea === undefined) { - errors.area = 'Area to define coordinates for not selected'; - } + // const selectedArea: optionTypes[] = [{ id: 10, name: 'Proximal' }]; + const areaCoordinats = { + area: '', + topX: '43', + topY: '4133', + bottomX: '89754', + bottomY: '53345', + }; - if ( - areaCoordinats?.top.X === undefined || - areaCoordinats?.top.Y === undefined || - areaCoordinats?.top.X.length < 1 || - areaCoordinats?.top.Y.length < 1 - ) { - errors.coordinateTop = 'Top coordinates not selected'; - } + const { control, register, handleSubmit, formState } = useForm({ + defaultValues: areaCoordinats, + resolver: zodResolver(schema), + }); + const { field } = useController({ name: 'area', control }); - if ( - areaCoordinats?.bottom.X === undefined || - areaCoordinats?.bottom.Y === undefined || - areaCoordinats?.bottom.X.length < 1 || - areaCoordinats?.bottom.Y.length < 1 - ) { - errors.coordinateBottom = 'Bottom coordinates not selected'; - } - console.log(errors); + const { errors } = formState; - return errors; - }; + const handleSelectChange = (changes: AutocompleteChanges) => { + console.log(changes); + console.log(changes.selectedItems); + console.log(changes.selectedItems[0]); - const finishSubmit = () => { - console.log(selectedArea); - console.log(areaCoordinats); + field.onChange(changes); + console.log(field.onChange); + console.log(field.value); + console.log(typeof field.value); + console.log(field.value); }; - useEffect(() => { - if (Object.keys(errors).length === 0 && submitting) { - finishSubmit(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [errors, submitting]); - - const saveChange = () => { - setErrors(validateValues(selectedArea, areaCoordinats)); - setSubmitting(true); + const handleSave = (formValues: FieldValues) => { + console.log(formValues); + setSaveAlert(true); }; if (isLoading)

Loading.....

; return ( - - {data?.success && {data.data.name}} - Set coordinates: - - - Area to define - - + + {data?.success && ( + {data.data.name} + )} + Set coordinates: + + option.name} - onOptionsChange={(changes: AutocompleteChanges) => - setSelectedArea(changes.selectedItems[0]) - } - > - - - - Top Left Corner -
-
-
-
-
- - Bottom Right Corner -
-
-
-
-
- {(errors.area || errors.coordinateBottom || errors.coordinateTop) && ( - - Highlighted fields required - - )} - -
-
- ); -}; - -/** - * - * - * -const CoordinateSet = ({ - label, - position, - areaCoordinats, - setAreaCoordinats, -}: { - label: string; - position: { - X: string; - Y: string; - }; - areaCoordinats: CoordinateType; - setAreaCoordinats: (areaCoordinats: CoordinateType) => void; -}) => { - return ( -
- {label} -
-
-
-
-
+ label={'Select area'} + options={modelAreas} + optionLabel={(option) => option.name} + onOptionsChange={handleSelectChange} + > +
+ {errors.area && errors.area?.message} +
+ + + + Top Left Corner +
+
+
+
+
+ + Bottom Right Corner +
+
+
+
+
+ + + + + + {'Area coordinates saved'} + + ); }; - */ diff --git a/yarn.lock b/yarn.lock index 788c30ed..c59ade2c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1424,6 +1424,11 @@ resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.4.tgz" integrity sha512-qprfWkn82Iw821mcKofJ5Pk9wgioHicxcQMxx+5zt5GSKoqdWvgG5AxVmpmUUjzTLPVSH5auBrhI93Deayn/DA== +"@hookform/resolvers@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-3.3.2.tgz#5c40f06fe8137390b071d961c66d27ee8f76f3bc" + integrity sha512-Tw+GGPnBp+5DOsSg4ek3LCPgkBOuOgS5DsDV7qsWNH9LZc433kgsWICjlsh2J9p04H2K66hsXPPb9qn9ILdUtA== + "@humanwhocodes/config-array@^0.11.11": version "0.11.11" resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz" @@ -8428,6 +8433,11 @@ react-error-overlay@^6.0.11: resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== +react-hook-form@^7.47.0: + version "7.47.0" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.47.0.tgz#a42f07266bd297ddf1f914f08f4b5f9783262f31" + integrity sha512-F/TroLjTICipmHeFlMrLtNLceO2xr1jU3CyiNla5zdwsGUGu2UOxxR4UyJgLlhMwLW/Wzp4cpJ7CPfgJIeKdSg== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" @@ -10506,3 +10516,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.22.4: + version "3.22.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff" + integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==