@noctuatech/uswds 0.0.7 → 0.0.9
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/README.md +6 -6
- package/assets/img/4c-lg-on-black.svg +10 -0
- package/package.json +3 -2
- package/src/lib/button/button.element.ts +16 -1
- package/src/lib/define.ts +7 -1
- package/src/lib/file-input/{file-input-preview.element.ts → file-input-preview/file-input-preview.element.ts} +20 -17
- package/src/lib/file-input/file-input.element.ts +33 -21
- package/src/lib/file-input/file-input.stories.ts +0 -2
- package/src/lib/input/input.element.ts +11 -8
- package/src/lib/modal/modal-close/modal-close.element.ts +55 -0
- package/src/lib/modal/modal-close/modal-close.test.ts +15 -0
- package/src/lib/modal/modal-heading/modal-heading.element.ts +38 -0
- package/src/lib/modal/modal-heading/modal-heading.test.ts +15 -0
- package/src/lib/modal/modal.element.ts +85 -0
- package/src/lib/modal/modal.stories.ts +41 -0
- package/src/lib/modal/modal.test.ts +25 -0
- package/src/lib/radio/radio.element.ts +1 -4
- package/src/lib/select/select.element.ts +1 -0
- package/src/lib/step-indicator/step/step-indicator-step.element.ts +110 -0
- package/src/lib/step-indicator/step/step-indicator-step.test.ts +15 -0
- package/src/lib/step-indicator/step-indicator.element.ts +24 -0
- package/src/lib/step-indicator/step-indicator.stories.ts +49 -0
- package/src/lib/step-indicator/step-indicator.test.ts +21 -0
- package/src/lib/summary-box/summary-box.element.ts +35 -0
- package/src/lib/summary-box/summary-box.stories.ts +24 -0
- package/src/lib/summary-box/summary-box.test.ts +15 -0
- package/src/lib.ts +7 -0
- package/target/lib/button/button.element.d.ts +3 -0
- package/target/lib/button/button.element.js +27 -3
- package/target/lib/button/button.element.js.map +1 -1
- package/target/lib/define.d.ts +6 -0
- package/target/lib/define.js +6 -0
- package/target/lib/define.js.map +1 -1
- package/target/lib/file-input/file-input-preview.element.js +1 -1
- package/target/lib/file-input/file-input-preview.element.js.map +1 -1
- package/target/lib/file-input/file-input.element.js +31 -16
- package/target/lib/file-input/file-input.element.js.map +1 -1
- package/target/lib/file-input/file-input.stories.js +0 -2
- package/target/lib/file-input/file-input.stories.js.map +1 -1
- package/target/lib/input/input.element.d.ts +1 -0
- package/target/lib/input/input.element.js +12 -8
- package/target/lib/input/input.element.js.map +1 -1
- package/target/lib/modal/modal-close/modal-close.element.d.ts +8 -0
- package/target/lib/modal/modal-close/modal-close.element.js +78 -0
- package/target/lib/modal/modal-close/modal-close.element.js.map +1 -0
- package/target/lib/modal/modal-close/modal-close.test.d.ts +1 -0
- package/target/lib/modal/modal-close/modal-close.test.js +11 -0
- package/target/lib/modal/modal-close/modal-close.test.js.map +1 -0
- package/target/lib/modal/modal-heading/modal-heading.element.d.ts +7 -0
- package/target/lib/modal/modal-heading/modal-heading.element.js +50 -0
- package/target/lib/modal/modal-heading/modal-heading.element.js.map +1 -0
- package/target/lib/modal/modal-heading/modal-heading.test.d.ts +1 -0
- package/target/lib/modal/modal-heading/modal-heading.test.js +11 -0
- package/target/lib/modal/modal-heading/modal-heading.test.js.map +1 -0
- package/target/lib/modal/modal.element.d.ts +13 -0
- package/target/lib/modal/modal.element.js +96 -0
- package/target/lib/modal/modal.element.js.map +1 -0
- package/target/lib/modal/modal.stories.d.ts +12 -0
- package/target/lib/modal/modal.stories.js +34 -0
- package/target/lib/modal/modal.stories.js.map +1 -0
- package/target/lib/modal/modal.test.d.ts +3 -0
- package/target/lib/modal/modal.test.js +21 -0
- package/target/lib/modal/modal.test.js.map +1 -0
- package/target/lib/radio/radio.element.js +1 -4
- package/target/lib/radio/radio.element.js.map +1 -1
- package/target/lib/select/select.element.js +1 -0
- package/target/lib/select/select.element.js.map +1 -1
- package/target/lib/step-indicator/step/step-indicator-step.element.d.ts +7 -0
- package/target/lib/step-indicator/step/step-indicator-step.element.js +122 -0
- package/target/lib/step-indicator/step/step-indicator-step.element.js.map +1 -0
- package/target/lib/step-indicator/step/step-indicator-step.test.d.ts +1 -0
- package/target/lib/step-indicator/step/step-indicator-step.test.js +11 -0
- package/target/lib/step-indicator/step/step-indicator-step.test.js.map +1 -0
- package/target/lib/step-indicator/step-indicator.element.d.ts +7 -0
- package/target/lib/step-indicator/step-indicator.element.js +36 -0
- package/target/lib/step-indicator/step-indicator.element.js.map +1 -0
- package/target/lib/step-indicator/step-indicator.stories.d.ts +21 -0
- package/target/lib/step-indicator/step-indicator.stories.js +42 -0
- package/target/lib/step-indicator/step-indicator.stories.js.map +1 -0
- package/target/lib/step-indicator/step-indicator.test.d.ts +1 -0
- package/target/lib/step-indicator/step-indicator.test.js +17 -0
- package/target/lib/step-indicator/step-indicator.test.js.map +1 -0
- package/target/lib/summary-box/summary-box.element.d.ts +7 -0
- package/target/lib/summary-box/summary-box.element.js +47 -0
- package/target/lib/summary-box/summary-box.element.js.map +1 -0
- package/target/lib/summary-box/summary-box.stories.d.ts +12 -0
- package/target/lib/summary-box/summary-box.stories.js +17 -0
- package/target/lib/summary-box/summary-box.stories.js.map +1 -0
- package/target/lib/summary-box/summary-box.test.d.ts +1 -0
- package/target/lib/summary-box/summary-box.test.js +11 -0
- package/target/lib/summary-box/summary-box.test.js.map +1 -0
- package/target/lib.d.ts +6 -0
- package/target/lib.js +6 -0
- package/target/lib.js.map +1 -1
- package/assets/css/global.css +0 -21
- package/assets/uswds.min.js +0 -1
- /package/src/lib/file-input/{file-input-preview.test.ts → file-input-preview/file-input-preview.test.ts} +0 -0
package/README.md
CHANGED
|
@@ -6,12 +6,12 @@ https://designsystem.digital.gov/
|
|
|
6
6
|
|
|
7
7
|
## Getting Started
|
|
8
8
|
|
|
9
|
+
```sh
|
|
10
|
+
npm i @noctuatech/uswds
|
|
11
|
+
```
|
|
12
|
+
|
|
9
13
|
```html
|
|
10
14
|
<style>
|
|
11
|
-
body:has(:not(:defined)) {
|
|
12
|
-
display: none;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
15
|
* {
|
|
16
16
|
font-family:
|
|
17
17
|
Source Sans Pro Web,
|
|
@@ -23,10 +23,10 @@ https://designsystem.digital.gov/
|
|
|
23
23
|
}
|
|
24
24
|
</style>
|
|
25
25
|
|
|
26
|
-
<script src="
|
|
26
|
+
<script src="/node_modules/@noctuatech/uswds/assets/uswds.min.js"></script>
|
|
27
27
|
|
|
28
28
|
<usa-config
|
|
29
|
-
spritesheet="
|
|
29
|
+
spritesheet="/node_modules/@noctuatech/uswds/assets/img/sprite.svg"
|
|
30
30
|
>
|
|
31
31
|
<usa-alert type="info">
|
|
32
32
|
<h3 slot="heading">Informative status</h3>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="299" height="283" viewBox="0 0 299 283">
|
|
3
|
+
<g fill="none" fill-rule="evenodd">
|
|
4
|
+
<polygon fill="#81AEFC" points="298.45 169.342 193.773 169.335 246.104 8.944"/>
|
|
5
|
+
<polygon fill="#4D52AF" points="233.844 0 201.49 99.116 64.451 0"/>
|
|
6
|
+
<polygon fill="#EE601D" points="4.458 183.269 89.148 122.016 141.5 282.401"/>
|
|
7
|
+
<polygon fill="#F6BD9C" points="52.346 8.624 137.027 69.889 0 169.023"/>
|
|
8
|
+
<polygon fill="#E6E6E6" points="156.817 282.475 124.476 183.354 293.859 183.343"/>
|
|
9
|
+
</g>
|
|
10
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noctuatech/uswds",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
"packages/**"
|
|
@@ -95,13 +95,14 @@
|
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
97
|
"@open-wc/testing": "^4.0.0",
|
|
98
|
-
"@rollup/plugin-node-resolve": "^
|
|
98
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
99
99
|
"@rollup/plugin-terser": "^0.4.4",
|
|
100
100
|
"@storybook/addon-essentials": "^8.4.4",
|
|
101
101
|
"@storybook/web-components": "^8.4.4",
|
|
102
102
|
"@storybook/web-components-vite": "^8.4.4",
|
|
103
103
|
"@types/mocha": "^10.0.7",
|
|
104
104
|
"@types/node": "^22.0.0",
|
|
105
|
+
"@uswds/uswds": "^3.10.0",
|
|
105
106
|
"@web/test-runner": "^0.19.0",
|
|
106
107
|
"browser-sync": "^3.0.3",
|
|
107
108
|
"husky": "^9.0.11",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attr, css, element, html, listen, query } from "@joist/element";
|
|
1
|
+
import { attr, css, element, html, listen, query, ready } from "@joist/element";
|
|
2
2
|
|
|
3
3
|
declare global {
|
|
4
4
|
interface HTMLElementTagNameMap {
|
|
@@ -18,6 +18,10 @@ export type ButtonVariant = (typeof BUTTON_VARIANTS)[number];
|
|
|
18
18
|
|
|
19
19
|
@element({
|
|
20
20
|
tagName: "usa-button",
|
|
21
|
+
shadowDomOpts: {
|
|
22
|
+
mode: "open",
|
|
23
|
+
delegatesFocus: true,
|
|
24
|
+
},
|
|
21
25
|
shadowDom: [
|
|
22
26
|
css`
|
|
23
27
|
:host {
|
|
@@ -182,9 +186,20 @@ export class USAButtonElement extends HTMLElement {
|
|
|
182
186
|
@attr()
|
|
183
187
|
accessor variant: ButtonVariant = "primary";
|
|
184
188
|
|
|
189
|
+
@attr()
|
|
190
|
+
accessor value = "";
|
|
191
|
+
|
|
192
|
+
accessor tabIndex = 0;
|
|
193
|
+
|
|
185
194
|
#internals = this.attachInternals();
|
|
186
195
|
#button = query("button");
|
|
187
196
|
|
|
197
|
+
@ready()
|
|
198
|
+
onReady() {
|
|
199
|
+
const input = this.#button();
|
|
200
|
+
input.autofocus = this.autofocus;
|
|
201
|
+
}
|
|
202
|
+
|
|
188
203
|
@listen("keydown", () => document.body)
|
|
189
204
|
onKeyDown(e: KeyboardEvent) {
|
|
190
205
|
if (this.type === "submit") {
|
package/src/lib/define.ts
CHANGED
|
@@ -4,7 +4,7 @@ import "./button/button.element.js";
|
|
|
4
4
|
import "./checkbox/checkbox.element.js";
|
|
5
5
|
import "./description/description.element.js";
|
|
6
6
|
import "./file-input/file-input.element.js";
|
|
7
|
-
import "./file-input/file-input-preview.element.js";
|
|
7
|
+
import "./file-input/file-input-preview/file-input-preview.element.js";
|
|
8
8
|
import "./icon/icon.element.js";
|
|
9
9
|
import "./input/input.element.js";
|
|
10
10
|
import "./input-mask/input-mask.element.js";
|
|
@@ -16,3 +16,9 @@ import "./select/select-option.element.js";
|
|
|
16
16
|
import "./tag/tag.element.js";
|
|
17
17
|
import "./accordion/accordion.element.js";
|
|
18
18
|
import "./side-nav/side-nav.element.js";
|
|
19
|
+
import "./summary-box/summary-box.element.js";
|
|
20
|
+
import "./step-indicator/step-indicator.element.js";
|
|
21
|
+
import "./step-indicator/step/step-indicator-step.element.js";
|
|
22
|
+
import "./modal/modal.element.js";
|
|
23
|
+
import "./modal/modal-close/modal-close.element.js";
|
|
24
|
+
import "./modal/modal-heading/modal-heading.element.js";
|
|
@@ -61,14 +61,23 @@ declare global {
|
|
|
61
61
|
margin-top: 1px;
|
|
62
62
|
}
|
|
63
63
|
`,
|
|
64
|
-
html
|
|
64
|
+
html`
|
|
65
|
+
<template>
|
|
66
|
+
<div class="preview-item">
|
|
67
|
+
<img height="40" width="40" aria-hidden="true" />
|
|
68
|
+
</div>
|
|
69
|
+
</template>
|
|
70
|
+
|
|
71
|
+
<slot class="preview-heading"></slot>
|
|
72
|
+
`,
|
|
65
73
|
],
|
|
66
74
|
})
|
|
67
75
|
export class USAFileInputPreviewElement extends HTMLElement {
|
|
68
76
|
@observe()
|
|
69
77
|
accessor files: FileList | null = null;
|
|
70
78
|
|
|
71
|
-
#items = new Map<string,
|
|
79
|
+
#items = new Map<string, Element>();
|
|
80
|
+
#template = query("template");
|
|
72
81
|
|
|
73
82
|
connectedCallback() {
|
|
74
83
|
this.onChange();
|
|
@@ -76,7 +85,9 @@ export class USAFileInputPreviewElement extends HTMLElement {
|
|
|
76
85
|
|
|
77
86
|
@effect()
|
|
78
87
|
onChange() {
|
|
79
|
-
|
|
88
|
+
const template = this.#template();
|
|
89
|
+
|
|
90
|
+
if (this.files && this.files.length) {
|
|
80
91
|
this.hidden = false;
|
|
81
92
|
|
|
82
93
|
let names = new Set<string>();
|
|
@@ -85,13 +96,15 @@ export class USAFileInputPreviewElement extends HTMLElement {
|
|
|
85
96
|
names.add(file.name);
|
|
86
97
|
|
|
87
98
|
if (!this.#items.has(file.name)) {
|
|
88
|
-
const
|
|
99
|
+
const clone = template.content.cloneNode(true);
|
|
100
|
+
const item = clone.childNodes[1] as Element;
|
|
101
|
+
|
|
89
102
|
item.id = file.name;
|
|
90
|
-
item.className = "preview-item";
|
|
91
103
|
|
|
92
|
-
const img =
|
|
104
|
+
const img = item.querySelector("img")!;
|
|
105
|
+
img.src = URL.createObjectURL(file);
|
|
93
106
|
|
|
94
|
-
item.append(
|
|
107
|
+
item.append(document.createTextNode(file.name));
|
|
95
108
|
|
|
96
109
|
this.shadowRoot!.append(item);
|
|
97
110
|
this.#items.set(file.name, item);
|
|
@@ -109,13 +122,3 @@ export class USAFileInputPreviewElement extends HTMLElement {
|
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
124
|
}
|
|
112
|
-
|
|
113
|
-
function createImagePreview(file: File) {
|
|
114
|
-
const img = new Image();
|
|
115
|
-
img.height = 40;
|
|
116
|
-
img.width = 40;
|
|
117
|
-
img.src = URL.createObjectURL(file);
|
|
118
|
-
img.ariaHidden = "true";
|
|
119
|
-
|
|
120
|
-
return img;
|
|
121
|
-
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import "./file-input-preview.element.js";
|
|
2
|
-
import "../link/link.element.js";
|
|
3
|
-
|
|
4
1
|
import { attr, css, element, html, listen, query } from "@joist/element";
|
|
5
2
|
import { effect, observe } from "@joist/observable";
|
|
6
3
|
|
|
@@ -22,6 +19,7 @@ declare global {
|
|
|
22
19
|
display: block;
|
|
23
20
|
max-width: 30rem;
|
|
24
21
|
position: relative;
|
|
22
|
+
margin-bottom: 1.5rem;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
label {
|
|
@@ -30,18 +28,23 @@ declare global {
|
|
|
30
28
|
|
|
31
29
|
input {
|
|
32
30
|
cursor: pointer;
|
|
33
|
-
height: 100%;
|
|
34
31
|
left: 0;
|
|
35
32
|
margin: 0;
|
|
36
33
|
max-width: none;
|
|
37
34
|
position: absolute;
|
|
38
35
|
text-indent: -999em;
|
|
39
|
-
top: 0;
|
|
40
36
|
width: 100%;
|
|
41
37
|
z-index: 1;
|
|
38
|
+
bottom: 0;
|
|
39
|
+
top: 0;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
42
|
+
input:focus {
|
|
43
|
+
outline: 0.25rem solid #2491ff;
|
|
44
|
+
outline-offset: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
label slot.label {
|
|
45
48
|
font-size: 1.06rem;
|
|
46
49
|
line-height: 1.3;
|
|
47
50
|
display: block;
|
|
@@ -51,30 +54,39 @@ declare global {
|
|
|
51
54
|
|
|
52
55
|
.box {
|
|
53
56
|
border: 1px dashed #adadad;
|
|
54
|
-
display:
|
|
57
|
+
display: flex;
|
|
55
58
|
font-size: 0.93rem;
|
|
56
59
|
position: relative;
|
|
57
60
|
text-align: center;
|
|
58
61
|
width: 100%;
|
|
59
62
|
max-width: 30rem;
|
|
60
|
-
|
|
63
|
+
height: 5.2rem;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.container {
|
|
69
|
+
position: relative;
|
|
61
70
|
}
|
|
62
71
|
`,
|
|
63
72
|
html`
|
|
64
73
|
<label>
|
|
65
|
-
<slot></slot>
|
|
66
|
-
|
|
67
|
-
|
|
74
|
+
<slot class="label"></slot>
|
|
75
|
+
|
|
76
|
+
<div class="container">
|
|
77
|
+
<input type="file" />
|
|
68
78
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
79
|
+
<div class="box">
|
|
80
|
+
<slot name="description">
|
|
81
|
+
Drag file here or <usa-link>choose from folder</usa-link>
|
|
82
|
+
</slot>
|
|
83
|
+
</div>
|
|
74
84
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
<usa-file-input-preview>
|
|
86
|
+
Selected file <usa-link>Change file</usa-link>
|
|
87
|
+
</usa-file-input-preview>
|
|
88
|
+
</div>
|
|
89
|
+
</label>
|
|
78
90
|
`,
|
|
79
91
|
],
|
|
80
92
|
})
|
|
@@ -125,14 +137,14 @@ export class USAFileInputElement extends HTMLElement {
|
|
|
125
137
|
|
|
126
138
|
const formData = new FormData();
|
|
127
139
|
|
|
128
|
-
if (input.files) {
|
|
140
|
+
if (input.files && input.files.length) {
|
|
129
141
|
box.style.display = "none";
|
|
130
142
|
|
|
131
143
|
for (let file of input.files) {
|
|
132
144
|
formData.append(this.name, file);
|
|
133
145
|
}
|
|
134
146
|
} else {
|
|
135
|
-
box.style.display = "
|
|
147
|
+
box.style.display = "flex";
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
this.#internals.setFormValue(formData);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attr, css, element, html, listen, query } from "@joist/element";
|
|
1
|
+
import { attr, css, element, html, listen, query, ready } from "@joist/element";
|
|
2
2
|
import { effect, observe } from "@joist/observable";
|
|
3
3
|
|
|
4
4
|
import { MaskableElement } from "../input-mask/maskable.element.js";
|
|
@@ -11,6 +11,10 @@ declare global {
|
|
|
11
11
|
|
|
12
12
|
@element({
|
|
13
13
|
tagName: "usa-input",
|
|
14
|
+
shadowDomOpts: {
|
|
15
|
+
mode: "open",
|
|
16
|
+
delegatesFocus: true,
|
|
17
|
+
},
|
|
14
18
|
shadowDom: [
|
|
15
19
|
css`
|
|
16
20
|
* {
|
|
@@ -18,13 +22,6 @@ declare global {
|
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
:host {
|
|
21
|
-
font-family:
|
|
22
|
-
Source Sans Pro Web,
|
|
23
|
-
Helvetica Neue,
|
|
24
|
-
Helvetica,
|
|
25
|
-
Roboto,
|
|
26
|
-
Arial,
|
|
27
|
-
sans-serif;
|
|
28
25
|
font-size: 1.06rem;
|
|
29
26
|
line-height: 1.3;
|
|
30
27
|
display: block;
|
|
@@ -98,6 +95,12 @@ export class USATextInputElement
|
|
|
98
95
|
input.setSelectionRange(start, end);
|
|
99
96
|
}
|
|
100
97
|
|
|
98
|
+
@ready()
|
|
99
|
+
onReady() {
|
|
100
|
+
const input = this.#input();
|
|
101
|
+
input.autofocus = this.autofocus;
|
|
102
|
+
}
|
|
103
|
+
|
|
101
104
|
@effect()
|
|
102
105
|
onChange() {
|
|
103
106
|
const input = this.#input();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { attr, css, element, html } from "@joist/element";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface HTMLElementTagNameMap {
|
|
5
|
+
"usa-modal-close": USAModalCloseElement;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@element({
|
|
10
|
+
tagName: "usa-modal-close",
|
|
11
|
+
shadowDom: [
|
|
12
|
+
css`
|
|
13
|
+
* {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:host {
|
|
18
|
+
display: block;
|
|
19
|
+
position: absolute;
|
|
20
|
+
top: 1rem;
|
|
21
|
+
right: 1rem;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
button {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
28
|
+
background: none;
|
|
29
|
+
border: none;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
height: 100%;
|
|
32
|
+
width: 100%;
|
|
33
|
+
border-radius: 100%;
|
|
34
|
+
padding: 0;
|
|
35
|
+
margin: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
button:focus {
|
|
39
|
+
outline: 0.25rem solid #2491ff;
|
|
40
|
+
outline-offset: 0;
|
|
41
|
+
}
|
|
42
|
+
`,
|
|
43
|
+
html`
|
|
44
|
+
<button aria-label="close modal">
|
|
45
|
+
<usa-icon icon="close"></usa-icon>
|
|
46
|
+
</button>
|
|
47
|
+
`,
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
export class USAModalCloseElement extends HTMLElement {
|
|
51
|
+
@attr({
|
|
52
|
+
name: "modal-action",
|
|
53
|
+
})
|
|
54
|
+
accessor modalAction = "close";
|
|
55
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "./modal-close.element.js";
|
|
2
|
+
|
|
3
|
+
import { fixture, html, assert } from "@open-wc/testing";
|
|
4
|
+
|
|
5
|
+
import { USAModalCloseElement } from "./modal-close.element.js";
|
|
6
|
+
|
|
7
|
+
describe("usa-modal-close", () => {
|
|
8
|
+
it("should be accessible", async () => {
|
|
9
|
+
const modalClose = await fixture<USAModalCloseElement>(html`
|
|
10
|
+
<usa-modal-close>Hello World</usa-modal-close>
|
|
11
|
+
`);
|
|
12
|
+
|
|
13
|
+
return assert.isAccessible(modalClose);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { css, element, html } from "@joist/element";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface HTMLElementTagNameMap {
|
|
5
|
+
"usa-modal-heading": USAModalHeadingElement;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@element({
|
|
10
|
+
tagName: "usa-modal-heading",
|
|
11
|
+
shadowDom: [
|
|
12
|
+
css`
|
|
13
|
+
:host {
|
|
14
|
+
font-family:
|
|
15
|
+
Merriweather Web,
|
|
16
|
+
Georgia,
|
|
17
|
+
Cambria,
|
|
18
|
+
Times New Roman,
|
|
19
|
+
Times,
|
|
20
|
+
serif !important;
|
|
21
|
+
|
|
22
|
+
font-size: 1.22rem;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
h2 {
|
|
26
|
+
margin-top: 0;
|
|
27
|
+
line-height: 1.4;
|
|
28
|
+
font-size: 1.45rem;
|
|
29
|
+
}
|
|
30
|
+
`,
|
|
31
|
+
html`
|
|
32
|
+
<h2>
|
|
33
|
+
<slot></slot>
|
|
34
|
+
</h2>
|
|
35
|
+
`,
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
export class USAModalHeadingElement extends HTMLElement {}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "./modal-heading.element.js";
|
|
2
|
+
|
|
3
|
+
import { fixture, html, assert } from "@open-wc/testing";
|
|
4
|
+
|
|
5
|
+
import { USAModalHeadingElement } from "./modal-heading.element.js";
|
|
6
|
+
|
|
7
|
+
describe("usa-modal-heading", () => {
|
|
8
|
+
it("should be accessible", async () => {
|
|
9
|
+
const modalHeading = await fixture<USAModalHeadingElement>(html`
|
|
10
|
+
<usa-modal-heading>Hello World</usa-modal-heading>
|
|
11
|
+
`);
|
|
12
|
+
|
|
13
|
+
return assert.isAccessible(modalHeading);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { css, element, html, listen, query } from "@joist/element";
|
|
2
|
+
|
|
3
|
+
declare global {
|
|
4
|
+
interface HTMLElementTagNameMap {
|
|
5
|
+
"usa-modal": USAModalElement;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@element({
|
|
10
|
+
tagName: "usa-modal",
|
|
11
|
+
shadowDom: [
|
|
12
|
+
css`
|
|
13
|
+
* {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:host {
|
|
18
|
+
display: contents;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
dialog {
|
|
22
|
+
border: none;
|
|
23
|
+
font-size: 1.06rem;
|
|
24
|
+
line-height: 1.5;
|
|
25
|
+
border-radius: 0.5rem;
|
|
26
|
+
background: #fff;
|
|
27
|
+
color: #1b1b1b;
|
|
28
|
+
max-width: 30rem;
|
|
29
|
+
padding-top: 4rem;
|
|
30
|
+
padding-left: 4rem;
|
|
31
|
+
padding-right: 4rem;
|
|
32
|
+
padding-bottom: 2rem;
|
|
33
|
+
width: 100%;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
::backdrop {
|
|
37
|
+
background: rgba(0, 0, 0, 0.7);
|
|
38
|
+
}
|
|
39
|
+
`,
|
|
40
|
+
html`
|
|
41
|
+
<dialog>
|
|
42
|
+
<slot></slot>
|
|
43
|
+
</dialog>
|
|
44
|
+
`,
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
export class USAModalElement extends HTMLElement {
|
|
48
|
+
modalAction: null | string = null;
|
|
49
|
+
|
|
50
|
+
#dialog = query("dialog");
|
|
51
|
+
|
|
52
|
+
openModal() {
|
|
53
|
+
const dialog = this.#dialog();
|
|
54
|
+
|
|
55
|
+
dialog.showModal();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
closeModal() {
|
|
59
|
+
const dialog = this.#dialog();
|
|
60
|
+
|
|
61
|
+
dialog.close();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@listen("click", () => document.body)
|
|
65
|
+
onGlobalModalAction(e: Event) {
|
|
66
|
+
if (e.target instanceof Element) {
|
|
67
|
+
const modalTarget = e.target.getAttribute("modal-target");
|
|
68
|
+
|
|
69
|
+
if (modalTarget === this.id) {
|
|
70
|
+
this.openModal();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@listen("click", (host) => host)
|
|
76
|
+
onModalAction(e: Event) {
|
|
77
|
+
if (e.target instanceof Element) {
|
|
78
|
+
this.modalAction = e.target.getAttribute("modal-action");
|
|
79
|
+
|
|
80
|
+
this.closeModal();
|
|
81
|
+
|
|
82
|
+
this.dispatchEvent(new Event("usa::modal::close"));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/web-components";
|
|
2
|
+
import { html } from "lit";
|
|
3
|
+
|
|
4
|
+
import type { USAModalElement } from "./modal.element.js";
|
|
5
|
+
|
|
6
|
+
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories
|
|
7
|
+
const meta = {
|
|
8
|
+
title: "usa-modal",
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
render() {
|
|
11
|
+
return html`
|
|
12
|
+
<usa-button modal-target="mymodal">Open Modal</usa-button>
|
|
13
|
+
|
|
14
|
+
<usa-modal id="mymodal">
|
|
15
|
+
<usa-modal-close></usa-modal-close>
|
|
16
|
+
|
|
17
|
+
<usa-modal-heading>
|
|
18
|
+
Are you sure you want to continue?
|
|
19
|
+
</usa-modal-heading>
|
|
20
|
+
|
|
21
|
+
<usa-input placeholder="foo@email.com" autofocus>
|
|
22
|
+
Enter your email to continue
|
|
23
|
+
</usa-input>
|
|
24
|
+
|
|
25
|
+
<usa-button modal-action="confirm">Yes I am sure</usa-button>
|
|
26
|
+
<usa-button modal-action="cancel" variant="outline">Cancel</usa-button>
|
|
27
|
+
</usa-modal>
|
|
28
|
+
`;
|
|
29
|
+
},
|
|
30
|
+
argTypes: {},
|
|
31
|
+
args: {},
|
|
32
|
+
} satisfies Meta<USAModalElement>;
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
|
|
36
|
+
type Story = StoryObj<USAModalElement>;
|
|
37
|
+
|
|
38
|
+
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
|
|
39
|
+
export const Primary: Story = {
|
|
40
|
+
args: {},
|
|
41
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import "./modal.element.js";
|
|
2
|
+
import "./modal-heading/modal-heading.element.js";
|
|
3
|
+
import "./modal-close/modal-close.element.js";
|
|
4
|
+
|
|
5
|
+
import { fixture, html, assert } from "@open-wc/testing";
|
|
6
|
+
|
|
7
|
+
import { USAModalElement } from "./modal.element.js";
|
|
8
|
+
|
|
9
|
+
describe("usa-modal", () => {
|
|
10
|
+
it("should be accessible", async () => {
|
|
11
|
+
const modal = await fixture<USAModalElement>(html`
|
|
12
|
+
<usa-modal open>
|
|
13
|
+
<usa-modal-close></usa-modal-close>
|
|
14
|
+
|
|
15
|
+
<usa-modal-heading>
|
|
16
|
+
Are you sure you want to continue?
|
|
17
|
+
</usa-modal-heading>
|
|
18
|
+
|
|
19
|
+
<p>This is some other example of content</p>
|
|
20
|
+
</usa-modal>
|
|
21
|
+
`);
|
|
22
|
+
|
|
23
|
+
return assert.isAccessible(modal);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -17,6 +17,7 @@ declare global {
|
|
|
17
17
|
flex-direction: column;
|
|
18
18
|
gap: 1rem;
|
|
19
19
|
max-width: 30rem;
|
|
20
|
+
margin-bottom: 1.5rem;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
label {
|
|
@@ -75,10 +76,6 @@ declare global {
|
|
|
75
76
|
slot {
|
|
76
77
|
display: flex;
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
-
slot#main {
|
|
80
|
-
margin-bottom: 0.5rem;
|
|
81
|
-
}
|
|
82
79
|
`,
|
|
83
80
|
html`<slot id="main"></slot>`,
|
|
84
81
|
],
|