@cssints/apg-specs 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,9 @@ import { ActionStep, Probe, ScenarioConfig, SpecBridge } from "../scenario.mjs";
6
6
  * cannot model a menu longer than four); the labels never reach the model.
7
7
  *
8
8
  * The spec's `menuOpen` axis is its own controlled contract (`controlled` /
9
- * `accepts` / `value`), separate from `createMenu`'s packed checked `value`.
10
- * `createMenu`'s open state is still uncontrolled, so the differential walk
11
- * stays uncontrolled and `setValue` / `hostToggle` are inert on both sides; the
12
- * controlled vocabulary is accepted here only so a scenario carrying it is
13
- * modelled instead of silently dropped (see the menu-button DIVERGENCES).
9
+ * `accepts` / `value`), and `createMenu`'s `open` pair implements it: the walk
10
+ * runs it uncontrolled, accepting, holding and controlled-open, and `setValue` /
11
+ * `hostToggle` are the host writing its own copy.
14
12
  */
15
13
  export interface MenuButtonConfig {
16
14
  readonly items: ReadonlyArray<string>;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "generatedFrom": "../../specs/apg/menu-button.qnt",
3
- "specHash": "f7bae37e4eea0a7fed07c26085f022035ad1d13c0802bfa3f43dc734251bc3cf",
3
+ "specHash": "8636cd7e8489abe537f78fcdfd70fef48666a438e85da14e781d9e13aec55014",
4
4
  "module": "apg_menu_button",
5
5
  "vars": [
6
6
  {
@@ -5159,8 +5159,28 @@
5159
5159
  "opcode": "ite",
5160
5160
  "args": [
5161
5161
  {
5162
- "kind": "name",
5163
- "name": "controlled"
5162
+ "kind": "app",
5163
+ "opcode": "and",
5164
+ "args": [
5165
+ {
5166
+ "kind": "name",
5167
+ "name": "controlled"
5168
+ },
5169
+ {
5170
+ "kind": "app",
5171
+ "opcode": "neq",
5172
+ "args": [
5173
+ {
5174
+ "kind": "name",
5175
+ "name": "v"
5176
+ },
5177
+ {
5178
+ "kind": "name",
5179
+ "name": "menuOpen"
5180
+ }
5181
+ ]
5182
+ }
5183
+ ]
5164
5184
  },
5165
5185
  {
5166
5186
  "kind": "app",
@@ -5194,8 +5214,28 @@
5194
5214
  "opcode": "ite",
5195
5215
  "args": [
5196
5216
  {
5197
- "kind": "name",
5198
- "name": "controlled"
5217
+ "kind": "app",
5218
+ "opcode": "and",
5219
+ "args": [
5220
+ {
5221
+ "kind": "name",
5222
+ "name": "controlled"
5223
+ },
5224
+ {
5225
+ "kind": "app",
5226
+ "opcode": "neq",
5227
+ "args": [
5228
+ {
5229
+ "kind": "name",
5230
+ "name": "v"
5231
+ },
5232
+ {
5233
+ "kind": "name",
5234
+ "name": "menuOpen"
5235
+ }
5236
+ ]
5237
+ }
5238
+ ]
5199
5239
  },
5200
5240
  {
5201
5241
  "kind": "app",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "generatedFrom": "../../specs/apg/slider.qnt",
3
- "specHash": "189f136f0b8c0e2ab5d6f3dc6145065fac2e2d0f97d2b54b720af8d8f390e635",
3
+ "specHash": "7f8cabe937ca10920c10b5bda488a1ab01e76b75b8ffc8ae61a4abf8d0c8131d",
4
4
  "module": "apg_slider",
5
5
  "vars": [
6
6
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "generatedFrom": "../../specs/apg/spinbutton.qnt",
3
- "specHash": "19c6cf077a7017c6cb2df08b2ff6749bed9f75056e77a5f7084b393967cca18f",
3
+ "specHash": "2cec7069c44874e465cbdf1a751fa272ff8272be19d3bc84ab2edc5574673e3a",
4
4
  "module": "apg_spinbutton",
5
5
  "vars": [
6
6
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cssints/apg-specs",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "description": "APG widget specs: Quint sources, generated models, scenario bridges and ported scenario sets, the test oracle of headless widgets",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,7 +24,9 @@
24
24
  // controlled, accepts the host answers with the same value (`accepting`): the
25
25
  // transition commits through the controlled value
26
26
  // `valueProp` is the only thing that changes `menuOpen` while `controlled`;
27
- // `setValue` / `hostToggle` are the host writing it. `control` / `acceptRequests`
27
+ // `setValue` / `hostToggle` are the host writing it, and a write that changes it
28
+ // takes the focus with it (a write of the value it already has is unobservable
29
+ // and moves no focus). `control` / `acceptRequests`
28
30
  // are configuration events (init is literal-only). A rejected request reverts
29
31
  // the *whole* transition (focus included), so the ARIA invariants still hold.
30
32
  //
@@ -272,12 +274,15 @@ module apg_menu_button {
272
274
  }
273
275
  }
274
276
 
275
- // commit: the host writes the controlled open state (a no-op while uncontrolled)
277
+ // commit: the host writes the controlled open state (a no-op while uncontrolled).
278
+ // A controlled host writing the value it already has is unobservable to the
279
+ // widget (nothing changes, so nothing notifies), so it moves no focus: only a
280
+ // write that changes the open state takes the focus with it.
276
281
  action setValue = {
277
282
  nondet v = Set(true, false).oneOf()
278
283
  all {
279
- focusLevel' = if (controlled) (if (v) 2 else 1) else focusLevel,
280
- focusIdx' = if (controlled) (if (v) 1 else 0) else focusIdx,
284
+ focusLevel' = if (and(controlled, v != menuOpen)) (if (v) 2 else 1) else focusLevel,
285
+ focusIdx' = if (and(controlled, v != menuOpen)) (if (v) 1 else 0) else focusIdx,
281
286
  menuOpen' = if (controlled) v else menuOpen,
282
287
  lastActivated' = lastActivated,
283
288
  controlled' = controlled,
@@ -45,10 +45,15 @@
45
45
  // press always land on the grid.
46
46
  // - Dragging (pointer capture, move events): only a discrete pointer
47
47
  // "press at value" is modelled, snapped to the step grid and clamped.
48
- // - Disabled / read-only, touch assistive-technology key synthesis, and
49
- // aria-valuetext formatting (units, locale): a real widget formats the text
50
- // from its own value; the spec only fixes WHEN the text is derived (always,
51
- // from valuenow).
48
+ // - Disabled / read-only: the primitive's widget-level `disabled` is a
49
+ // deliberate extension of the pattern, and the machine has no `disabled`
50
+ // var. So the model carries no invariant for it ("a disabled thumb drops
51
+ // every key and rail press and announces aria-disabled"); its oracle is the
52
+ // DOM test in `packages/headless/test/slider.browser.test.tsx`. Read-only is
53
+ // not offered at all.
54
+ // - Touch assistive-technology key synthesis, and aria-valuetext formatting
55
+ // (units, locale): a real widget formats the text from its own value; the
56
+ // spec only fixes WHEN the text is derived (always, from valuenow).
52
57
  // - The range (0..10) is fixed; `configure` varies step, large step and the
53
58
  // starting value. The bridge rejects a config outside the variant table.
54
59
  module apg_slider {
@@ -49,6 +49,12 @@
49
49
  // is replaced wholesale by a sample text.
50
50
  // - the commit/revert policy (Enter/Escape/blur) is a widely used
51
51
  // convention, not APG text; so is the buttons' aria-disabled-at-bound.
52
+ // - a widget-level `disabled`: a deliberate extension of the pattern, not
53
+ // modelled. The machine's only disabled state is the steppers' at-bound
54
+ // one (`DisabledOnlyAtBound`); it has no `disabled` var, so it cannot say
55
+ // "while disabled, keys, typing and both steppers change nothing and both
56
+ // steppers announce aria-disabled". The DOM test in
57
+ // `packages/headless/test/spinbutton.browser.test.tsx` is its oracle.
52
58
  // - press-and-hold auto-repeat on the buttons, mouse wheel.
53
59
  //
54
60
  // Generator notes: no action preconditions (guards live in the assignment