Skip to content

Commit

Permalink
feat: updateArray: rework to support different parameter combinatio…
Browse files Browse the repository at this point in the history
…ns for update functions (i.e. single function, array of functions, and rest parameters) and move `state` parameter to first position for uncurried overload
  • Loading branch information
MrWolfZ committed Apr 15, 2018
1 parent b89c30e commit f82abf8
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This release requires TypeScript >=2.8.0 for the conditional type support.
* `setErrors`: rework to support different parameter combinations for errors (i.e. single error object, array of error objects, and rest parameters) and move `state` parameter to first position for uncurried overload ([15ea555](https://github.com/MrWolfZ/ngrx-forms/commit/15ea555))
* `setUserDefinedProperty`: move `state` parameter to first position for uncurried overload ([520c384](https://github.com/MrWolfZ/ngrx-forms/commit/520c384))
* `setValue`: move `state` parameter to first position for uncurried overload ([1a69795](https://github.com/MrWolfZ/ngrx-forms/commit/1a69795))
* `updateArray`: rework to support different parameter combinations for update functions (i.e. single function, array of functions, and rest parameters) and move `state` parameter to first position for uncurried overload

#### Features

Expand Down
41 changes: 38 additions & 3 deletions src/update-function/update-array.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createFormArrayState } from '../state';
import { createFormArrayState, InferredControlState } from '../state';
import { FORM_CONTROL_ID } from './test-util';
import { updateArray } from './update-array';
import { updateGroup } from './update-group';
import { ProjectFn2 } from './util';

describe(updateArray.name, () => {
it('should apply the provided functions to control children', () => {
Expand Down Expand Up @@ -67,8 +68,42 @@ describe(updateArray.name, () => {
const expected1 = { ...state.controls[0], value: 'D' };
const expected2 = { ...state.controls[1], value: 'E' };
const expected3 = { ...state.controls[2], value: 'F' };
let resultState = updateArray<typeof expected1.value>(s => s.value === 'A' ? expected1 : s.value === 'B' ? expected3 : s)(state);
resultState = updateArray<typeof expected1.value>(s => s.value === 'F' ? expected2 : s.value === 'C' ? expected3 : s)(resultState);
const resultState = updateArray<typeof expected1.value>(
s => s.value === 'A' ? expected1 : s.value === 'B' ? expected3 : s,
s => s.value === 'F' ? expected2 : s.value === 'C' ? expected3 : s,
)(state);
expect(resultState.controls[0]).toBe(expected1);
expect(resultState.controls[1]).toBe(expected2);
expect(resultState.controls[2]).toBe(expected3);
});

it('should apply multiple provided functions as param array one after another', () => {
const state = createFormArrayState(FORM_CONTROL_ID, ['A', 'B', 'C']);
const expected1 = { ...state.controls[0], value: 'D' };
const expected2 = { ...state.controls[1], value: 'E' };
const expected3 = { ...state.controls[2], value: 'F' };
const updateFunction1: ProjectFn2<InferredControlState<typeof expected1.value>, typeof state> =
s => s.value === 'A' ? expected1 : s.value === 'B' ? expected3 : s;
const updateFunction2: ProjectFn2<InferredControlState<typeof expected1.value>, typeof state> =
s => s.value === 'F' ? expected2 : s.value === 'C' ? expected3 : s;
const resultState = updateArray<typeof expected1.value>(
updateFunction1,
[updateFunction2] as any,
)(state);
expect(resultState.controls[0]).toBe(expected1);
expect(resultState.controls[1]).toBe(expected2);
expect(resultState.controls[2]).toBe(expected3);
});

it('should apply multiple provided functions as array one after another', () => {
const state = createFormArrayState(FORM_CONTROL_ID, ['A', 'B', 'C']);
const expected1 = { ...state.controls[0], value: 'D' };
const expected2 = { ...state.controls[1], value: 'E' };
const expected3 = { ...state.controls[2], value: 'F' };
const resultState = updateArray<typeof expected1.value>([
s => s.value === 'A' ? expected1 : s.value === 'B' ? expected3 : s,
s => s.value === 'F' ? expected2 : s.value === 'C' ? expected3 : s,
])(state);
expect(resultState.controls[0]).toBe(expected1);
expect(resultState.controls[1]).toBe(expected2);
expect(resultState.controls[2]).toBe(expected3);
Expand Down
66 changes: 56 additions & 10 deletions src/update-function/update-array.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { computeArrayState, FormArrayState, InferredControlState } from '../state';
import { ProjectFn2 } from './util';
import { computeArrayState, FormArrayState, InferredControlState, isFormState } from '../state';
import { ensureState, ProjectFn2 } from './util';

function updateArrayControlsState<TValue>(updateFn: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>) {
return (state: FormArrayState<TValue>) => {
Expand Down Expand Up @@ -39,9 +39,30 @@ function updateArraySingle<TValue>(updateFn: ProjectFn2<InferredControlState<TVa
* ```
*/
export function updateArray<TValue>(
updateFn: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>,
...updateFnArr: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[]
): (state: FormArrayState<TValue>) => FormArrayState<TValue>;

/**
* This update function takes an array of update functions and returns
* a projection function that applies all update functions one after another to
* a form array state.
*
* The following (contrived) example uses this function to validate all its
* children to be required and mark them as dirty.
*
* ```typescript
* const arrayUpdateFn = updateArray<string>(
* validate(required),
* markAsDirty,
* );
* const updatedState = arrayUpdateFn(state);
* ```
*/
export function updateArray<TValue>(
updateFnArr: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[],
): (state: FormArrayState<TValue>) => FormArrayState<TValue>;

/**
* This update function takes a form array state and a variable number of update
* functions applies all update functions one after another to the state.
Expand All @@ -59,19 +80,44 @@ export function updateArray<TValue>(
*/
export function updateArray<TValue>(
state: FormArrayState<TValue>,
updateFn: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>,
...updateFnArr: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[]
): FormArrayState<TValue>;

/**
* This update function takes a form array state and an array of update
* functions applies all update functions one after another to the state.
*
* The following (contrived) example uses this function to validate all its
* children to be required and mark them as dirty.
*
* ```typescript
* const updatedState = updateArray<string>(
* state,
* validate(required),
* markAsDirty,
* );
* ```
*/
export function updateArray<TValue>(
stateOrFunction: FormArrayState<TValue> | ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>,
...updateFnArr: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[]
state: FormArrayState<TValue>,
updateFnArr: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[],
): FormArrayState<TValue>;

export function updateArray<TValue>(
stateOrFunctionOrFunctionArray:
| FormArrayState<TValue>
| ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>
| ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[],
updateFnOrUpdateFnArr?: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>> | ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[],
...rest: ProjectFn2<InferredControlState<TValue>, FormArrayState<TValue>>[]
) {
if (typeof stateOrFunction !== 'function') {
const [first, ...rest] = updateFnArr;
return updateArray(first, ...rest)(stateOrFunction);
if (isFormState(stateOrFunctionOrFunctionArray)) {
const updateFnArr = Array.isArray(updateFnOrUpdateFnArr) ? updateFnOrUpdateFnArr : [updateFnOrUpdateFnArr!];
return updateFnArr.reduce((s, updateFn) => updateArraySingle<TValue>(updateFn)(s), stateOrFunctionOrFunctionArray);
}

return (state: FormArrayState<TValue>): FormArrayState<TValue> => {
return [stateOrFunction as any, ...updateFnArr].reduce((s, updateFn) => updateArraySingle<TValue>(updateFn)(s), state);
};
let updateFnArr = Array.isArray(stateOrFunctionOrFunctionArray) ? stateOrFunctionOrFunctionArray : [stateOrFunctionOrFunctionArray];
updateFnArr = updateFnOrUpdateFnArr === undefined ? updateFnArr : updateFnArr.concat(updateFnOrUpdateFnArr);
return (s: FormArrayState<TValue>) => updateArray<TValue>(ensureState(s), updateFnArr.concat(rest));
}

0 comments on commit f82abf8

Please sign in to comment.