From 3c2395d46cd0309cf332b885d14f8a68b7383998 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 12:12:23 +0200 Subject: [PATCH 01/22] chore(debug): debug data flow --- .../src/DatagridNumberFilter.tsx | 7 +++ .../src/components/FilterComponent.tsx | 7 ++- .../src/functions.ts | 44 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index f7a7f83962..985487c086 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -3,6 +3,7 @@ import { DatagridNumberFilterContainerProps, DefaultFilterEnum } from "../typing import { FilterComponent } from "./components/FilterComponent"; import { Alert, FilterType, getFilterDispatcher, generateUUID } from "@mendix/pluggable-widgets-commons/components/web"; +import { usePropInspect, useId, useLog } from "@mendix/pluggable-widgets-commons"; import { Big } from "big.js"; import { @@ -22,6 +23,9 @@ import { translateFilters } from "./utils/filters"; export default function DatagridNumberFilter(props: DatagridNumberFilterContainerProps): ReactElement { const id = useRef(`NumberFilter${generateUUID()}`); + const debugId = useId("DatagridNumberFilter"); + const log = useLog(debugId); + usePropInspect(debugId)(props); const FilterContext = getFilterDispatcher(); const alertMessage = ( @@ -70,6 +74,9 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine ? translateFilters(singleInitialFilter) : translateFilters(multipleInitialFilters?.[attributes[0].id]); + log(`defaultFilter: ${JSON.stringify(defaultFilter, null, 2)}`); + log(`defaultValue: ${JSON.stringify(props.defaultValue?.value, null, 2)}`); + const errorMessage = getAttributeTypeErrorMessage(attributes[0].type); if (errorMessage) { return {errorMessage}; diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index c2a2490a20..398194e05e 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -1,6 +1,6 @@ import { createElement, CSSProperties, ReactElement, useCallback, useEffect, useRef, useState } from "react"; import { FilterSelector } from "@mendix/pluggable-widgets-commons/components/web"; -import { debounce } from "@mendix/pluggable-widgets-commons"; +import { debounce, useId, usePropInspect, useLog } from "@mendix/pluggable-widgets-commons"; import { DefaultFilterEnum } from "../../typings/DatagridNumberFilterProps"; import { Big } from "big.js"; @@ -22,17 +22,22 @@ interface FilterComponentProps { } export function FilterComponent(props: FilterComponentProps): ReactElement { + const id = useId("NumberFilter"); + const log = useLog(id); + usePropInspect(id)(props); const [type, setType] = useState(props.defaultFilter); const [value, setValue] = useState(undefined); const [valueInput, setValueInput] = useState(undefined); const inputRef = useRef(null); useEffect(() => { + log("effect -> props.value is changed, set valueInput and value"); setValueInput(props.value?.toString() ?? ""); setValue(props.value); }, [props.value]); useEffect(() => { + log("effect -> value or type is changed, call updateFilters"); props.updateFilters?.(value, type); }, [value, type]); diff --git a/packages/shared/pluggable-widgets-commons/src/functions.ts b/packages/shared/pluggable-widgets-commons/src/functions.ts index 3d68e89a1b..634ca11b4b 100644 --- a/packages/shared/pluggable-widgets-commons/src/functions.ts +++ b/packages/shared/pluggable-widgets-commons/src/functions.ts @@ -1,3 +1,4 @@ +import { useState } from "react"; import { ActionValue, DynamicValue, EditableValue, ValueStatus } from "mendix"; export const executeAction = (action?: ActionValue): void => { @@ -38,3 +39,46 @@ export const debounce = any>(func: F, waitFor: nu return debounced as F; }; + +export function useId(name?: string): string { + const [id] = useState(() => { + const num = Math.random().toFixed(9).slice(2); + return name ? `${name}-${num}` : num; + }); + + return id; +} + +function createInspect(id?: string): (props: any) => void { + let prevProps: any = {}; + return (currentProps: any) => { + console.log(`[DEBUG]`); + console.log(`[DEBUG] Render`, id); + const keys = new Set([...Object.keys(prevProps), ...Object.keys(currentProps)]); + const changed = Array.from(keys).some(k => { + const notEq = prevProps[k] !== currentProps[k]; + if (notEq) { + console.log(`[DEBUG] > prop [${k}] changed`); + } + return notEq; + }); + if (!changed) { + console.log("[DEBUG] No prop changes", id); + } + prevProps = currentProps; + }; +} + +export function usePropInspect(id?: string): (props: any) => void { + const [inspect] = useState(() => createInspect(id)); + return inspect; +} + +function createLog(id: string): (msg: string) => void { + return (msg: string) => console.log(`[DEBUG] >`, msg, id); +} + +export function useLog(id: string): (msg: string) => void { + const [log] = useState(() => createLog(id)); + return log; +} From 5f867a2df861013f8f0f8950ca95fa7e9aa03760 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 17:09:26 +0200 Subject: [PATCH 02/22] refactor(datagrid-number-filter): extract filter state --- .../src/DatagridNumberFilter.tsx | 5 +- .../src/components/FilterComponent.tsx | 107 ++++++++---------- .../src/features/filter-state.ts | 34 ++++++ .../typings/FilterType.d.ts | 1 + .../src/functions.ts | 27 +++-- 5 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts create mode 100644 packages/pluggableWidgets/datagrid-number-filter-web/typings/FilterType.d.ts diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index 985487c086..25c7256cf8 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -86,8 +86,8 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine ); }} diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 398194e05e..6505d46900 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -1,56 +1,43 @@ -import { createElement, CSSProperties, ReactElement, useCallback, useEffect, useRef, useState } from "react"; +import { ChangeEventHandler, createElement, CSSProperties, ReactElement, useRef, memo } from "react"; import { FilterSelector } from "@mendix/pluggable-widgets-commons/components/web"; -import { debounce, useId, usePropInspect, useLog } from "@mendix/pluggable-widgets-commons"; +import { useId, useLog, usePropInspect } from "@mendix/pluggable-widgets-commons"; import { DefaultFilterEnum } from "../../typings/DatagridNumberFilterProps"; import { Big } from "big.js"; import classNames from "classnames"; +import { useFilterState } from "../features/filter-state"; -interface FilterComponentProps { +type FilterType = DefaultFilterEnum; + +interface FilterProps { adjustable: boolean; + initialFilterType: FilterType; className?: string; - defaultFilter: DefaultFilterEnum; - delay: number; id?: string; placeholder?: string; screenReaderButtonCaption?: string; screenReaderInputCaption?: string; tabIndex?: number; styles?: CSSProperties; - updateFilters?: (value: Big | undefined, type: DefaultFilterEnum) => void; - value?: Big; } -export function FilterComponent(props: FilterComponentProps): ReactElement { - const id = useId("NumberFilter"); - const log = useLog(id); - usePropInspect(id)(props); - const [type, setType] = useState(props.defaultFilter); - const [value, setValue] = useState(undefined); - const [valueInput, setValueInput] = useState(undefined); - const inputRef = useRef(null); - - useEffect(() => { - log("effect -> props.value is changed, set valueInput and value"); - setValueInput(props.value?.toString() ?? ""); - setValue(props.value); - }, [props.value]); - - useEffect(() => { - log("effect -> value or type is changed, call updateFilters"); - props.updateFilters?.(value, type); - }, [value, type]); +interface FilterComponentProps extends FilterProps { + inputChangeDelay: number; + updateFilters?: (value: Big | undefined, type: DefaultFilterEnum) => void; +} - const onChange = useCallback( - debounce((value?: Big) => setValue(value), props.delay), - [props.delay] - ); +interface FilterInputProps extends FilterProps { + onFilterTypeClick: (type: FilterType) => void; + onInputChange: ChangeEventHandler; + inputValue: string; + inputRef?: React.ClassAttributes["ref"]; + inputDisabled?: boolean; +} - const focusInput = useCallback(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, [inputRef]); +function FilterInput(props: FilterInputProps): ReactElement { + const { current: initialFilterType } = useRef(props.initialFilterType); + const id = useId("PureNumberFilter"); + usePropInspect(id)(props); return (
{ - setType(prev => { - if (prev === type) { - return prev; - } - focusInput(); - return type; - }); - }, - [focusInput] - )} + defaultFilter={initialFilterType} + onChange={props.onFilterTypeClick} options={ [ { value: "greater", label: "Greater than" }, @@ -92,22 +68,31 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { { - const value = e.target.value; - if (value && !isNaN(Number(value))) { - setValueInput(value); - onChange(new Big(Number(value))); - } else { - setValueInput(value); - onChange(undefined); - } - }} + disabled={props.inputDisabled} + onChange={props.onInputChange} placeholder={props.placeholder} - ref={inputRef} + ref={props.inputRef} type="number" - value={valueInput} + value={props.inputValue} />
); } + +const PureFilterInput = memo(FilterInput); + +export function FilterComponent(props: FilterComponentProps): ReactElement { + const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ inputValue: "5", type: "greater" })); + const log = useLog("FilterComponent"); + log("Rerender"); + return ( + + ); +} diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts new file mode 100644 index 0000000000..b0204f474f --- /dev/null +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts @@ -0,0 +1,34 @@ +import { ChangeEventHandler, useState, useMemo } from "react"; +import { FilterType } from "../../typings/FilterType"; +// import type { Big } from "big.js"; + +type FilterState = { + type: FilterType; + inputValue: string; +}; + +type InputChangeHandler = ChangeEventHandler; + +type TypeClickHandler = (type: FilterType) => void; + +function updateState(key: K, value: V): (prev: S) => S { + return prev => (prev[key] !== value ? { ...prev, [key]: value } : prev); +} + +export function useFilterState(initialState: () => FilterState): [FilterState, InputChangeHandler, TypeClickHandler] { + const [state, setState] = useState(initialState); + const [onInputChange, onTypeClick] = useMemo(() => { + const inputHandler: InputChangeHandler = event => setState(updateState("inputValue", event.target.value)); + const clickHandler: TypeClickHandler = type => setState(updateState("type", type)); + + return [inputHandler, clickHandler]; + }, []); + + return [state, onInputChange, onTypeClick]; +} + +// type ChangeDispatch = (value: Big | undefined, type: FilterType) => void; + +// function useStateChangeEffects(state: FilterState, dispatch:) { +// const [state, setState] = useFilterState(); +// } diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/typings/FilterType.d.ts b/packages/pluggableWidgets/datagrid-number-filter-web/typings/FilterType.d.ts new file mode 100644 index 0000000000..eb9fcd0c59 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-number-filter-web/typings/FilterType.d.ts @@ -0,0 +1 @@ +export { DefaultFilterEnum as FilterType } from "./DatagridNumberFilterProps"; diff --git a/packages/shared/pluggable-widgets-commons/src/functions.ts b/packages/shared/pluggable-widgets-commons/src/functions.ts index 634ca11b4b..8be2278282 100644 --- a/packages/shared/pluggable-widgets-commons/src/functions.ts +++ b/packages/shared/pluggable-widgets-commons/src/functions.ts @@ -49,36 +49,45 @@ export function useId(name?: string): string { return id; } -function createInspect(id?: string): (props: any) => void { +const debugMsg = (...args: any[]): void => console.debug("[DEBUG]", ...args); + +function debugHeader(id: string): void { + debugMsg(); + debugMsg(`Component:`, id); +} + +function createInspect(id: string): (props: any) => void { let prevProps: any = {}; return (currentProps: any) => { - console.log(`[DEBUG]`); - console.log(`[DEBUG] Render`, id); + debugHeader(id); const keys = new Set([...Object.keys(prevProps), ...Object.keys(currentProps)]); const changed = Array.from(keys).some(k => { const notEq = prevProps[k] !== currentProps[k]; if (notEq) { - console.log(`[DEBUG] > prop [${k}] changed`); + debugMsg(` > prop [${k}] changed`); } return notEq; }); if (!changed) { - console.log("[DEBUG] No prop changes", id); + debugMsg(" > No prop changes"); } prevProps = currentProps; }; } -export function usePropInspect(id?: string): (props: any) => void { +export function usePropInspect(id: string): (props: any) => void { const [inspect] = useState(() => createInspect(id)); return inspect; } -function createLog(id: string): (msg: string) => void { - return (msg: string) => console.log(`[DEBUG] >`, msg, id); +function createLog(id: string): (...args: string[]) => void { + return (...args: string[]) => { + debugHeader(id); + debugMsg(" >", ...args); + }; } -export function useLog(id: string): (msg: string) => void { +export function useLog(id: string): (...args: string[]) => void { const [log] = useState(() => createLog(id)); return log; } From 5f817cc7370d445afce4976def478dca0d42924f Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 17:49:04 +0200 Subject: [PATCH 03/22] refactor(datagrid-number-filter): add change effects --- .../src/components/FilterComponent.tsx | 4 ++- .../src/features/filter-state.ts | 30 ++++++++++++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 6505d46900..8b60061acd 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -5,7 +5,7 @@ import { useId, useLog, usePropInspect } from "@mendix/pluggable-widgets-commons import { DefaultFilterEnum } from "../../typings/DatagridNumberFilterProps"; import { Big } from "big.js"; import classNames from "classnames"; -import { useFilterState } from "../features/filter-state"; +import { useFilterState, useStateChangeEffects } from "../features/filter-state"; type FilterType = DefaultFilterEnum; @@ -83,6 +83,8 @@ const PureFilterInput = memo(FilterInput); export function FilterComponent(props: FilterComponentProps): ReactElement { const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ inputValue: "5", type: "greater" })); + useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b)); + const log = useLog("FilterComponent"); log("Rerender"); return ( diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts index b0204f474f..0012bd1e7e 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts @@ -1,6 +1,7 @@ -import { ChangeEventHandler, useState, useMemo } from "react"; +import { ChangeEventHandler, useState, useMemo, useRef, useEffect } from "react"; import { FilterType } from "../../typings/FilterType"; -// import type { Big } from "big.js"; +import { Big } from "big.js"; +import { debounce, useEventCallback } from "@mendix/pluggable-widgets-commons"; type FilterState = { type: FilterType; @@ -27,8 +28,23 @@ export function useFilterState(initialState: () => FilterState): [FilterState, I return [state, onInputChange, onTypeClick]; } -// type ChangeDispatch = (value: Big | undefined, type: FilterType) => void; - -// function useStateChangeEffects(state: FilterState, dispatch:) { -// const [state, setState] = useFilterState(); -// } +type ChangeDispatch = (value: Big | undefined, type: FilterType) => void; + +export function useStateChangeEffects(state: FilterState, dispatch: ChangeDispatch): void { + const stableDispatch = useEventCallback(dispatch); + const [stableDispatchDelayed] = useState(() => debounce(stableDispatch, 1000)); + const inputRef = useRef(undefined); + const prevStateRef = useRef(state); + + useEffect(() => { + const { current: prevState } = prevStateRef; + if (state.type !== prevState.type) { + stableDispatch(new Big(5), state.type); + inputRef.current?.focus(); + } else if (state.inputValue !== prevState.inputValue) { + stableDispatchDelayed(new Big(1), state.type); + } + prevStateRef.current = state; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state]); +} From 70eeecdb24c680a1a2437b230023fa812727a3a1 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 18:24:09 +0200 Subject: [PATCH 04/22] refactor(datagrid-number-filter): set focus on filter type change --- .../src/components/FilterComponent.tsx | 3 ++- .../src/features/filter-state.ts | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 8b60061acd..7cc0fa9120 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -83,7 +83,7 @@ const PureFilterInput = memo(FilterInput); export function FilterComponent(props: FilterComponentProps): ReactElement { const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ inputValue: "5", type: "greater" })); - useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b)); + const [inputRef] = useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b)); const log = useLog("FilterComponent"); log("Rerender"); @@ -93,6 +93,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { initialFilterType="greater" onFilterTypeClick={onFilterTypeClick} onInputChange={onInputChange} + inputRef={inputRef} inputValue={state.inputValue} inputDisabled={state.type === "empty" || state.type === "notEmpty"} /> diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts index 0012bd1e7e..077b83aaee 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts @@ -1,4 +1,4 @@ -import { ChangeEventHandler, useState, useMemo, useRef, useEffect } from "react"; +import { ChangeEventHandler, useState, useMemo, useRef, useEffect, MutableRefObject } from "react"; import { FilterType } from "../../typings/FilterType"; import { Big } from "big.js"; import { debounce, useEventCallback } from "@mendix/pluggable-widgets-commons"; @@ -30,10 +30,13 @@ export function useFilterState(initialState: () => FilterState): [FilterState, I type ChangeDispatch = (value: Big | undefined, type: FilterType) => void; -export function useStateChangeEffects(state: FilterState, dispatch: ChangeDispatch): void { +export function useStateChangeEffects( + state: FilterState, + dispatch: ChangeDispatch +): [MutableRefObject] { const stableDispatch = useEventCallback(dispatch); const [stableDispatchDelayed] = useState(() => debounce(stableDispatch, 1000)); - const inputRef = useRef(undefined); + const inputRef = useRef(null); const prevStateRef = useRef(state); useEffect(() => { @@ -47,4 +50,6 @@ export function useStateChangeEffects(state: FilterState, dispatch: ChangeDispat prevStateRef.current = state; // eslint-disable-next-line react-hooks/exhaustive-deps }, [state]); + + return [inputRef]; } From d7d9b165debed5b47b274c537f8a90b8541b8bd2 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 19:01:12 +0200 Subject: [PATCH 05/22] feat(datagrid-number-filter-web): add initial value setup --- .../src/DatagridNumberFilter.tsx | 7 ++++++- .../src/components/FilterComponent.tsx | 9 +++++++-- .../src/features/filter-state.ts | 5 +++-- .../datagrid-number-filter-web/src/utils/value.ts | 13 +++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 packages/pluggableWidgets/datagrid-number-filter-web/src/utils/value.ts diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index 25c7256cf8..e8761ba2b6 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -74,7 +74,6 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine ? translateFilters(singleInitialFilter) : translateFilters(multipleInitialFilters?.[attributes[0].id]); - log(`defaultFilter: ${JSON.stringify(defaultFilter, null, 2)}`); log(`defaultValue: ${JSON.stringify(props.defaultValue?.value, null, 2)}`); const errorMessage = getAttributeTypeErrorMessage(attributes[0].type); @@ -82,11 +81,16 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine return {errorMessage}; } + if (props.defaultValue && props.defaultValue.status === "loading") { + return null; + } + return ( { + console.log("[DEBUG] >>> updateFilters"); if ( (value && !props.valueAttribute?.value?.eq(value)) || value !== props.valueAttribute?.value diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 7cc0fa9120..89b17ac45e 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -6,6 +6,7 @@ import { DefaultFilterEnum } from "../../typings/DatagridNumberFilterProps"; import { Big } from "big.js"; import classNames from "classnames"; import { useFilterState, useStateChangeEffects } from "../features/filter-state"; +import { toInputValue } from "../utils/value"; type FilterType = DefaultFilterEnum; @@ -23,6 +24,7 @@ interface FilterProps { interface FilterComponentProps extends FilterProps { inputChangeDelay: number; + initialFilterValue?: Big; updateFilters?: (value: Big | undefined, type: DefaultFilterEnum) => void; } @@ -82,7 +84,10 @@ function FilterInput(props: FilterInputProps): ReactElement { const PureFilterInput = memo(FilterInput); export function FilterComponent(props: FilterComponentProps): ReactElement { - const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ inputValue: "5", type: "greater" })); + const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ + inputValue: toInputValue(props.initialFilterValue), + type: props.initialFilterType + })); const [inputRef] = useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b)); const log = useLog("FilterComponent"); @@ -90,7 +95,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { return ( { const { current: prevState } = prevStateRef; if (state.type !== prevState.type) { - stableDispatch(new Big(5), state.type); + stableDispatch(toBig(state.inputValue), state.type); inputRef.current?.focus(); } else if (state.inputValue !== prevState.inputValue) { - stableDispatchDelayed(new Big(1), state.type); + stableDispatchDelayed(toBig(state.inputValue), state.type); } prevStateRef.current = state; // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/utils/value.ts b/packages/pluggableWidgets/datagrid-number-filter-web/src/utils/value.ts new file mode 100644 index 0000000000..bf9a2bb1f2 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/utils/value.ts @@ -0,0 +1,13 @@ +import { Big } from "big.js"; + +export function toBig(value: any): Big | undefined { + try { + return new Big(value); + } catch { + return undefined; + } +} + +export function toInputValue(value: Big | undefined): string { + return value instanceof Big ? value.toString() : ""; +} From 1c7ef697c686d426939e73c1eb5e20b92a3c4950 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 19:10:22 +0200 Subject: [PATCH 06/22] fix: prevent unwanted props --- .../src/components/FilterComponent.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 89b17ac45e..fde3852a9f 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -94,13 +94,20 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { log("Rerender"); return ( ); } From feab6e0bd09b74253898f21ddbdebbe5848581b8 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 19:15:49 +0200 Subject: [PATCH 07/22] refactor: use type alias --- .../src/components/FilterComponent.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index fde3852a9f..5479432b22 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -1,15 +1,12 @@ import { ChangeEventHandler, createElement, CSSProperties, ReactElement, useRef, memo } from "react"; import { FilterSelector } from "@mendix/pluggable-widgets-commons/components/web"; import { useId, useLog, usePropInspect } from "@mendix/pluggable-widgets-commons"; - -import { DefaultFilterEnum } from "../../typings/DatagridNumberFilterProps"; +import { FilterType } from "../../typings/FilterType"; import { Big } from "big.js"; import classNames from "classnames"; import { useFilterState, useStateChangeEffects } from "../features/filter-state"; import { toInputValue } from "../utils/value"; -type FilterType = DefaultFilterEnum; - interface FilterProps { adjustable: boolean; initialFilterType: FilterType; @@ -25,7 +22,7 @@ interface FilterProps { interface FilterComponentProps extends FilterProps { inputChangeDelay: number; initialFilterValue?: Big; - updateFilters?: (value: Big | undefined, type: DefaultFilterEnum) => void; + updateFilters?: (value: Big | undefined, type: FilterType) => void; } interface FilterInputProps extends FilterProps { @@ -63,7 +60,7 @@ function FilterInput(props: FilterInputProps): ReactElement { { value: "smallerEqual", label: "Smaller than or equal" }, { value: "empty", label: "Empty" }, { value: "notEmpty", label: "Not empty" } - ] as Array<{ value: DefaultFilterEnum; label: string }> + ] as Array<{ value: FilterType; label: string }> } /> )} From c62c6761def51111819387c3bc10059daf9de84d Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 19:29:20 +0200 Subject: [PATCH 08/22] refactor: pass delay from widget settings --- .../src/components/FilterComponent.tsx | 2 +- .../datagrid-number-filter-web/src/features/filter-state.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index 5479432b22..e3989fcbcd 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -85,7 +85,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { inputValue: toInputValue(props.initialFilterValue), type: props.initialFilterType })); - const [inputRef] = useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b)); + const [inputRef] = useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b), props.inputChangeDelay); const log = useLog("FilterComponent"); log("Rerender"); diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts index 958652dfc9..b805796935 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/features/filter-state.ts @@ -33,10 +33,11 @@ type ChangeDispatch = (value: Big | undefined, type: FilterType) => void; export function useStateChangeEffects( state: FilterState, - dispatch: ChangeDispatch + dispatch: ChangeDispatch, + inputChangeDelay: number ): [MutableRefObject] { const stableDispatch = useEventCallback(dispatch); - const [stableDispatchDelayed] = useState(() => debounce(stableDispatch, 1000)); + const [stableDispatchDelayed] = useState(() => debounce(stableDispatch, inputChangeDelay)); const inputRef = useRef(null); const prevStateRef = useRef(state); From 92287f34669fc9276445bdf6039c0d793bac18b8 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 19:39:30 +0200 Subject: [PATCH 09/22] fix(debug): print all changed props --- .../shared/pluggable-widgets-commons/src/functions.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/shared/pluggable-widgets-commons/src/functions.ts b/packages/shared/pluggable-widgets-commons/src/functions.ts index 8be2278282..a5ad4d3e66 100644 --- a/packages/shared/pluggable-widgets-commons/src/functions.ts +++ b/packages/shared/pluggable-widgets-commons/src/functions.ts @@ -61,13 +61,13 @@ function createInspect(id: string): (props: any) => void { return (currentProps: any) => { debugHeader(id); const keys = new Set([...Object.keys(prevProps), ...Object.keys(currentProps)]); - const changed = Array.from(keys).some(k => { - const notEq = prevProps[k] !== currentProps[k]; - if (notEq) { + let changed = false; + for (const k of keys) { + if (prevProps[k] !== currentProps[k]) { debugMsg(` > prop [${k}] changed`); + changed = true; } - return notEq; - }); + } if (!changed) { debugMsg(" > No prop changes"); } From 03f6c59fe46a8956538ba5a780b03ceb5d5996da Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Fri, 7 Apr 2023 20:00:41 +0200 Subject: [PATCH 10/22] refactor: remove debugger code --- .../src/DatagridNumberFilter.tsx | 8 -------- .../src/components/FilterComponent.tsx | 5 ----- 2 files changed, 13 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index e8761ba2b6..c017fb8522 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -1,9 +1,7 @@ import { createElement, ReactElement, useRef } from "react"; import { DatagridNumberFilterContainerProps, DefaultFilterEnum } from "../typings/DatagridNumberFilterProps"; - import { FilterComponent } from "./components/FilterComponent"; import { Alert, FilterType, getFilterDispatcher, generateUUID } from "@mendix/pluggable-widgets-commons/components/web"; -import { usePropInspect, useId, useLog } from "@mendix/pluggable-widgets-commons"; import { Big } from "big.js"; import { @@ -23,9 +21,6 @@ import { translateFilters } from "./utils/filters"; export default function DatagridNumberFilter(props: DatagridNumberFilterContainerProps): ReactElement { const id = useRef(`NumberFilter${generateUUID()}`); - const debugId = useId("DatagridNumberFilter"); - const log = useLog(debugId); - usePropInspect(debugId)(props); const FilterContext = getFilterDispatcher(); const alertMessage = ( @@ -74,8 +69,6 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine ? translateFilters(singleInitialFilter) : translateFilters(multipleInitialFilters?.[attributes[0].id]); - log(`defaultValue: ${JSON.stringify(props.defaultValue?.value, null, 2)}`); - const errorMessage = getAttributeTypeErrorMessage(attributes[0].type); if (errorMessage) { return {errorMessage}; @@ -99,7 +92,6 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine styles={props.style} tabIndex={props.tabIndex} updateFilters={(value: Big | undefined, type: DefaultFilterEnum): void => { - console.log("[DEBUG] >>> updateFilters"); if ( (value && !props.valueAttribute?.value?.eq(value)) || value !== props.valueAttribute?.value diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx index e3989fcbcd..4087f42f19 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/FilterComponent.tsx @@ -1,6 +1,5 @@ import { ChangeEventHandler, createElement, CSSProperties, ReactElement, useRef, memo } from "react"; import { FilterSelector } from "@mendix/pluggable-widgets-commons/components/web"; -import { useId, useLog, usePropInspect } from "@mendix/pluggable-widgets-commons"; import { FilterType } from "../../typings/FilterType"; import { Big } from "big.js"; import classNames from "classnames"; @@ -35,8 +34,6 @@ interface FilterInputProps extends FilterProps { function FilterInput(props: FilterInputProps): ReactElement { const { current: initialFilterType } = useRef(props.initialFilterType); - const id = useId("PureNumberFilter"); - usePropInspect(id)(props); return (
props.updateFilters?.(a, b), props.inputChangeDelay); - const log = useLog("FilterComponent"); - log("Rerender"); return ( Date: Fri, 7 Apr 2023 20:02:39 +0200 Subject: [PATCH 11/22] refactor(datagrid-number-filter-web): dispatch change on every change --- .../src/DatagridNumberFilter.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index c017fb8522..1265785bdb 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -92,13 +92,8 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine styles={props.style} tabIndex={props.tabIndex} updateFilters={(value: Big | undefined, type: DefaultFilterEnum): void => { - if ( - (value && !props.valueAttribute?.value?.eq(value)) || - value !== props.valueAttribute?.value - ) { - props.valueAttribute?.setValue(value); - props.onChange?.execute(); - } + props.valueAttribute?.setValue(value); + props.onChange?.execute(); const conditions = attributes ?.map(attribute => getFilterCondition(attribute, value, type)) .filter((filter): filter is FilterCondition => filter !== undefined); From 8a66171e9de32a4eb36de887a0c2c84629934f37 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Thu, 13 Apr 2023 12:06:26 +0200 Subject: [PATCH 12/22] test(datagrid-number-filter-web): fix tests to reflect new behavior --- .../__tests__/DatagridNumberFilter.spec.tsx | 8 +- .../__tests__/FilterComponent.spec.tsx | 73 ++++++++++++------- 2 files changed, 51 insertions(+), 30 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx index 2a96f8487f..3c3ba2bbd8 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/DatagridNumberFilter.spec.tsx @@ -63,20 +63,20 @@ describe("Number Filter", () => { render((new Big(100))} />); expect(screen.getByRole("spinbutton")).toHaveValue(100); }); - it("sync value and defaultValue when defaultValue changes from undefined to number", () => { + it("do not sync value and defaultValue when defaultValue changes from undefined to number", () => { const { rerender } = render(); expect(screen.getByRole("spinbutton")).toHaveValue(null); rerender((new Big(100))} />); - expect(screen.getByRole("spinbutton")).toHaveValue(100); + expect(screen.getByRole("spinbutton")).toHaveValue(null); }); - it("sync value and defaultValue when defaultValue changes from number to undefined", async () => { + it("do not sync value and defaultValue when defaultValue changes from number to undefined", async () => { const { rerender } = render( (new Big(100))} /> ); expect(screen.getByRole("spinbutton")).toHaveValue(100); rerender(); await waitFor(() => { - expect(screen.getByRole("spinbutton")).toHaveValue(null); + expect(screen.getByRole("spinbutton")).toHaveValue(100); }); }); }); diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/FilterComponent.spec.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/FilterComponent.spec.tsx index d5eeb90d27..58b55ea58f 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/FilterComponent.spec.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/components/__tests__/FilterComponent.spec.tsx @@ -1,4 +1,4 @@ -import { render, shallow } from "enzyme"; +import { render, mount } from "enzyme"; import { createElement } from "react"; import { FilterComponent } from "../FilterComponent"; @@ -6,13 +6,15 @@ jest.useFakeTimers(); describe("Filter component", () => { it("renders correctly", () => { - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); it("renders correctly when not adjustable by user", () => { - const component = render(); + const component = render( + + ); expect(component).toMatchSnapshot(); }); @@ -21,8 +23,8 @@ describe("Filter component", () => { const component = render( @@ -33,24 +35,35 @@ describe("Filter component", () => { it("calls updateFilters when value changes", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( - + const component = mount( + ); const input = component.find("input"); input.simulate("change", { target: { value: "test" } }); + jest.advanceTimersByTime(500); + expect(updateFiltersHandler).toBeCalled(); }); it("debounces calls for updateFilters when value changes with numbers", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( - + const component = mount( + ); - // Initial call with default filter - expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toBeCalledTimes(0); const input = component.find("input"); input.simulate("change", { target: { value: "0" } }); @@ -59,22 +72,26 @@ describe("Filter component", () => { input.simulate("change", { target: { value: "2" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(2); + expect(updateFiltersHandler).toBeCalledTimes(1); input.simulate("change", { target: { value: "3" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(3); + expect(updateFiltersHandler).toBeCalledTimes(2); }); it("debounces calls for updateFilters when value changes with decimals", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( - + const component = mount( + ); - // Initial call with default filter - expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toBeCalledTimes(0); const input = component.find("input"); input.simulate("change", { target: { value: "0.0" } }); @@ -83,22 +100,26 @@ describe("Filter component", () => { input.simulate("change", { target: { value: "4" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(2); + expect(updateFiltersHandler).toBeCalledTimes(1); input.simulate("change", { target: { value: "6.8" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(3); + expect(updateFiltersHandler).toBeCalledTimes(2); }); it("debounces calls for updateFilters when value changes with invalid input", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( - + const component = mount( + ); - // Initial call with default filter - expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toBeCalledTimes(0); const input = component.find("input"); input.simulate("change", { target: { value: "test1" } }); @@ -107,13 +128,13 @@ describe("Filter component", () => { input.simulate("change", { target: { value: "test3" } }); jest.advanceTimersByTime(500); - // Consecutive invalid numbers wont call useState with empty value twice - // this is why we expect func to be called 1 time expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toHaveBeenLastCalledWith(undefined, "equal"); input.simulate("change", { target: { value: "test4" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toBeCalledTimes(2); + expect(updateFiltersHandler).toHaveBeenLastCalledWith(undefined, "equal"); }); }); From c16854aa30c901f8a9091e1a1b791f407a95d056 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Thu, 13 Apr 2023 13:41:49 +0200 Subject: [PATCH 13/22] refactor(datagrid-number-filter-web): rename prop --- .../src/DatagridNumberFilter.editorPreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.editorPreview.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.editorPreview.tsx index 86ab79fc27..47ab9a6c63 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.editorPreview.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.editorPreview.tsx @@ -8,8 +8,8 @@ export function preview(props: DatagridNumberFilterPreviewProps): ReactElement { Date: Thu, 13 Apr 2023 16:01:00 +0200 Subject: [PATCH 14/22] fix(datagrid-text-filter-web): stop syncing value and defaultValue --- .../src/DatagridTextFilter.editorPreview.tsx | 6 +- .../src/DatagridTextFilter.tsx | 17 +-- .../src/components/FilterComponent.tsx | 110 +++++++++--------- .../src/features/filter-state.ts | 55 +++++++++ .../typings/FilterType.d.ts | 1 + 5 files changed, 124 insertions(+), 65 deletions(-) create mode 100644 packages/pluggableWidgets/datagrid-text-filter-web/src/features/filter-state.ts create mode 100644 packages/pluggableWidgets/datagrid-text-filter-web/typings/FilterType.d.ts diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.editorPreview.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.editorPreview.tsx index 471fe5224b..af61bfa56d 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.editorPreview.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.editorPreview.tsx @@ -8,13 +8,13 @@ export function preview(props: DatagridTextFilterPreviewProps): ReactElement { ); } diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx index 17db93ae59..b16e7da900 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx @@ -77,12 +77,17 @@ export default function DatagridTextFilter(props: DatagridTextFilterContainerPro return {errorMessage}; } + if (props.defaultValue && props.defaultValue.status === "loading") { + return null; + } + return ( { - const attributeCurrentValue = props.valueAttribute?.value || ""; - if (value !== attributeCurrentValue) { - props.valueAttribute?.setValue(value); - props.onChange?.execute(); - } + props.valueAttribute?.setValue(value); + props.onChange?.execute(); const conditions = attributes ?.map(attribute => getFilterCondition(attribute, value, type)) .filter((filter): filter is FilterCondition => filter !== undefined); @@ -104,7 +106,6 @@ export default function DatagridTextFilter(props: DatagridTextFilterContainerPro filterType: FilterType.STRING }); }} - value={defaultFilter?.value ?? props.defaultValue?.value} /> ); }} diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/FilterComponent.tsx index b4c4f28604..6f545a1eb0 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/FilterComponent.tsx @@ -1,50 +1,37 @@ -import { createElement, CSSProperties, ReactElement, useCallback, useEffect, useRef, useState } from "react"; +import { createElement, CSSProperties, ReactElement, memo, useRef, ChangeEventHandler } from "react"; import { FilterSelector } from "@mendix/pluggable-widgets-commons/components/web"; -import { debounce } from "@mendix/pluggable-widgets-commons"; - -import { DefaultFilterEnum } from "../../typings/DatagridTextFilterProps"; +import { useFilterState, useStateChangeEffects } from "../features/filter-state"; +import { FilterType } from "../../typings/FilterType"; import classNames from "classnames"; -interface FilterComponentProps { +interface FilterProps { adjustable: boolean; + initialFilterType: FilterType; className?: string; - defaultFilter: DefaultFilterEnum; - delay: number; id?: string; placeholder?: string; - tabIndex?: number; screenReaderButtonCaption?: string; screenReaderInputCaption?: string; + tabIndex?: number; styles?: CSSProperties; - updateFilters?: (value: string, type: DefaultFilterEnum) => void; - value?: string; } -export function FilterComponent(props: FilterComponentProps): ReactElement { - const [type, setType] = useState(props.defaultFilter); - const [value, setValue] = useState(""); - const [valueInput, setValueInput] = useState(""); - const inputRef = useRef(null); - - useEffect(() => { - setValueInput(props.value ?? ""); - setValue(props.value ?? ""); - }, [props.value]); - - useEffect(() => { - props.updateFilters?.(value, type); - }, [value, type]); +interface FilterComponentProps extends FilterProps { + inputChangeDelay: number; + initialFilterValue?: string; + updateFilters?: (value: string | undefined, type: FilterType) => void; +} - const onChange = useCallback( - debounce((value: string) => setValue(value), props.delay), - [props.delay] - ); +interface FilterInputProps extends FilterProps { + onFilterTypeClick: (type: FilterType) => void; + onInputChange: ChangeEventHandler; + inputValue: string; + inputRef?: React.ClassAttributes["ref"]; + inputDisabled?: boolean; +} - const focusInput = useCallback(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, [inputRef]); +function FilterInput(props: FilterInputProps): ReactElement { + const { current: initialFilterType } = useRef(props.initialFilterType); return (
{ - setType(prev => { - if (prev === type) { - return prev; - } - focusInput(); - return type; - }); - }, - [focusInput] - )} + defaultFilter={initialFilterType} + onChange={props.onFilterTypeClick} options={ [ { value: "contains", label: "Contains" }, @@ -82,23 +58,49 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { { value: "smallerEqual", label: "Smaller than or equal" }, { value: "empty", label: "Empty" }, { value: "notEmpty", label: "Not empty" } - ] as Array<{ value: DefaultFilterEnum; label: string }> + ] as Array<{ value: FilterType; label: string }> } /> )} { - setValueInput(e.target.value); - onChange(e.target.value); - }} + disabled={props.inputDisabled} + onChange={props.onInputChange} placeholder={props.placeholder} - ref={inputRef} + ref={props.inputRef} type="text" - value={valueInput} + value={props.inputValue} />
); } + +const PureFilterInput = memo(FilterInput); + +export function FilterComponent(props: FilterComponentProps): ReactElement { + const [state, onInputChange, onFilterTypeClick] = useFilterState(() => ({ + inputValue: props.initialFilterValue ?? "", + type: props.initialFilterType + })); + const [inputRef] = useStateChangeEffects(state, (a, b) => props.updateFilters?.(a, b), props.inputChangeDelay); + + return ( + + ); +} diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/features/filter-state.ts b/packages/pluggableWidgets/datagrid-text-filter-web/src/features/filter-state.ts new file mode 100644 index 0000000000..288864f2d6 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/features/filter-state.ts @@ -0,0 +1,55 @@ +import { ChangeEventHandler, useState, useMemo, useRef, useEffect, MutableRefObject } from "react"; +import { FilterType } from "../../typings/FilterType"; +import { debounce, useEventCallback } from "@mendix/pluggable-widgets-commons"; + +type FilterState = { + type: FilterType; + inputValue: string; +}; + +type InputChangeHandler = ChangeEventHandler; + +type TypeClickHandler = (type: FilterType) => void; + +function updateState(key: K, value: V): (prev: S) => S { + return prev => (prev[key] !== value ? { ...prev, [key]: value } : prev); +} + +export function useFilterState(initialState: () => FilterState): [FilterState, InputChangeHandler, TypeClickHandler] { + const [state, setState] = useState(initialState); + const [onInputChange, onTypeClick] = useMemo(() => { + const inputHandler: InputChangeHandler = event => setState(updateState("inputValue", event.target.value)); + const clickHandler: TypeClickHandler = type => setState(updateState("type", type)); + + return [inputHandler, clickHandler]; + }, []); + + return [state, onInputChange, onTypeClick]; +} + +type ChangeDispatch = (value: string | undefined, type: FilterType) => void; + +export function useStateChangeEffects( + state: FilterState, + dispatch: ChangeDispatch, + inputChangeDelay: number +): [MutableRefObject] { + const stableDispatch = useEventCallback(dispatch); + const [stableDispatchDelayed] = useState(() => debounce(stableDispatch, inputChangeDelay)); + const inputRef = useRef(null); + const prevStateRef = useRef(state); + + useEffect(() => { + const { current: prevState } = prevStateRef; + if (state.type !== prevState.type) { + stableDispatch(state.inputValue, state.type); + inputRef.current?.focus(); + } else if (state.inputValue !== prevState.inputValue) { + stableDispatchDelayed(state.inputValue, state.type); + } + prevStateRef.current = state; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [state]); + + return [inputRef]; +} diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/typings/FilterType.d.ts b/packages/pluggableWidgets/datagrid-text-filter-web/typings/FilterType.d.ts new file mode 100644 index 0000000000..4005087572 --- /dev/null +++ b/packages/pluggableWidgets/datagrid-text-filter-web/typings/FilterType.d.ts @@ -0,0 +1 @@ +export { DefaultFilterEnum as FilterType } from "./DatagridTextFilterProps"; \ No newline at end of file From c509b03c94f7c0c0589ef939952f51097318959c Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 13:58:04 +0200 Subject: [PATCH 15/22] fix(datagrid-dropdown-filter-web): stop syncing value and defaultValue --- .../src/DatagridDropdownFilter.tsx | 6 +++++- .../src/components/FilterComponent.tsx | 11 ++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/DatagridDropdownFilter.tsx b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/DatagridDropdownFilter.tsx index 7c3ff001a3..e064588db9 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/DatagridDropdownFilter.tsx +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/DatagridDropdownFilter.tsx @@ -1,5 +1,5 @@ import { useFilterContextValue } from "@mendix/pluggable-widgets-commons/components/web"; -import { createElement, ReactElement } from "react"; +import { createElement, Fragment, ReactElement } from "react"; import { DatagridDropdownFilterContainerProps } from "../typings/DatagridDropdownFilterProps"; import { EnumerationFilter } from "./components/EnumerationFilter"; import { ErrorBox } from "./components/ErrorBox"; @@ -13,6 +13,10 @@ export default function DatagridDropdownFilter(props: DatagridDropdownFilterCont return ; } + if (props.defaultValue?.status === "loading") { + return ; + } + const Filter = context.value.associationProperties ? AssociationFilter : EnumerationFilter; return ; diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/FilterComponent.tsx index d4170dd423..43c0e752a0 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/FilterComponent.tsx @@ -64,6 +64,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { const [selectedFilters, setSelectedFilters] = useState([]); const [show, setShow] = useState(false); const [dropdownWidth, setDropdownWidth] = useState(0); + const { current: initialFilterValue } = useRef(defaultValue); const defaultValuesLoaded = useRef(false); const componentRef = useRef(null); @@ -108,8 +109,8 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { useEffect(() => { if (!defaultValuesLoaded.current && options.length > 0) { if (multiSelect) { - if (defaultValue) { - const initialOptions = defaultValue + if (initialFilterValue) { + const initialOptions = initialFilterValue .split(",") .map(value => options.find(option => option.value === value)) .filter(Boolean) as FilterOption[]; @@ -121,7 +122,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { } } else { // We want to add empty option caption - const initialOption = options.find(option => option.value === defaultValue) ?? options[0]; + const initialOption = options.find(option => option.value === initialFilterValue) ?? options[0]; setValueInput(initialOption?.caption ?? ""); setSelectedFilters(prev => { @@ -134,7 +135,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { } defaultValuesLoaded.current = true; } - }, [defaultValue, emptyOptionCaption, multiSelect, options, setMultiSelectFilters]); + }, [initialFilterValue, emptyOptionCaption, multiSelect, options, setMultiSelectFilters]); useEffect(() => { const emptyOption = multiSelect @@ -155,7 +156,7 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { // Resets the option to reload default values defaultValuesLoaded.current = false; - }, [emptyOptionCaption, multiSelect, optionsProp, defaultValue]); + }, [emptyOptionCaption, multiSelect, optionsProp, initialFilterValue]); // This side effect meant to sync filter value with parents // But, because updateFilters is might be "unstable" function From 0916fcb29321e9ce1e92d84f3c06c5579c6b48f2 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 16:11:06 +0200 Subject: [PATCH 16/22] test(datagrid-dropdown-filter-web): update tests --- .../components/__tests__/DataGridDropdownFilter.spec.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/__tests__/DataGridDropdownFilter.spec.tsx b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/__tests__/DataGridDropdownFilter.spec.tsx index 3842fecdf2..5657ff3d15 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/__tests__/DataGridDropdownFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/components/__tests__/DataGridDropdownFilter.spec.tsx @@ -109,7 +109,7 @@ describe("Dropdown Filter", () => { expect(screen.getByRole("textbox")).toHaveValue("enum_value_1"); }); - it("sync defaultValue with state when defaultValue changes from undefined to string", async () => { + it("don't sync defaultValue with state when defaultValue changes from undefined to string", async () => { const { rerender } = render( { ); await waitFor(() => { - expect(screen.getByRole("textbox")).toHaveValue("enum_value_1"); + expect(screen.getByRole("textbox")).toHaveValue(""); }); }); - it("sync defaultValue with state when defaultValue changes from string to undefined", async () => { + it("don't sync defaultValue with state when defaultValue changes from string to undefined", async () => { mockCtx(["xyz", "abc"]); const { rerender } = render( { ); await waitFor(() => { - expect(screen.getByRole("textbox")).toHaveValue(""); + expect(screen.getByRole("textbox")).toHaveValue("xyz"); }); }); }); From aadf5411b0dc2ae804f121398ca2c741c10e7dbb Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 16:49:26 +0200 Subject: [PATCH 17/22] fix(datagrid-date-filter-web): stop syncing value and defaultValue --- .../src/DatagridDateFilter.tsx | 9 +++++++++ .../src/components/FilterComponent.tsx | 16 ---------------- .../__tests__/DatagridDateFilter.spec.tsx | 8 ++++---- .../__tests__/FilterComponent.spec.tsx | 8 ++++---- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/DatagridDateFilter.tsx b/packages/pluggableWidgets/datagrid-date-filter-web/src/DatagridDateFilter.tsx index cf1a841943..9fd87139d7 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/DatagridDateFilter.tsx +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/DatagridDateFilter.tsx @@ -97,6 +97,10 @@ export default function DatagridDateFilter(props: DatagridDateFilterContainerPro return {errorMessage}; } + if (isLoadingDefaultValues(props)) { + return null; + } + return ( status === "loading"); +} diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/FilterComponent.tsx b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/FilterComponent.tsx index 140986f512..0f31b62840 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/FilterComponent.tsx +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/FilterComponent.tsx @@ -33,22 +33,6 @@ export function FilterComponent(props: FilterComponentProps): ReactElement { const [rangeValues, setRangeValues] = useState([props.defaultStartDate, props.defaultEndDate]); const pickerRef = useRef(null); - useEffect(() => { - setValue(prev => { - if (prev?.toISOString() === props.defaultValue?.toISOString()) { - return prev; - } - - return props.defaultValue; - }); - }, [props.defaultValue]); - - useEffect(() => { - if (props.defaultStartDate || props.defaultEndDate) { - setRangeValues([props.defaultStartDate, props.defaultEndDate]); - } - }, [props.defaultStartDate, props.defaultEndDate]); - useEffect(() => { props.updateFilters?.(value, rangeValues, type); }, [value, rangeValues, type]); diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx index 0854b61b31..edce9750f4 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/DatagridDateFilter.spec.tsx @@ -102,17 +102,17 @@ describe("Date Filter", () => { expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); }); - it("sync value when defaultValue changes from undefined to date", async () => { + it("don't sync value when defaultValue changes from undefined to date", async () => { // 946684800000 = 01.01.2000 const date = new Date(946684800000); const { rerender } = render(); expect(screen.getByRole("textbox")).toHaveValue(""); rerender((date)} />); - expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); + expect(screen.getByRole("textbox")).toHaveValue(""); }); - it("sync value when defaultValue changes from date to undefined", async () => { + it("don't sync value when defaultValue changes from date to undefined", async () => { // 946684800000 = 01.01.2000 const date = new Date(946684800000); const { rerender } = render( @@ -121,7 +121,7 @@ describe("Date Filter", () => { expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); rerender(); - expect(screen.getByRole("textbox")).toHaveValue(""); + expect(screen.getByRole("textbox")).toHaveValue("01/01/2000"); }); }); diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/FilterComponent.spec.tsx b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/FilterComponent.spec.tsx index bcc4720191..01801fb67e 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/FilterComponent.spec.tsx +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/components/__tests__/FilterComponent.spec.tsx @@ -40,7 +40,7 @@ describe("Filter component", () => { }); describe("with defaultValue", () => { - it("call updateFilters when defaultValue get new value", () => { + it("don't call updateFilters when defaultValue get new value", () => { const date = new Date(946684800000); const updateFilters = jest.fn(); const { rerender } = render_fromTestingLibrary( @@ -49,7 +49,7 @@ describe("Filter component", () => { // First time updateFilters is called on initial mount expect(updateFilters).toBeCalledTimes(1); - expect(updateFilters.mock.calls[0][0]).toBe(date); + expect(updateFilters).toHaveBeenLastCalledWith(date, [undefined, undefined], "equal"); const nextValue = new Date(999999900000); @@ -62,8 +62,8 @@ describe("Filter component", () => { /> ); - expect(updateFilters).toBeCalledTimes(2); - expect(updateFilters.mock.calls[1][0]).toBe(nextValue); + expect(updateFilters).toBeCalledTimes(1); + expect(updateFilters).toHaveBeenLastCalledWith(date, [undefined, undefined], "equal"); }); it("don't call updateFilters when defaultValue get same value", () => { From 75400a326c747ebb955339feed2472b5da9b2b40 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 16:58:44 +0200 Subject: [PATCH 18/22] fix: rename props --- .../__tests__/FilterComponent.spec.tsx | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx index b8d5a31fb6..2ff95b852e 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx @@ -6,13 +6,15 @@ jest.useFakeTimers(); describe("Filter component", () => { it("renders correctly", () => { - const component = render(); + const component = render(); expect(component).toMatchSnapshot(); }); it("renders correctly when not adjustable by user", () => { - const component = render(); + const component = render( + + ); expect(component).toMatchSnapshot(); }); @@ -23,8 +25,8 @@ describe("Filter component", () => { adjustable screenReaderButtonCaption="my label" screenReaderInputCaption="my label" - defaultFilter="contains" - delay={500} + initialFilterType="contains" + inputChangeDelay={500} /> ); @@ -34,7 +36,12 @@ describe("Filter component", () => { it("calls updateFilters when value changes", () => { const updateFiltersHandler = jest.fn(); const component = shallow( - + ); const input = component.find("input"); @@ -46,7 +53,12 @@ describe("Filter component", () => { it("debounces calls for updateFilters when value changes", () => { const updateFiltersHandler = jest.fn(); const component = shallow( - + ); // Initial call with default filter From af276756c81471c96f6fb1dfb82f322865fd282f Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 17:06:14 +0200 Subject: [PATCH 19/22] test(datagrid-text-filter-web): update unit tests --- .../__tests__/DatagridTextFilter.spec.tsx | 14 +++++------ .../__tests__/FilterComponent.spec.tsx | 23 +++++++++++-------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx index 6003901116..54ae5140e8 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/DatagridTextFilter.spec.tsx @@ -30,7 +30,7 @@ describe("Text Filter", () => { delete (global as any)["com.mendix.widgets.web.UUID"]; }); - describe("sync input value with defaultValue prop", () => { + describe("with defaultValue prop", () => { beforeAll(() => { (window as any)["com.mendix.widgets.web.filterable.filterContext"] = createContext({ filterDispatcher: jest.fn(), @@ -38,7 +38,7 @@ describe("Text Filter", () => { } as FilterContextValue); }); - it("sync value when defaultValue changes from undefined to string", async () => { + it("don't sync value when defaultValue changes from undefined to string", async () => { const { rerender } = render(); expect(screen.getByRole("textbox")).toHaveValue(""); @@ -46,10 +46,10 @@ describe("Text Filter", () => { // rerender component with new `defaultValue` const defaultValue = dynamicValue("xyz"); rerender(); - expect(screen.getByRole("textbox")).toHaveValue("xyz"); + expect(screen.getByRole("textbox")).toHaveValue(""); }); - it("sync value when defaultValue changes from string to string", async () => { + it("don't sync value when defaultValue changes from string to string", async () => { const { rerender } = render( ("abc")} /> ); @@ -59,10 +59,10 @@ describe("Text Filter", () => { // rerender component with new `defaultValue` const defaultValue = dynamicValue("xyz"); rerender(); - expect(screen.getByRole("textbox")).toHaveValue("xyz"); + expect(screen.getByRole("textbox")).toHaveValue("abc"); }); - it("sync value when defaultValue changes from string to undefined", async () => { + it("don't sync value when defaultValue changes from string to undefined", async () => { const { rerender } = render( ("abc")} /> ); @@ -71,7 +71,7 @@ describe("Text Filter", () => { // rerender component with new `defaultValue` rerender(); - expect(screen.getByRole("textbox")).toHaveValue(""); + expect(screen.getByRole("textbox")).toHaveValue("abc"); }); }); diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx index 2ff95b852e..a37bb67220 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/components/__tests__/FilterComponent.spec.tsx @@ -1,4 +1,4 @@ -import { render, shallow } from "enzyme"; +import { render, mount } from "enzyme"; import { createElement } from "react"; import { FilterComponent } from "../FilterComponent"; @@ -35,7 +35,7 @@ describe("Filter component", () => { it("calls updateFilters when value changes", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( + const component = mount( { const input = component.find("input"); input.simulate("change", { target: { value: "test" } }); - - expect(updateFiltersHandler).toBeCalled(); + expect(updateFiltersHandler).toBeCalledTimes(0); + jest.advanceTimersByTime(500); + expect(updateFiltersHandler).toBeCalledTimes(1); }); it("debounces calls for updateFilters when value changes", () => { const updateFiltersHandler = jest.fn(); - const component = shallow( + const component = mount( { /> ); - // Initial call with default filter - expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toBeCalledTimes(0); const input = component.find("input"); input.simulate("change", { target: { value: "test" } }); jest.advanceTimersByTime(499); + expect(updateFiltersHandler).toBeCalledTimes(0); + input.simulate("change", { target: { value: "test2" } }); input.simulate("change", { target: { value: "test3" } }); jest.advanceTimersByTime(500); - - expect(updateFiltersHandler).toBeCalledTimes(2); + expect(updateFiltersHandler).toBeCalledTimes(1); + expect(updateFiltersHandler).toHaveBeenCalledWith("test3", "contains"); input.simulate("change", { target: { value: "test" } }); jest.advanceTimersByTime(500); - expect(updateFiltersHandler).toBeCalledTimes(3); + expect(updateFiltersHandler).toBeCalledTimes(2); + expect(updateFiltersHandler).toHaveBeenCalledWith("test", "contains"); }); }); From eaacceadbf65e8b3bb14bb3ca631375fa943f0ba Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Tue, 18 Apr 2023 17:24:51 +0200 Subject: [PATCH 20/22] chore(data-widgets): bump version and update changelog --- .../datagrid-date-filter-web/CHANGELOG.md | 8 ++++++++ .../datagrid-date-filter-web/package.json | 2 +- .../datagrid-date-filter-web/src/package.xml | 2 +- .../datagrid-dropdown-filter-web/CHANGELOG.md | 8 ++++++++ .../datagrid-dropdown-filter-web/package.json | 2 +- .../datagrid-dropdown-filter-web/src/package.xml | 2 +- .../datagrid-number-filter-web/CHANGELOG.md | 8 ++++++++ .../datagrid-number-filter-web/package.json | 2 +- .../datagrid-number-filter-web/src/package.xml | 2 +- .../datagrid-text-filter-web/CHANGELOG.md | 8 ++++++++ .../datagrid-text-filter-web/package.json | 2 +- .../datagrid-text-filter-web/src/package.xml | 2 +- 12 files changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-date-filter-web/CHANGELOG.md index 241d355a3b..d375bcc767 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-date-filter-web/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed an issue with widget rendering and performance. + +### Breaking changes + +- We introduce a breaking change that affects how widget is reacting on default value changes. Starting with this version, widget use the default value attribute only as an initial value, and any further changes to the default value attribute will be ignored. + ## [2.4.2] - 2022-09-29 ### Fixed diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/package.json b/packages/pluggableWidgets/datagrid-date-filter-web/package.json index 946d9c1079..f35edb2214 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/package.json +++ b/packages/pluggableWidgets/datagrid-date-filter-web/package.json @@ -1,7 +1,7 @@ { "name": "datagrid-date-filter-web", "widgetName": "DatagridDateFilter", - "version": "2.4.2", + "version": "2.5.0", "description": "", "copyright": "© Mendix Technology BV 2023. All rights reserved.", "private": true, diff --git a/packages/pluggableWidgets/datagrid-date-filter-web/src/package.xml b/packages/pluggableWidgets/datagrid-date-filter-web/src/package.xml index 051d75e6df..6cbad36fa9 100644 --- a/packages/pluggableWidgets/datagrid-date-filter-web/src/package.xml +++ b/packages/pluggableWidgets/datagrid-date-filter-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-dropdown-filter-web/CHANGELOG.md index 6f40443c53..313669a1b1 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed an issue with widget rendering and performance. + +### Breaking changes + +- We introduce a breaking change that affects how widget is reacting on default value changes. Starting with this version, widget use the default value attribute only as an initial value, and any further changes to the default value attribute will be ignored. + ## [2.3.0] - 2023-02-17 ### Changed diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/package.json b/packages/pluggableWidgets/datagrid-dropdown-filter-web/package.json index 7e3ac3c1f8..3a1a76d60b 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/package.json +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/package.json @@ -1,7 +1,7 @@ { "name": "datagrid-dropdown-filter-web", "widgetName": "DatagridDropdownFilter", - "version": "2.3.0", + "version": "2.4.0", "description": "", "copyright": "© Mendix Technology BV 2023. All rights reserved.", "private": true, diff --git a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/package.xml b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/package.xml index 6f14d7639d..868bb75d88 100644 --- a/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/package.xml +++ b/packages/pluggableWidgets/datagrid-dropdown-filter-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-number-filter-web/CHANGELOG.md index 7c6bf44115..d1d70bda07 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-number-filter-web/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed an issue with widget rendering and performance. + +### Breaking changes + +- We introduce a breaking change that affects how widget is reacting on default value changes. Starting with this version, widget use the default value attribute only as an initial value, and any further changes to the default value attribute will be ignored. + ## [2.3.1] - 2022-08-11 ### Fixed diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/package.json b/packages/pluggableWidgets/datagrid-number-filter-web/package.json index d041b06257..8bc4c797b1 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/package.json +++ b/packages/pluggableWidgets/datagrid-number-filter-web/package.json @@ -1,7 +1,7 @@ { "name": "datagrid-number-filter-web", "widgetName": "DatagridNumberFilter", - "version": "2.3.1", + "version": "2.4.0", "description": "", "copyright": "© Mendix Technology BV 2023. All rights reserved.", "private": true, diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/package.xml b/packages/pluggableWidgets/datagrid-number-filter-web/src/package.xml index 5d86993529..8dc3946227 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/package.xml +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/CHANGELOG.md b/packages/pluggableWidgets/datagrid-text-filter-web/CHANGELOG.md index 0f2c3a386a..3ddcf4fe54 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/CHANGELOG.md +++ b/packages/pluggableWidgets/datagrid-text-filter-web/CHANGELOG.md @@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We fixed an issue with widget rendering and performance. + +### Breaking changes + +- We introduce a breaking change that affects how widget is reacting on default value changes. Starting with this version, widget use the default value attribute only as an initial value, and any further changes to the default value attribute will be ignored. + ## [2.3.2] - 2022-08-11 ### Fixed diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/package.json b/packages/pluggableWidgets/datagrid-text-filter-web/package.json index 8fabb2faaf..30e999840c 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/package.json +++ b/packages/pluggableWidgets/datagrid-text-filter-web/package.json @@ -1,7 +1,7 @@ { "name": "datagrid-text-filter-web", "widgetName": "DatagridTextFilter", - "version": "2.3.2", + "version": "2.4.0", "description": "", "copyright": "© Mendix Technology BV 2023. All rights reserved.", "private": true, diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/package.xml b/packages/pluggableWidgets/datagrid-text-filter-web/src/package.xml index 85477b056b..f08bb6cc35 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/package.xml +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/package.xml @@ -1,6 +1,6 @@ - + From d9b8c5243d6f4e956ccfbdac08612cde081c5fef Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Mon, 1 May 2023 13:47:25 +0200 Subject: [PATCH 21/22] chore(data-widgets): prepare 2.7.1 release --- packages/modules/data-widgets/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/modules/data-widgets/package.json b/packages/modules/data-widgets/package.json index c10677f00b..ed159c0573 100644 --- a/packages/modules/data-widgets/package.json +++ b/packages/modules/data-widgets/package.json @@ -1,7 +1,7 @@ { "name": "data-widgets", "moduleName": "Data Widgets", - "version": "2.7.0", + "version": "2.7.1", "license": "Apache-2.0", "copyright": "© Mendix Technology BV 2023. All rights reserved.", "private": true, From 32dd86790494d6cd5bcd66aa2b6fad428f68d6c9 Mon Sep 17 00:00:00 2001 From: Illia Obukhau Date: Mon, 1 May 2023 14:49:21 +0200 Subject: [PATCH 22/22] style(data-widgets): align status check --- .../datagrid-number-filter-web/src/DatagridNumberFilter.tsx | 2 +- .../datagrid-text-filter-web/src/DatagridTextFilter.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx index 1265785bdb..5d178b9dd0 100644 --- a/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx +++ b/packages/pluggableWidgets/datagrid-number-filter-web/src/DatagridNumberFilter.tsx @@ -74,7 +74,7 @@ export default function DatagridNumberFilter(props: DatagridNumberFilterContaine return {errorMessage}; } - if (props.defaultValue && props.defaultValue.status === "loading") { + if (props.defaultValue?.status === "loading") { return null; } diff --git a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx index b16e7da900..133d354a46 100644 --- a/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx +++ b/packages/pluggableWidgets/datagrid-text-filter-web/src/DatagridTextFilter.tsx @@ -77,7 +77,7 @@ export default function DatagridTextFilter(props: DatagridTextFilterContainerPro return {errorMessage}; } - if (props.defaultValue && props.defaultValue.status === "loading") { + if (props.defaultValue?.status === "loading") { return null; }