Skip to content

Commit

Permalink
fix(grid): fix spinner missing on save (#28)
Browse files Browse the repository at this point in the history
* Fix spinner not spinning on save

* Imports

* Don't update bearing if it hasnt changed

* Made gridCell generic again

* Tidy and deps

* Rename MyFormProps to GridFormProps

* Hide multiedit warning

* Tidy
  • Loading branch information
matttdawson committed Oct 25, 2022
1 parent 856ebca commit c056bee
Show file tree
Hide file tree
Showing 14 changed files with 65 additions and 69 deletions.
3 changes: 2 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"name": "step-ag-grid-web",
"name": "@linzjs/step-ag-grid",
"repository": "github:linz/step-ag-grid.git",
"version": "0.1.0",
"private": true,
"resolutions": {
Expand Down
20 changes: 11 additions & 9 deletions web/src/components/GridCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ import { ColDef, ICellEditorParams } from "ag-grid-community";

type SaveFn = (selectedRows: any[]) => Promise<boolean>;

export interface MyFormProps {
export interface GridFormProps {
cellEditorParams: ICellEditorParams;
updateValue: (saveFn: (selectedRows: any[]) => Promise<boolean>) => Promise<boolean>;
saving: boolean;
}

export interface GenericCellEditorParams {
multiEdit?: boolean;
form?: (props: MyFormProps) => JSX.Element;
form?: (props: GridFormProps) => JSX.Element;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down Expand Up @@ -65,27 +65,29 @@ export const GenericCellEditorComponent = <RowType extends BaseGridRow, FormProp
) => {
const { updatingCells } = useContext(GridContext);

const { data } = props;
const { colDef, data } = props;
const { cellEditorParams } = props.colDef;
const multiEdit = cellEditorParams?.multiEdit ?? false;
const field = props.colDef.field ?? "";

const [saving, setSaving] = useState(false);

const updateValue = useCallback(
async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> =>
!saving && (await updatingCells({ data, multiEdit, field }, saveFn, setSaving)),
async (saveFn: (selectedRows: any[]) => Promise<boolean>): Promise<boolean> => {
return !saving && (await updatingCells({ data, multiEdit, field }, saveFn, setSaving));
},
[data, field, multiEdit, saving, updatingCells],
);

if (cellEditorParams == null) return <></>;

// The key=${saving} ensures the cell re-renders when the updatingContext redraws.
return (
<>
{cellEditorParams.form && (
<cellEditorParams.form key={`${saving}`} cellEditorParams={props} updateValue={updateValue} saving={saving} />
<div>
<div>{colDef.cellRenderer ? <colDef.cellRenderer {...props} saving={saving} /> : props.value}</div>
{cellEditorParams?.form && (
<cellEditorParams.form cellEditorParams={props} updateValue={updateValue} saving={saving} />
)}
</>
</div>
);
};
19 changes: 11 additions & 8 deletions web/src/components/GridPopoutHook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { ICellEditorParams } from "ag-grid-community";
import { useCallback, useContext, useEffect, useRef, useState } from "react";
import { GridContext } from "../contexts/GridContext";
import { ControlledMenu } from "@szhsin/react-menu";
import { MyFormProps } from "./GridCell";
import { GridFormProps } from "./GridCell";
import { hasParentClass } from "../utils/util";

export const useGridPopoutHook = (props: MyFormProps, save?: (selectedRows: any[]) => Promise<boolean>) => {
export const useGridPopoutHook = (props: GridFormProps, save?: (selectedRows: any[]) => Promise<boolean>) => {
const { cellEditorParams, saving, updateValue } = props;
const { eGridCell } = cellEditorParams as ICellEditorParams;
const { stopEditing } = useContext(GridContext);
const saveButtonRef = useRef<HTMLButtonElement>(null);
const anchorRef = useRef(eGridCell);
anchorRef.current = eGridCell;
const [isOpen, setOpen] = useState(false);
Expand All @@ -17,8 +18,6 @@ export const useGridPopoutHook = (props: MyFormProps, save?: (selectedRows: any[
setOpen(true);
}, []);

const cellRenderer = cellEditorParams.column.getColDef().cellRenderer;

const triggerSave = useCallback(
async (reason?: string) => {
if (reason == "cancel" || !save || (await updateValue(save))) {
Expand All @@ -38,10 +37,14 @@ export const useGridPopoutHook = (props: MyFormProps, save?: (selectedRows: any[
if (!clickIsWithinMenu(ev)) {
ev.preventDefault();
ev.stopPropagation();
triggerSave().then();
// There's an issue in React17
// the cell doesn't refresh during update if save is invoked from a native event
// This doesn't happen in React18
// To work around it, I invoke the save by clicking on an invisible button in the dropdown
saveButtonRef.current?.click();
}
},
[clickIsWithinMenu, triggerSave],
[clickIsWithinMenu],
);

const handleScreenMouseEvent = useCallback(
Expand Down Expand Up @@ -71,7 +74,6 @@ export const useGridPopoutHook = (props: MyFormProps, save?: (selectedRows: any[
(children: JSX.Element) => {
return (
<>
{cellRenderer ? cellRenderer(cellEditorParams) : cellEditorParams.value}
{anchorRef.current && (
<ControlledMenu
state={isOpen ? "open" : "closed"}
Expand All @@ -95,12 +97,13 @@ export const useGridPopoutHook = (props: MyFormProps, save?: (selectedRows: any[
/>
)}
{children}
<button ref={saveButtonRef} onClick={() => triggerSave().then()} style={{ display: "none" }} />
</ControlledMenu>
)}
</>
);
},
[cellRenderer, cellEditorParams, isOpen, saving, triggerSave],
[isOpen, saving, triggerSave],
);

return {
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/gridForm/GridFormDropDown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
import { GridContext } from "../../contexts/GridContext";
import { delay } from "lodash-es";
import debounce from "debounce-promise";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { useGridPopoutHook } from "../GridPopoutHook";

export interface GridPopoutEditDropDownSelectedItem<RowType, ValueType> {
Expand Down Expand Up @@ -35,7 +35,7 @@ export interface GridFormPopoutDropDownProps<RowType, ValueType> {
optionsRequestCancel?: () => void;
}

export const GridFormDropDown = <RowType extends BaseGridRow, ValueType>(props: MyFormProps) => {
export const GridFormDropDown = <RowType extends BaseGridRow, ValueType>(props: GridFormProps) => {
const { getSelectedRows } = useContext(GridContext);
const { popoutWrapper } = useGridPopoutHook(props);

Expand Down
15 changes: 9 additions & 6 deletions web/src/components/gridForm/GridFormEditBearing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,29 @@ import { useCallback, useState } from "react";
import { BaseGridRow } from "../Grid";
import { TextInputFormatted } from "../../lui/TextInputFormatted";
import { bearingNumberParser, bearingStringValidator, convertDDToDMS } from "../../utils/bearing";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { useGridPopoutHook } from "../GridPopoutHook";

export interface GridFormEditBearingProps<RowType extends BaseGridRow> {
placeHolder: string;
onSave?: (selectedRows: RowType[], value: number | null) => Promise<boolean>;
}

export const GridFormEditBearing = <RowType extends BaseGridRow>(props: MyFormProps) => {
export const GridFormEditBearing = <RowType extends BaseGridRow>(props: GridFormProps) => {
const { colDef } = props.cellEditorParams;
const formProps: GridFormEditBearingProps<RowType> = colDef.cellEditorParams;
const field = colDef.field;
const [value, setValue] = useState<string>(
props.cellEditorParams?.value == null ? "" : `${props.cellEditorParams.value}`,
);
const originalValue = props.cellEditorParams?.value;
const [value, setValue] = useState<string>(`${originalValue ?? ""}`);

const save = useCallback(
async (selectedRows: RowType[]): Promise<boolean> => {
if (bearingStringValidator(value)) return false;
const parsedValue = bearingNumberParser(value);
// Value didn't change so don't save just cancel
if (parsedValue === originalValue) {
return true;
}
if (formProps.onSave) {
return await formProps.onSave(selectedRows, parsedValue);
} else {
Expand All @@ -35,7 +38,7 @@ export const GridFormEditBearing = <RowType extends BaseGridRow>(props: MyFormPr
}
return true;
},
[field, formProps, value],
[field, formProps, originalValue, value],
);
const { popoutWrapper, triggerSave } = useGridPopoutHook(props, save);

Expand Down
7 changes: 3 additions & 4 deletions web/src/components/gridForm/GridFormMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext, useEffect, useState } from "react";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { ICellEditorParams } from "ag-grid-community";
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
import { BaseGridRow } from "../Grid";
Expand All @@ -13,9 +13,8 @@ export interface GridFormMessageProps<RowType extends BaseGridRow> {
) => Promise<string | JSX.Element> | string | JSX.Element;
}

export const GridFormMessage = <RowType extends BaseGridRow>(props: MyFormProps) => {
const { colDef } = props.cellEditorParams;
const formProps: GridFormMessageProps<RowType> = colDef.cellEditorParams;
export const GridFormMessage = <RowType extends BaseGridRow>(props: GridFormProps) => {
const formProps: GridFormMessageProps<RowType> = props.cellEditorParams.colDef.cellEditorParams;
const { getSelectedRows } = useContext(GridContext);

const [message, setMessage] = useState<string | JSX.Element | null>(null);
Expand Down
33 changes: 18 additions & 15 deletions web/src/components/gridForm/GridFormMultiSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
import { GridContext } from "../../contexts/GridContext";
import { delay } from "lodash-es";
import { LuiCheckboxInput } from "@linzjs/lui";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { useGridPopoutHook } from "../GridPopoutHook";

interface FinalSelectOption<ValueType> {
Expand Down Expand Up @@ -36,13 +36,15 @@ export interface GridFormMultiSelectProps<RowType, ValueType> {
| ((selectedRows: RowType[]) => Promise<SelectOption<ValueType>[]> | SelectOption<ValueType>[]);
}

export const GridFormMultiSelect = <RowType extends BaseGridRow, ValueType>(props: MyFormProps) => {
export const GridFormMultiSelect = <RowType extends BaseGridRow, ValueType>(props: GridFormProps) => {
const { getSelectedRows } = useContext(GridContext);

const { cellEditorParams } = props;
const { colDef } = cellEditorParams;
const formProps: GridFormMultiSelectProps<RowType, ValueType> = colDef.cellEditorParams;
const field = colDef.field ?? colDef.colId ?? "";
// implement multi-edit when needed
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { multiEdit } = colDef.cellEditorParams;

const [filter, setFilter] = useState("");
Expand Down Expand Up @@ -163,20 +165,21 @@ export const GridFormMultiSelect = <RowType extends BaseGridRow, ValueType>(prop
}}
/>
</MenuItem>
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
{(ref) =>
selectedValues.includes(item.value) &&
item.subComponent &&
item.subComponent(
{
setValue: (value: any) => {
subSelectedValues.current[item.value as string] = value;
{selectedValues.includes(item.value) && item.subComponent && (
<FocusableItem className={"LuiDeprecatedForms"} key={`${item.value}_subcomponent`}>
{(ref) =>
item.subComponent &&
item.subComponent(
{
setValue: (value: any) => {
subSelectedValues.current[item.value as string] = value;
},
},
},
ref,
)
}
</FocusableItem>
ref,
)
}
</FocusableItem>
)}
</>
),
)}
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/gridForm/GridFormPopoutMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useCallback, useContext, useEffect, useRef, useState } from "react";
import { GridContext } from "../../contexts/GridContext";
import { ComponentLoadingWrapper } from "../ComponentLoadingWrapper";
import { MenuDivider, MenuItem } from "@szhsin/react-menu";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { useGridPopoutHook } from "../GridPopoutHook";

export interface GridFormPopoutMenuProps<RowType> {
Expand All @@ -27,7 +27,7 @@ export interface MenuOption<RowType> {
* NOTE: If the popout menu doesn't appear on single click when also selecting row it's because
* you need a useMemo around your columnDefs
*/
export const GridFormPopoutMenu = <RowType extends BaseGridRow>(props: MyFormProps) => {
export const GridFormPopoutMenu = <RowType extends BaseGridRow>(props: GridFormProps) => {
const { popoutWrapper } = useGridPopoutHook(props);
const { colDef } = props.cellEditorParams;
const formProps: GridFormPopoutMenuProps<RowType> = colDef.cellEditorParams;
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/gridForm/GridFormTextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useState } from "react";
import { wait } from "../../utils/util";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { TextAreaInput } from "../../lui/TextArea";
import { useGridPopoutHook } from "../GridPopoutHook";

Expand All @@ -11,7 +11,7 @@ interface FormTextAreaProps {
width?: string | number;
}

export const GridFormTextArea = (props: MyFormProps) => {
export const GridFormTextArea = (props: GridFormProps) => {
const { cellEditorParams } = props;
const { colDef } = cellEditorParams;
const formProps = colDef.cellEditorParams as FormTextAreaProps;
Expand Down
4 changes: 2 additions & 2 deletions web/src/components/gridForm/GridFormTextInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useState } from "react";
import { wait } from "../../utils/util";
import { MyFormProps } from "../GridCell";
import { GridFormProps } from "../GridCell";
import { TextInputFormatted } from "../../lui/TextInputFormatted";
import { useGridPopoutHook } from "../GridPopoutHook";

Expand All @@ -11,7 +11,7 @@ interface GridFormTextInputProps {
width?: string | number;
}

export const GridFormTextInput = (props: MyFormProps) => {
export const GridFormTextInput = (props: GridFormProps) => {
const { cellEditorParams } = props;
const { colDef } = cellEditorParams;
const formProps = colDef.cellEditorParams as GridFormTextInputProps;
Expand Down
13 changes: 0 additions & 13 deletions web/src/components/gridPopoverEdit/GridPopover.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { bearingValueFormatter } from "../../utils/bearing";
import { GridCell } from "../GridCell";
import { GridFormEditBearing, GridFormEditBearingProps } from "../gridForm/GridFormEditBearing";
import { BaseGridRow } from "../Grid";
import { GridPopupProps } from "./GridPopover";

export const GridPopoverEditBearing = <RowType extends BaseGridRow>(
colDef: GenericCellColDef<GridFormEditBearingProps<RowType>>,
Expand Down
6 changes: 3 additions & 3 deletions web/src/stories/components/FormTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "./FormTest.scss";
import { useCallback, useContext, useState } from "react";
import { LuiTextInput } from "@linzjs/lui";
import { wait } from "../../utils/util";
import { GridGenericCellEditorFormContextParams, MyFormProps } from "../../components/GridCell";
import { GridFormProps } from "../../components/GridCell";
import { useGridPopoutHook } from "../../components/GridPopoutHook";
import { GridContext } from "../../contexts/GridContext";

Expand All @@ -15,7 +15,7 @@ export interface IFormTestRow {
plan: string;
}

export const FormTest = (props: MyFormProps): JSX.Element => {
export const FormTest = (props: GridFormProps): JSX.Element => {
const { cellEditorParams } = props;
const { getSelectedRows } = useContext(GridContext);
const [v1, v2, ...v3] = cellEditorParams.value.split(" ");
Expand All @@ -32,7 +32,7 @@ export const FormTest = (props: MyFormProps): JSX.Element => {
cellEditorParams.data.name = [nameType, numba, plan].join(" ");
await wait(1000);
return true;
}, [cellEditorParams, nameType, numba, plan]);
}, [cellEditorParams.data, getSelectedRows, nameType, numba, plan]);
const { popoutWrapper } = useGridPopoutHook(props, save);

return popoutWrapper(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { UpdatingContextProvider } from "../../contexts/UpdatingContextProvider"
import { ColDef } from "ag-grid-community";
import { wait } from "../../utils/util";
import { GridCell } from "../../components/GridCell";
import { GridFormPopoutMenuProps } from "../../components/gridForm/GridFormPopoutMenu";
import { GridPopoutEditDropDown } from "../../components/gridPopoverEdit/GridPopoverEditDropDown";

export default {
Expand Down

0 comments on commit c056bee

Please sign in to comment.