Skip to content

Commit

Permalink
Add react-utils package
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Dec 17, 2024
1 parent 9022d4a commit 0df78b9
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 15 deletions.
29 changes: 16 additions & 13 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ const pkgList = {
core: "@preact/signals-core",
preact: "@preact/signals",
react: "@preact/signals-react",
"react/utils": "@preact/signals-react/utils",
"react/auto": "@preact/signals-react/auto",
"react/runtime": "@preact/signals-react/runtime",
"react-transform": "@preact/signals-react-transform",
Expand Down Expand Up @@ -304,19 +305,21 @@ module.exports = function (config) {
customLaunchers: localLaunchers,

files: [
...filteredPkgList.some(i => /^react/.test(i)) ? [
{
// Provide some NodeJS globals to run babel in a browser environment
pattern: "test/browser/nodeGlobals.js",
watched: false,
type: "js",
},
{
pattern: "test/browser/babel.js",
watched: false,
type: "js",
},
] : [],
...(filteredPkgList.some(i => /^react/.test(i))
? [
{
// Provide some NodeJS globals to run babel in a browser environment
pattern: "test/browser/nodeGlobals.js",
watched: false,
type: "js",
},
{
pattern: "test/browser/babel.js",
watched: false,
type: "js",
},
]
: []),
{
pattern:
process.env.TESTS ||
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
"private": true,
"scripts": {
"prebuild": "shx rm -rf packages/*/dist/",
"build": "pnpm build:core && pnpm build:preact && pnpm build:react-runtime && pnpm build:react-auto && pnpm build:react && pnpm build:react-transform",
"build": "pnpm build:core && pnpm build:preact && pnpm build:react-runtime && pnpm build:react-auto && pnpm build:react && pnpm build:react-transform && pnpm build:react-utils",
"_build": "microbundle --raw --globals @preact/signals-core=preactSignalsCore,preact/hooks=preactHooks,@preact/signals-react/runtime=reactSignalsRuntime",
"build:core": "pnpm _build --cwd packages/core && pnpm postbuild:core",
"build:preact": "pnpm _build --cwd packages/preact && pnpm postbuild:preact",
"build:react": "pnpm _build --cwd packages/react --external \"react,@preact/signals-react/runtime,@preact/signals-core\" && pnpm postbuild:react",
"build:react-utils": "pnpm _build --cwd packages/react/utils && pnpm postbuild:react-utils",
"build:react-auto": "pnpm _build --cwd packages/react/auto && pnpm postbuild:react-auto",
"build:react-runtime": "pnpm _build --cwd packages/react/runtime && pnpm postbuild:react-runtime",
"build:react-transform": "pnpm _build --no-compress --cwd packages/react-transform",
"postbuild:core": "cd packages/core/dist && shx mv -f index.d.ts signals-core.d.ts",
"postbuild:preact": "cd packages/preact/dist && shx mv -f preact/src/index.d.ts signals.d.ts && shx rm -rf preact",
"postbuild:react": "cd packages/react/dist && shx mv -f react/src/index.d.ts signals.d.ts && shx rm -rf react",
"postbuild:react-utils": "cd packages/react/utils/dist && shx mv -f react/utils/src/index.d.ts . && shx rm -rf react",
"postbuild:react-auto": "cd packages/react/auto/dist && shx mv -f react/auto/src/*.d.ts . && shx rm -rf react",
"postbuild:react-runtime": "cd packages/react/runtime/dist && shx mv -f react/runtime/src/*.d.ts . && shx rm -rf react",
"lint": "pnpm lint:eslint && pnpm lint:tsc",
Expand Down
9 changes: 8 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@
"import": "./auto/dist/auto.mjs",
"require": "./auto/dist/auto.js"
},
"./auto/package.json": "./auto/package.json"
"./auto/package.json": "./auto/package.json",
"./utils": {
"types": "./utils/dist/index.d.ts",
"browser": "./utils/dist/utils.module.js",
"import": "./utils/dist/utils.mjs",
"require": "./utils/dist/utils.js"
},
"./utils/package.json": "./utils/package.json"
},
"mangle": "../../mangle.json",
"files": [
Expand Down
26 changes: 26 additions & 0 deletions packages/react/utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@preact/signals-react-runtime",
"description": "Sub package for @preact/signals-react that contains some useful utilities",
"private": true,
"amdName": "reactSignalsutils",
"main": "dist/utils.js",
"module": "dist/utils.module.js",
"unpkg": "dist/utils.min.js",
"types": "dist/index.d.ts",
"source": "src/index.ts",
"mangle": "../../../mangle.json",
"exports": {
".": {
"types": "./dist/index.d.ts",
"browser": "./dist/utils.module.js",
"import": "./dist/utils.mjs",
"require": "./dist/utils.js"
}
},
"dependencies": {
"@preact/signals-core": "workspace:^1.3.0"
},
"peerDependencies": {
"react": "^16.14.0 || 17.x || 18.x || 19.x"
}
}
45 changes: 45 additions & 0 deletions packages/react/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ReadonlySignal, Signal } from "@preact/signals-core";
import { useSignal } from "@preact/signals-react";
import { useSignals } from "@preact/signals-react/runtime";
import { Fragment, createElement, useMemo } from "react";

interface ShowProps<T = boolean> {
when: Signal<T> | ReadonlySignal<T>;
fallback?: JSX.Element;
children: JSX.Element | ((value: T) => JSX.Element);
}

export function Show<T = boolean>(props: ShowProps<T>): JSX.Element | null {
useSignals();
const value = props.when.value;
if (!value) return props.fallback || null;
return typeof props.children === "function"
? props.children(value)
: props.children;
}

interface ForProps<T> {
each: Signal<Array<T>> | ReadonlySignal<Array<T>>;
fallback?: JSX.Element;
children: (value: T, index: number) => JSX.Element;
}

export function For<T>(props: ForProps<T>): JSX.Element | null {
useSignals();
const cache = useMemo(() => new Map(), []);
const list = props.each.value;
if (!list.length) return props.fallback || null;
const items = list.map((value, key) => {
if (!cache.has(value)) {
cache.set(value, props.children(value, key));
}
return cache.get(value);
});
return createElement(Fragment, { children: items });
}

export function useLiveSignal<T>(value: Signal<T> | ReadonlySignal<T>) {
const s = useSignal(value);
if (s.peek() !== value) s.value = value;
return s;
}
69 changes: 69 additions & 0 deletions packages/react/utils/test/browser/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { For, Show } from "../../src";
import {
act,
checkHangingAct,
createRoot,
Root,
} from "../../../test/shared/utils";
import { signal } from "@preact/signals-react";
import { createElement } from "react";

describe("@preact/signals-react-utils", () => {
let scratch: HTMLDivElement;
let root: Root;
async function render(element: Parameters<Root["render"]>[0]) {
await act(() => root.render(element));
}

beforeEach(async () => {
scratch = document.createElement("div");
document.body.appendChild(scratch);
root = await createRoot(scratch);
});

afterEach(async () => {
checkHangingAct();
await act(() => root.unmount());
scratch.remove();
});

describe("<Show />", () => {
it("Should reactively show an element", () => {
const toggle = signal(false)!;
const Paragraph = (p: any) => <p>{p.children}</p>;
act(() => {
render(
<Show when={toggle} fallback={<Paragraph>Hiding</Paragraph>}>
<Paragraph>Showing</Paragraph>
</Show>
);
});
expect(scratch.innerHTML).to.eq("<p>Hiding</p>");

act(() => {
toggle.value = true;
});
expect(scratch.innerHTML).to.eq("<p>Showing</p>");
});
});

describe("<For />", () => {
it("Should iterate over a list of signals", () => {
const list = signal<Array<string>>([])!;
const Paragraph = (p: any) => <p>{p.children}</p>;
act(() => {
render(
<For each={list} fallback={<Paragraph>No items</Paragraph>}>
{item => <Paragraph key={item}>{item}</Paragraph>}
</For>
);
});
expect(scratch.innerHTML).to.eq("<p>No items</p>");

act(() => {
list.value = ["foo", "bar"];
});
expect(scratch.innerHTML).to.eq("<p>foo</p><p>bar</p>");
});
});
});
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@preact/signals-core": ["./packages/core/src/index.ts"],
"@preact/signals": ["./packages/preact/src/index.ts"],
"@preact/signals-react": ["./packages/react/src/index.ts"],
"@preact/signals-react/utils": ["./packages/react/utils/src/index.ts"],
"@preact/signals-react/runtime": [
"./packages/react/runtime/src/index.ts"
],
Expand Down

0 comments on commit 0df78b9

Please sign in to comment.