@excom/super-form 0.1.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/.rush/temp/chunked-rush-logs/super-form.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/super-form.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/super-form.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +5 -0
- package/index.ts +17 -0
- package/package.json +46 -0
- package/rush-logs/super-form.apply-exports.cache.log +1 -0
- package/rush-logs/super-form.apply-exports.log +1 -0
- package/rush-logs/super-form.build_docs.cache.log +1 -0
- package/rush-logs/super-form.build_docs.log +1 -0
- package/rush-logs/super-form.build_package-metas.cache.log +1 -0
- package/rush-logs/super-form.build_package-metas.log +1 -0
- package/super-form.ts +100 -0
- package/support/custom-elements.json +107 -0
- package/support/demos/external-trigger.html +43 -0
- package/support/demos/simple.html +31 -0
- package/support/dist-docs/super-form.md +212 -0
- package/support/docs/README.md +61 -0
- package/support/package-meta.json +224 -0
- package/support/tests/__snapshots__/external-trigger.view.test.ts.snap +23 -0
- package/support/tests/__snapshots__/simple.view.test.ts.snap +23 -0
- package/support/tests/external-trigger.view.test.ts +57 -0
- package/support/tests/simple.view.test.ts +63 -0
- package/support/tests/super-form.test.ts +319 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# super-form
|
|
2
|
+
|
|
3
|
+
Submit AJAX requests with HTML forms. Pair it with Quark to render the response.
|
|
4
|
+
|
|
5
|
+
<include-content data-demo="simple"></include-content>
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Makes AJAX requests** JSON payload is built from each input's `name` and `type` attributes
|
|
10
|
+
- **Progressively enhanced** Doesn't replace the native `form`; it enhances it. Everything you know about `form` and `input` still applies.
|
|
11
|
+
- **Provides data** Use Quark to render the response
|
|
12
|
+
- **Submit command** `--submit` submits programmatically (`<button command="--submit" commandfor="…">`)
|
|
13
|
+
- **Highly configurable** Headers, credentials, redirect, etc
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
<include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Just wrap a regular form. Form fields become a JSON payload via their `name`: dot-separated names nest, and a trailing `[]` collects same-named fields into an array. `input[type]` determines the type conversion.
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<super-form>
|
|
25
|
+
<form action="/api/signup" method="post">
|
|
26
|
+
<input name="isAvailable" type="checkbox"> <!-- -> { isAvailable: true } -->
|
|
27
|
+
<input name="address.city" value="Anytown"> <!-- -> { address: { city: "Anytown" } } -->
|
|
28
|
+
<input name="tags[]" value="smart">
|
|
29
|
+
<input name="tags[]" value="kind"> <!-- -> { tags: ["smart", "kind"] } -->
|
|
30
|
+
<button type="submit">Sign up</button>
|
|
31
|
+
</form>
|
|
32
|
+
</super-form>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Hook the lifecycle state with CSS:
|
|
36
|
+
|
|
37
|
+
```css
|
|
38
|
+
super-form[is-loading] { /* form currently submitting, show loading spinner */ }
|
|
39
|
+
super-form[is-error]::before { content: "An error occurred." }
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or Quark:
|
|
43
|
+
|
|
44
|
+
```quark
|
|
45
|
+
super-form[is-success] {
|
|
46
|
+
$res: prop("provision").body;
|
|
47
|
+
span { content: $res.json.email; }
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### API Reference
|
|
52
|
+
|
|
53
|
+
<include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
|
|
54
|
+
|
|
55
|
+
### Examples
|
|
56
|
+
|
|
57
|
+
#### Comprehensive
|
|
58
|
+
|
|
59
|
+
This example shows loading state, error state, rendering, and triggering from outside the form.
|
|
60
|
+
|
|
61
|
+
<include-content data-demo="external-trigger"></include-content>
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
{
|
|
2
|
+
"shortName": "super-form",
|
|
3
|
+
"package": {
|
|
4
|
+
"name": "@excom/super-form",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"description": "<super-form> custom element",
|
|
7
|
+
"peerDependencies": {},
|
|
8
|
+
"excom": {
|
|
9
|
+
"packageType": "kit-element"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"demos": {
|
|
13
|
+
"external-trigger": "<section>\n <super-form id=\"demo-super-form-external\" class=\"tag-article grid\">\n <form action=\"/api/echo\" method=\"post\">\n <label>\n Nickname\n <input type=\"text\" name=\"nickname\" placeholder=\"John\" required>\n </label>\n </form>\n <ul></ul>\n <ul></ul>\n <template id=\"demo-super-form-item\">\n <li class=\"tag-code\"></li>\n </template>\n </super-form>\n <button type=\"button\" command=\"--submit\" commandfor=\"demo-super-form-external\">\n Save (outside the form)\n </button>\n <style>\n #demo-super-form-external-trigger { max-width: none; }\n #demo-super-form-external-trigger super-form {\n &[is-loading] {\n opacity: 0.5;\n border: 1px dashed yellow;\n }\n &[is-success] { border: 1px solid green; }\n &[is-error] {\n border: 1px solid red;\n &::before { content: \"An error occurred.\"; }\n }\n ul:first-of-type::before { content: \"Submitted JSON:\"; }\n ul:last-of-type::before { content: \"Response data:\"; }\n li { display: block; }\n }\n </style>\n <quark-sheet>\n super-form[is-success] {\n $res: prop(\"provision\");\n li { content: \"#{index}: #{item}\"; }\n ul:first-of-type { content: iterate($res.body.json, \"#demo-super-form-item\"); }\n ul:last-of-type { content: iterate($res, \"#demo-super-form-item\"); }\n }\n </quark-sheet>\n</section>\n",
|
|
14
|
+
"simple": "<section>\n <super-form>\n <form action=\"/api/echo\" method=\"post\">\n <input type=\"text\" name=\"name\" placeholder=\"Your name\" required>\n <button type=\"submit\">Submit</button>\n </form>\n <h4></h4>\n <span></span>\n </super-form>\n <quark-sheet>\n super-form[is-success] {\n $res: prop(\"provision\");\n h4 { content: \"Echoed name: #{$res.body.json.name}\"; }\n }\n super-form {\n $res: prop(\"provision\");\n span { content: \"HTTP status code: #{$res.status or \"\"}\"; }\n }\n </quark-sheet>\n <style>\n #demo-super-form-simple super-form[is-loading] {\n animation: pulse 1s linear infinite;\n pointer-events: none;\n }\n @keyframes pulse {\n 0% { opacity: 0.2; }\n 50% { opacity: 0.6; }\n 100% { opacity: 0.2; }\n }\n </style>\n</section>\n"
|
|
15
|
+
},
|
|
16
|
+
"readme": "<h1 id=\"md-super-form\">super-form</h1>\n<p>Submit AJAX requests with HTML forms. Pair it with Quark to render the response.</p>\n<p><include-content data-demo=\"simple\"></include-content></p>\n<h2 id=\"md-features\">Features</h2>\n<ul>\n<li><strong>Makes AJAX requests</strong> JSON payload is built from each input's <code>name</code> and <code>type</code> attributes</li>\n<li><strong>Progressively enhanced</strong> Doesn't replace the native <code>form</code>; it enhances it. Everything you know about <code>form</code> and <code>input</code> still applies.</li>\n<li><strong>Provides data</strong> Use Quark to render the response</li>\n<li><strong>Submit command</strong> <code>--submit</code> submits programmatically (<code><button command="--submit" commandfor="…"></code>)</li>\n<li><strong>Highly configurable</strong> Headers, credentials, redirect, etc</li>\n</ul>\n<h2 id=\"md-installation\">Installation</h2>\n<p><include-content is-active template-ref=\"/views/install-section/install-section.html\"></include-content></p>\n<h2 id=\"md-usage\">Usage</h2>\n<p>Just wrap a regular form. Form fields become a JSON payload via their <code>name</code>: dot-separated names nest, and a trailing <code>[]</code> collects same-named fields into an array. <code>input[type]</code> determines the type conversion.</p>\n<include-content data-language=\"html\"><template><super-form>\n <form action=\"/api/signup\" method=\"post\">\n <input name=\"isAvailable\" type=\"checkbox\"> <!-- -> { isAvailable: true } -->\n <input name=\"address.city\" value=\"Anytown\"> <!-- -> { address: { city: \"Anytown\" } } -->\n <input name=\"tags[]\" value=\"smart\">\n <input name=\"tags[]\" value=\"kind\"> <!-- -> { tags: [\"smart\", \"kind\"] } -->\n <button type=\"submit\">Sign up</button>\n </form>\n</super-form></template></include-content>\n<p>Hook the lifecycle state with CSS:</p>\n<include-content data-language=\"css\"><template>super-form[is-loading] { /* form currently submitting, show loading spinner */ }\nsuper-form[is-error]::before { content: \"An error occurred.\" }</template></include-content>\n<p>Or Quark:</p>\n<include-content data-language=\"quark\"><template>super-form[is-success] {\n $res: prop(\"provision\").body;\n span { content: $res.json.email; }\n}</template></include-content>\n<h3 id=\"md-api-reference\">API Reference</h3>\n<p><include-content is-active template-ref=\"/views/api-reference/api-reference.html\"></include-content></p>\n<h3 id=\"md-examples\">Examples</h3>\n<h4 id=\"md-comprehensive\">Comprehensive</h4>\n<p>This example shows loading state, error state, rendering, and triggering from outside the form.</p>\n<p><include-content data-demo=\"external-trigger\"></include-content></p>\n",
|
|
17
|
+
"docs": {
|
|
18
|
+
"readme": "<h1 id=\"md-super-form\">super-form</h1>\n<p>Submit AJAX requests with HTML forms. Pair it with Quark to render the response.</p>\n<p><include-content data-demo=\"simple\"></include-content></p>\n<h2 id=\"md-features\">Features</h2>\n<ul>\n<li><strong>Makes AJAX requests</strong> JSON payload is built from each input's <code>name</code> and <code>type</code> attributes</li>\n<li><strong>Progressively enhanced</strong> Doesn't replace the native <code>form</code>; it enhances it. Everything you know about <code>form</code> and <code>input</code> still applies.</li>\n<li><strong>Provides data</strong> Use Quark to render the response</li>\n<li><strong>Submit command</strong> <code>--submit</code> submits programmatically (<code><button command="--submit" commandfor="…"></code>)</li>\n<li><strong>Highly configurable</strong> Headers, credentials, redirect, etc</li>\n</ul>\n<h2 id=\"md-installation\">Installation</h2>\n<p><include-content is-active template-ref=\"/views/install-section/install-section.html\"></include-content></p>\n<h2 id=\"md-usage\">Usage</h2>\n<p>Just wrap a regular form. Form fields become a JSON payload via their <code>name</code>: dot-separated names nest, and a trailing <code>[]</code> collects same-named fields into an array. <code>input[type]</code> determines the type conversion.</p>\n<include-content data-language=\"html\"><template><super-form>\n <form action=\"/api/signup\" method=\"post\">\n <input name=\"isAvailable\" type=\"checkbox\"> <!-- -> { isAvailable: true } -->\n <input name=\"address.city\" value=\"Anytown\"> <!-- -> { address: { city: \"Anytown\" } } -->\n <input name=\"tags[]\" value=\"smart\">\n <input name=\"tags[]\" value=\"kind\"> <!-- -> { tags: [\"smart\", \"kind\"] } -->\n <button type=\"submit\">Sign up</button>\n </form>\n</super-form></template></include-content>\n<p>Hook the lifecycle state with CSS:</p>\n<include-content data-language=\"css\"><template>super-form[is-loading] { /* form currently submitting, show loading spinner */ }\nsuper-form[is-error]::before { content: \"An error occurred.\" }</template></include-content>\n<p>Or Quark:</p>\n<include-content data-language=\"quark\"><template>super-form[is-success] {\n $res: prop(\"provision\").body;\n span { content: $res.json.email; }\n}</template></include-content>\n<h3 id=\"md-api-reference\">API Reference</h3>\n<p><include-content is-active template-ref=\"/views/api-reference/api-reference.html\"></include-content></p>\n<h3 id=\"md-examples\">Examples</h3>\n<h4 id=\"md-comprehensive\">Comprehensive</h4>\n<p>This example shows loading state, error state, rendering, and triggering from outside the form.</p>\n<p><include-content data-demo=\"external-trigger\"></include-content></p>\n"
|
|
19
|
+
},
|
|
20
|
+
"installation": {
|
|
21
|
+
"name": "@excom/super-form",
|
|
22
|
+
"shortName": "super-form",
|
|
23
|
+
"version": "0.1.0",
|
|
24
|
+
"description": "<super-form> custom element",
|
|
25
|
+
"packageType": "kit-element",
|
|
26
|
+
"cdn": "<script src=\"https://unpkg.com/@excom/kit-utils/dist/index.umd.min.js\"></script>\n<script src=\"https://unpkg.com/@excom/neutron/dist/index.umd.min.js\"></script>\n<script src=\"https://unpkg.com/@excom/super-form@0.1.0/dist/index.umd.min.js\"></script>",
|
|
27
|
+
"install": {
|
|
28
|
+
"npm": "npm install @excom/super-form"
|
|
29
|
+
},
|
|
30
|
+
"imports": {
|
|
31
|
+
"js": "import \"@excom/super-form\";",
|
|
32
|
+
"html": "<!-- import path to `node_modules` will depend on your build setup -->\n<script type=\"module\" src=\"/node_modules/@excom/super-form\"></script>\n<link rel=\"stylesheet\" href=\"/node_modules/@excom/super-form\">"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": []
|
|
35
|
+
},
|
|
36
|
+
"elementApis": [
|
|
37
|
+
{
|
|
38
|
+
"tag": "super-form",
|
|
39
|
+
"summary": "Zero-JS <code>fetch()</code> submit for a plain <code><form></code>.",
|
|
40
|
+
"kind": "class",
|
|
41
|
+
"attributes": [
|
|
42
|
+
{
|
|
43
|
+
"name": "api-method",
|
|
44
|
+
"type": "string",
|
|
45
|
+
"description": "HTTP method. Always uppercased before the request is sent.",
|
|
46
|
+
"fieldName": "apiMethod",
|
|
47
|
+
"surface": "option",
|
|
48
|
+
"default": "\"GET\"",
|
|
49
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "api-url",
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "Endpoint URL. When the request has no body, the JSON payload (from <code>form-ref</code> or custom <code>doFetch()</code> args) is merged in as query params instead.",
|
|
55
|
+
"fieldName": "apiUrl",
|
|
56
|
+
"surface": "option",
|
|
57
|
+
"default": "\"\"",
|
|
58
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"name": "fetch-credentials",
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "<code>RequestInit.credentials</code> mode.",
|
|
64
|
+
"fieldName": "fetchCredentials",
|
|
65
|
+
"surface": "option",
|
|
66
|
+
"default": "\"include\"",
|
|
67
|
+
"values": "\"omit\" | \"same-origin\" | \"include\"",
|
|
68
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "fetch-redirect",
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "<code>RequestInit.redirect</code> mode. Unset defers to the browser default (<code>follow</code>).",
|
|
74
|
+
"fieldName": "fetchRedirect",
|
|
75
|
+
"surface": "option",
|
|
76
|
+
"values": "\"follow\" | \"error\" | \"manual\"",
|
|
77
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "form-ref",
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "CSS selector for the <code><form></code> to intercept. The form's <code>action</code> / <code>method</code> / <code>enctype</code> take priority over <code>api-url</code> / <code>api-method</code> below. Must be a descendant to be heard directly — point elsewhere and invoke the <code>--submit</code> command instead.",
|
|
83
|
+
"fieldName": "formRef",
|
|
84
|
+
"surface": "option",
|
|
85
|
+
"default": "\":scope form\"",
|
|
86
|
+
"values": "<CSS Selector>"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "form-ref",
|
|
90
|
+
"type": "string",
|
|
91
|
+
"description": "CSS selector for a <code><form></code> to source the request from — its <code>action</code> (URL), <code>method</code>, <code>enctype</code> (Content-Type), and field values (as the JSON payload) all take priority over the matching attributes below. Omit to build the request entirely from attributes / custom <code>doFetch()</code> args.",
|
|
92
|
+
"fieldName": "formRef",
|
|
93
|
+
"surface": "option",
|
|
94
|
+
"values": "<CSS Selector>",
|
|
95
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "has-body",
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"description": "Force a request body even for methods that don't imply one (<code>GET</code> / <code>HEAD</code>). Already implied for <code>POST</code> / <code>PUT</code> / <code>PATCH</code>.",
|
|
101
|
+
"fieldName": "hasBody",
|
|
102
|
+
"surface": "option",
|
|
103
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "header-accept",
|
|
107
|
+
"type": "string",
|
|
108
|
+
"description": "<code>Accept</code> request header.",
|
|
109
|
+
"fieldName": "headerAccept",
|
|
110
|
+
"surface": "option",
|
|
111
|
+
"default": "\"application/json\"",
|
|
112
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "header-cache-control",
|
|
116
|
+
"type": "string",
|
|
117
|
+
"description": "<code>Cache-Control</code> request header. Unset by default (browser default caching applies).",
|
|
118
|
+
"fieldName": "headerCacheControl",
|
|
119
|
+
"surface": "option",
|
|
120
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"name": "header-content-type",
|
|
124
|
+
"type": "string",
|
|
125
|
+
"description": "<code>Content-Type</code> request header. Dropped entirely when the request has no body.",
|
|
126
|
+
"fieldName": "headerContentType",
|
|
127
|
+
"surface": "option",
|
|
128
|
+
"default": "\"application/json\"",
|
|
129
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"name": "is-error",
|
|
133
|
+
"type": "boolean",
|
|
134
|
+
"description": "The most recent request failed (non-2xx status, network error, or a thrown error other than <code>AbortError</code>). Fires with the <code>error</code> event.",
|
|
135
|
+
"fieldName": "isError",
|
|
136
|
+
"surface": "state",
|
|
137
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"name": "is-loading",
|
|
141
|
+
"type": "boolean",
|
|
142
|
+
"description": "A request is currently in flight.",
|
|
143
|
+
"fieldName": "isLoading",
|
|
144
|
+
"surface": "state",
|
|
145
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"name": "is-success",
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"description": "The most recent request resolved successfully. Mutually exclusive with <code>is-error</code>.",
|
|
151
|
+
"fieldName": "isSuccess",
|
|
152
|
+
"surface": "state",
|
|
153
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
"events": [
|
|
157
|
+
{
|
|
158
|
+
"name": "super-form-error",
|
|
159
|
+
"description": "Dispatched when the request fails — non-2xx status, network error, or a thrown error. <code>event.detail</code> is the error payload (see <code>provision</code>). Not dispatched for aborted requests.",
|
|
160
|
+
"type": "FetchableErrorEvent",
|
|
161
|
+
"typeExpanded": "CustomEvent & { type: \"{tag}-error\"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; } | { message: string; stack?: string }; bubbles: true; cancelable: true; composed: true }",
|
|
162
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"name": "super-form-loading",
|
|
166
|
+
"description": "Dispatched immediately before the request is sent.",
|
|
167
|
+
"type": "FetchableLoadingEvent",
|
|
168
|
+
"typeExpanded": "CustomEvent & { type: \"{tag}-loading\"; detail: void; bubbles: true; cancelable: true; composed: true }",
|
|
169
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"name": "super-form-submit",
|
|
173
|
+
"description": "Internal — dispatched whenever a submit is about to run (real <code>submit</code> or the <code>--submit</code> command). Built by <code>getFetchArgs()</code>. Cancelable; default action calls <code>doFetch()</code>.",
|
|
174
|
+
"type": "SuperFormSubmitEvent",
|
|
175
|
+
"typeExpanded": "CustomEvent & { type: \"super-form-submit\"; detail: [url: string, requestInit: RequestInit]; bubbles: true; cancelable: true; composed: true }",
|
|
176
|
+
"defaultAction": "Calls <code>doFetch(url, requestInit)</code> with the event's detail."
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
"name": "super-form-success",
|
|
180
|
+
"description": "Dispatched when the request resolves successfully. <code>event.detail</code> is the parsed response (see <code>provision</code>).",
|
|
181
|
+
"type": "FetchableSuccessEvent",
|
|
182
|
+
"typeExpanded": "CustomEvent & { type: \"{tag}-success\"; detail: { bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }; bubbles: true; cancelable: true; composed: true }",
|
|
183
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
184
|
+
}
|
|
185
|
+
],
|
|
186
|
+
"slots": [],
|
|
187
|
+
"cssProperties": [],
|
|
188
|
+
"cssClasses": [],
|
|
189
|
+
"cssAliases": [],
|
|
190
|
+
"listens": [
|
|
191
|
+
{
|
|
192
|
+
"name": "submit",
|
|
193
|
+
"description": "The default action of the <code><form></code> matched by <code>form-ref</code> (or any descendant <code><form></code>); prevented, then converted into <code>super-form-submit</code>.",
|
|
194
|
+
"type": "SuperFormNativeSubmitEvent",
|
|
195
|
+
"typeExpanded": "SubmitEvent & { type: \"submit\"; bubbles: true; cancelable: true; composed: false; }"
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
"commands": [
|
|
199
|
+
{
|
|
200
|
+
"name": "--submit",
|
|
201
|
+
"description": "Submits programmatically (<code><button command="--submit" commandfor="…"></code>) — the only option when <code>form-ref</code> points to a form that isn't a descendant, since this element can't hear its <code>submit</code> event directly."
|
|
202
|
+
}
|
|
203
|
+
],
|
|
204
|
+
"defaultActions": [
|
|
205
|
+
{
|
|
206
|
+
"name": "super-form-submit",
|
|
207
|
+
"description": "Calls <code>doFetch(url, requestInit)</code> with the event's detail."
|
|
208
|
+
}
|
|
209
|
+
],
|
|
210
|
+
"expectedChildren": [],
|
|
211
|
+
"provisions": [
|
|
212
|
+
{
|
|
213
|
+
"name": "provision",
|
|
214
|
+
"type": "FetchResponse",
|
|
215
|
+
"typeExpanded": "{ bodyUsed: boolean; headers: [string, string][]; ok: boolean; redirected: boolean; status: number; statusText: string; type: ResponseType; url: string; body: unknown; }",
|
|
216
|
+
"description": "Response payload on success, or error payload on failure. Success shape: <code>{ status, statusText, ok, headers, url, redirected, bodyUsed, type, body }</code>. Failure shape is either that same response shape (server responded with an error status) or <code>{ message, stack }</code> (request never completed). Not reflected as an attribute.",
|
|
217
|
+
"fieldName": "provision",
|
|
218
|
+
"inheritedFrom": "@excom/fetchable-element"
|
|
219
|
+
}
|
|
220
|
+
]
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
"exportedFiles": {}
|
|
224
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`external-trigger view > lists the echo after an outside trigger > complexity 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"attributeRuns": 12,
|
|
6
|
+
"closest": 20,
|
|
7
|
+
"getVar": 2,
|
|
8
|
+
"importNode": 12,
|
|
9
|
+
"listenerRuns": 0,
|
|
10
|
+
"matches": 6,
|
|
11
|
+
"parentElement": 25,
|
|
12
|
+
"quarkRuns": 2,
|
|
13
|
+
"queryScopeCost": 88,
|
|
14
|
+
"querySelectorAll": 12,
|
|
15
|
+
"removeAttribute": 1,
|
|
16
|
+
"ruleRuns": 12,
|
|
17
|
+
"schedulePaint": 12,
|
|
18
|
+
"setAttribute": 4,
|
|
19
|
+
"setVar": 1,
|
|
20
|
+
"textContent": 10,
|
|
21
|
+
"variableRuns": 1,
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`simple view > echoes the submitted name and stays within the complexity budget > complexity 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"attributeRuns": 2,
|
|
6
|
+
"closest": 0,
|
|
7
|
+
"getVar": 2,
|
|
8
|
+
"importNode": 0,
|
|
9
|
+
"listenerRuns": 0,
|
|
10
|
+
"matches": 6,
|
|
11
|
+
"parentElement": 4,
|
|
12
|
+
"quarkRuns": 3,
|
|
13
|
+
"queryScopeCost": 15,
|
|
14
|
+
"querySelectorAll": 4,
|
|
15
|
+
"removeAttribute": 1,
|
|
16
|
+
"ruleRuns": 6,
|
|
17
|
+
"schedulePaint": 2,
|
|
18
|
+
"setAttribute": 2,
|
|
19
|
+
"setVar": 2,
|
|
20
|
+
"textContent": 2,
|
|
21
|
+
"variableRuns": 2,
|
|
22
|
+
}
|
|
23
|
+
`;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { invokeCommand } from "@excom/neutron";
|
|
2
|
+
import "@excom/event-handler";
|
|
3
|
+
import "@excom/quark-sheet";
|
|
4
|
+
import "../../index";
|
|
5
|
+
import {
|
|
6
|
+
afterEach,
|
|
7
|
+
describe,
|
|
8
|
+
expect,
|
|
9
|
+
it,
|
|
10
|
+
spyFetch,
|
|
11
|
+
waitForEvent,
|
|
12
|
+
} from "@excom/heft-rig/profiles/default/config/test-utils";
|
|
13
|
+
import {
|
|
14
|
+
expectComplexity,
|
|
15
|
+
flush,
|
|
16
|
+
measureComplexity,
|
|
17
|
+
mountView,
|
|
18
|
+
readDemo,
|
|
19
|
+
} from "@excom/quark/support/tests/view-helpers";
|
|
20
|
+
|
|
21
|
+
describe("external-trigger view", () => {
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
document.body.innerHTML = "";
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("lists the echo after an outside trigger", async () => {
|
|
27
|
+
spyFetch({
|
|
28
|
+
status: 200,
|
|
29
|
+
body: JSON.stringify({ json: { nickname: "Ada" } }),
|
|
30
|
+
});
|
|
31
|
+
const { root, quark } = await mountView(
|
|
32
|
+
readDemo(import.meta.url, "external-trigger"),
|
|
33
|
+
);
|
|
34
|
+
const form = root.querySelector<HTMLSuperFormElement>("super-form")!;
|
|
35
|
+
const trigger = root.querySelector<HTMLButtonElement>("button[command]")!;
|
|
36
|
+
root.querySelector<HTMLInputElement>('input[name="nickname"]')!.value =
|
|
37
|
+
"Ada";
|
|
38
|
+
|
|
39
|
+
expect(trigger.getAttribute("command")).toBe("--submit");
|
|
40
|
+
expect(trigger.getAttribute("commandfor")).toBe(form.id);
|
|
41
|
+
|
|
42
|
+
// happy-dom has no Command API: dispatch what the button would have
|
|
43
|
+
const meter = measureComplexity(quark!);
|
|
44
|
+
await waitForEvent(form, "super-form-success", () => {
|
|
45
|
+
invokeCommand(form, "--submit", trigger);
|
|
46
|
+
});
|
|
47
|
+
// `[is-success]` is observed as an attribute change (deferred run), then
|
|
48
|
+
// the iterate rows render and their own rule runs: two settle cycles
|
|
49
|
+
await flush();
|
|
50
|
+
await flush();
|
|
51
|
+
const budget = meter.take();
|
|
52
|
+
meter.stop();
|
|
53
|
+
expect(form.hasAttribute("is-success")).toBe(true);
|
|
54
|
+
expect(root.querySelector("ul")?.textContent).toMatch(/Ada/);
|
|
55
|
+
expectComplexity(budget);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import "@excom/quark-sheet";
|
|
2
|
+
import "../../index";
|
|
3
|
+
import {
|
|
4
|
+
expectComplexity,
|
|
5
|
+
flush,
|
|
6
|
+
measureComplexity,
|
|
7
|
+
} from "@excom/quark/support/tests/helpers";
|
|
8
|
+
import {
|
|
9
|
+
afterEach,
|
|
10
|
+
describe,
|
|
11
|
+
expect,
|
|
12
|
+
fixture,
|
|
13
|
+
it,
|
|
14
|
+
spyFetch,
|
|
15
|
+
waitForEvent,
|
|
16
|
+
} from "@excom/heft-rig/profiles/default/config/test-utils";
|
|
17
|
+
import { readFileSync } from "node:fs";
|
|
18
|
+
import { dirname, resolve } from "node:path";
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
const demoHtml = readFileSync(
|
|
22
|
+
resolve(dirname(fileURLToPath(import.meta.url)), "../demos/simple.html"),
|
|
23
|
+
"utf8",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
describe("simple view", () => {
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
document.body.innerHTML = "";
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("echoes the submitted name and stays within the complexity budget", async () => {
|
|
32
|
+
spyFetch({
|
|
33
|
+
status: 200,
|
|
34
|
+
body: JSON.stringify({ json: { name: "Ada" } }),
|
|
35
|
+
});
|
|
36
|
+
const root = fixture<HTMLElement>(demoHtml);
|
|
37
|
+
const sheet = root.querySelector<HTMLQuarkSheetElement>("quark-sheet")!;
|
|
38
|
+
const superForm = root.querySelector<HTMLSuperFormElement>("super-form")!;
|
|
39
|
+
await flush();
|
|
40
|
+
expect(sheet.quarkInstance).toBeDefined();
|
|
41
|
+
|
|
42
|
+
const nameInput = root.querySelector<HTMLInputElement>(
|
|
43
|
+
'input[name="name"]',
|
|
44
|
+
)!;
|
|
45
|
+
nameInput.value = "Ada";
|
|
46
|
+
|
|
47
|
+
const meter = measureComplexity(sheet.quarkInstance!);
|
|
48
|
+
await waitForEvent(superForm, "super-form-success", () => {
|
|
49
|
+
superForm
|
|
50
|
+
.getFormElement()!
|
|
51
|
+
.dispatchEvent(new Event("submit", { bubbles: true }));
|
|
52
|
+
});
|
|
53
|
+
await flush();
|
|
54
|
+
const budget = meter.take();
|
|
55
|
+
meter.stop();
|
|
56
|
+
|
|
57
|
+
expect(root.querySelector("h4")?.textContent).toBe("Echoed name: Ada");
|
|
58
|
+
expect(root.querySelector("span")?.textContent).toBe(
|
|
59
|
+
"HTTP status code: 200",
|
|
60
|
+
);
|
|
61
|
+
expectComplexity(budget);
|
|
62
|
+
});
|
|
63
|
+
});
|