From 23b8c7cd57e993e4c3713c34cbf4dd9f99decd18 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 09:38:03 +0200 Subject: [PATCH 01/17] Move similar field component to same folder and extract repeated code in selector --- .../MultipleSelector.tsx | 30 ++++++------------- src/webapp/components/selector/Selector.tsx | 19 +++--------- .../selector/utils/selectorHelper.ts | 12 ++++++++ .../{text-area => text-input}/TextArea.tsx | 2 +- .../TextInput.tsx} | 6 ++-- src/webapp/components/utils/selectorHelper.ts | 0 6 files changed, 29 insertions(+), 40 deletions(-) rename src/webapp/components/{multiple-selector => selector}/MultipleSelector.tsx (85%) create mode 100644 src/webapp/components/selector/utils/selectorHelper.ts rename src/webapp/components/{text-area => text-input}/TextArea.tsx (99%) rename src/webapp/components/{input-field/InputField.tsx => text-input/TextInput.tsx} (94%) create mode 100644 src/webapp/components/utils/selectorHelper.ts diff --git a/src/webapp/components/multiple-selector/MultipleSelector.tsx b/src/webapp/components/selector/MultipleSelector.tsx similarity index 85% rename from src/webapp/components/multiple-selector/MultipleSelector.tsx rename to src/webapp/components/selector/MultipleSelector.tsx index d5c3940c..95f402a8 100644 --- a/src/webapp/components/multiple-selector/MultipleSelector.tsx +++ b/src/webapp/components/selector/MultipleSelector.tsx @@ -2,18 +2,13 @@ import React, { useCallback } from "react"; import styled from "styled-components"; import { Select, InputLabel, MenuItem, FormHelperText, Chip } from "@material-ui/core"; import { IconChevronDown24, IconCross16 } from "@dhis2/ui"; - -export type MultipleSelectorOption = { - value: T; - label: string; - disabled?: boolean; -}; +import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; type MultipleSelectorProps = { id: string; selected: T[]; - onChange: (value: MultipleSelectorOption["value"][]) => void; - options: MultipleSelectorOption[]; + onChange: (value: SelectorOption["value"][]) => void; + options: SelectorOption[]; label?: string; placeholder?: string; disabled?: boolean; @@ -26,7 +21,7 @@ type MultipleSelectorProps = { export const MultipleSelector: React.FC = React.memo( ({ id, - label = "", + label, placeholder = "", selected, onChange, @@ -37,13 +32,6 @@ export const MultipleSelector: React.FC = React.memo( error = false, required = false, }) => { - const getLabelFromValue = useCallback( - (value: MultipleSelectorOption["value"]) => { - return options.find(option => option.value === value)?.label || ""; - }, - [options] - ); - const handleChange = useCallback( ( event: React.ChangeEvent<{ @@ -51,7 +39,7 @@ export const MultipleSelector: React.FC = React.memo( }>, _child: React.ReactNode ) => { - const value = event.target.value as MultipleSelectorOption["value"][]; + const value = event.target.value as SelectorOption["value"][]; onChange(value); }, [onChange] @@ -60,7 +48,7 @@ export const MultipleSelector: React.FC = React.memo( const handleDelete = useCallback( ( event: React.MouseEvent, - value: MultipleSelectorOption["value"] + value: SelectorOption["value"] ) => { event.stopPropagation(); onChange(selected?.filter(selection => selection !== value)); @@ -85,12 +73,12 @@ export const MultipleSelector: React.FC = React.memo( IconComponent={IconChevronDown24} error={error} renderValue={(selected: unknown) => - (selected as MultipleSelectorOption["value"][])?.length ? ( + (selected as SelectorOption["value"][])?.length ? (
- {(selected as MultipleSelectorOption["value"][]).map(value => ( + {(selected as SelectorOption["value"][]).map(value => ( } onDelete={event => handleDelete(event, value)} onMouseDown={event => handleDelete(event, value)} diff --git a/src/webapp/components/selector/Selector.tsx b/src/webapp/components/selector/Selector.tsx index 98c0ad41..e7a88c8d 100644 --- a/src/webapp/components/selector/Selector.tsx +++ b/src/webapp/components/selector/Selector.tsx @@ -2,12 +2,7 @@ import React, { useCallback } from "react"; import styled from "styled-components"; import { Select, InputLabel, MenuItem, FormHelperText } from "@material-ui/core"; import { IconChevronDown24 } from "@dhis2/ui"; - -export type SelectorOption = { - value: T; - label: string; - disabled?: boolean; -}; +import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; type SelectorProps = { id: string; @@ -26,7 +21,7 @@ type SelectorProps = { export const Selector: React.FC = React.memo( ({ id, - label = "", + label, placeholder = "", selected, onChange, @@ -37,13 +32,6 @@ export const Selector: React.FC = React.memo( error = false, required = false, }) => { - const getLabelFromValue = useCallback( - (value: SelectorOption["value"]) => { - return options.find(option => option.value === value)?.label || ""; - }, - [options] - ); - const handleChange = useCallback( ( event: React.ChangeEvent<{ @@ -74,7 +62,8 @@ export const Selector: React.FC = React.memo( IconComponent={IconChevronDown24} error={error} renderValue={(selected: unknown) => - getLabelFromValue(selected as SelectorOption["value"]) || placeholder + getLabelFromValue(selected as SelectorOption["value"], options) || + placeholder } displayEmpty > diff --git a/src/webapp/components/selector/utils/selectorHelper.ts b/src/webapp/components/selector/utils/selectorHelper.ts new file mode 100644 index 00000000..7b23414f --- /dev/null +++ b/src/webapp/components/selector/utils/selectorHelper.ts @@ -0,0 +1,12 @@ +export type SelectorOption = { + value: T; + label: string; + disabled?: boolean; +}; + +export function getLabelFromValue( + value: SelectorOption["value"], + options: SelectorOption[] +) { + return options.find(option => option.value === value)?.label; +} diff --git a/src/webapp/components/text-area/TextArea.tsx b/src/webapp/components/text-input/TextArea.tsx similarity index 99% rename from src/webapp/components/text-area/TextArea.tsx rename to src/webapp/components/text-input/TextArea.tsx index 8e6342c0..d6f3302b 100644 --- a/src/webapp/components/text-area/TextArea.tsx +++ b/src/webapp/components/text-input/TextArea.tsx @@ -17,7 +17,7 @@ type TextAreaProps = { export const TextArea: React.FC = React.memo( ({ id, - label = "", + label, value, onChange, disabled = false, diff --git a/src/webapp/components/input-field/InputField.tsx b/src/webapp/components/text-input/TextInput.tsx similarity index 94% rename from src/webapp/components/input-field/InputField.tsx rename to src/webapp/components/text-input/TextInput.tsx index c53ac20c..367244bf 100644 --- a/src/webapp/components/input-field/InputField.tsx +++ b/src/webapp/components/text-input/TextInput.tsx @@ -2,7 +2,7 @@ import React from "react"; import { TextField, InputLabel } from "@material-ui/core"; import styled from "styled-components"; -type InputFieldProps = { +type TextInputProps = { id: string; label?: string; value: string; @@ -14,10 +14,10 @@ type InputFieldProps = { error?: boolean; }; -export const InputField: React.FC = React.memo( +export const TextInput: React.FC = React.memo( ({ id, - label = "", + label, value, onChange, helperText = "", diff --git a/src/webapp/components/utils/selectorHelper.ts b/src/webapp/components/utils/selectorHelper.ts new file mode 100644 index 00000000..e69de29b From bb23eef59b0ee4afe21e92ad85ccbff59119a88b Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 09:39:02 +0200 Subject: [PATCH 02/17] Delete default value string in components in label and id --- src/webapp/components/add-new-option/AddNewOption.tsx | 2 +- src/webapp/components/date-picker/DatePicker.tsx | 4 ++-- src/webapp/components/not-applicable-checkbox/NACheckbox.tsx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/webapp/components/add-new-option/AddNewOption.tsx b/src/webapp/components/add-new-option/AddNewOption.tsx index 26adbd2a..16186c5d 100644 --- a/src/webapp/components/add-new-option/AddNewOption.tsx +++ b/src/webapp/components/add-new-option/AddNewOption.tsx @@ -11,7 +11,7 @@ type AddNewOptionProps = { }; export const AddNewOption: React.FC = React.memo( - ({ id, label = "", onAddNewOption }) => { + ({ id, label, onAddNewOption }) => { return ( diff --git a/src/webapp/components/date-picker/DatePicker.tsx b/src/webapp/components/date-picker/DatePicker.tsx index 5715a856..7942ab72 100644 --- a/src/webapp/components/date-picker/DatePicker.tsx +++ b/src/webapp/components/date-picker/DatePicker.tsx @@ -20,8 +20,8 @@ type DatePickerProps = { export const DatePicker: React.FC = React.memo( ({ - id = "", - label = "", + id, + label, value, onChange, disabled = false, diff --git a/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx b/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx index e0026b50..8670fb7f 100644 --- a/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx +++ b/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx @@ -17,7 +17,7 @@ type NACheckboxProps = { export const NACheckbox: React.FC = React.memo( ({ id, - label = "", + label, checked, onChange, helperText = "", From efc8d44c5dba12a61655cf33ccf9d795b93b9bc5 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 09:40:46 +0200 Subject: [PATCH 03/17] Merge forms in one page for create and another for edit and change paths in Router --- .../{form-page => form}/FormPage.tsx | 6 ++-- .../{form-section => form}/FormSection.tsx | 15 ++++++---- .../layout/side-bar/SideBarContent.tsx | 2 +- src/webapp/pages/Router.tsx | 28 ++++++++----------- .../pages/assign-role/AssignRolePage.tsx | 16 ----------- .../pages/create-event/CreateEventPage.tsx | 8 ------ .../pages/create-form/CreateFormPage.tsx | 14 ++++++++++ .../CreateIncidentActionPlanPage.tsx | 16 ----------- .../CreateRiskAssessmentPage.tsx | 16 ----------- src/webapp/pages/edit-form/EditFormPage.tsx | 14 ++++++++++ 10 files changed, 54 insertions(+), 81 deletions(-) rename src/webapp/components/{form-page => form}/FormPage.tsx (90%) rename src/webapp/components/{form-section => form}/FormSection.tsx (82%) delete mode 100644 src/webapp/pages/assign-role/AssignRolePage.tsx delete mode 100644 src/webapp/pages/create-event/CreateEventPage.tsx create mode 100644 src/webapp/pages/create-form/CreateFormPage.tsx delete mode 100644 src/webapp/pages/create-incident-action-plan/CreateIncidentActionPlanPage.tsx delete mode 100644 src/webapp/pages/create-risk-assessment/CreateRiskAssessmentPage.tsx create mode 100644 src/webapp/pages/edit-form/EditFormPage.tsx diff --git a/src/webapp/components/form-page/FormPage.tsx b/src/webapp/components/form/FormPage.tsx similarity index 90% rename from src/webapp/components/form-page/FormPage.tsx rename to src/webapp/components/form/FormPage.tsx index 6fa37af9..e6a3f961 100644 --- a/src/webapp/components/form-page/FormPage.tsx +++ b/src/webapp/components/form/FormPage.tsx @@ -16,13 +16,13 @@ type FormPageProps = { }; export const FormPage: React.FC = React.memo( - ({ title, subtitle = "", saveLabel = "", cancelLabel = "", children, onSave, onCancel }) => { + ({ title, subtitle, saveLabel, cancelLabel, children, onSave, onCancel }) => { return (
- {title ? {title} : null} - {subtitle ? {subtitle} : null} + {title && {title}} + {subtitle && {subtitle}} {i18n.t("Indicates required")}
diff --git a/src/webapp/components/form-section/FormSection.tsx b/src/webapp/components/form/FormSection.tsx similarity index 82% rename from src/webapp/components/form-section/FormSection.tsx rename to src/webapp/components/form/FormSection.tsx index 836a6086..cc28e8a0 100644 --- a/src/webapp/components/form-section/FormSection.tsx +++ b/src/webapp/components/form/FormSection.tsx @@ -28,7 +28,7 @@ export const FormSection: React.FC = React.memo( {hasSeparator && } {title && ( - + {title} @@ -37,7 +37,7 @@ export const FormSection: React.FC = React.memo( )} )} - {children} + {children} ); @@ -55,16 +55,21 @@ const Container = styled.div<{ direction: string }>` width: 100%; gap: ${props => (props.direction === "row" ? "48px" : "24px")}; align-items: ${props => (props.direction === "row" ? "center" : "flex-start")}; + @media (max-width: 600px) { + flex-direction: column; + align-items: flex-start; + } `; -const TitleContainer = styled.div` +const TitleContainer = styled.div<{ direction: string }>` display: flex; align-items: center; gap: 4px; + width: 30%; `; -const FormContainer = styled.div` - width: 100%; +const FormContainer = styled.div<{ fulWidth: boolean }>` + width: ${props => (props.fulWidth ? "100%" : "70%")}; `; const RequiredText = styled.span` diff --git a/src/webapp/components/layout/side-bar/SideBarContent.tsx b/src/webapp/components/layout/side-bar/SideBarContent.tsx index 21372b87..9444d017 100644 --- a/src/webapp/components/layout/side-bar/SideBarContent.tsx +++ b/src/webapp/components/layout/side-bar/SideBarContent.tsx @@ -46,7 +46,7 @@ export const SideBarContent: React.FC = React.memo( const history = useHistory(); const goToCreateEvent = useCallback(() => { - history.push(`/create-event`); + history.push(`/create/diseaseOutbreakEvent`); }, [history]); return ( diff --git a/src/webapp/pages/Router.tsx b/src/webapp/pages/Router.tsx index 8665b9db..cf3aa77e 100644 --- a/src/webapp/pages/Router.tsx +++ b/src/webapp/pages/Router.tsx @@ -6,35 +6,31 @@ import { EventTrackerPage } from "./event-tracker/EventTrackerPage"; import { IncidentActionPlanPage } from "./incident-action-plan/IncidentActionPlanPage"; import { ResourcesPage } from "./resources/ResourcesPage"; import { IMTeamBuilderPage } from "./incident-management-team-builder/IMTeamBuilderPage"; -import { CreateEventPage } from "./create-event/CreateEventPage"; -import { CreateRiskAssessmentPage } from "./create-risk-assessment/CreateRiskAssessmentPage"; -import { CreateIncidentActionPlanPage } from "./create-incident-action-plan/CreateIncidentActionPlanPage"; -import { AssignRolePage } from "./assign-role/AssignRolePage"; +import { EditFormPage } from "./edit-form/EditFormPage"; +import { CreateFormPage } from "./create-form/CreateFormPage"; export function Router() { return ( - } /> - } /> + } /> } + path="/edit/:formType/:diseaseOutbreakEventId/:id?" + render={() => } /> } + path="/event-tracker/:diseaseOutbreakEvent" + render={() => } /> - } /> } + path="/incident-management-team-builder/:diseaseOutbreakEvent" + render={() => } /> } + path="/:diseaseOutbreakEvent/incident-action-plan/:incidentActionPlan" + render={() => } /> - } /> + } /> {/* Default route */} } /> diff --git a/src/webapp/pages/assign-role/AssignRolePage.tsx b/src/webapp/pages/assign-role/AssignRolePage.tsx deleted file mode 100644 index 0d36ad1d..00000000 --- a/src/webapp/pages/assign-role/AssignRolePage.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; - -import { Layout } from "../../components/layout/Layout"; -import i18n from "../../../utils/i18n"; - -export const AssignRolePage: React.FC = React.memo(() => { - return ( - - AssignRolePage - - ); -}); diff --git a/src/webapp/pages/create-event/CreateEventPage.tsx b/src/webapp/pages/create-event/CreateEventPage.tsx deleted file mode 100644 index fb08a60f..00000000 --- a/src/webapp/pages/create-event/CreateEventPage.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from "react"; - -import { Layout } from "../../components/layout/Layout"; -import i18n from "../../../utils/i18n"; - -export const CreateEventPage: React.FC = React.memo(() => { - return ; -}); diff --git a/src/webapp/pages/create-form/CreateFormPage.tsx b/src/webapp/pages/create-form/CreateFormPage.tsx new file mode 100644 index 00000000..af749d80 --- /dev/null +++ b/src/webapp/pages/create-form/CreateFormPage.tsx @@ -0,0 +1,14 @@ +import React from "react"; + +import { Layout } from "../../components/layout/Layout"; +import { FormPage } from "../../components/form/FormPage"; + +export const CreateFormPage: React.FC = React.memo(() => { + return ( + + {}}> +
CreateFormPage
+
+
+ ); +}); diff --git a/src/webapp/pages/create-incident-action-plan/CreateIncidentActionPlanPage.tsx b/src/webapp/pages/create-incident-action-plan/CreateIncidentActionPlanPage.tsx deleted file mode 100644 index 12a9b0be..00000000 --- a/src/webapp/pages/create-incident-action-plan/CreateIncidentActionPlanPage.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; - -import { Layout } from "../../components/layout/Layout"; -import i18n from "../../../utils/i18n"; - -export const CreateIncidentActionPlanPage: React.FC = React.memo(() => { - return ( - - CreateIncidentActionPlanPage - - ); -}); diff --git a/src/webapp/pages/create-risk-assessment/CreateRiskAssessmentPage.tsx b/src/webapp/pages/create-risk-assessment/CreateRiskAssessmentPage.tsx deleted file mode 100644 index c4dcbdc3..00000000 --- a/src/webapp/pages/create-risk-assessment/CreateRiskAssessmentPage.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; - -import { Layout } from "../../components/layout/Layout"; -import i18n from "../../../utils/i18n"; - -export const CreateRiskAssessmentPage: React.FC = React.memo(() => { - return ( - - CreateRiskAssessmentPage - - ); -}); diff --git a/src/webapp/pages/edit-form/EditFormPage.tsx b/src/webapp/pages/edit-form/EditFormPage.tsx new file mode 100644 index 00000000..f1f57979 --- /dev/null +++ b/src/webapp/pages/edit-form/EditFormPage.tsx @@ -0,0 +1,14 @@ +import React from "react"; + +import { Layout } from "../../components/layout/Layout"; +import { FormPage } from "../../components/form/FormPage"; + +export const EditFormPage: React.FC = React.memo(() => { + return ( + + {}}> +
EditFormPage
+
+
+ ); +}); From 2106be7a53c82bab888f91ac82882940b7862fe0 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 09:42:08 +0200 Subject: [PATCH 04/17] Update tranlations --- i18n/en.pot | 28 ++++++++++++++-------------- i18n/es.po | 26 +++++++++++++------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 674d6b9d..18dced0b 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-06-27T12:51:45.020Z\n" -"PO-Revision-Date: 2024-06-27T12:51:45.020Z\n" +"POT-Creation-Date: 2024-06-28T07:41:17.026Z\n" +"PO-Revision-Date: 2024-06-28T07:41:17.026Z\n" msgid "Add new option" msgstr "" @@ -35,18 +35,6 @@ msgstr "" msgid "Last updated: " msgstr "" -msgid "Incident Management Team Builder" -msgstr "" - -msgid "Cholera in NW Province, June 2023" -msgstr "" - -msgid "Incident Action Plan" -msgstr "" - -msgid "Create Risk Assessment" -msgstr "" - msgid "Dashboard" msgstr "" @@ -77,12 +65,21 @@ msgstr "" msgid "Risk Assessment" msgstr "" +msgid "Create Risk Assessment" +msgstr "" + msgid "Risk assessment incomplete" msgstr "" msgid "Risks associated with this event have not yet been assessed." msgstr "" +msgid "Incident Action Plan" +msgstr "" + +msgid "Cholera in NW Province, June 2023" +msgstr "" + msgid "Response actions" msgstr "" @@ -101,5 +98,8 @@ msgstr "" msgid "Edit Team" msgstr "" +msgid "Incident Management Team Builder" +msgstr "" + msgid "Resources" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 797aabe5..502b3435 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-06-27T12:51:45.020Z\n" +"POT-Creation-Date: 2024-06-28T07:41:17.026Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -35,18 +35,6 @@ msgstr "" msgid "Last updated: " msgstr "" -msgid "Incident Management Team Builder" -msgstr "" - -msgid "Cholera in NW Province, June 2023" -msgstr "" - -msgid "Incident Action Plan" -msgstr "" - -msgid "Create Risk Assessment" -msgstr "" - msgid "Dashboard" msgstr "" @@ -77,12 +65,21 @@ msgstr "" msgid "Risk Assessment" msgstr "" +msgid "Create Risk Assessment" +msgstr "" + msgid "Risk assessment incomplete" msgstr "" msgid "Risks associated with this event have not yet been assessed." msgstr "" +msgid "Incident Action Plan" +msgstr "" + +msgid "Cholera in NW Province, June 2023" +msgstr "" + msgid "Response actions" msgstr "" @@ -101,6 +98,9 @@ msgstr "" msgid "Edit Team" msgstr "" +msgid "Incident Management Team Builder" +msgstr "" + msgid "Resources" msgstr "" From 7f8ddf3954486e47a6d9955a619117d99aa809f5 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 09:53:58 +0200 Subject: [PATCH 05/17] Delete lodash library and add a TODO in SearcInput to add debounce function to Collection --- package.json | 2 -- src/webapp/components/search-input/SearchInput.tsx | 9 ++++----- yarn.lock | 5 ----- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 258b6c67..a649d7aa 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "d2": "31.10.2", "d2-manifest": "1.0.0", "font-awesome": "4.7.0", - "lodash": "^4.17.21", "purify-ts": "1.2.0", "purify-ts-extra-codec": "0.6.0", "react": "^18.2.0", @@ -50,7 +49,6 @@ "@testing-library/react": "^14.0.0", "@types/classnames": "2.3.1", "@types/isomorphic-fetch": "^0.0.36", - "@types/lodash": "^4.17.5", "@types/material-ui": "^0.21.12", "@types/node": "18", "@types/node-localstorage": "^1.3.0", diff --git a/src/webapp/components/search-input/SearchInput.tsx b/src/webapp/components/search-input/SearchInput.tsx index 3e0bac51..1e45691e 100644 --- a/src/webapp/components/search-input/SearchInput.tsx +++ b/src/webapp/components/search-input/SearchInput.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useState } from "react"; import { TextField } from "@material-ui/core"; import { IconSearch24 } from "@dhis2/ui"; import styled from "styled-components"; -import _ from "lodash"; import i18n from "../../../utils/i18n"; @@ -19,14 +18,14 @@ export const SearchInput: React.FC = React.memo( useEffect(() => updateStateValue(value), [value]); - // eslint-disable-next-line react-hooks/exhaustive-deps + // TODO: needs debounce function from Collection const onChangeDebounced = useCallback( - _.debounce((value: string) => { + (value: string) => { if (onChange) { onChange(value); } - }, 400), - [] + }, + [onChange] ); const handleChange = useCallback( diff --git a/yarn.lock b/yarn.lock index 2710f263..61d79c4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4283,11 +4283,6 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash@^4.17.5": - version "4.17.5" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.5.tgz#e6c29b58e66995d57cd170ce3e2a61926d55ee04" - integrity sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw== - "@types/material-ui@^0.21.12": version "0.21.12" resolved "https://registry.yarnpkg.com/@types/material-ui/-/material-ui-0.21.12.tgz#208e8b7e49a545bb704fa7e865986afde1b33384" From 13af8a5b4ef466d0bc97f10cf8d22d47c6c8b731 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 10:07:38 +0200 Subject: [PATCH 06/17] Use only one component for the forms --- .../form/{FormPage.tsx => FormLayout.tsx} | 10 +++++----- src/webapp/pages/Router.tsx | 7 +++---- src/webapp/pages/create-form/CreateFormPage.tsx | 14 -------------- src/webapp/pages/edit-form/EditFormPage.tsx | 14 -------------- src/webapp/pages/form/FormPage.tsx | 14 ++++++++++++++ 5 files changed, 22 insertions(+), 37 deletions(-) rename src/webapp/components/form/{FormPage.tsx => FormLayout.tsx} (92%) delete mode 100644 src/webapp/pages/create-form/CreateFormPage.tsx delete mode 100644 src/webapp/pages/edit-form/EditFormPage.tsx create mode 100644 src/webapp/pages/form/FormPage.tsx diff --git a/src/webapp/components/form/FormPage.tsx b/src/webapp/components/form/FormLayout.tsx similarity index 92% rename from src/webapp/components/form/FormPage.tsx rename to src/webapp/components/form/FormLayout.tsx index e6a3f961..a01eeba6 100644 --- a/src/webapp/components/form/FormPage.tsx +++ b/src/webapp/components/form/FormLayout.tsx @@ -5,7 +5,7 @@ import i18n from "../../../utils/i18n"; import { Button } from "../button/Button"; import { Separator } from "../separator/Separator"; -type FormPageProps = { +type FormLayoutProps = { title?: string; subtitle?: string; saveLabel?: string; @@ -15,10 +15,10 @@ type FormPageProps = { onCancel?: () => void; }; -export const FormPage: React.FC = React.memo( +export const FormLayout: React.FC = React.memo( ({ title, subtitle, saveLabel, cancelLabel, children, onSave, onCancel }) => { return ( - +
{title && {title}} @@ -38,12 +38,12 @@ export const FormPage: React.FC = React.memo( )} - + ); } ); -const StyledFormPage = styled.div` +const StyledFormLayout = styled.div` width: 100%; `; diff --git a/src/webapp/pages/Router.tsx b/src/webapp/pages/Router.tsx index cf3aa77e..3ee47f08 100644 --- a/src/webapp/pages/Router.tsx +++ b/src/webapp/pages/Router.tsx @@ -6,17 +6,16 @@ import { EventTrackerPage } from "./event-tracker/EventTrackerPage"; import { IncidentActionPlanPage } from "./incident-action-plan/IncidentActionPlanPage"; import { ResourcesPage } from "./resources/ResourcesPage"; import { IMTeamBuilderPage } from "./incident-management-team-builder/IMTeamBuilderPage"; -import { EditFormPage } from "./edit-form/EditFormPage"; -import { CreateFormPage } from "./create-form/CreateFormPage"; +import { FormPage } from "./form/FormPage"; export function Router() { return ( - } /> + } /> } + render={() => } /> { - return ( - - {}}> -
CreateFormPage
-
-
- ); -}); diff --git a/src/webapp/pages/edit-form/EditFormPage.tsx b/src/webapp/pages/edit-form/EditFormPage.tsx deleted file mode 100644 index f1f57979..00000000 --- a/src/webapp/pages/edit-form/EditFormPage.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; - -import { Layout } from "../../components/layout/Layout"; -import { FormPage } from "../../components/form/FormPage"; - -export const EditFormPage: React.FC = React.memo(() => { - return ( - - {}}> -
EditFormPage
-
-
- ); -}); diff --git a/src/webapp/pages/form/FormPage.tsx b/src/webapp/pages/form/FormPage.tsx new file mode 100644 index 00000000..5199b37d --- /dev/null +++ b/src/webapp/pages/form/FormPage.tsx @@ -0,0 +1,14 @@ +import React from "react"; + +import { Layout } from "../../components/layout/Layout"; +import { FormLayout } from "../../components/form/FormLayout"; + +export const FormPage: React.FC = React.memo(() => { + return ( + + {}}> +
FormPage
+
+
+ ); +}); From c9e0fb8a80f0e62f9438a23e5d2c14f057984456 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Fri, 28 Jun 2024 10:12:01 +0200 Subject: [PATCH 07/17] Remove dark-secondary --- src/webapp/components/button/Button.tsx | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/webapp/components/button/Button.tsx b/src/webapp/components/button/Button.tsx index 00621876..00894a1d 100644 --- a/src/webapp/components/button/Button.tsx +++ b/src/webapp/components/button/Button.tsx @@ -1,11 +1,10 @@ import React from "react"; import { Button as MUIButton } from "@material-ui/core"; -import styled from "styled-components"; type ButtonProps = { children?: React.ReactNode; variant?: "contained" | "outlined"; - color?: "primary" | "secondary" | "dark-secondary"; + color?: "primary" | "secondary"; disabled?: boolean; startIcon?: React.ReactNode; onClick: () => void; @@ -21,22 +20,16 @@ export const Button: React.FC = React.memo( onClick, }) => { return ( - {children} - + ); } ); - -const StyledButton = styled(MUIButton)<{ $darkBorder: boolean }>` - border-color: ${props => - props.$darkBorder ? props.theme.palette.button.borderDarkSecondary : "initial"}; -`; From 6c325f3412bc305450068293d9797837a26716ad Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Mon, 1 Jul 2024 09:35:41 +0200 Subject: [PATCH 08/17] Selector components full typed and change generic T to Value --- .../components/selector/MultipleSelector.tsx | 177 +++++++++--------- src/webapp/components/selector/Selector.tsx | 131 ++++++------- .../selector/utils/selectorHelper.ts | 10 +- 3 files changed, 151 insertions(+), 167 deletions(-) diff --git a/src/webapp/components/selector/MultipleSelector.tsx b/src/webapp/components/selector/MultipleSelector.tsx index 95f402a8..1c0d3fc3 100644 --- a/src/webapp/components/selector/MultipleSelector.tsx +++ b/src/webapp/components/selector/MultipleSelector.tsx @@ -4,11 +4,11 @@ import { Select, InputLabel, MenuItem, FormHelperText, Chip } from "@material-ui import { IconChevronDown24, IconCross16 } from "@dhis2/ui"; import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; -type MultipleSelectorProps = { +type MultipleSelectorProps = { id: string; - selected: T[]; - onChange: (value: SelectorOption["value"][]) => void; - options: SelectorOption[]; + selected: Value[]; + onChange: (value: Value[]) => void; + options: SelectorOption[]; label?: string; placeholder?: string; disabled?: boolean; @@ -18,97 +18,88 @@ type MultipleSelectorProps = { required?: boolean; }; -export const MultipleSelector: React.FC = React.memo( - ({ - id, - label, - placeholder = "", - selected, - onChange, - options, - disabled = false, - helperText = "", - errorText = "", - error = false, - required = false, - }) => { - const handleChange = useCallback( - ( - event: React.ChangeEvent<{ - value: unknown; - }>, - _child: React.ReactNode - ) => { - const value = event.target.value as SelectorOption["value"][]; - onChange(value); - }, - [onChange] - ); +export function MultipleSelector({ + id, + label, + placeholder = "", + selected, + onChange, + options, + disabled = false, + helperText = "", + errorText = "", + error = false, + required = false, +}: MultipleSelectorProps): JSX.Element { + const handleChange = useCallback( + ( + event: React.ChangeEvent<{ + value: unknown; + }>, + _child: React.ReactNode + ) => { + const value = event.target.value as Value[]; + onChange(value); + }, + [onChange] + ); - const handleDelete = useCallback( - ( - event: React.MouseEvent, - value: SelectorOption["value"] - ) => { - event.stopPropagation(); - onChange(selected?.filter(selection => selection !== value)); - }, - [onChange, selected] - ); + const handleDelete = useCallback( + (event: React.MouseEvent, value: Value) => { + event.stopPropagation(); + onChange(selected?.filter(selection => selection !== value)); + }, + [onChange, selected] + ); - return ( - - {label && ( - - )} - - (selected as SelectorOption["value"][])?.length ? ( -
- {(selected as SelectorOption["value"][]).map(value => ( - } - onDelete={event => handleDelete(event, value)} - onMouseDown={event => handleDelete(event, value)} - /> - ))} -
- ) : ( - placeholder - ) - } - displayEmpty - multiple - > - {options.map(option => ( - - {option.label} - - ))} -
- - {error && !!errorText ? errorText : helperText} - -
- ); - } -); + return ( + + {label && ( + + )} + + (selected as Value[])?.length ? ( +
+ {(selected as Value[]).map(value => ( + } + onDelete={event => handleDelete(event, value)} + onMouseDown={event => handleDelete(event, value)} + /> + ))} +
+ ) : ( + placeholder + ) + } + displayEmpty + multiple + > + {options.map(option => ( + + {option.label} + + ))} +
+ + {error && !!errorText ? errorText : helperText} + +
+ ); +} const Container = styled.div` display: flex; diff --git a/src/webapp/components/selector/Selector.tsx b/src/webapp/components/selector/Selector.tsx index e7a88c8d..51a35058 100644 --- a/src/webapp/components/selector/Selector.tsx +++ b/src/webapp/components/selector/Selector.tsx @@ -4,11 +4,11 @@ import { Select, InputLabel, MenuItem, FormHelperText } from "@material-ui/core" import { IconChevronDown24 } from "@dhis2/ui"; import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; -type SelectorProps = { +type SelectorProps = { id: string; - selected: T; - onChange: (value: SelectorOption["value"]) => void; - options: SelectorOption[]; + selected: Value; + onChange: (value: Value) => void; + options: SelectorOption[]; label?: string; placeholder?: string; disabled?: boolean; @@ -18,72 +18,65 @@ type SelectorProps = { required?: boolean; }; -export const Selector: React.FC = React.memo( - ({ - id, - label, - placeholder = "", - selected, - onChange, - options, - disabled = false, - helperText = "", - errorText = "", - error = false, - required = false, - }) => { - const handleChange = useCallback( - ( - event: React.ChangeEvent<{ - value: unknown; - }>, - _child: React.ReactNode - ) => { - const value = event.target.value as SelectorOption["value"]; - onChange(value); - }, - [onChange] - ); +export function Selector({ + id, + label, + placeholder = "", + selected, + onChange, + options, + disabled = false, + helperText = "", + errorText = "", + error = false, + required = false, +}: SelectorProps): JSX.Element { + const handleChange = useCallback( + ( + event: React.ChangeEvent<{ + value: unknown; + }>, + _child: React.ReactNode + ) => { + const value = event.target.value as Value; + onChange(value); + }, + [onChange] + ); - return ( - - {label && ( - - )} - - getLabelFromValue(selected as SelectorOption["value"], options) || - placeholder - } - displayEmpty - > - {options.map(option => ( - - {option.label} - - ))} - - - {error && !!errorText ? errorText : helperText} - - - ); - } -); + return ( + + {label && ( + + )} + + getLabelFromValue(selected as Value, options) || placeholder + } + displayEmpty + > + {options.map(option => ( + + {option.label} + + ))} + + + {error && !!errorText ? errorText : helperText} + + + ); +} const Container = styled.div` display: flex; diff --git a/src/webapp/components/selector/utils/selectorHelper.ts b/src/webapp/components/selector/utils/selectorHelper.ts index 7b23414f..a60749e0 100644 --- a/src/webapp/components/selector/utils/selectorHelper.ts +++ b/src/webapp/components/selector/utils/selectorHelper.ts @@ -1,12 +1,12 @@ -export type SelectorOption = { - value: T; +export type SelectorOption = { + value: Value; label: string; disabled?: boolean; }; -export function getLabelFromValue( - value: SelectorOption["value"], - options: SelectorOption[] +export function getLabelFromValue( + value: Value, + options: SelectorOption[] ) { return options.find(option => option.value === value)?.label; } From 82fb228592b5ffec7252b8614ab200f35b8dd2bb Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Mon, 1 Jul 2024 12:12:31 +0200 Subject: [PATCH 09/17] Improve paths in Router --- src/webapp/pages/Router.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/webapp/pages/Router.tsx b/src/webapp/pages/Router.tsx index 3ee47f08..060884a2 100644 --- a/src/webapp/pages/Router.tsx +++ b/src/webapp/pages/Router.tsx @@ -13,23 +13,17 @@ export function Router() { } /> + } /> + } /> } - /> - } - /> - } /> } /> - } /> + } /> {/* Default route */} } /> From add9d75a354cb2d8cfc403bbad036da84e113fac Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Mon, 1 Jul 2024 14:24:28 +0200 Subject: [PATCH 10/17] remove unnecesary file --- src/webapp/components/utils/selectorHelper.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/webapp/components/utils/selectorHelper.ts diff --git a/src/webapp/components/utils/selectorHelper.ts b/src/webapp/components/utils/selectorHelper.ts deleted file mode 100644 index e69de29b..00000000 From 56f3bbe52729dc8888b3844a74f92d7d76adec3d Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Tue, 2 Jul 2024 14:48:26 +0200 Subject: [PATCH 11/17] Add more UI components, improve Selector, MultipleSelector and RadioButtons and add debounced to SearchInput --- .../NACheckbox.tsx => checkbox/Checkbox.tsx} | 15 +-- src/webapp/components/loader/Loader.tsx | 22 ++++ .../radio-buttons-group/RadioButtonsGroup.tsx | 115 +++++++++++------- .../components/search-input/SearchInput.tsx | 23 +++- .../components/selector/MultipleSelector.tsx | 5 +- src/webapp/components/selector/Selector.tsx | 5 +- .../selector/utils/selectorHelper.ts | 8 +- src/webapp/components/utils/option.ts | 5 + 8 files changed, 130 insertions(+), 68 deletions(-) rename src/webapp/components/{not-applicable-checkbox/NACheckbox.tsx => checkbox/Checkbox.tsx} (85%) create mode 100644 src/webapp/components/loader/Loader.tsx create mode 100644 src/webapp/components/utils/option.ts diff --git a/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx b/src/webapp/components/checkbox/Checkbox.tsx similarity index 85% rename from src/webapp/components/not-applicable-checkbox/NACheckbox.tsx rename to src/webapp/components/checkbox/Checkbox.tsx index 8670fb7f..811615d3 100644 --- a/src/webapp/components/not-applicable-checkbox/NACheckbox.tsx +++ b/src/webapp/components/checkbox/Checkbox.tsx @@ -1,10 +1,8 @@ import React, { useCallback } from "react"; -import { Checkbox, InputLabel, FormHelperText } from "@material-ui/core"; +import { Checkbox as MUICheckbox, InputLabel, FormHelperText } from "@material-ui/core"; import styled from "styled-components"; -import i18n from "../../../utils/i18n"; - -type NACheckboxProps = { +type CheckboxProps = { id: string; label?: string; checked: boolean; @@ -12,9 +10,12 @@ type NACheckboxProps = { helperText?: string; disabled?: boolean; indeterminate?: boolean; + errorText?: string; + error?: boolean; + required?: boolean; }; -export const NACheckbox: React.FC = React.memo( +export const Checkbox: React.FC = React.memo( ({ id, label, @@ -34,7 +35,7 @@ export const NACheckbox: React.FC = React.memo( return ( - = React.memo( inputProps={{ "aria-label": label || `${id}-label` }} /> {helperText} diff --git a/src/webapp/components/loader/Loader.tsx b/src/webapp/components/loader/Loader.tsx new file mode 100644 index 00000000..f9962ef3 --- /dev/null +++ b/src/webapp/components/loader/Loader.tsx @@ -0,0 +1,22 @@ +import { Backdrop, CircularProgress } from "@material-ui/core"; +import React from "react"; +import styled from "styled-components"; + +export const Loader: React.FC = () => ( + + + + + +); + +const StyledLoaderContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; +`; + +const StyledBackdrop = styled(Backdrop)` + color: ${props => props.theme.palette.common.white}; + z-index: 1; +`; diff --git a/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx b/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx index b1659f52..a9d1e3f6 100644 --- a/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx +++ b/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx @@ -1,62 +1,87 @@ -import { FormControlLabel, Radio, RadioGroup, FormHelperText } from "@material-ui/core"; +import { FormControlLabel, InputLabel, Radio, RadioGroup, FormHelperText } from "@material-ui/core"; import React from "react"; import styled from "styled-components"; -export type RadioOption = { - value: T; - label: string; - disabled?: boolean; -}; +import { Option } from "../utils/option"; -type RadioButtonsGroupProps = { +type RadioButtonsGroupProps = { id: string; selected: string; + label?: string; onChange: (event: React.ChangeEvent) => void; - options: RadioOption[]; + options: Option[]; gap?: string; helperText?: string; errorText?: string; error?: boolean; + disabled?: boolean; + required?: boolean; }; -export const RadioButtonsGroup: React.FC = React.memo( - ({ - id, - selected, - onChange, - options, - gap = "24px", - helperText = "", - errorText = "", - error = false, - }) => { - return ( - <> - - {options.map(option => ( - } - label={option.label} - disabled={option.disabled} - aria-label={option.label} - /> - ))} - - - {error && !!errorText ? errorText : helperText} - - - ); +export function RadioButtonsGroup({ + id, + selected, + label, + onChange, + options, + gap = "24px", + helperText = "", + errorText = "", + error = false, + disabled = false, + required = false, +}: RadioButtonsGroupProps): JSX.Element { + return ( + + {label && ( + + )} + + {options.map(option => ( + } + label={option.label} + disabled={option.disabled || disabled} + aria-label={option.label} + /> + ))} + + + {error && !!errorText ? errorText : helperText} + + + ); +} + +const Container = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +const Label = styled(InputLabel)` + display: inline-block; + font-weight: 700; + font-size: 0.875rem; + color: ${props => props.theme.palette.text.primary}; + margin-block-end: 8px; + + &.required::after { + content: "*"; + color: ${props => props.theme.palette.common.red}; + margin-inline-start: 4px; } -); +`; const StyledRadioGroup = styled(RadioGroup)<{ gap: string }>` flex-direction: row; diff --git a/src/webapp/components/search-input/SearchInput.tsx b/src/webapp/components/search-input/SearchInput.tsx index 1e45691e..d8820949 100644 --- a/src/webapp/components/search-input/SearchInput.tsx +++ b/src/webapp/components/search-input/SearchInput.tsx @@ -18,13 +18,11 @@ export const SearchInput: React.FC = React.memo( useEffect(() => updateStateValue(value), [value]); - // TODO: needs debounce function from Collection + // eslint-disable-next-line react-hooks/exhaustive-deps const onChangeDebounced = useCallback( - (value: string) => { - if (onChange) { - onChange(value); - } - }, + debounce((value: string) => { + if (onChange) onChange(value); + }, 400), [onChange] ); @@ -61,6 +59,19 @@ export const SearchInput: React.FC = React.memo( } ); +function debounce any>(func: F, delay: number) { + let timeout: ReturnType | null = null; + + const debounced = (...args: Parameters): void => { + if (timeout !== null) { + clearTimeout(timeout); + } + timeout = setTimeout(() => func(...args), delay); + }; + + return debounced as (...args: Parameters) => ReturnType; +} + const Container = styled.div` display: flex; flex-direction: column; diff --git a/src/webapp/components/selector/MultipleSelector.tsx b/src/webapp/components/selector/MultipleSelector.tsx index 1c0d3fc3..fc0ff41b 100644 --- a/src/webapp/components/selector/MultipleSelector.tsx +++ b/src/webapp/components/selector/MultipleSelector.tsx @@ -2,13 +2,14 @@ import React, { useCallback } from "react"; import styled from "styled-components"; import { Select, InputLabel, MenuItem, FormHelperText, Chip } from "@material-ui/core"; import { IconChevronDown24, IconCross16 } from "@dhis2/ui"; -import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; +import { getLabelFromValue } from "./utils/selectorHelper"; +import { Option } from "../utils/option"; type MultipleSelectorProps = { id: string; selected: Value[]; onChange: (value: Value[]) => void; - options: SelectorOption[]; + options: Option[]; label?: string; placeholder?: string; disabled?: boolean; diff --git a/src/webapp/components/selector/Selector.tsx b/src/webapp/components/selector/Selector.tsx index 51a35058..cb2914f4 100644 --- a/src/webapp/components/selector/Selector.tsx +++ b/src/webapp/components/selector/Selector.tsx @@ -2,13 +2,14 @@ import React, { useCallback } from "react"; import styled from "styled-components"; import { Select, InputLabel, MenuItem, FormHelperText } from "@material-ui/core"; import { IconChevronDown24 } from "@dhis2/ui"; -import { SelectorOption, getLabelFromValue } from "./utils/selectorHelper"; +import { getLabelFromValue } from "./utils/selectorHelper"; +import { Option } from "../utils/option"; type SelectorProps = { id: string; selected: Value; onChange: (value: Value) => void; - options: SelectorOption[]; + options: Option[]; label?: string; placeholder?: string; disabled?: boolean; diff --git a/src/webapp/components/selector/utils/selectorHelper.ts b/src/webapp/components/selector/utils/selectorHelper.ts index a60749e0..ee172ba6 100644 --- a/src/webapp/components/selector/utils/selectorHelper.ts +++ b/src/webapp/components/selector/utils/selectorHelper.ts @@ -1,12 +1,8 @@ -export type SelectorOption = { - value: Value; - label: string; - disabled?: boolean; -}; +import { Option } from "../../utils/option"; export function getLabelFromValue( value: Value, - options: SelectorOption[] + options: Option[] ) { return options.find(option => option.value === value)?.label; } diff --git a/src/webapp/components/utils/option.ts b/src/webapp/components/utils/option.ts new file mode 100644 index 00000000..d73a14e8 --- /dev/null +++ b/src/webapp/components/utils/option.ts @@ -0,0 +1,5 @@ +export type Option = { + value: Value; + label: string; + disabled?: boolean; +}; From 64fc4ad0f04a1243201f2161e6f3cdcdfa8f4d6f Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Thu, 4 Jul 2024 15:27:52 +0200 Subject: [PATCH 12/17] Remove comments --- i18n/en.pot | 7 ++----- i18n/es.po | 20 +------------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 18dced0b..6fea8157 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-06-28T07:41:17.026Z\n" -"PO-Revision-Date: 2024-06-28T07:41:17.026Z\n" +"POT-Creation-Date: 2024-07-04T12:34:38.409Z\n" +"PO-Revision-Date: 2024-07-04T12:34:38.409Z\n" msgid "Add new option" msgstr "" @@ -23,9 +23,6 @@ msgstr "" msgid "Create Event" msgstr "" -msgid "N/A" -msgstr "" - msgid "Close" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 502b3435..14ad8ebf 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-06-28T07:41:17.026Z\n" +"POT-Creation-Date: 2024-07-04T12:34:38.409Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -23,9 +23,6 @@ msgstr "" msgid "Create Event" msgstr "" -msgid "N/A" -msgstr "" - msgid "Close" msgstr "" @@ -103,18 +100,3 @@ msgstr "" msgid "Resources" msgstr "" - -#~ msgid "Add" -#~ msgstr "AƱadir" - -#~ msgid "List" -#~ msgstr "Listar" - -#~ msgid "Back" -#~ msgstr "Volver" - -#~ msgid "Help" -#~ msgstr "Ayuda" - -#~ msgid "Hello {{name}}" -#~ msgstr "Hola {{name}}" From 3031a13ca1a72f64a0e4c160d3f47d90f3223141 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Thu, 4 Jul 2024 15:29:45 +0200 Subject: [PATCH 13/17] Separate components at same level for more redeability and avoid re-render because inputProps object --- src/webapp/components/avatar-card/AvatarCard.tsx | 1 + src/webapp/components/checkbox/Checkbox.tsx | 8 ++++++-- src/webapp/components/date-picker/DatePicker.tsx | 1 + src/webapp/components/form/FormLayout.tsx | 4 ++++ src/webapp/components/form/FormSection.tsx | 3 +++ src/webapp/components/layout/Layout.tsx | 1 + .../components/layout/header-bar/HeaderBar.tsx | 4 ++++ .../layout/main-content/MainContent.tsx | 3 +++ src/webapp/components/notice-box/NoticeBox.tsx | 1 + .../components/profile-modal/ProfileModal.tsx | 3 +++ .../radio-buttons-group/RadioButtonsGroup.tsx | 2 ++ .../components/search-input/SearchInput.tsx | 15 +++++++++------ src/webapp/components/section/Section.tsx | 4 ++++ .../components/selector/MultipleSelector.tsx | 2 ++ src/webapp/components/selector/Selector.tsx | 2 ++ src/webapp/components/stats-card/StatsCard.tsx | 3 +++ src/webapp/components/text-input/TextArea.tsx | 2 ++ src/webapp/components/text-input/TextInput.tsx | 1 + 18 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/webapp/components/avatar-card/AvatarCard.tsx b/src/webapp/components/avatar-card/AvatarCard.tsx index c3996df6..b2e61747 100644 --- a/src/webapp/components/avatar-card/AvatarCard.tsx +++ b/src/webapp/components/avatar-card/AvatarCard.tsx @@ -16,6 +16,7 @@ export const AvatarCard: React.FC = React.memo( + {children} ); diff --git a/src/webapp/components/checkbox/Checkbox.tsx b/src/webapp/components/checkbox/Checkbox.tsx index 811615d3..47634f61 100644 --- a/src/webapp/components/checkbox/Checkbox.tsx +++ b/src/webapp/components/checkbox/Checkbox.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from "react"; +import React, { useCallback, useMemo } from "react"; import { Checkbox as MUICheckbox, InputLabel, FormHelperText } from "@material-ui/core"; import styled from "styled-components"; @@ -32,6 +32,8 @@ export const Checkbox: React.FC = React.memo( [onChange] ); + const inputProps = useMemo(() => ({ "aria-label": label || `${id}-label` }), [id, label]); + return ( @@ -42,12 +44,14 @@ export const Checkbox: React.FC = React.memo( onChange={handleChange} disabled={disabled} size="small" - inputProps={{ "aria-label": label || `${id}-label` }} + inputProps={inputProps} /> + + {helperText} ); diff --git a/src/webapp/components/date-picker/DatePicker.tsx b/src/webapp/components/date-picker/DatePicker.tsx index 7942ab72..d91eb55f 100644 --- a/src/webapp/components/date-picker/DatePicker.tsx +++ b/src/webapp/components/date-picker/DatePicker.tsx @@ -37,6 +37,7 @@ export const DatePicker: React.FC = React.memo( {label} )} + = React.memo( {title && {title}} {subtitle && {subtitle}}
+ {i18n.t("Indicates required")}
+ {children} +
+ {onCancel && ( diff --git a/src/webapp/components/form/FormSection.tsx b/src/webapp/components/form/FormSection.tsx index cc28e8a0..f5e7fae0 100644 --- a/src/webapp/components/form/FormSection.tsx +++ b/src/webapp/components/form/FormSection.tsx @@ -26,17 +26,20 @@ export const FormSection: React.FC = React.memo( return ( {hasSeparator && } + {title && ( {title} + {onClickInfo && ( } onClick={onClickInfo} /> )} )} + {children} diff --git a/src/webapp/components/layout/Layout.tsx b/src/webapp/components/layout/Layout.tsx index 0ce3f570..a46edc49 100644 --- a/src/webapp/components/layout/Layout.tsx +++ b/src/webapp/components/layout/Layout.tsx @@ -33,6 +33,7 @@ export const Layout: React.FC = React.memo(
diff --git a/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx b/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx index a9d1e3f6..da9c44a8 100644 --- a/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx +++ b/src/webapp/components/radio-buttons-group/RadioButtonsGroup.tsx @@ -38,6 +38,7 @@ export function RadioButtonsGroup({ {label} )} + ({ /> ))} + {error && !!errorText ? errorText : helperText} diff --git a/src/webapp/components/search-input/SearchInput.tsx b/src/webapp/components/search-input/SearchInput.tsx index d8820949..1ee7e43a 100644 --- a/src/webapp/components/search-input/SearchInput.tsx +++ b/src/webapp/components/search-input/SearchInput.tsx @@ -12,17 +12,19 @@ type SearchInputProps = { disabled?: boolean; }; +const INPUT_PROPS = { "aria-label": "search" }; + export const SearchInput: React.FC = React.memo( ({ value, onChange, placeholder = "", disabled = false }) => { const [stateValue, updateStateValue] = useState(value); useEffect(() => updateStateValue(value), [value]); - // eslint-disable-next-line react-hooks/exhaustive-deps - const onChangeDebounced = useCallback( - debounce((value: string) => { - if (onChange) onChange(value); - }, 400), + const onChangeDebounced = React.useMemo( + () => + debounce((value: string) => { + if (onChange) onChange(value); + }, 400), [onChange] ); @@ -44,6 +46,7 @@ export const SearchInput: React.FC = React.memo( + = React.memo( onKeyDown={handleKeydown} disabled={disabled} variant="outlined" - inputProps={{ "aria-label": "search" }} + inputProps={INPUT_PROPS} />
); diff --git a/src/webapp/components/section/Section.tsx b/src/webapp/components/section/Section.tsx index 2d3b9637..3c830442 100644 --- a/src/webapp/components/section/Section.tsx +++ b/src/webapp/components/section/Section.tsx @@ -25,9 +25,11 @@ export const Section: React.FC = React.memo( return ( {hasSeparator && } +
{title ? {title} : null} + {lastUpdated ? ( @@ -37,8 +39,10 @@ export const Section: React.FC = React.memo( ) : null} + {headerButtom ?
{headerButtom}
: null}
+ {children}
); diff --git a/src/webapp/components/selector/MultipleSelector.tsx b/src/webapp/components/selector/MultipleSelector.tsx index fc0ff41b..12ae7c00 100644 --- a/src/webapp/components/selector/MultipleSelector.tsx +++ b/src/webapp/components/selector/MultipleSelector.tsx @@ -60,6 +60,7 @@ export function MultipleSelector({ {label} )} + ({ ))} + {error && !!errorText ? errorText : helperText} diff --git a/src/webapp/components/selector/Selector.tsx b/src/webapp/components/selector/Selector.tsx index cb2914f4..e2aedd36 100644 --- a/src/webapp/components/selector/Selector.tsx +++ b/src/webapp/components/selector/Selector.tsx @@ -52,6 +52,7 @@ export function Selector({ {label} )} + ({ ))} + {error && !!errorText ? errorText : helperText} diff --git a/src/webapp/components/stats-card/StatsCard.tsx b/src/webapp/components/stats-card/StatsCard.tsx index b265db9c..de706cc8 100644 --- a/src/webapp/components/stats-card/StatsCard.tsx +++ b/src/webapp/components/stats-card/StatsCard.tsx @@ -26,8 +26,11 @@ export const StatsCard: React.FC = React.memo( {`${stat}${isPercentage ? " %" : ""}`} + {pretitle} + {title} + {subtitle} diff --git a/src/webapp/components/text-input/TextArea.tsx b/src/webapp/components/text-input/TextArea.tsx index d6f3302b..576ebaa5 100644 --- a/src/webapp/components/text-input/TextArea.tsx +++ b/src/webapp/components/text-input/TextArea.tsx @@ -40,6 +40,7 @@ export const TextArea: React.FC = React.memo( {label} )} + = React.memo( disabled={disabled} $hasError={error} /> + {error && !!errorText ? errorText : helperText} diff --git a/src/webapp/components/text-input/TextInput.tsx b/src/webapp/components/text-input/TextInput.tsx index 367244bf..5e4e0c44 100644 --- a/src/webapp/components/text-input/TextInput.tsx +++ b/src/webapp/components/text-input/TextInput.tsx @@ -33,6 +33,7 @@ export const TextInput: React.FC = React.memo( {label} )} + Date: Thu, 4 Jul 2024 15:30:33 +0200 Subject: [PATCH 14/17] Remove examples and add a TODO for adding sections --- src/webapp/pages/dashboard/DashboardPage.tsx | 7 +-- .../pages/event-tracker/EventTrackerPage.tsx | 41 +------------ .../event-tracker/RiskAssessmentSection.tsx | 30 ---------- src/webapp/pages/form/FormPage.tsx | 9 +-- .../IncidentActionPlanPage.tsx | 60 +------------------ .../IMTeamBuilderPage.tsx | 4 +- src/webapp/pages/resources/ResourcesPage.tsx | 2 +- 7 files changed, 11 insertions(+), 142 deletions(-) delete mode 100644 src/webapp/pages/event-tracker/RiskAssessmentSection.tsx diff --git a/src/webapp/pages/dashboard/DashboardPage.tsx b/src/webapp/pages/dashboard/DashboardPage.tsx index e0a84bf4..1c3415d0 100644 --- a/src/webapp/pages/dashboard/DashboardPage.tsx +++ b/src/webapp/pages/dashboard/DashboardPage.tsx @@ -4,15 +4,12 @@ import i18n from "../../../utils/i18n"; import { Layout } from "../../components/layout/Layout"; import { Section } from "../../components/section/Section"; +// TODO: Add every section here, first it's just an example + export const DashboardPage: React.FC = React.memo(() => { return (
Respond, alert, watch content
-
- All public health events content -
-
7-1-7 performance content
-
Performance overview content
); }); diff --git a/src/webapp/pages/event-tracker/EventTrackerPage.tsx b/src/webapp/pages/event-tracker/EventTrackerPage.tsx index cf54bad0..318e447b 100644 --- a/src/webapp/pages/event-tracker/EventTrackerPage.tsx +++ b/src/webapp/pages/event-tracker/EventTrackerPage.tsx @@ -1,45 +1,10 @@ import React from "react"; -import { IconEdit24 } from "@dhis2/ui"; import i18n from "../../../utils/i18n"; import { Layout } from "../../components/layout/Layout"; -import { Section } from "../../components/section/Section"; -import { Button } from "../../components/button/Button"; -import { RiskAssessmentSection } from "./RiskAssessmentSection"; + +// TODO: Add every section here export const EventTrackerPage: React.FC = React.memo(() => { - return ( - -
} - > - {i18n.t("Edit Details")} - - } - hasSeparator - > - Event details -
-
- Districts affected content -
- -
- Overview content -
-
Cases content
-
- 7-1-7 performance content -
-
- ); + return ; }); diff --git a/src/webapp/pages/event-tracker/RiskAssessmentSection.tsx b/src/webapp/pages/event-tracker/RiskAssessmentSection.tsx deleted file mode 100644 index 0a16112f..00000000 --- a/src/webapp/pages/event-tracker/RiskAssessmentSection.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import { IconEdit24 } from "@dhis2/ui"; - -import i18n from "../../../utils/i18n"; -import { Section } from "../../components/section/Section"; -import { Button } from "../../components/button/Button"; -import { NoticeBox } from "../../components/notice-box/NoticeBox"; - -export const RiskAssessmentSection: React.FC = React.memo(() => { - return ( -
} - > - {i18n.t("Create Risk Assessment")} - - } - hasSeparator - > - -
{i18n.t("Risks associated with this event have not yet been assessed.")}
-
-
- ); -}); diff --git a/src/webapp/pages/form/FormPage.tsx b/src/webapp/pages/form/FormPage.tsx index 5199b37d..0dadbb41 100644 --- a/src/webapp/pages/form/FormPage.tsx +++ b/src/webapp/pages/form/FormPage.tsx @@ -1,14 +1,7 @@ import React from "react"; import { Layout } from "../../components/layout/Layout"; -import { FormLayout } from "../../components/form/FormLayout"; export const FormPage: React.FC = React.memo(() => { - return ( - - {}}> -
FormPage
-
-
- ); + return ; }); diff --git a/src/webapp/pages/incident-action-plan/IncidentActionPlanPage.tsx b/src/webapp/pages/incident-action-plan/IncidentActionPlanPage.tsx index 906ac431..10cd1ac4 100644 --- a/src/webapp/pages/incident-action-plan/IncidentActionPlanPage.tsx +++ b/src/webapp/pages/incident-action-plan/IncidentActionPlanPage.tsx @@ -1,69 +1,15 @@ import React from "react"; -import { IconEdit24 } from "@dhis2/ui"; import i18n from "../../../utils/i18n"; import { Layout } from "../../components/layout/Layout"; -import { Section } from "../../components/section/Section"; -import { Button } from "../../components/button/Button"; + +// TODO: Add every section here, first it's just an example export const IncidentActionPlanPage: React.FC = React.memo(() => { return ( -
IAP details
-
} - > - {i18n.t("Edit Response Actions")} - - } - > - Response actions content -
-
} - > - {i18n.t("Edit Action Plan")} - - } - > - Action plan content -
-
} - > - {i18n.t("Edit Team")} - - } - > - Team content -
-
+ > ); }); diff --git a/src/webapp/pages/incident-management-team-builder/IMTeamBuilderPage.tsx b/src/webapp/pages/incident-management-team-builder/IMTeamBuilderPage.tsx index 9d54bf66..37b10744 100644 --- a/src/webapp/pages/incident-management-team-builder/IMTeamBuilderPage.tsx +++ b/src/webapp/pages/incident-management-team-builder/IMTeamBuilderPage.tsx @@ -8,8 +8,6 @@ export const IMTeamBuilderPage: React.FC = React.memo(() => { - IMTeamBuilderPage - + > ); }); diff --git a/src/webapp/pages/resources/ResourcesPage.tsx b/src/webapp/pages/resources/ResourcesPage.tsx index 4aac11b1..f3ce3e51 100644 --- a/src/webapp/pages/resources/ResourcesPage.tsx +++ b/src/webapp/pages/resources/ResourcesPage.tsx @@ -4,5 +4,5 @@ import { Layout } from "../../components/layout/Layout"; import i18n from "../../../utils/i18n"; export const ResourcesPage: React.FC = React.memo(() => { - return ResourcesPage; + return ; }); From d3e0244b155b9a0ea39524d32258d7c8af41a0c1 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Thu, 4 Jul 2024 15:38:37 +0200 Subject: [PATCH 15/17] Update trasnlations --- i18n/en.pot | 52 ++-------------------------------------------------- i18n/es.po | 50 +------------------------------------------------- 2 files changed, 3 insertions(+), 99 deletions(-) diff --git a/i18n/en.pot b/i18n/en.pot index 6fea8157..42705daf 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ msgstr "" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" -"POT-Creation-Date: 2024-07-04T12:34:38.409Z\n" -"PO-Revision-Date: 2024-07-04T12:34:38.409Z\n" +"POT-Creation-Date: 2024-07-04T13:31:07.802Z\n" +"PO-Revision-Date: 2024-07-04T13:31:07.802Z\n" msgid "Add new option" msgstr "" @@ -38,63 +38,15 @@ msgstr "" msgid "Respond, alert, watch" msgstr "" -msgid "All public health events" -msgstr "" - -msgid "7-1-7 performance" -msgstr "" - -msgid "Performance overview" -msgstr "" - msgid "Event Tracker" msgstr "" -msgid "Edit Details" -msgstr "" - -msgid "Districts affected" -msgstr "" - -msgid "Overview" -msgstr "" - -msgid "Risk Assessment" -msgstr "" - -msgid "Create Risk Assessment" -msgstr "" - -msgid "Risk assessment incomplete" -msgstr "" - -msgid "Risks associated with this event have not yet been assessed." -msgstr "" - msgid "Incident Action Plan" msgstr "" msgid "Cholera in NW Province, June 2023" msgstr "" -msgid "Response actions" -msgstr "" - -msgid "Edit Response Actions" -msgstr "" - -msgid "Action plan" -msgstr "" - -msgid "Edit Action Plan" -msgstr "" - -msgid "Team" -msgstr "" - -msgid "Edit Team" -msgstr "" - msgid "Incident Management Team Builder" msgstr "" diff --git a/i18n/es.po b/i18n/es.po index 14ad8ebf..4e848d1c 100644 --- a/i18n/es.po +++ b/i18n/es.po @@ -1,7 +1,7 @@ msgid "" msgstr "" "Project-Id-Version: i18next-conv\n" -"POT-Creation-Date: 2024-07-04T12:34:38.409Z\n" +"POT-Creation-Date: 2024-07-04T13:31:07.802Z\n" "PO-Revision-Date: 2018-10-25T09:02:35.143Z\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -38,63 +38,15 @@ msgstr "" msgid "Respond, alert, watch" msgstr "" -msgid "All public health events" -msgstr "" - -msgid "7-1-7 performance" -msgstr "" - -msgid "Performance overview" -msgstr "" - msgid "Event Tracker" msgstr "" -msgid "Edit Details" -msgstr "" - -msgid "Districts affected" -msgstr "" - -msgid "Overview" -msgstr "" - -msgid "Risk Assessment" -msgstr "" - -msgid "Create Risk Assessment" -msgstr "" - -msgid "Risk assessment incomplete" -msgstr "" - -msgid "Risks associated with this event have not yet been assessed." -msgstr "" - msgid "Incident Action Plan" msgstr "" msgid "Cholera in NW Province, June 2023" msgstr "" -msgid "Response actions" -msgstr "" - -msgid "Edit Response Actions" -msgstr "" - -msgid "Action plan" -msgstr "" - -msgid "Edit Action Plan" -msgstr "" - -msgid "Team" -msgstr "" - -msgid "Edit Team" -msgstr "" - msgid "Incident Management Team Builder" msgstr "" From b9978b51f00abe1ed6612386acac456075fc4d2e Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Thu, 4 Jul 2024 16:23:12 +0200 Subject: [PATCH 16/17] debounce function does not return anything --- src/webapp/components/search-input/SearchInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webapp/components/search-input/SearchInput.tsx b/src/webapp/components/search-input/SearchInput.tsx index 1ee7e43a..bb744691 100644 --- a/src/webapp/components/search-input/SearchInput.tsx +++ b/src/webapp/components/search-input/SearchInput.tsx @@ -72,7 +72,7 @@ function debounce any>(func: F, delay: number) { timeout = setTimeout(() => func(...args), delay); }; - return debounced as (...args: Parameters) => ReturnType; + return debounced; } const Container = styled.div` From 2433a0b2df4f8e5aadca9da1b7dedaea2e50df70 Mon Sep 17 00:00:00 2001 From: Ana Garcia Date: Mon, 8 Jul 2024 08:17:09 +0200 Subject: [PATCH 17/17] Same height to selectors and text inputs --- src/webapp/components/search-input/SearchInput.tsx | 4 ++++ src/webapp/components/selector/MultipleSelector.tsx | 1 + src/webapp/components/selector/Selector.tsx | 1 + src/webapp/components/text-input/TextInput.tsx | 4 ++++ 4 files changed, 10 insertions(+) diff --git a/src/webapp/components/search-input/SearchInput.tsx b/src/webapp/components/search-input/SearchInput.tsx index bb744691..e98be317 100644 --- a/src/webapp/components/search-input/SearchInput.tsx +++ b/src/webapp/components/search-input/SearchInput.tsx @@ -100,7 +100,11 @@ const IconContainer = styled.div<{ $disabled?: boolean }>` const StyledTextField = styled(TextField)<{ error?: boolean }>` font-weight: 400; + height: 40px; font-size: 0.875rem; + .MuiOutlinedInput-root { + height: 40px; + } color: ${props => props.theme.palette.common.grey1}; .MuiFormHelperText-root { color: ${props => diff --git a/src/webapp/components/selector/MultipleSelector.tsx b/src/webapp/components/selector/MultipleSelector.tsx index 12ae7c00..0a03f06d 100644 --- a/src/webapp/components/selector/MultipleSelector.tsx +++ b/src/webapp/components/selector/MultipleSelector.tsx @@ -130,6 +130,7 @@ const StyledFormHelperText = styled(FormHelperText)<{ error?: boolean }>` `; const StyledSelect = styled(Select)<{ error?: boolean }>` + height: 40px; .MuiOutlinedInput-notchedOutline { border-color: ${props => props.error ? props.theme.palette.common.red600 : props.theme.palette.common.grey500}; diff --git a/src/webapp/components/selector/Selector.tsx b/src/webapp/components/selector/Selector.tsx index e2aedd36..044897a1 100644 --- a/src/webapp/components/selector/Selector.tsx +++ b/src/webapp/components/selector/Selector.tsx @@ -107,6 +107,7 @@ const StyledFormHelperText = styled(FormHelperText)<{ error?: boolean }>` `; const StyledSelect = styled(Select)<{ error?: boolean }>` + height: 40px; .MuiOutlinedInput-notchedOutline { border-color: ${props => props.error ? props.theme.palette.common.red600 : props.theme.palette.common.grey500}; diff --git a/src/webapp/components/text-input/TextInput.tsx b/src/webapp/components/text-input/TextInput.tsx index 5e4e0c44..2b86b63c 100644 --- a/src/webapp/components/text-input/TextInput.tsx +++ b/src/webapp/components/text-input/TextInput.tsx @@ -70,6 +70,10 @@ const Label = styled(InputLabel)` `; const StyledTextField = styled(TextField)<{ error?: boolean }>` + height: 40px; + .MuiOutlinedInput-root { + height: 40px; + } .MuiFormHelperText-root { color: ${props => props.error ? props.theme.palette.common.red700 : props.theme.palette.common.grey700};