soma test: instrument 5 overlay demos for permutation runner

Adds `data-perm-step` + `<meta name="perm-axes">` + perms seed to the
5 overlay demos sharing Dialog's open/close shape:

- alert-dialog — trigger open → cancel. `?perm.open=true` seeds.
  `disabled` deliberately NOT in axes: disabling the Provider blocks
  the Trigger click path the runner depends on.
- popover — trigger open → close button. `?perm.open=true` seeds open1.
- tooltip — focus trigger (mode=focus, 1s settle — tooltip opens after
  700ms delay). No close step: tooltip closes on blur.
- drawer — trigger open → close button (500ms settle for exit animation).
  `?perm.open=true` seeds basicOpen.
- dropdown-menu — trigger open → Escape close (mode=key, 500ms settle).
  Second step targets the Menu.Content selector because the Content
  only mounts after the Trigger step fires (runner handles dynamic
  step discovery).

All five declare `dir` as the only axis (universal RTL variant). The
other orientation / disabled axes don't apply to these overlay shapes.

## Coverage delta

- Before: 3 instrumented demos · 9 variants · 34 steps.
- After:  8 instrumented demos · 19 variants · 62 steps.

2x jump in per-CI-run validations with zero infra changes — just applying
the v1-v3 convention to additional demos.

## Verification

- `npm run check`: 0 errors in soma/morfo/test-route scope.
- `npm run smoke`: 65/65 routes.
- `npm run morfo:check`: 66/66 morfos.
- `npm run perm:check`: 8/8 demos · 62 steps · 19 variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
semantuix
dev 6 months ago
parent 545d47148c
commit 1e8500bfac

@ -4,10 +4,14 @@
import { createForm, type StandardSchemaV1 } from '$soma/components/form'; import { createForm, type StandardSchemaV1 } from '$soma/components/form';
import * as Form from '$soma/components/form'; import * as Form from '$soma/components/form';
import Portal from '$soma/components/internal/portal.svelte'; import Portal from '$soma/components/internal/portal.svelte';
import { Perms } from '../_perms.svelte';
// Perm-runner v2/v3 seed (see `<meta name="perm-axes">` below).
const perms = Perms.getOr({} as Record<string, string | number | boolean>);
// ── Example 1 — interactive testbed (every public prop wired) ──────────── // ── Example 1 — interactive testbed (every public prop wired) ────────────
let open = $state(false); let open = $state((perms.open as boolean) ?? false);
let disabled = $state(false); let disabled = $state((perms.disabled as boolean) ?? false);
let escapeBehavior = $state<'close' | 'ignore'>('close'); let escapeBehavior = $state<'close' | 'ignore'>('close');
let actionLabel = $state('Delete account'); let actionLabel = $state('Delete account');
let cancelLabel = $state('Keep it'); let cancelLabel = $state('Keep it');
@ -74,6 +78,11 @@
<svelte:head> <svelte:head>
<title>AlertDialog · Soma</title> <title>AlertDialog · Soma</title>
<!-- Axes the perm-runner v3 should generate URL variants for. `disabled`
is deliberately NOT here: disabling the Provider blocks the Trigger
click and breaks the step cycle. The interactive control still
exercises disabled state at baseline for manual testing. -->
<meta name="perm-axes" content="dir" />
</svelte:head> </svelte:head>
<div class="page"> <div class="page">
@ -118,7 +127,9 @@
</div> </div>
<AlertDialog.Provider bind:open {disabled}> <AlertDialog.Provider bind:open {disabled}>
<AlertDialog.Trigger>Delete my account</AlertDialog.Trigger> <AlertDialog.Trigger data-perm-step="0" data-perm-label="open alert dialog">
Delete my account
</AlertDialog.Trigger>
<Portal> <Portal>
<AlertDialog.Overlay /> <AlertDialog.Overlay />
@ -128,7 +139,12 @@
This action is permanent and cannot be undone. All your data will be removed. This action is permanent and cannot be undone. All your data will be removed.
</AlertDialog.Description> </AlertDialog.Description>
<div class="actions"> <div class="actions">
<AlertDialog.Cancel aria-label={cancelLabel} onclick={onCancel}> <AlertDialog.Cancel
aria-label={cancelLabel}
onclick={onCancel}
data-perm-step="1"
data-perm-label="cancel alert dialog"
>
{cancelLabel} {cancelLabel}
</AlertDialog.Cancel> </AlertDialog.Cancel>
<AlertDialog.Action <AlertDialog.Action

