Skip to content
Ooidget

Ooidget Component Format 0.1Editor's Draft

Interface and tests

A component file MAY declare its interface: what a host may set, what it will hear, and what it may call. With about and per-member descriptions, the interface is the component’s documentation; tools generate reference pages and property editors from it. A component MAY also carry tests that any runtime can run.

{
  "about": "A volume knob.",
  "widget": { "state": { "volume": 0.5, "muted": false } },
  "interface": {
    "props": {
      "volume": { "type": "number", "minimum": 0, "maximum": 1, "description": "Output level, linear." },
      "muted": { "type": "boolean" }
    },
    "events": { "changed": { "description": "The volume moved.", "params": { "type": "number" } } },
    "methods": {
      "reset": { "description": "Back to the default level.", "source": "s => ({ volume: 0.5, muted: false })" },
      "nudge": { "params": { "type": "object", "properties": { "by": { "type": "number" } }, "required": ["by"] },
                 "fn": "knob#nudge" },
      "mute": { "description": "The behaviour's own mute handler." }
    }
  },
  "tests": [
    { "name": "the wheel raises it", "steps": [{ "wheel": [24, 24, 1], "expect": { "volume": 0.52 } }] },
    { "name": "reset", "state": { "volume": 0.9 }, "steps": [{ "call": ["reset"], "expect": { "volume": 0.5 } }] }
  ]
}

Props

interface.props maps root state members to a schema each, in JSON Schema 2020-12 [JSON-SCHEMA]. A prop’s default is the component’s initial state; a runtime reports it as the schema’s default.

When a host sets state, a runtime MUST check the patch against the props, refuse a member the props do not declare, and apply nothing of a patch with an error. It MUST check at least type, enum, const, minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf, minLength, maxLength, items, minItems, maxItems, properties, required and additionalProperties; other keywords MAY be treated as documentation. A component without props takes any patch. State the widget’s own behaviours write is not checked.

A host’s set is not a change made inside the widget: runtimes MUST NOT report it back as one (on the web, no change event), so a framework that passes state down and listens for changes cannot loop.

Events

interface.events documents the commands the component sends: each by its $cmd name, with a description and the schema of its params ($args).

Methods

interface.methods names what a host may call. A method’s params is the schema its arguments MUST fit; a call whose arguments do not fit fails without effect. The method runs:

  1. its fn, a function reference, else
  2. its source, an ECMAScript function’s source, else
  3. the root behaviour’s handler of the method’s name.

It is called with the root’s view and the arguments, and what it returns is taken as a handler’s return value: a state patch and, maybe, a command.

Tests

tests is an array of tests, each { name, state?, size?, steps }. A runtime runs one in a widget of its own: started from the component’s state with state applied, laid out at size (else the component’s), and laid out again before each step. A step does, in order, what it names, then checks what it expects:

MemberDoes
seta host’s set (a refused patch fails the test)
applymerges into state unchecked
call[name, args?]: calls a method
down, move, up, click[x, y]: pointer input in design units; click is move, down and up
wheel[x, y, notches]
key{ key, code, shift, ctrl, alt, meta } to the focused instance
expectthe state MUST hold these members (numbers within 1e-9)
commandsthe commands the step produced MUST match, in order

These are the steps of the conformance suite’s scenes, so a component’s tests run anywhere the suite does. Hosts and tools SHOULD offer to run them: an editor on every save, a store before it lists a component.

In a host

C ABIWeb
describeoo_document_describedescribe(bundle, name), el.describe()
setoo_widget_setw.set(patch), el.state = patch
calloo_widget_callw.call(name, args), el.call(name, args)
a child’s own stateoo_widget_write
testsoo_document_testrunTests(bundle, name), el.test()

On the web, <oo-widget> is a standard custom element: its properties and DOM events are how React, Solid, Vue, Svelte, Angular and plain pages all use it.

Accessibility audit

A runtime SHOULD offer an audit of a widget against WCAG 2.2 [WCAG22] for the success criteria a frame decides: 1.4.3 Contrast (Minimum), text against the solid fill under it; 2.1.1 Keyboard, operable instances are focusable; 2.4.7 Focus Visible, a focused instance draws differently; 2.5.8 Target Size (Minimum), operable instances are at least 24 × 24 design units; and 4.1.2 Name, Role, Value. Each finding names its criterion, level and instance. Passing the audit is not conformance: names that mean something, a sensible order, zoom and reflow remain a person’s and the host’s to judge.