Skip to content

Commit

Permalink
feat: add store and entity unmount callbacks (#55)
Browse files Browse the repository at this point in the history
  • Loading branch information
bocembocem authored Jun 6, 2023
1 parent 4df2b00 commit 8f7c4c6
Show file tree
Hide file tree
Showing 19 changed files with 137 additions and 162 deletions.
2 changes: 0 additions & 2 deletions src/lib/core/components/Form/constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export const REMOVED_ITEM = '____removed-item';

export const OBJECT_ARRAY_FLAG = '____arr-obj';

export const OBJECT_ARRAY_CNT = '____arr-obj-cnt';
45 changes: 26 additions & 19 deletions src/lib/core/components/Form/hooks/__tests__/useField.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ErrorMessages, dynamicConfig} from '../../../../../kit';
import {SpecTypes} from '../../../../constants';
import {ArraySpec, ObjectSpec, StringSpec} from '../../../../types';
import {DynamicField} from '../../DynamicField';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM} from '../../constants';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../../constants';
import {WonderMirror} from '../../types';

const spec: ObjectSpec = {type: SpecTypes.Object, viewSpec: {type: ''}};
Expand Down Expand Up @@ -356,15 +356,25 @@ describe('Form/hooks/useField', () => {

test('onDrop (array item)', () => {
const mirror: WonderMirror = {field: {}, controller: {}};
const _name = `${name}>`;
const _value = {[_name]: value[name]};
const _spec: ArraySpec = {
type: SpecTypes.Array,
items: {type: SpecTypes.String, viewSpec: {type: ''}},
viewSpec: {type: 'base'},
};
const _value = {
[name]: {
'<0>': 'item',
[OBJECT_ARRAY_FLAG]: true,
[OBJECT_ARRAY_CNT]: 1,
},
};

render(
<Form initialValues={_value} onSubmit={_.noop}>
{() => (
<DynamicField
name={_name}
spec={spec}
name={name}
spec={_spec}
config={dynamicConfig}
__mirror={mirror}
/>
Expand All @@ -373,21 +383,19 @@ describe('Form/hooks/useField', () => {
);

act(() => {
mirror.controller[_name]?.useField?.input.onDrop();
mirror.controller[`${name}.<0>`]?.useField?.input.onDrop();
});

expect(mirror.controller[_name]?.useField?.input.value).toBe(REMOVED_ITEM);
expect(mirror.controller[_name]?.useField?.arrayInput.value).toBe(REMOVED_ITEM);
expect(mirror.field.useStore?.store.values[_name]).toBe(REMOVED_ITEM);
expect(mirror.field.useStore?.store.errors[_name]).toBe(undefined);

expect(mirror.controller[_name]?.useField?.meta.dirty).toBe(true);
expect(mirror.controller[_name]?.useField?.meta.error).toBe(undefined);
expect(mirror.controller[_name]?.useField?.meta.invalid).toBe(false);
expect(mirror.controller[_name]?.useField?.meta.modified).toBe(true);
expect(mirror.controller[_name]?.useField?.meta.touched).toBe(true);
expect(mirror.controller[_name]?.useField?.meta.valid).toBe(true);
expect(mirror.controller[_name]?.useField?.meta.visited).toBe(true);
const nextValue = {
[OBJECT_ARRAY_FLAG]: true,
[OBJECT_ARRAY_CNT]: 1,
};

expect(mirror.controller[name]?.useField?.input.value).toMatchObject(nextValue);
expect(mirror.controller[name]?.useField?.arrayInput.value).toMatchObject(nextValue);
expect(mirror.field.useStore?.store.values[name]).toMatchObject(nextValue);

expect(mirror.controller[`${name}.<0>`]).toBe(undefined);
});

test('onItemAdd/onItemRemove', () => {
Expand Down Expand Up @@ -576,7 +584,6 @@ describe('Form/hooks/useField', () => {
});

const nextValue1 = {
'<0>': REMOVED_ITEM,
'____arr-obj': true,
'____arr-obj-cnt': 1,
};
Expand Down
62 changes: 31 additions & 31 deletions src/lib/core/components/Form/hooks/useField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import _ from 'lodash';

import {isArraySpec, isNumberSpec, isObjectSpec} from '../../../helpers';
import {Spec} from '../../../types';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM} from '../constants';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../constants';
import {
DynamicFormsContext,
FieldArrayValue,
FieldObjectValue,
FieldRenderProps,
FieldValue,
ValidateError,
Expand Down Expand Up @@ -39,18 +40,12 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
validate: propsValidate,
tools,
parentOnChange,
parentOnUnmount,
parentOnUnmount: externalParentOnUnmount,
}: UseFieldProps<Value, SpecType>): FieldRenderProps<Value> => {
const firstRenderRef = React.useRef(true);

const validate = React.useCallback(
(value: Value) => {
if (value === REMOVED_ITEM) {
return;
}

return propsValidate?.(transformArrOut(value));
},
(value: Value) => propsValidate?.(transformArrOut(value)),
[propsValidate],
);

Expand Down Expand Up @@ -101,7 +96,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
const error = validate?.(_value);
let value = transformArrIn(_value);

if (isNumberSpec(spec) && value && value !== REMOVED_ITEM && !error) {
if (isNumberSpec(spec) && value && !error) {
value = Number(value) as Value;
}

Expand Down Expand Up @@ -142,14 +137,14 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({

const onDrop = () => {
if (isArrayItem(name)) {
onChange(REMOVED_ITEM as Value);
(externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
} else {
onChange(undefined as Value);
}
};

return {onChange, onDrop};
}, [initialValue, setState, name, validate, spec]);
}, [initialValue, setState, name, validate, spec, externalParentOnUnmount, tools.onUnmount]);

const onBlur = React.useCallback(() => {
setState((state) => ({
Expand All @@ -167,6 +162,26 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
}));
}, [setState]);

const parentOnUnmount = React.useCallback(
(childName: string) => {
if (isArraySpec(spec) || isObjectSpec(spec)) {
onChange(
(currentValue) =>
currentValue
? (_.omit(
currentValue as FieldArrayValue | FieldObjectValue,
childName.split(`${name}.`)[1],
) as Value)
: currentValue,
{
[childName]: false,
},
);
}
},
[onChange, name, spec],
);

const renderProps: FieldRenderProps<Value> = React.useMemo(() => {
const onItemAdd = (_value: FieldValue) => {
const stateValue = (state.value || {
Expand Down Expand Up @@ -195,24 +210,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
};

const onItemRemove = (idx: string | number) => {
const value = {
...(state.value as FieldArrayValue),
[`<${idx}>`]: REMOVED_ITEM,
} as Value;
const error = validate?.(value);

setState((state) => ({
...state,
dirty: !_.isEqual(value, initialValue),
error,
invalid: Boolean(error),
modified: true,
pristine: value === initialValue,
touched: true,
valid: !error,
value,
visited: true,
}));
parentOnUnmount(`${name}.<${idx}>`);
};

return {
Expand All @@ -223,6 +221,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
onBlur,
onFocus,
onDrop,
parentOnUnmount,
},
arrayInput: {
name,
Expand All @@ -244,6 +243,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
onBlur,
onFocus,
onDrop,
parentOnUnmount,
]);

React.useEffect(() => {
Expand All @@ -259,7 +259,7 @@ export const useField = <Value extends FieldValue, SpecType extends Spec>({
firstRenderRef.current = false;

return () => {
(parentOnUnmount ? parentOnUnmount : tools.onUnmount)(name);
(externalParentOnUnmount ? externalParentOnUnmount : tools.onUnmount)(name);
};
}, []);

Expand Down
12 changes: 11 additions & 1 deletion src/lib/core/components/Form/hooks/useIntegrationFF.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ export const useIntegrationFF = (store: DynamicFieldStore, withoutDebounce?: boo

const change = React.useMemo(() => {
const cb = (value: FieldValue) => {
form.change(store.name, _.get(transformArrOut(value), store.name));
if (store.name) {
form.change(store.name, _.get(transformArrOut(value), store.name));
}
};

if (withoutDebounce) {
Expand All @@ -56,5 +58,13 @@ export const useIntegrationFF = (store: DynamicFieldStore, withoutDebounce?: boo
change(store.values);
}, [store.values]);

React.useEffect(() => {
return () => {
if (store.name) {
form.change(store.name, undefined);
}
};
}, []);

return watcher;
};
1 change: 1 addition & 0 deletions src/lib/core/components/Form/types/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface FieldRenderProps<Value extends FieldValue> {
) => void;
onFocus: (event?: React.FocusEvent<HTMLElement>) => void;
onDrop: () => void;
parentOnUnmount: (childName: string) => void;
};
arrayInput: {
name: string;
Expand Down
9 changes: 2 additions & 7 deletions src/lib/core/components/Form/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'lodash';

import {SpecTypes} from '../../../constants';
import {FormValue, ObjectValue} from '../../../types';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG, REMOVED_ITEM} from '../constants';
import {OBJECT_ARRAY_CNT, OBJECT_ARRAY_FLAG} from '../constants';

export const isCorrectConfig = (candidate: any) =>
Object.values(SpecTypes).every(
Expand Down Expand Up @@ -47,12 +47,7 @@ export const transformArrOut = <Type extends FormValue, ReturnType extends FormV
if (_.isObject(value) && !_.isArray(value)) {
if ((value as ObjectValue)[OBJECT_ARRAY_FLAG]) {
const _value = Object.keys(value)
.filter(
(key) =>
key !== OBJECT_ARRAY_FLAG &&
key !== OBJECT_ARRAY_CNT &&
(value as ObjectValue)[key] !== REMOVED_ITEM,
)
.filter((key) => key !== OBJECT_ARRAY_FLAG && key !== OBJECT_ARRAY_CNT)
.map((key) => key.split('<').join('').split('>').join(''))
.sort((a, b) => Number(a) - Number(b))
.map((key) => transformArrOut((value as ObjectValue)[`<${key}>`])) as ReturnType;
Expand Down
17 changes: 3 additions & 14 deletions src/lib/kit/components/Inputs/ArrayBase/ArrayBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
FieldValue,
OBJECT_ARRAY_CNT,
OBJECT_ARRAY_FLAG,
REMOVED_ITEM,
Spec,
ValidateError,
isArraySpec,
Expand All @@ -25,12 +24,7 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
const keys = React.useMemo(
() =>
Object.keys(arrayInput.value || {})
.filter(
(k) =>
k !== OBJECT_ARRAY_FLAG &&
k !== OBJECT_ARRAY_CNT &&
arrayInput.value[k] !== REMOVED_ITEM,
)
.filter((k) => k !== OBJECT_ARRAY_FLAG && k !== OBJECT_ARRAY_CNT)
.map((k) => k.split('<').join('').split('>').join(''))
.sort((a, b) => Number(a) - Number(b)),
[arrayInput.value],
Expand Down Expand Up @@ -86,11 +80,6 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
[input.onChange, input.name],
);

const parentOnUnmount = React.useCallback(
(childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}),
[input.onChange],
);

const items = React.useMemo(
() =>
keys.map((key, idx) => {
Expand All @@ -104,14 +93,14 @@ export const ArrayBase: ArrayInput = ({spec, name, arrayInput, input}) => {
<Controller
value={input.value?.[`<${key}>`]}
parentOnChange={parentOnChange}
parentOnUnmount={parentOnUnmount}
parentOnUnmount={input.parentOnUnmount}
spec={itemSpec}
name={`${name}.<${key}>`}
key={`${name}.<${key}>`}
/>
);
}),
[keys.join(''), name, getItemSpec, parentOnChange, parentOnUnmount, input.value],
[keys.join(''), name, getItemSpec, parentOnChange, input.parentOnUnmount, input.value],
);

if (!itemSpecCorrect) {
Expand Down
7 changes: 1 addition & 6 deletions src/lib/kit/components/Inputs/CardOneOf/CardOneOf.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ export const CardOneOf: ObjectIndependentInput = (props) => {
[input.onChange, input.name],
);

const parentOnUnmount = React.useCallback(
(childName: string) => input.onChange((currentValue) => currentValue, {[childName]: false}),
[input.onChange],
);

useErrorChecker({name, meta, open, setOpen});

return (
Expand All @@ -80,7 +75,7 @@ export const CardOneOf: ObjectIndependentInput = (props) => {
spec={specProperties[oneOfValue]}
name={`${name}.${oneOfValue}`}
parentOnChange={parentOnChange}
parentOnUnmount={parentOnUnmount}
parentOnUnmount={input.parentOnUnmount}
key={`${name}.${oneOfValue}`}
/>
) : null}
Expand Down
9 changes: 2 additions & 7 deletions src/lib/kit/components/Inputs/ObjectBase/ObjectBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ export const ObjectBase: ObjectIndependentInput = ({spec, name, Layout, ...restP
[restProps.input.onChange, restProps.input.name],
);

const parentOnUnmount = React.useCallback(
(childName: string) =>
restProps.input.onChange((currentValue) => currentValue, {[childName]: false}),
[restProps.input.onChange],
);
const content = React.useMemo(() => {
if (!_.isObjectLike(spec.properties) || !Object.keys(spec.properties || {}).length) {
return null;
Expand All @@ -73,7 +68,7 @@ export const ObjectBase: ObjectIndependentInput = ({spec, name, Layout, ...restP
spec={specProperties[property]}
name={`${name ? name + '.' : ''}${property}`}
parentOnChange={parentOnChange}
parentOnUnmount={parentOnUnmount}
parentOnUnmount={restProps.input.parentOnUnmount}
key={`${name ? name + '.' : ''}${property}`}
/>
) : null,
Expand All @@ -87,7 +82,7 @@ export const ObjectBase: ObjectIndependentInput = ({spec, name, Layout, ...restP
restProps.input.value,
addBtn,
parentOnChange,
parentOnUnmount,
restProps.input.parentOnUnmount,
]);

if (!Layout || !content) {
Expand Down
Loading

0 comments on commit 8f7c4c6

Please sign in to comment.