@ -1,7 +1,10 @@
<script lang="ts"> <script lang="ts">
import * as Drawer from '$soma/components/drawer'; import * as Drawer from '$soma/components/drawer';
import { Perms } from '../_perms.svelte';
let basicOpen = $state(false); const perms = Perms.getOr({} as Record<string, string | number | boolean>);
let basicOpen = $state((perms.open as boolean) ?? false);
let sideDrawer = $state<{ open: boolean; dir: Drawer.DrawerDirection }>({ let sideDrawer = $state<{ open: boolean; dir: Drawer.DrawerDirection }>({
open: false, open: false,
dir: 'bottom' dir: 'bottom'
@ -24,6 +27,11 @@
} }
</script> </script>
<svelte:head>
<title>Drawer · Soma</title>
<meta name="perm-axes" content="dir" />
</svelte:head>
<div class="page"> <div class="page">
<h1>Drawer</h1> <h1>Drawer</h1>
@ -33,7 +41,7 @@
<h2>Basic</h2> <h2>Basic</h2>
<p>Bottom drawer, swipe down to dismiss.</p> <p>Bottom drawer, swipe down to dismiss.</p>
<Drawer.Provider bind:open={basicOpen}> <Drawer.Provider bind:open={basicOpen}>
<Drawer.Trigger>Open</Drawer.Trigger> <Drawer.Trigger data-perm-step="0" data-perm-label="open drawer">Open</Drawer.Trigger>
<Drawer.Overlay /> <Drawer.Overlay />
<Drawer.Content> <Drawer.Content>
<Drawer.Handle><span class="pill"></span></Drawer.Handle> <Drawer.Handle><span class="pill"></span></Drawer.Handle>
@ -42,7 +50,9 @@
<Drawer.Description>Swipe down or tap overlay to dismiss.</Drawer.Description> <Drawer.Description>Swipe down or tap overlay to dismiss.</Drawer.Description>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.</p> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore.</p>
<div class="actions"> <div class="actions">
<Drawer.Close>Close</Drawer.Close> <Drawer.Close data-perm-step="1" data-perm-label="close drawer" data-perm-settle="500">
Close
</Drawer.Close>
</div> </div>
</div> </div>
</Drawer.Content> </Drawer.Content>

@ -10,6 +10,7 @@
<svelte:head> <svelte:head>
<title>DropdownMenu · Soma</title> <title>DropdownMenu · Soma</title>
<meta name="perm-axes" content="dir" />
</svelte:head> </svelte:head>
<div class="page"> <div class="page">
@ -22,9 +23,15 @@
<p>Click trigger to open. Arrow keys navigate. Enter/Space selects. Escape closes.</p> <p>Click trigger to open. Arrow keys navigate. Enter/Space selects. Escape closes.</p>
<Menu.Provider> <Menu.Provider>
<Menu.Trigger>Actions</Menu.Trigger> <Menu.Trigger data-perm-step="0" data-perm-label="open dropdown menu">Actions</Menu.Trigger>
<Portal> <Portal>
<Menu.Content sideOffset={8}> <Menu.Content
sideOffset={8}
data-perm-step="1"
data-perm-mode='key="Escape"'
data-perm-label="close via Escape"
data-perm-settle="500"
>
<Menu.Item onSelect={() => alert('New Tab')}>New Tab</Menu.Item> <Menu.Item onSelect={() => alert('New Tab')}>New Tab</Menu.Item>
<Menu.Item onSelect={() => alert('New Window')}>New Window</Menu.Item> <Menu.Item onSelect={() => alert('New Window')}>New Window</Menu.Item>
<Menu.Item disabled>New Private Window</Menu.Item> <Menu.Item disabled>New Private Window</Menu.Item>

@ -1,8 +1,12 @@
<script lang="ts"> <script lang="ts">
import * as Popover from '$soma/components/popover'; import * as Popover from '$soma/components/popover';
import Portal from '$soma/components/internal/portal.svelte'; import Portal from '$soma/components/internal/portal.svelte';
import { Perms } from '../_perms.svelte';
let open1 = $state(false); // Perm-runner seed. `?perm.open=true` opens the first popover at mount.
const perms = Perms.getOr({} as Record<string, string | number | boolean>);
let open1 = $state((perms.open as boolean) ?? false);
let open2 = $state(false); let open2 = $state(false);
let open3 = $state(false); let open3 = $state(false);
let open4 = $state(false); let open4 = $state(false);
@ -13,6 +17,7 @@
<svelte:head> <svelte:head>
<title>Popover · Soma</title> <title>Popover · Soma</title>
<meta name="perm-axes" content="dir" />
</svelte:head> </svelte:head>
<div class="page"> <div class="page">
@ -28,14 +33,16 @@
<p>Click trigger to toggle. Closes on Escape or click outside.</p> <p>Click trigger to toggle. Closes on Escape or click outside.</p>
<Popover.Provider bind:open={open1}> <Popover.Provider bind:open={open1}>
<Popover.Trigger> <Popover.Trigger data-perm-step="0" data-perm-label="open popover">
Toggle popover ({open1 ? 'open' : 'closed'}) Toggle popover ({open1 ? 'open' : 'closed'})
</Popover.Trigger> </Popover.Trigger>
<Portal> <Portal>
<Popover.Content sideOffset={8}> <Popover.Content sideOffset={8}>
<p>This is a basic popover positioned below the trigger.</p> <p>This is a basic popover positioned below the trigger.</p>
<Popover.Close>Close</Popover.Close> <Popover.Close data-perm-step="1" data-perm-label="close popover">
Close
</Popover.Close>
<Popover.Arrow /> <Popover.Arrow />
</Popover.Content> </Popover.Content>
</Portal> </Portal>

@ -8,6 +8,7 @@
<svelte:head> <svelte:head>
<title>Tooltip · Soma</title> <title>Tooltip · Soma</title>
<meta name="perm-axes" content="dir" />
</svelte:head> </svelte:head>
<div class="page"> <div class="page">
@ -22,7 +23,12 @@
<Tooltip.Group> <Tooltip.Group>
<Tooltip.Provider> <Tooltip.Provider>
<Tooltip.Trigger> <Tooltip.Trigger>
<button>Hover me</button> <button
data-perm-step="0"
data-perm-mode="focus"
data-perm-label="focus tooltip trigger (opens after delay)"
data-perm-settle="1000"
>Hover me</button>
</Tooltip.Trigger> </Tooltip.Trigger>
<Portal> <Portal>
<Tooltip.Content sideOffset={8}> <Tooltip.Content sideOffset={8}>

Loading…
Cancel
Save

Powered by TurnKey Linux.