jetix
TypeScript icon, indicating that this package has built-in type declarations

0.2.3 • Public • Published

Jetix

Minimal wiring for TypeScript components made of pure functions.

Also contains lightweight prevention of anti-patterns like state mutation and manually calling declarative actions.

Examples:


Actions and tasks

The component callback receives an object exposing action, task, rootAction and rootTask functions.

export default component(
  ({ action, task, rootAction, rootTask }) => ({
    // Initial action
    init: action( "ShowMessage", { text: "Hello World!" } ),
  })
);

Props and state

All action handlers, task callbacks and view functions receive props, state and rootState via a Context input.

view(id, { props, state, rootState }) {
  return div(`#${id}-message`, [
    // Render from props and state
    h1(props.title),
    div(state.text)
  ]);
}

Hello World!

import { component, html, mount, Config, Next, Task, VNode } from "jetix";
import { setDocTitle} from "../services/browser";
const { div } = html;
 
export type Props = Readonly<{
  placeholder: string;
}>;
 
export type State = Readonly<{
  text: string;
  done: boolean;
}>;
 
export type Actions = Readonly<{
  ShowMessage: { text: string };
  PageReady: { done: boolean };
}>;
 
export type Tasks = Readonly<{
  SetDocTitle: { title: string };
}>;
 
type Component = {
  Props: Props;
  State: State;
  Actions: Actions;
  Tasks: Tasks;
};
 
 
const app = component<Component>(
  ({ action, task }): Config<Component> => ({
 
    // Initial state
    state: ({ placeholder }): State => ({
      text: placeholder,
      done: false
    }),
 
    // Initial action
    init: action(
      "ShowMessage",
      { text: "Hello World!" }
    ),
 
    // Action handlers return new state, and any next actions/tasks
    actions: {
      ShowMessage: ({ text }, { state }): { state: State; next: Next } => {
        return {
          state: { ...state, text },
          next: task("SetDocTitle", { title: text })
        };
      },
      PageReady: ({ done }, { state }): { state: State } => {
        return {
          state: { ...state, done }
        };
      },
    },
 
    // Task handlers provide callbacks for effects and async operations that may fail
    tasks: {
      SetDocTitle: ({ title }): Task<null, State> => ({
        perform: (): Promise<void> => setDocTitle(title),
        success: (): Next => action("PageReady", { done: true }),
        failure: (): Next => action("PageReady", { done: false })
      })
    },
 
    // View renders from props & state
    view(id, { state }): VNode {
      return div(`#${id}-message`, [
        div(state.text),
        div(state.done ? '✅' : '❎')
      ]);
    }
 
  })
);
 
document.addEventListener(
  "DOMContentLoaded",
  (): void => mount({ app, props: { placeholder: "Loading" } })
);
 
export default app;

Unit tests

For tests the action and task functions just return data, so component logic can be tested without mocks.

import { testComponent, NextData } from "jetix";
import app, { State } from "./app";
 
describe("App", () => {
 
  const { action, task, config, initialState } = testComponent(app, { placeholder: "placeholder" });
 
  it("should set initial state", () => {
    expect(initialState).toEqual({ text: "placeholder", done: false });
  });
 
  it("should run initial action", () => {
    expect(config.init).toEqual({
      name: "ShowMessage",
      data: { text: "Hello World!" }
    });
  });
 
  describe("'ShowMessage' action", () => {
    const { state, next } = action<State>("ShowMessage", { text: "Hello World!"});
 
    it("should update state", () => {
      expect(state).toEqual({
        ...initialState,
        text: "Hello World!"
      });
    });
 
    it("should return next", () => {
      const { name, data } = next as NextData;
      expect(name).toBe("SetDocTitle");
      expect(data).toEqual({ title: "Hello World!" });
    });
  });
 
  describe("'SetDocTitle' task", () => {
    const { perform, success, failure } = task("SetDocTitle", { title: "test" });
 
    it("should provide perform", () => {
      expect(perform).toBeDefined();
    });
 
    it("should handle success", () => {
      const { name, data } = success() as NextData;
      expect(name).toBe("PageReady");
      expect(data).toEqual({ done: true });
    });
 
    it("should handle failure", () => {
      const { name, data } = failure() as NextData;
      expect(name).toBe("PageReady");
      expect(data).toEqual({ done: false });
    });
  });
 
});

Package Sidebar

Install

npm i jetix

Weekly Downloads

0

Version

0.2.3

License

MIT

Unpacked Size

27.3 kB

Total Files

11

Last publish

Collaborators

  • rcrawford