Skip to content

Commit

Permalink
feat(every): add short form
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeysova committed Feb 3, 2022
1 parent e72bc31 commit 532d522
Show file tree
Hide file tree
Showing 6 changed files with 201 additions and 13 deletions.
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ event3(5); // => triggered { event1: true, event2: "demo", event3: 5 }
event1(true); // nothing
event2('demo'); // nothing
reset();
event3(5) // nothing
event3(5); // nothing

event1(true); // nothing
event2('demo'); // nothing
Expand All @@ -387,10 +387,7 @@ import { every } from 'patronum/every';
const $isPasswordCorrect = createStore(true);
const $isEmailCorrect = createStore(true);

const $isFormCorrect = every({
predicate: true,
stores: [$isPasswordCorrect, $isEmailCorrect],
});
const $isFormCorrect = every([$isPasswordCorrect, $isEmailCorrect], true);

$isFormCorrect.watch(console.log); // => true
```
Expand Down
26 changes: 26 additions & 0 deletions src/every/every.fork.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,29 @@ test('allow predicate to use store', async () => {
await allSettled(setSource, { scope, params: false });
expect(scope.getState($result)).toBeFalsy();
});

test('allow predicate to use store in short form', async () => {
const setSource = createEvent<boolean>();
const setPredicate = createEvent<boolean>();

const $predicate = createStore(false).on(setPredicate, (_, value) => value);

const $first = createStore(true);
const $second = createStore(false).on(setSource, (_, value) => value);
const $third = createStore(true);

const $result = every([$first, $second, $third], $predicate);

const scope = fork();

expect(scope.getState($result)).toBeFalsy();

await allSettled(setSource, { scope, params: true });
expect(scope.getState($result)).toBeFalsy();

await allSettled(setPredicate, { scope, params: true });
expect(scope.getState($result)).toBeTruthy();

await allSettled(setSource, { scope, params: false });
expect(scope.getState($result)).toBeFalsy();
});
104 changes: 104 additions & 0 deletions src/every/every.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,107 @@ test('allow predicate to use store', () => {
setSource(false);
expect($result.getState()).toBeFalsy();
});

describe('short', () => {
test('boolean predicate', () => {
const fn = jest.fn();
const change = createEvent();

const $first = createStore(true);
const $second = createStore(false).on(change, () => true);
const $third = createStore(true);

const $result = every([$first, $second, $third], true);

$result.watch(fn);
expect(fn).toHaveBeenCalledWith(false);

change();
expect(argumentHistory(fn)).toMatchInlineSnapshot(`
Array [
false,
true,
]
`);
});

test('number predicate', () => {
const fn = jest.fn();
const change = createEvent();

const $first = createStore(4);
const $second = createStore(2).on(change, () => 4);
const $third = createStore(4);

const $result = every([$first, $second, $third], 4);

$result.watch(fn);
expect(fn).toHaveBeenCalledWith(false);

change();
expect(argumentHistory(fn)).toMatchInlineSnapshot(`
Array [
false,
true,
]
`);
});

test('function predicate', () => {
const fn = jest.fn();
const change = createEvent();

const $first = createStore(10);
const $second = createStore(0).on(change, () => 5);
const $third = createStore(3);

const $result = every([$first, $second, $third], (value) => value > 0);

$result.watch(fn);
expect(fn).toHaveBeenCalledWith(false);

change();
expect(argumentHistory(fn)).toMatchInlineSnapshot(`
Array [
false,
true,
]
`);
});

test('initially true', () => {
const fn = jest.fn();

const $first = createStore(10);
const $second = createStore(2);
const $third = createStore(3);

const $result = every([$first, $second, $third], (value) => value > 0);

$result.watch(fn);
expect(fn).toHaveBeenCalledWith(true);
});

test('allow predicate to use store', () => {
const setSource = createEvent<boolean>();
const setPredicate = createEvent<boolean>();

const $predicate = createStore(false).on(setPredicate, (_, value) => value);

const $first = createStore(true);
const $second = createStore(false).on(setSource, (_, value) => value);
const $third = createStore(true);

const $result = every([$first, $second, $third], $predicate);
expect($result.getState()).toBeFalsy();

setSource(true);
expect($result.getState()).toBeFalsy();

setPredicate(true);
expect($result.getState()).toBeTruthy();

setSource(false);
expect($result.getState()).toBeFalsy();
});
});
35 changes: 27 additions & 8 deletions src/every/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { combine, is, Store } from 'effector';

export function every<T>(_: {
Expand All @@ -20,13 +21,32 @@ export function every<T>(_: {
stores: Array<Store<T>>;
}): Store<boolean>;

export function every<T>({
predicate,
stores,
}: {
predicate: T | ((value: T) => boolean) | Store<T>;
stores: Array<Store<T>>;
}): Store<boolean> {
export function every<T>(stores: Store<T>[], predicate: Store<T>): Store<boolean>;
export function every<T>(stores: Store<T>[], predicate: T): Store<boolean>;
export function every<T>(
stores: Store<T>[],
predicate: (value: T) => boolean,
): Store<boolean>;

export function every<T>(
configOrStores:
| {
predicate: T | ((value: T) => boolean) | Store<T>;
stores: Array<Store<T>>;
}
| Store<T>[],
predicateOrNone?: Store<T> | T | ((value: T) => boolean),
): Store<boolean> {
let stores: Store<T>[] = [];
let predicate: Store<T> | T | ((value: T) => boolean) = () => false;
if (Array.isArray(configOrStores)) {
stores = configOrStores;
predicate = predicateOrNone!;
} else if (Array.isArray(configOrStores.stores)) {
stores = configOrStores.stores;
predicate = configOrStores.predicate;
}

let checker;
if (isFunction(predicate)) {
checker = predicate;
Expand All @@ -43,7 +63,6 @@ export function every<T>({
return combine($checker, $values, (checker, values) => values.every(checker));
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
function isFunction<T>(value: unknown): value is (value: T) => boolean {
return typeof value === 'function';
}
15 changes: 15 additions & 0 deletions src/every/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,18 @@ const $isFormCorrect = every({

console.assert(true === $isFormCorrect.getState());
```

