undercity 1.0.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.
- package/AGENTS.md +26 -0
- package/README.md +58 -0
- package/actions/AGENTS.md +41 -0
- package/actions/_shared/container.js +16 -0
- package/actions/auth/ask-login/action.json +15 -0
- package/actions/auth/ask-signup/action.json +14 -0
- package/actions/display/clear/action.json +18 -0
- package/actions/display/clear/action.test.js +32 -0
- package/actions/display/markdown/action.json +12 -0
- package/actions/display/markdown/action.test.js +32 -0
- package/actions/display/rawHtml/action.json +24 -0
- package/actions/display/rawHtml/action.test.js +32 -0
- package/actions/display/safeHtml/action.json +24 -0
- package/actions/display/safeHtml/action.test.js +32 -0
- package/actions/display/text/action.js +9 -0
- package/actions/display/text/action.json +12 -0
- package/actions/display/text/action.test.js +40 -0
- package/actions/display/value/action.json +24 -0
- package/actions/display/value/action.test.js +32 -0
- package/actions/dom/addClass/action.json +23 -0
- package/actions/dom/addClass/action.test.js +32 -0
- package/actions/dom/focus/action.json +17 -0
- package/actions/dom/focus/action.test.js +32 -0
- package/actions/dom/hide/action.json +18 -0
- package/actions/dom/hide/action.test.js +32 -0
- package/actions/dom/removeClass/action.json +22 -0
- package/actions/dom/removeClass/action.test.js +32 -0
- package/actions/dom/scroll/action.json +29 -0
- package/actions/dom/scroll/action.test.js +32 -0
- package/actions/dom/setAttr/action.json +29 -0
- package/actions/dom/setAttr/action.test.js +32 -0
- package/actions/dom/setHtml/action.json +22 -0
- package/actions/dom/setHtml/action.test.js +32 -0
- package/actions/dom/setStyle/action.json +29 -0
- package/actions/dom/setStyle/action.test.js +32 -0
- package/actions/dom/setText/action.json +24 -0
- package/actions/dom/setText/action.test.js +32 -0
- package/actions/dom/show/action.json +18 -0
- package/actions/dom/show/action.test.js +32 -0
- package/actions/dom/toggle/action.json +17 -0
- package/actions/dom/toggle/action.test.js +32 -0
- package/actions/dom/toggleClass/action.json +22 -0
- package/actions/dom/toggleClass/action.test.js +32 -0
- package/actions/event/emit/action.json +24 -0
- package/actions/event/emit/action.test.js +32 -0
- package/actions/event/on/action.json +23 -0
- package/actions/event/on/action.test.js +32 -0
- package/actions/event/waitFor/action.json +28 -0
- package/actions/event/waitFor/action.test.js +32 -0
- package/actions/forms/bindField/action.js +27 -0
- package/actions/forms/bindField/action.json +24 -0
- package/actions/forms/bindField/action.test.js +20 -0
- package/actions/forms/check/action.json +22 -0
- package/actions/forms/check/action.test.js +32 -0
- package/actions/forms/clearErrors/action.json +11 -0
- package/actions/forms/clearErrors/action.test.js +35 -0
- package/actions/forms/clearField/action.json +17 -0
- package/actions/forms/clearField/action.test.js +32 -0
- package/actions/forms/getField/action.json +24 -0
- package/actions/forms/getField/action.test.js +32 -0
- package/actions/forms/getRange/action.json +22 -0
- package/actions/forms/getRange/action.test.js +32 -0
- package/actions/forms/getSelect/action.json +22 -0
- package/actions/forms/getSelect/action.test.js +32 -0
- package/actions/forms/index.js +140 -0
- package/actions/forms/serialize/action.json +24 -0
- package/actions/forms/serialize/action.test.js +32 -0
- package/actions/forms/setCheck/action.json +23 -0
- package/actions/forms/setCheck/action.test.js +32 -0
- package/actions/forms/setError/action.json +22 -0
- package/actions/forms/setError/action.test.js +32 -0
- package/actions/forms/setField/action.js +14 -0
- package/actions/forms/setField/action.json +24 -0
- package/actions/forms/setField/action.test.js +32 -0
- package/actions/forms/submit/action.json +18 -0
- package/actions/forms/submit/action.test.js +32 -0
- package/actions/forms/validate/action.json +24 -0
- package/actions/forms/validate/action.test.js +32 -0
- package/actions/http/delete/action.json +22 -0
- package/actions/http/delete/action.test.js +32 -0
- package/actions/http/get/action.json +24 -0
- package/actions/http/get/action.test.js +32 -0
- package/actions/http/post/action.json +30 -0
- package/actions/http/post/action.test.js +32 -0
- package/actions/http/put/action.json +27 -0
- package/actions/http/put/action.test.js +32 -0
- package/actions/http/upload/action.json +35 -0
- package/actions/http/upload/action.test.js +32 -0
- package/actions/index.js +306 -0
- package/actions/index.json +5 -0
- package/actions/input/askChoice/action.json +29 -0
- package/actions/input/askChoice/action.test.js +32 -0
- package/actions/input/askConfirm/action.json +24 -0
- package/actions/input/askConfirm/action.test.js +32 -0
- package/actions/input/askDate/action.json +24 -0
- package/actions/input/askDate/action.test.js +32 -0
- package/actions/input/askEmail/action.json +24 -0
- package/actions/input/askEmail/action.test.js +32 -0
- package/actions/input/askNumber/action.json +35 -0
- package/actions/input/askNumber/action.test.js +32 -0
- package/actions/input/askPassword/action.json +24 -0
- package/actions/input/askPassword/action.test.js +32 -0
- package/actions/input/askText/action.json +36 -0
- package/actions/input/askText/action.test.js +32 -0
- package/actions/logic/delay/action.json +18 -0
- package/actions/logic/delay/action.test.js +32 -0
- package/actions/logic/if/action.json +28 -0
- package/actions/logic/if/action.test.js +32 -0
- package/actions/logic/log/action.json +18 -0
- package/actions/logic/log/action.test.js +32 -0
- package/actions/logic/random/action.json +36 -0
- package/actions/logic/random/action.test.js +32 -0
- package/actions/logic/transform/action.json +24 -0
- package/actions/logic/transform/action.test.js +32 -0
- package/actions/media/askAudioUpload/action.json +30 -0
- package/actions/media/askAudioUpload/action.test.js +32 -0
- package/actions/media/askFileUpload/action.json +36 -0
- package/actions/media/askFileUpload/action.test.js +32 -0
- package/actions/media/askImageUpload/action.json +37 -0
- package/actions/media/askImageUpload/action.test.js +32 -0
- package/actions/media/askVideoUpload/action.js +74 -0
- package/actions/media/askVideoUpload/action.json +44 -0
- package/actions/media/askVideoUpload/action.test.js +51 -0
- package/actions/media/captureWebcam/action.json +24 -0
- package/actions/media/captureWebcam/action.test.js +32 -0
- package/actions/nav/back/action.json +11 -0
- package/actions/nav/back/action.test.js +35 -0
- package/actions/nav/goto/action.json +18 -0
- package/actions/nav/goto/action.test.js +32 -0
- package/actions/nav/redirect/action.json +28 -0
- package/actions/nav/redirect/action.test.js +32 -0
- package/actions/nav/reload/action.json +11 -0
- package/actions/nav/reload/action.test.js +35 -0
- package/actions/nav/reset/action.json +11 -0
- package/actions/nav/reset/action.test.js +35 -0
- package/actions/render/alert/action.js +12 -0
- package/actions/render/alert/action.json +36 -0
- package/actions/render/alert/action.test.js +32 -0
- package/actions/render/button/action.js +15 -0
- package/actions/render/button/action.json +44 -0
- package/actions/render/button/action.test.js +37 -0
- package/actions/render/clear/action.js +7 -0
- package/actions/render/clear/action.json +11 -0
- package/actions/render/clear/action.test.js +35 -0
- package/actions/render/divider/action.js +8 -0
- package/actions/render/divider/action.json +11 -0
- package/actions/render/divider/action.test.js +35 -0
- package/actions/render/field/action.js +17 -0
- package/actions/render/field/action.json +59 -0
- package/actions/render/field/action.test.js +57 -0
- package/actions/render/link/action.js +20 -0
- package/actions/render/link/action.json +30 -0
- package/actions/render/link/action.test.js +32 -0
- package/actions/render/markdown/action.json +18 -0
- package/actions/render/markdown/action.test.js +32 -0
- package/actions/render/paragraph/action.js +9 -0
- package/actions/render/paragraph/action.json +32 -0
- package/actions/render/paragraph/action.test.js +32 -0
- package/actions/render/section/action.js +10 -0
- package/actions/render/section/action.json +18 -0
- package/actions/render/section/action.test.js +32 -0
- package/actions/render/subtitle/action.js +9 -0
- package/actions/render/subtitle/action.json +18 -0
- package/actions/render/subtitle/action.test.js +32 -0
- package/actions/render/title/action.js +9 -0
- package/actions/render/title/action.json +30 -0
- package/actions/render/title/action.test.js +44 -0
- package/actions/session/clear/action.json +11 -0
- package/actions/session/clear/action.test.js +35 -0
- package/actions/session/load/action.json +22 -0
- package/actions/session/load/action.test.js +32 -0
- package/actions/session/local/action.json +22 -0
- package/actions/session/local/action.test.js +32 -0
- package/actions/session/save/action.json +22 -0
- package/actions/session/save/action.test.js +32 -0
- package/actions/ui/accordion/action.json +23 -0
- package/actions/ui/accordion/action.test.js +32 -0
- package/actions/ui/badge/action.json +22 -0
- package/actions/ui/badge/action.test.js +32 -0
- package/actions/ui/collapse/action.json +23 -0
- package/actions/ui/collapse/action.test.js +32 -0
- package/actions/ui/hideModal/action.json +17 -0
- package/actions/ui/hideModal/action.test.js +32 -0
- package/actions/ui/loading/action.json +18 -0
- package/actions/ui/loading/action.test.js +32 -0
- package/actions/ui/modal/action.json +18 -0
- package/actions/ui/modal/action.test.js +32 -0
- package/actions/ui/progress/action.json +24 -0
- package/actions/ui/progress/action.test.js +32 -0
- package/actions/ui/toast/action.json +29 -0
- package/actions/ui/toast/action.test.js +32 -0
- package/actions/ui/tooltip/action.json +17 -0
- package/actions/ui/tooltip/action.test.js +32 -0
- package/actions/user/carry/action.json +25 -0
- package/actions/user/carry/action.test.js +32 -0
- package/actions/user/check/action.json +24 -0
- package/actions/user/check/action.test.js +32 -0
- package/actions/user/clear/action.json +11 -0
- package/actions/user/clear/action.test.js +35 -0
- package/actions/user/delete/action.json +17 -0
- package/actions/user/delete/action.test.js +32 -0
- package/actions/user/dump/action.json +11 -0
- package/actions/user/dump/action.test.js +35 -0
- package/actions/user/get/action.json +24 -0
- package/actions/user/get/action.test.js +32 -0
- package/actions/user/merge/action.json +18 -0
- package/actions/user/merge/action.test.js +32 -0
- package/actions/user/set/action.json +24 -0
- package/actions/user/set/action.test.js +32 -0
- package/generator/base/css/bootstrap.min.css +6 -0
- package/generator/base/icons/app-indicator.svg +4 -0
- package/generator/base/icons/backpack.svg +4 -0
- package/generator/base/icons/broadcast.svg +3 -0
- package/generator/base/icons/bullseye.svg +6 -0
- package/generator/base/icons/chat-dots.svg +4 -0
- package/generator/base/icons/check-circle.svg +4 -0
- package/generator/base/icons/clipboard-check.svg +5 -0
- package/generator/base/icons/clipboard.svg +4 -0
- package/generator/base/icons/copy.svg +3 -0
- package/generator/base/icons/cursor.svg +3 -0
- package/generator/base/icons/diamond.svg +3 -0
- package/generator/base/icons/exclamation-triangle.svg +4 -0
- package/generator/base/icons/film.svg +3 -0
- package/generator/base/icons/floppy.svg +4 -0
- package/generator/base/icons/gear-wide-connected.svg +3 -0
- package/generator/base/icons/gear.svg +4 -0
- package/generator/base/icons/globe.svg +3 -0
- package/generator/base/icons/image.svg +4 -0
- package/generator/base/icons/layout-text-window.svg +4 -0
- package/generator/base/icons/lightning-charge.svg +3 -0
- package/generator/base/icons/magic.svg +3 -0
- package/generator/base/icons/pencil-square.svg +4 -0
- package/generator/base/icons/record-circle.svg +4 -0
- package/generator/base/icons/robot.svg +4 -0
- package/generator/base/icons/shield-check.svg +4 -0
- package/generator/base/icons/shield-lock.svg +4 -0
- package/generator/base/icons/signpost.svg +3 -0
- package/generator/base/icons/stars.svg +3 -0
- package/generator/base/icons/type.svg +3 -0
- package/generator/base/js/bootstrap.bundle.min.js +7 -0
- package/package.json +14 -0
- package/packages/undercity-http-server/index.js +249 -0
- package/packages/undercity-http-server/package.json +10 -0
- package/packages/undercity-parser/index.js +323 -0
- package/packages/undercity-parser/lexer.js +128 -0
- package/packages/undercity-parser/package.json +11 -0
- package/plugins/forms.js +397 -0
- package/plugins/index.js +83 -0
- package/plugins/multipage.js +165 -0
- package/plugins/wizard.js +239 -0
- package/projects/asd/project.json +1031 -0
- package/projects/test-1/project.json +335 -0
- package/projects/test-a/project.json +456 -0
- package/public/icons/arrows-angle-expand.svg +3 -0
- package/public/icons/arrows-fullscreen.svg +3 -0
- package/public/icons/bezier2.svg +3 -0
- package/public/icons/bootstrap/app-indicator.svg +4 -0
- package/public/icons/bootstrap/arrow-clockwise.svg +4 -0
- package/public/icons/bootstrap/arrow-counterclockwise.svg +4 -0
- package/public/icons/bootstrap/arrow-left.svg +3 -0
- package/public/icons/bootstrap/arrows-angle-expand.svg +3 -0
- package/public/icons/bootstrap/arrows-fullscreen.svg +3 -0
- package/public/icons/bootstrap/backpack.svg +4 -0
- package/public/icons/bootstrap/bezier2.svg +3 -0
- package/public/icons/bootstrap/bookmark-check.svg +4 -0
- package/public/icons/bootstrap/bookmark-plus.svg +4 -0
- package/public/icons/bootstrap/box-arrow-right.svg +4 -0
- package/public/icons/bootstrap/box-arrow-up.svg +4 -0
- package/public/icons/bootstrap/broadcast.svg +3 -0
- package/public/icons/bootstrap/bullseye.svg +6 -0
- package/public/icons/bootstrap/chat-dots.svg +4 -0
- package/public/icons/bootstrap/check-circle.svg +4 -0
- package/public/icons/bootstrap/check2.svg +3 -0
- package/public/icons/bootstrap/clipboard-check.svg +5 -0
- package/public/icons/bootstrap/clipboard.svg +4 -0
- package/public/icons/bootstrap/clock-history.svg +5 -0
- package/public/icons/bootstrap/command.svg +3 -0
- package/public/icons/bootstrap/copy.svg +3 -0
- package/public/icons/bootstrap/cursor.svg +3 -0
- package/public/icons/bootstrap/diagram-3.svg +3 -0
- package/public/icons/bootstrap/diamond.svg +3 -0
- package/public/icons/bootstrap/exclamation-triangle.svg +4 -0
- package/public/icons/bootstrap/eye.svg +4 -0
- package/public/icons/bootstrap/file-earmark-plus.svg +4 -0
- package/public/icons/bootstrap/film.svg +3 -0
- package/public/icons/bootstrap/floppy.svg +4 -0
- package/public/icons/bootstrap/folder2-open.svg +3 -0
- package/public/icons/bootstrap/gear-wide-connected.svg +3 -0
- package/public/icons/bootstrap/gear.svg +4 -0
- package/public/icons/bootstrap/globe.svg +3 -0
- package/public/icons/bootstrap/grid-3x3-gap.svg +3 -0
- package/public/icons/bootstrap/house-door.svg +3 -0
- package/public/icons/bootstrap/image.svg +4 -0
- package/public/icons/bootstrap/layout-text-window.svg +4 -0
- package/public/icons/bootstrap/lightning-charge.svg +3 -0
- package/public/icons/bootstrap/magic.svg +3 -0
- package/public/icons/bootstrap/pencil-square.svg +4 -0
- package/public/icons/bootstrap/pencil.svg +3 -0
- package/public/icons/bootstrap/play.svg +3 -0
- package/public/icons/bootstrap/plus-circle.svg +4 -0
- package/public/icons/bootstrap/plus-lg.svg +3 -0
- package/public/icons/bootstrap/record-circle.svg +4 -0
- package/public/icons/bootstrap/robot.svg +4 -0
- package/public/icons/bootstrap/save.svg +3 -0
- package/public/icons/bootstrap/scissors.svg +3 -0
- package/public/icons/bootstrap/shield-check.svg +4 -0
- package/public/icons/bootstrap/shield-lock.svg +4 -0
- package/public/icons/bootstrap/signpost.svg +3 -0
- package/public/icons/bootstrap/stars.svg +3 -0
- package/public/icons/bootstrap/stop-circle.svg +4 -0
- package/public/icons/bootstrap/terminal.svg +4 -0
- package/public/icons/bootstrap/trash3.svg +3 -0
- package/public/icons/bootstrap/type.svg +3 -0
- package/public/icons/bootstrap/x-circle.svg +4 -0
- package/public/icons/bootstrap/x-lg.svg +3 -0
- package/public/icons/bootstrap/zoom-in.svg +5 -0
- package/public/icons/bootstrap/zoom-out.svg +5 -0
- package/public/icons/bullseye.svg +6 -0
- package/public/icons/check2.svg +3 -0
- package/public/icons/cursor.svg +3 -0
- package/public/icons/diamond.svg +3 -0
- package/public/icons/eye.svg +4 -0
- package/public/icons/file-earmark-plus.svg +4 -0
- package/public/icons/floppy.svg +4 -0
- package/public/icons/gear.svg +4 -0
- package/public/icons/lightning-charge.svg +3 -0
- package/public/icons/pencil.svg +3 -0
- package/public/icons/play.svg +3 -0
- package/public/icons/plus-circle.svg +4 -0
- package/public/icons/record-circle.svg +4 -0
- package/public/icons/robot.svg +4 -0
- package/public/icons/save.svg +3 -0
- package/public/icons/stop-circle.svg +4 -0
- package/public/icons/terminal.svg +4 -0
- package/public/icons/trash3.svg +3 -0
- package/public/icons/x-circle.svg +4 -0
- package/public/icons/zoom-in.svg +5 -0
- package/public/icons/zoom-out.svg +5 -0
- package/public/index.html +424 -0
- package/public/testbench.html +899 -0
- package/scripts/extract-actions.js +128 -0
- package/server.js +11 -0
- package/src/emitter.js +48 -0
- package/src/generator/css.js +135 -0
- package/src/generator/index.js +122 -0
- package/src/generator/md-renderer-src.js +77 -0
- package/src/generator/page.js +300 -0
- package/src/generator/runtime.js +1632 -0
- package/src/generator/templates.js +508 -0
- package/src/ide/action-library.js +856 -0
- package/src/ide/af-icons.js +127 -0
- package/src/ide/app.js +1375 -0
- package/src/ide/command-line/commands.js +242 -0
- package/src/ide/command-line/index.js +329 -0
- package/src/ide/command-line/parser.js +21 -0
- package/src/ide/css/ide.css +1501 -0
- package/src/ide/graph.js +282 -0
- package/src/ide/history.js +46 -0
- package/src/ide/map-builder.js +583 -0
- package/src/ide/project-api.js +39 -0
- package/src/ide/savant-chat.js +513 -0
- package/src/ide/savant.js +1287 -0
- package/src/ide/thing-library.js +89 -0
- package/src/ide/undercity-map.js +978 -0
- package/src/lib/icons.js +72 -0
- package/src/lib/scope.js +88 -0
- package/src/lib/signal.js +155 -0
- package/src/lib/state-machine.js +113 -0
- package/src/server/index.js +96 -0
- package/src/server/routes/actions.js +144 -0
- package/src/server/routes/ai.js +176 -0
- package/src/server/routes/generate.js +54 -0
- package/src/server/routes/projects.js +106 -0
- package/src/server/routes/reset.js +30 -0
- package/src/server/routes/submit.js +30 -0
- package/src/server/routes/templates.js +139 -0
- package/src/server/routes/things.js +33 -0
- package/templates/auth-flow.json +335 -0
- package/templates/blank.json +39 -0
- package/things/auth-server/thing.json +17 -0
- package/things/persona-live/thing.json +20 -0
- package/things/workflow/thing.json +15 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "event.waitFor",
|
|
3
|
+
"category": "events",
|
|
4
|
+
"categoryLabel": "Events",
|
|
5
|
+
"icon": "broadcast",
|
|
6
|
+
"color": "var(--sol-cyan)",
|
|
7
|
+
"label": "Wait For Event",
|
|
8
|
+
"desc": "Pause flow until a named event fires (returns a Promise).",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "event",
|
|
13
|
+
"label": "Event name",
|
|
14
|
+
"type": "text"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "timeout",
|
|
18
|
+
"label": "Timeout (ms)",
|
|
19
|
+
"type": "number",
|
|
20
|
+
"default": 0
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"name": "into",
|
|
24
|
+
"label": "Store in",
|
|
25
|
+
"type": "text"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for event.waitFor
|
|
3
|
+
* Wait For Event: Pause flow until a named event fires (returns a Promise).
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'event.waitFor — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Event ?? RT.event;
|
|
16
|
+
if (!ns) throw new Error('Namespace Event/event not found in runtime');
|
|
17
|
+
if (typeof ns.waitFor !== 'function') throw new Error('event.waitFor is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'event.waitFor — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Event.waitFor?.("", 0);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* form.bindField — runtime implementation
|
|
3
|
+
*
|
|
4
|
+
* Bidirectional live sync between a named DOM input and an inventory key.
|
|
5
|
+
* After this step runs:
|
|
6
|
+
* - Field value is set from inventory immediately.
|
|
7
|
+
* - Typing in the field writes to inventory in real time.
|
|
8
|
+
* - External inventory changes (via Inventory.set) update the field.
|
|
9
|
+
*
|
|
10
|
+
* In runtime.js, this lives inside the Actions object:
|
|
11
|
+
*
|
|
12
|
+
* bindField(fieldName, invKey) {
|
|
13
|
+
* const el = document.querySelector(`[name="${fieldName}"]`);
|
|
14
|
+
* if (!el) return;
|
|
15
|
+
* const v = Inventory.get(invKey);
|
|
16
|
+
* if (v !== null && v !== undefined) el.value = String(v);
|
|
17
|
+
* const onInput = () => Inventory.set(invKey, el.value);
|
|
18
|
+
* el.addEventListener('input', onInput);
|
|
19
|
+
* const offBus = Bus.on('inventory:changed', ({ key }) => {
|
|
20
|
+
* if ((key === invKey || key === '*') && el.ownerDocument.contains(el)) {
|
|
21
|
+
* const nv = String(Inventory.get(invKey) ?? '');
|
|
22
|
+
* if (el.value !== nv) el.value = nv;
|
|
23
|
+
* }
|
|
24
|
+
* });
|
|
25
|
+
* _pwRegisterBinding(() => { el.removeEventListener('input', onInput); offBus(); });
|
|
26
|
+
* },
|
|
27
|
+
*/
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.bindField",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "link-45deg",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Bind Field to Inventory",
|
|
8
|
+
"desc": "Synchronize a form field with an inventory key. Sets the field immediately when this step runs, then automatically keeps both in sync: typing in the field updates inventory, and inventory changes update the field.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text",
|
|
15
|
+
"placeholder": "email"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "key",
|
|
19
|
+
"label": "Inventory key",
|
|
20
|
+
"type": "inventory-key",
|
|
21
|
+
"placeholder": "userEmail"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — form.bindField
|
|
3
|
+
* Runs in the Undercity testbench (browser) via /api/actions/tests.
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, assert } from '/src/lib/testbench.js';
|
|
6
|
+
|
|
7
|
+
describe('form.bindField', () => {
|
|
8
|
+
it('has correct metadata', async () => {
|
|
9
|
+
const res = await fetch('/actions/forms/bindField/action.json');
|
|
10
|
+
const meta = await res.json();
|
|
11
|
+
assert.equal(meta.id, 'form.bindField');
|
|
12
|
+
assert.equal(meta.category, 'forms');
|
|
13
|
+
assert(Array.isArray(meta.params), 'params must be an array');
|
|
14
|
+
const keys = meta.params.map(p => p.name);
|
|
15
|
+
assert(keys.includes('name'), 'must have name param');
|
|
16
|
+
assert(keys.includes('key'), 'must have key param');
|
|
17
|
+
assert(meta.params.find(p => p.name === 'key')?.type === 'inventory-key',
|
|
18
|
+
'key param must be inventory-key type');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.check",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Get Checkbox / Radio",
|
|
8
|
+
"desc": "Read checked state of a checkbox or radio group.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "into",
|
|
18
|
+
"label": "Into var",
|
|
19
|
+
"type": "text"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.check
|
|
3
|
+
* Get Checkbox / Radio: Read checked state of a checkbox or radio group.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.check — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.check !== 'function') throw new Error('form.check is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.check — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.check?.("");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.clearErrors",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Clear All Errors",
|
|
8
|
+
"desc": "Hide all [data-error] elements on the page.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.clearErrors
|
|
3
|
+
* Clear All Errors: Hide all [data-error] elements on the page.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.clearErrors — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.clearErrors !== 'function') throw new Error('form.clearErrors is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.clearErrors — can be called with no arguments',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
// Smoke test: calling the action should not throw
|
|
25
|
+
// (may produce DOM changes or be a no-op depending on state)
|
|
26
|
+
try {
|
|
27
|
+
await RT.Form.clearErrors?.();
|
|
28
|
+
} catch (e) {
|
|
29
|
+
// Some actions require specific DOM state — acceptable
|
|
30
|
+
if (e instanceof TypeError) throw e;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.clearField",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Clear Field",
|
|
8
|
+
"desc": "Reset a field to empty.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.clearField
|
|
3
|
+
* Clear Field: Reset a field to empty.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.clearField — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.clearField !== 'function') throw new Error('form.clearField is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.clearField — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.clearField?.("");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.getField",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Get Field Value",
|
|
8
|
+
"desc": "Read a form field by name and optionally store in inventory.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text",
|
|
15
|
+
"placeholder": "email"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "into",
|
|
19
|
+
"label": "Into var",
|
|
20
|
+
"type": "text",
|
|
21
|
+
"placeholder": "email"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.getField
|
|
3
|
+
* Get Field Value: Read a form field by name and optionally store in inventory.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.getField — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.getField !== 'function') throw new Error('form.getField is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.getField — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.getField?.("email");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.getRange",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Get Range Slider",
|
|
8
|
+
"desc": "Read the current value of a range input.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "into",
|
|
18
|
+
"label": "Into var",
|
|
19
|
+
"type": "text"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.getRange
|
|
3
|
+
* Get Range Slider: Read the current value of a range input.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.getRange — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.getRange !== 'function') throw new Error('form.getRange is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.getRange — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.getRange?.("");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.getSelect",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Get Select Value",
|
|
8
|
+
"desc": "Read the selected option value from a <select> element.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "name",
|
|
13
|
+
"label": "Field name",
|
|
14
|
+
"type": "text"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"name": "into",
|
|
18
|
+
"label": "Into var",
|
|
19
|
+
"type": "text"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.getSelect
|
|
3
|
+
* Get Select Value: Read the selected option value from a <select> element.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.getSelect — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.getSelect !== 'function') throw new Error('form.getSelect is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.getSelect — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.getSelect?.("");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* actions/forms/index.js — Forms category plugin
|
|
3
|
+
*
|
|
4
|
+
* Browser-side ES module. Loaded by actions/index.js and installed via App.use().
|
|
5
|
+
* Registers the Forms action category in the Savant, replacing the
|
|
6
|
+
* static definition that used to live in action-library.js.
|
|
7
|
+
*
|
|
8
|
+
* To add a new forms action:
|
|
9
|
+
* 1. Create actions/forms/<actionName>/ with action.json + action.js
|
|
10
|
+
* 2. Add the definition to FORMS_ACTIONS below
|
|
11
|
+
* 3. Add the runtime implementation to src/generator/runtime.js → Actions
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export const FormsPlugin = {
|
|
15
|
+
name: 'actions/forms',
|
|
16
|
+
install(app) {
|
|
17
|
+
app.registerActions('forms', FORMS_CATEGORY);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const FORMS_CATEGORY = {
|
|
22
|
+
label: 'Forms',
|
|
23
|
+
icon: 'clipboard-check',
|
|
24
|
+
color: 'var(--sol-yellow)',
|
|
25
|
+
actions: {
|
|
26
|
+
|
|
27
|
+
'form.getField': {
|
|
28
|
+
label: 'Get Field Value',
|
|
29
|
+
desc: 'Read the current value of a form field once when this step runs. Saves to inventory under the key you specify in "Save as".',
|
|
30
|
+
params: [
|
|
31
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'email' },
|
|
32
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'email' },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
'form.setField': {
|
|
37
|
+
label: 'Set Field Value',
|
|
38
|
+
desc: 'Set a form field\'s value from an inventory key once when this step runs.',
|
|
39
|
+
params: [
|
|
40
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'email' },
|
|
41
|
+
{ name: 'value', label: 'Inventory key', type: 'inventory-key', placeholder: 'userEmail' },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
'form.bindField': {
|
|
46
|
+
label: 'Bind Field to Inventory',
|
|
47
|
+
desc: 'Synchronize a form field with an inventory key — live and automatic. Sets the field immediately, then keeps both in sync: typing updates inventory, inventory changes update the field.',
|
|
48
|
+
params: [
|
|
49
|
+
{ name: 'name', label: 'Field name', type: 'text', placeholder: 'email' },
|
|
50
|
+
{ name: 'key', label: 'Inventory key', type: 'inventory-key', placeholder: 'userEmail' },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
'form.clearField': {
|
|
55
|
+
label: 'Clear Field',
|
|
56
|
+
desc: 'Reset a form field to empty.',
|
|
57
|
+
params: [
|
|
58
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
'form.serialize': {
|
|
63
|
+
label: 'Serialize Form',
|
|
64
|
+
desc: 'Collect all fields of a form into one inventory key as an object.',
|
|
65
|
+
params: [
|
|
66
|
+
{ name: 'selector', label: 'Form selector', type: 'text', placeholder: '#my-form' },
|
|
67
|
+
{ name: 'into', label: 'Save as', type: 'text', placeholder: 'formData' },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
'form.validate': {
|
|
72
|
+
label: 'Validate Form',
|
|
73
|
+
desc: 'Run HTML5 constraint validation. Marks fields is-valid/is-invalid. Saves true/false to inventory under "Valid? (save as)".',
|
|
74
|
+
params: [
|
|
75
|
+
{ name: 'selector', label: 'Form selector', type: 'text', placeholder: '#my-form' },
|
|
76
|
+
{ name: 'into', label: 'Valid? (save as)', type: 'text', placeholder: 'isValid' },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
'form.setError': {
|
|
81
|
+
label: 'Set Field Error',
|
|
82
|
+
desc: 'Show a validation error message under a field using [data-error="name"].',
|
|
83
|
+
params: [
|
|
84
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
85
|
+
{ name: 'msg', label: 'Message', type: 'text' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
'form.clearErrors': {
|
|
90
|
+
label: 'Clear All Errors',
|
|
91
|
+
desc: 'Hide all [data-error] elements and remove is-invalid class from all fields.',
|
|
92
|
+
params: [],
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
'form.submit': {
|
|
96
|
+
label: 'Submit Form',
|
|
97
|
+
desc: 'Programmatically trigger form submission.',
|
|
98
|
+
params: [
|
|
99
|
+
{ name: 'selector', label: 'Form', type: 'text', placeholder: '#my-form' },
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
'form.check': {
|
|
104
|
+
label: 'Get Checkbox / Radio',
|
|
105
|
+
desc: 'Read the checked state of a checkbox or radio group once when this step runs. Saves true/false to inventory under the key you specify in "Save as".',
|
|
106
|
+
params: [
|
|
107
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
108
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
'form.setCheck': {
|
|
113
|
+
label: 'Set Checkbox',
|
|
114
|
+
desc: 'Check or uncheck a checkbox by field name.',
|
|
115
|
+
params: [
|
|
116
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
117
|
+
{ name: 'checked', label: 'Checked', type: 'boolean', default: true },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
'form.getSelect': {
|
|
122
|
+
label: 'Get Select Value',
|
|
123
|
+
desc: 'Read the selected option from a <select> once when this step runs. Saves to inventory under the key you specify in "Save as".',
|
|
124
|
+
params: [
|
|
125
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
126
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
'form.getRange': {
|
|
131
|
+
label: 'Get Range Slider',
|
|
132
|
+
desc: 'Read the current value of a range input once when this step runs. Saves to inventory under the key you specify in "Save as".',
|
|
133
|
+
params: [
|
|
134
|
+
{ name: 'name', label: 'Field name', type: 'text' },
|
|
135
|
+
{ name: 'into', label: 'Save as', type: 'text' },
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
},
|
|
140
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "form.serialize",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"categoryLabel": "Forms",
|
|
5
|
+
"icon": "clipboard-check",
|
|
6
|
+
"color": "var(--sol-yellow)",
|
|
7
|
+
"label": "Serialize Form",
|
|
8
|
+
"desc": "Collect all fields of a form into an inventory key.",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"params": [
|
|
11
|
+
{
|
|
12
|
+
"name": "selector",
|
|
13
|
+
"label": "Form selector",
|
|
14
|
+
"type": "text",
|
|
15
|
+
"placeholder": "#my-form"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"name": "into",
|
|
19
|
+
"label": "Into var",
|
|
20
|
+
"type": "text",
|
|
21
|
+
"placeholder": "formData"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* action.test.js — Unit tests for form.serialize
|
|
3
|
+
* Serialize Form: Collect all fields of a form into an inventory key.
|
|
4
|
+
*
|
|
5
|
+
* Each test receives (RT, sandbox) where:
|
|
6
|
+
* RT — the generated runtime module (Inventory, Navigator, Actions, ...)
|
|
7
|
+
* sandbox — a <div> containing #pw-content, #pw-form, #pw-loading
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const tests = [
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
name: 'form.serialize — is exported from runtime',
|
|
14
|
+
run(RT) {
|
|
15
|
+
const ns = RT.Form ?? RT.form;
|
|
16
|
+
if (!ns) throw new Error('Namespace Form/form not found in runtime');
|
|
17
|
+
if (typeof ns.serialize !== 'function') throw new Error('form.serialize is not a function');
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'form.serialize — can be called with default params',
|
|
23
|
+
async run(RT, sandbox) {
|
|
24
|
+
try {
|
|
25
|
+
await RT.Form.serialize?.("#my-form");
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (e instanceof TypeError) throw e;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
];
|