Skip to content

Commit

Permalink
feat: nullish and debounce functions (#287)
Browse files Browse the repository at this point in the history
# Motivation

Copy/move nullish and debounce functions from NNS-dapp to utils. That way we can use these in NNS-dapp but also in ic-js libraries or gix-cmp.
  • Loading branch information
peterpeterparker committed Feb 12, 2023
1 parent 7f4c415 commit 22d962c
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
- ckBTC `v0.0.2`
- utils `v0.0.13`

## Features

- new utils moved from NNS-dapp: `isNullish`, `nonNullish`, `notEmptyString` and `debounce`

## Build

- bump agent-js `v0.15.3`
Expand Down
34 changes: 34 additions & 0 deletions packages/utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ npm i @dfinity/agent @dfinity/candid @dfinity/principal
- [asciiStringToByteArray](#gear-asciistringtobytearray)
- [assertNonNullish](#gear-assertnonnullish)
- [assertPercentageNumber](#gear-assertpercentagenumber)
- [debounce](#gear-debounce)
- [toNullable](#gear-tonullable)
- [fromNullable](#gear-fromnullable)
- [fromDefinedNullable](#gear-fromdefinednullable)
- [isNullish](#gear-isnullish)
- [nonNullish](#gear-nonnullish)
- [notEmptyString](#gear-notemptystring)
- [principalToSubAccount](#gear-principaltosubaccount)
- [smallerVersion](#gear-smallerversion)

Expand Down Expand Up @@ -131,6 +135,12 @@ Parameters:
| ------------------------ | ------------------------------ |
| `assertPercentageNumber` | `(percentage: number) => void` |

#### :gear: debounce

| Function | Type |
| ---------- | -------------------------------------------------------------------- |
| `debounce` | `(func: Function, timeout?: number) => (...args: unknown[]) => void` |

#### :gear: toNullable

| Function | Type |
Expand All @@ -149,6 +159,30 @@ Parameters:
| --------------------- | ---------------------------- |
| `fromDefinedNullable` | `<T>(value: [] or [T]) => T` |

#### :gear: isNullish

Is null or undefined

| Function | Type |
| ----------- | -------------------------------------- |
| `isNullish` | `<T>(argument: T) => argument is null` |

#### :gear: nonNullish

Not null and not undefined

| Function | Type |
| ------------ | ------------------------------------------------ |
| `nonNullish` | `<T>(argument: T) => argument is NonNullable<T>` |

#### :gear: notEmptyString

Not null and not undefined and not empty

| Function | Type |
| ---------------- | ---------------------------- |
| `notEmptyString` | `(value: string) => boolean` |

#### :gear: principalToSubAccount

Convert a principal to a Uint8Array 32 length.
Expand Down
2 changes: 2 additions & 0 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export * from "./utils/actor.utils";
export * from "./utils/agent.utils";
export * from "./utils/arrays.utils";
export * from "./utils/asserts.utils";
export * from "./utils/debounce.utils";
export * from "./utils/did.utils";
export * from "./utils/nullish.utils";
export * from "./utils/principal.utils";
export * from "./utils/version.utils";
60 changes: 60 additions & 0 deletions packages/utils/src/utils/debounce.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { debounce } from "./debounce.utils";

describe("debounce-utils", () => {
let callback: jest.Mock;

beforeAll(() =>
jest.spyOn(console, "error").mockImplementation(() => undefined)
);

afterAll(() => jest.resetAllMocks());

beforeEach(() => {
jest.useFakeTimers();
jest.spyOn(global, "setTimeout");
callback = jest.fn();
});

afterEach(() => jest.useRealTimers());

it("should debounce function with timeout", () => {
const testDebounce = debounce(callback, 100);

testDebounce();
testDebounce();
testDebounce();

expect(setTimeout).toHaveBeenCalledTimes(3);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 100);
expect(callback).not.toBeCalled();

jest.runAllTimers();

expect(callback).toHaveBeenCalledTimes(1);
});

it("should debounce one function call", () => {
debounce(callback)();

expect(callback).not.toBeCalled();

jest.runAllTimers();

expect(callback).toBeCalled();
expect(callback).toHaveBeenCalledTimes(1);
});

it("should debounce multiple functions call", () => {
const anotherCallback = jest.fn();

const test = debounce(anotherCallback);
test();
test();
test();

jest.runAllTimers();

expect(anotherCallback).toBeCalled();
expect(anotherCallback).toHaveBeenCalledTimes(1);
});
});
17 changes: 17 additions & 0 deletions packages/utils/src/utils/debounce.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* eslint-disable-next-line @typescript-eslint/ban-types */
export const debounce = (func: Function, timeout?: number) => {
let timer: NodeJS.Timer | undefined;

return (...args: unknown[]) => {
const next = () => func(...args);

if (timer) {
clearTimeout(timer);
}

timer = setTimeout(
next,
timeout !== undefined && timeout > 0 ? timeout : 300
);
};
};
45 changes: 45 additions & 0 deletions packages/utils/src/utils/nullish.utils.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { isNullish, nonNullish, notEmptyString } from "./nullish.utils";

describe("nullish-utils", () => {
describe("isNullish", () => {
it("should determine nullable", () => {
expect(isNullish(null)).toBeTruthy();
expect(isNullish(undefined)).toBeTruthy();
expect(isNullish(0)).toBeFalsy();
expect(isNullish(1)).toBeFalsy();
expect(isNullish("")).toBeFalsy();
expect(isNullish([])).toBeFalsy();
});
});

describe("nonNullish", () => {
it("should determine not nullable", () => {
expect(nonNullish(null)).toBeFalsy();
expect(nonNullish(undefined)).toBeFalsy();
expect(nonNullish(0)).toBeTruthy();
expect(nonNullish(1)).toBeTruthy();
expect(nonNullish("")).toBeTruthy();
expect(nonNullish([])).toBeTruthy();
});
});

describe("isNullish", () => {
it("should determine nullable", () => {
expect(isNullish(null)).toBeTruthy();
expect(isNullish(undefined)).toBeTruthy();
expect(isNullish(0)).toBeFalsy();
expect(isNullish(1)).toBeFalsy();
expect(isNullish("")).toBeFalsy();
expect(isNullish([])).toBeFalsy();
});
});

describe("notEmptyString", () => {
it("should determine not empty", () => {
expect(notEmptyString(null)).toBeFalsy();
expect(notEmptyString(undefined)).toBeFalsy();
expect(notEmptyString("")).toBeFalsy();
expect(notEmptyString("test")).toBeTruthy();
});
});
});
13 changes: 13 additions & 0 deletions packages/utils/src/utils/nullish.utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** Is null or undefined */
export const isNullish = <T>(
argument: T | undefined | null
): argument is undefined | null => argument === null || argument === undefined;

/** Not null and not undefined */
export const nonNullish = <T>(
argument: T | undefined | null
): argument is NonNullable<T> => !isNullish(argument);

/** Not null and not undefined and not empty */
export const notEmptyString = (value: string | undefined | null): boolean =>
nonNullish(value) && value !== "";

0 comments on commit 22d962c

Please sign in to comment.