From ca77ffe798a4801b896c3f60aac8b46175fb7999 Mon Sep 17 00:00:00 2001 From: matttdawson <89495499+matttdawson@users.noreply.github.com> Date: Tue, 8 Nov 2022 16:11:20 +1300 Subject: [PATCH] feat: improved subComponents using a context (#84) * feat: improved subComponents using a context, fix: renamed maxlength to maxLength * tidy up code * Unused imports * Unused imports, check for null on validate --- src/components/GridSubComponentTextArea.tsx | 32 +++++++++++------ .../gridForm/GridFormMultiSelect.tsx | 34 +++++++++++-------- src/components/gridForm/GridFormTextArea.tsx | 6 ++-- src/components/gridForm/GridFormTextInput.tsx | 6 ++-- .../gridForm/GridSubComponentProps.ts | 6 ---- src/contexts/GridSubComponentContext.ts | 21 ++++++++++++ .../components/ActionButton.stories.tsx | 2 +- src/stories/grid/FormTest.tsx | 11 +++--- .../grid/GridPopoutBearing.stories.tsx | 2 +- .../grid/GridPopoutEditDropDown.stories.tsx | 2 +- .../grid/GridPopoutEditGeneric.stories.tsx | 2 +- .../GridPopoutEditGenericTextArea.stories.tsx | 6 ++-- .../GridPopoutEditMultiSelect.stories.tsx | 4 +-- src/stories/grid/GridReadOnly.stories.tsx | 2 +- 14 files changed, 83 insertions(+), 53 deletions(-) delete mode 100644 src/components/gridForm/GridSubComponentProps.ts create mode 100644 src/contexts/GridSubComponentContext.ts diff --git a/src/components/GridSubComponentTextArea.tsx b/src/components/GridSubComponentTextArea.tsx index 1e29dd0b..2769da58 100644 --- a/src/components/GridSubComponentTextArea.tsx +++ b/src/components/GridSubComponentTextArea.tsx @@ -1,24 +1,36 @@ -import { useCallback, useEffect } from "react"; -import { GridSubComponentProps } from "./gridForm/GridSubComponentProps"; +import { useCallback, useContext, useEffect } from "react"; import { TextInputFormatted } from "../lui/TextInputFormatted"; +import { GridSubComponentContext } from "../contexts/GridSubComponentContext"; -export interface GridSubComponentTextAreaProps extends GridSubComponentProps { +export interface GridSubComponentTextAreaProps { placeholder?: string; required?: boolean; - maxlength?: number; + maxLength?: number; width?: string | number; - validate: (value: string) => string | null; + validate?: (value: string) => string | null; + defaultValue: string; } export const GridSubComponentTextArea = (props: GridSubComponentTextAreaProps): JSX.Element => { - const { value, setValue, setValid, triggerSave } = props; + const { value, setValue, setValid, triggerSave } = useContext(GridSubComponentContext); + + // If is not initialised yet as it's just been created then set the default value + useEffect(() => { + if (value == null) setValue(props.defaultValue); + }, [props.defaultValue, setValue, value]); + const validate = useCallback( - (value: string) => { + (value: string | null) => { + if (value == null) return null; + // This can happen because subcomponent is invoked without type safety + if (typeof value !== "string") { + console.error("Value is not a string", value); + } if (props.required && value.length === 0) { return `Some text is required`; } - if (props.maxlength && value.length > props.maxlength) { - return `Text must be no longer than ${props.maxlength} characters`; + if (props.maxLength && value.length > props.maxLength) { + return `Text must be no longer than ${props.maxLength} characters`; } if (props.validate) { return props.validate(value); @@ -29,7 +41,7 @@ export const GridSubComponentTextArea = (props: GridSubComponentTextAreaProps): ); useEffect(() => { - setValid(validate(value) == null); + setValid(value != null && validate(value) == null); }, [setValid, validate, value]); return ( diff --git a/src/components/gridForm/GridFormMultiSelect.tsx b/src/components/gridForm/GridFormMultiSelect.tsx index 9c743ec6..d2c7c511 100644 --- a/src/components/gridForm/GridFormMultiSelect.tsx +++ b/src/components/gridForm/GridFormMultiSelect.tsx @@ -10,7 +10,7 @@ import { useGridPopoverHook } from "../GridPopoverHook"; import { MenuSeparatorString } from "./GridFormDropDown"; import { CellEditorCommon, CellParams } from "../GridCell"; import { ClickEvent } from "../../react-menu3/types"; -import { GridSubComponentProps } from "./GridSubComponentProps"; +import { GridSubComponentContext } from "contexts/GridSubComponentContext"; interface MultiFinalSelectOption { value: ValueType; @@ -208,20 +208,24 @@ export const GridFormMultiSelect = ( {selectedValues.includes(item.value) && item.subComponent && ( - {(ref: any) => - item.subComponent && - item.subComponent({ - ref, - value: subSelectedValues[`${item.value}`], - setValue: (value: any) => { - subSelectedValues[`${item.value}`] = value; - setSubSelectedValues({ ...subSelectedValues }); - }, - setValid: (valid: boolean) => { - subComponentIsValid.current[`${item.value}`] = valid; - }, - triggerSave, - } as GridSubComponentProps) + {(_: any) => + item.subComponent && ( + { + subSelectedValues[`${item.value}`] = value; + setSubSelectedValues({ ...subSelectedValues }); + }, + setValid: (valid: boolean) => { + subComponentIsValid.current[`${item.value}`] = valid; + }, + triggerSave, + }} + > + + + ) } )} diff --git a/src/components/gridForm/GridFormTextArea.tsx b/src/components/gridForm/GridFormTextArea.tsx index 065a3784..8cbd0ee6 100644 --- a/src/components/gridForm/GridFormTextArea.tsx +++ b/src/components/gridForm/GridFormTextArea.tsx @@ -7,7 +7,7 @@ import { CellEditorCommon, CellParams } from "../GridCell"; export interface GridFormTextAreaProps extends CellEditorCommon { placeholder?: string; required?: boolean; - maxlength?: number; + maxLength?: number; width?: string | number; validate?: (value: string) => string | null; onSave?: (selectedRows: RowType[], value: string) => Promise; @@ -21,8 +21,8 @@ export const GridFormTextArea = (_props: GridFormTe if (props.required && value.length == 0) { return `Some text is required`; } - if (props.maxlength && value.length > props.maxlength) { - return `Text must be no longer than ${props.maxlength} characters`; + if (props.maxLength && value.length > props.maxLength) { + return `Text must be no longer than ${props.maxLength} characters`; } if (props.validate) { return props.validate(value); diff --git a/src/components/gridForm/GridFormTextInput.tsx b/src/components/gridForm/GridFormTextInput.tsx index 827b314f..fce8fb64 100644 --- a/src/components/gridForm/GridFormTextInput.tsx +++ b/src/components/gridForm/GridFormTextInput.tsx @@ -8,7 +8,7 @@ export interface GridFormTextInputProps extends Cel placeholder?: string; units?: string; required?: boolean; - maxlength?: number; + maxLength?: number; width?: string | number; // Return null for ok, otherwise an error string validate?: (value: string, data: RowType) => string | null; @@ -25,8 +25,8 @@ export const GridFormTextInput = (_props: GridFormT if (props.required && trimmedValue.length == 0) { return `Some text is required`; } - if (props.maxlength && trimmedValue.length > props.maxlength) { - return `Text must be no longer than ${props.maxlength} characters`; + if (props.maxLength && trimmedValue.length > props.maxLength) { + return `Text must be no longer than ${props.maxLength} characters`; } if (props.validate) { return props.validate(trimmedValue, props.data); diff --git a/src/components/gridForm/GridSubComponentProps.ts b/src/components/gridForm/GridSubComponentProps.ts deleted file mode 100644 index f30f8beb..00000000 --- a/src/components/gridForm/GridSubComponentProps.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface GridSubComponentProps { - value: string; - setValue: (value: string) => void; - setValid: (valid: boolean) => void; - triggerSave: () => Promise; -} diff --git a/src/contexts/GridSubComponentContext.ts b/src/contexts/GridSubComponentContext.ts new file mode 100644 index 00000000..d514066f --- /dev/null +++ b/src/contexts/GridSubComponentContext.ts @@ -0,0 +1,21 @@ +import { createContext } from "react"; + +export interface GridSubComponentContextType { + value: any; + setValue: (value: string) => void; + setValid: (valid: boolean) => void; + triggerSave: () => Promise; +} + +export const GridSubComponentContext = createContext({ + value: "GridSubComponentContext value no context", + setValue: () => { + console.error("GridSubComponentContext setValue no context"); + }, + setValid: () => { + console.error("GridSubComponentContext setValid no context"); + }, + triggerSave: async () => { + console.error("GridSubComponentContext triggerSave no context"); + }, +}); diff --git a/src/stories/components/ActionButton.stories.tsx b/src/stories/components/ActionButton.stories.tsx index 0766c77a..aeba5316 100644 --- a/src/stories/components/ActionButton.stories.tsx +++ b/src/stories/components/ActionButton.stories.tsx @@ -7,7 +7,7 @@ import { useCallback, useState } from "react"; import { wait } from "../../utils/util"; export default { - title: "Components / ButtonTextWithIcon", + title: "Components / ActionButton", component: ActionButton, args: {}, } as ComponentMeta; diff --git a/src/stories/grid/FormTest.tsx b/src/stories/grid/FormTest.tsx index 728bc962..aae244cb 100644 --- a/src/stories/grid/FormTest.tsx +++ b/src/stories/grid/FormTest.tsx @@ -18,23 +18,22 @@ export interface IFormTestRow { export const FormTest = (_props: CellEditorCommon): JSX.Element => { const props = _props as CellParams; - const [v1, v2, ...v3] = props.value.split(" "); + const [v1, ...v2] = props.value.split(" "); const [nameType, setNameType] = useState(v1); - const [numba, setNumba] = useState(v2); - const [plan, setPlan] = useState(v3.join(" ")); + const [numba, setNumba] = useState(v2.join(" ")); const save = useCallback(async (): Promise => { // eslint-disable-next-line no-console - console.log("onSave", props.selectedRows, nameType, numba, plan); + console.log("onSave", props.selectedRows, nameType, numba); // @ts-ignore - props.selectedRows.forEach((row) => (row["name"] = [nameType, numba, plan].join(" "))); + props.selectedRows.forEach((row) => (row["name"] = [nameType, numba].join(" "))); await wait(1000); // Close form return true; - }, [nameType, numba, plan, props.selectedRows]); + }, [nameType, numba, props.selectedRows]); const [showModal, setShowModal] = useState(false); diff --git a/src/stories/grid/GridPopoutBearing.stories.tsx b/src/stories/grid/GridPopoutBearing.stories.tsx index 7068aa93..a2780626 100644 --- a/src/stories/grid/GridPopoutBearing.stories.tsx +++ b/src/stories/grid/GridPopoutBearing.stories.tsx @@ -83,7 +83,7 @@ const GridReadOnlyTemplate: ComponentStory = (props: GridProps) => [], ); - const [rowData, setRowData] = useState([ + const [rowData] = useState([ { id: 1000, bearing1: 1.234, bearingCorrection: 90 }, { id: 1001, bearing1: 1.565, bearingCorrection: 240 }, { id: 1002, bearing1: null, bearingCorrection: 355.1 }, diff --git a/src/stories/grid/GridPopoutEditDropDown.stories.tsx b/src/stories/grid/GridPopoutEditDropDown.stories.tsx index fe803f5e..df2da8a1 100644 --- a/src/stories/grid/GridPopoutEditDropDown.stories.tsx +++ b/src/stories/grid/GridPopoutEditDropDown.stories.tsx @@ -245,7 +245,7 @@ const GridEditDropDownTemplate: ComponentStory = (props: GridProps) [optionsFn, optionsObjects], ); - const [rowData, setRowData] = useState([ + const [rowData] = useState([ { id: 1000, position: "Tester", diff --git a/src/stories/grid/GridPopoutEditGeneric.stories.tsx b/src/stories/grid/GridPopoutEditGeneric.stories.tsx index a09580c0..cca8ae73 100644 --- a/src/stories/grid/GridPopoutEditGeneric.stories.tsx +++ b/src/stories/grid/GridPopoutEditGeneric.stories.tsx @@ -57,7 +57,7 @@ const GridPopoutEditGenericTemplate: ComponentStory = (props: GridP [], ); - const [rowData, setRowData] = useState([ + const [rowData] = useState([ { id: 1000, name: "IS IS DP12345", nameType: "IS", numba: "IX", plan: "DP 12345" }, { id: 1001, name: "PEG V SD523", nameType: "PEG", numba: "V", plan: "SD 523" }, ] as IFormTestRow[]); diff --git a/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx b/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx index 9fa17a8c..a3943c91 100644 --- a/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx +++ b/src/stories/grid/GridPopoutEditGenericTextArea.stories.tsx @@ -61,7 +61,7 @@ const GridPopoutEditGenericTemplate: ComponentStory = (props: GridP multiEdit: true, editorParams: { required: true, - maxlength: 12, + maxLength: 12, placeholder: "Enter some text...", validate: (value: string) => { if (value === "never") return "The value 'never' is not allowed"; @@ -88,7 +88,7 @@ const GridPopoutEditGenericTemplate: ComponentStory = (props: GridP { multiEdit: true, editorParams: { - maxlength: 12, + maxLength: 12, placeholder: "Enter distance...", units: "m", validate: (value: string) => { @@ -115,7 +115,7 @@ const GridPopoutEditGenericTemplate: ComponentStory = (props: GridP multiEdit: true, editorParams: { required: true, - maxlength: 32, + maxLength: 32, placeholder: "Enter some text...", validate: (value: string) => { diff --git a/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx b/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx index 34a0212f..b76c2d65 100644 --- a/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +++ b/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx @@ -80,7 +80,7 @@ const GridEditMultiSelectTemplate: ComponentStory = (props: GridPro { value: "other", label: "Other", - subComponent: (props) => , + subComponent: () => , }, ], initialSelectedValues: () => ({ @@ -128,7 +128,7 @@ const GridEditMultiSelectTemplate: ComponentStory = (props: GridPro ]; }, []); - const [rowData, setRowData] = useState([ + const [rowData] = useState([ { id: 1000, position: "Tester", position2: "1", position3: "Tester" }, { id: 1001, position: "Developer", position2: "2", position3: "Developer" }, ] as ITestRow[]); diff --git a/src/stories/grid/GridReadOnly.stories.tsx b/src/stories/grid/GridReadOnly.stories.tsx index 5b863a56..80dd5173 100644 --- a/src/stories/grid/GridReadOnly.stories.tsx +++ b/src/stories/grid/GridReadOnly.stories.tsx @@ -151,7 +151,7 @@ const GridReadOnlyTemplate: ComponentStory = (props: GridProps) => [], ); - const [rowData, setRowData] = useState([ + const [rowData] = useState([ { id: 1000, position: "Tester", age: 30, desc: "Tests application", dd: "1" }, { id: 1001, position: "Developer", age: 12, desc: "Develops application", dd: "2" }, ]);