## Shorthands

```ts
$result = every(stores, value);
$result = every(stores, (value) => false);
$result = every(stores, $predicate);
```

Shorthand have the same rules as the main overrides, just it uses positional arguments instead of object-form.

### Arguments

1. `stores` _(`Array<Store<T>>`)_ — List of stores to compare with predicate in the second argument
2. `predicate` _(`Store<T> | (value: T) => boolean | T`)_ — Predicate to compare with
27 changes: 27 additions & 0 deletions test-typings/every.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,30 @@ import { every } from '../src/every';
// @ts-expect-error
expectType<Store<boolean>>(every({ predicate: $invalid3, stores: [$a, $b] }));
}

// Short
{
const predicate = (value: number) => value > 0;
const $predicate = createStore(0);
const $a = createStore(0);
const $b = createStore(1);
const $invalid1 = createStore('');
const $invalid2 = createStore(true);
const $invalid3 = createStore({});

expectType<Store<boolean>>(every([$a, $b], 0));
// @ts-expect-error
every([$a, $invalid1], 0);

expectType<Store<boolean>>(every([$a, $b], predicate));
// @ts-expect-error
every([$a, $invalid1], predicate);

expectType<Store<boolean>>(every([$a, $b], $predicate));
// @ts-expect-error
expectType<Store<boolean>>(every([$a, $b], $invalid1));
// @ts-expect-error
expectType<Store<boolean>>(every([$a, $b], $invalid2));
// @ts-expect-error
expectType<Store<boolean>>(every([$a, $b], $invalid3));
}

0 comments on commit 532d522

Please sign in to comment.