@kubex/zinc 1.1.143 → 1.1.144
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/dist/custom-elements.json +155 -74
- package/dist/vscode.html-custom-data.json +11 -11
- package/dist/web-types.json +23 -23
- package/dist/zn.d.ts +13 -0
- package/dist/zn.min.js +49 -48
- package/package.json +1 -1
- package/src/components/confirm/confirm.component.ts +83 -8
- package/src/components/confirm/confirm.test.ts +84 -0
package/package.json
CHANGED
|
@@ -9,6 +9,17 @@ import ZnDialog from "../dialog";
|
|
|
9
9
|
|
|
10
10
|
import styles from './confirm.scss';
|
|
11
11
|
|
|
12
|
+
interface PageletAlert {
|
|
13
|
+
action?: string;
|
|
14
|
+
style?: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PageletResponse {
|
|
19
|
+
status?: number;
|
|
20
|
+
actions?: PageletAlert[];
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
/**
|
|
13
24
|
* @summary Short summary of the component's intended use.
|
|
14
25
|
* @documentation https://zinc.style/components/confirm-modal
|
|
@@ -83,6 +94,12 @@ export default class ZnConfirm extends ZincElement {
|
|
|
83
94
|
/** Internal loading state used when showLoading is enabled */
|
|
84
95
|
@state() private loading: boolean = false;
|
|
85
96
|
|
|
97
|
+
@state() private failed: boolean = false;
|
|
98
|
+
|
|
99
|
+
@state() private failure: string = '';
|
|
100
|
+
|
|
101
|
+
private submitted: HTMLFormElement | null = null;
|
|
102
|
+
|
|
86
103
|
protected firstUpdated(_changedProperties: PropertyValues) {
|
|
87
104
|
super.firstUpdated(_changedProperties);
|
|
88
105
|
if (this.open) {
|
|
@@ -116,15 +133,66 @@ export default class ZnConfirm extends ZincElement {
|
|
|
116
133
|
show = (event: Event | undefined = undefined) => {
|
|
117
134
|
const trigger = event?.target as HTMLButtonElement
|
|
118
135
|
if (trigger?.disabled) return;
|
|
119
|
-
this.
|
|
136
|
+
this.reset();
|
|
120
137
|
this.dialog.show();
|
|
121
138
|
}
|
|
122
139
|
|
|
123
140
|
hide() {
|
|
124
|
-
this.
|
|
141
|
+
this.reset();
|
|
125
142
|
this.dialog.hide();
|
|
126
143
|
}
|
|
127
144
|
|
|
145
|
+
disconnectedCallback() {
|
|
146
|
+
super.disconnectedCallback();
|
|
147
|
+
this.stopWatching();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private reset() {
|
|
151
|
+
this.stopWatching();
|
|
152
|
+
this.loading = false;
|
|
153
|
+
this.failed = false;
|
|
154
|
+
this.failure = '';
|
|
155
|
+
if (this.dialog?.closer) {
|
|
156
|
+
this.dialog.closer.disabled = false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private stopWatching() {
|
|
161
|
+
this.submitted?.removeEventListener('complete', this.settle);
|
|
162
|
+
this.submitted?.removeEventListener('error', this.settle);
|
|
163
|
+
this.submitted = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The console dispatches the pagelet lifecycle on the form it submitted. Without it the loading
|
|
168
|
+
* state only ends when a response happens to reload the page, so every failure hangs the dialog.
|
|
169
|
+
*/
|
|
170
|
+
private settle = (event: Event) => {
|
|
171
|
+
const response = (event as CustomEvent<{ response?: PageletResponse }>).detail?.response;
|
|
172
|
+
this.stopWatching();
|
|
173
|
+
if (event.type === 'error' || this.responseFailed(response)) {
|
|
174
|
+
this.loading = false;
|
|
175
|
+
this.failed = true;
|
|
176
|
+
this.failure = this.alertTitle(response);
|
|
177
|
+
if (this.dialog?.closer) {
|
|
178
|
+
this.dialog.closer.disabled = false;
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
this.hide();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private responseFailed(response?: PageletResponse): boolean {
|
|
186
|
+
if (!response) return true;
|
|
187
|
+
if ((response.status ?? 0) >= 400) return true;
|
|
188
|
+
return (response.actions ?? []).some(action => action.action === 'alert' && action.style === 'error');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private alertTitle(response?: PageletResponse): string {
|
|
192
|
+
const alert = (response?.actions ?? []).find(action => action.action === 'alert' && action.style === 'error');
|
|
193
|
+
return alert?.title ?? '';
|
|
194
|
+
}
|
|
195
|
+
|
|
128
196
|
|
|
129
197
|
render() {
|
|
130
198
|
const src = {
|
|
@@ -153,10 +221,11 @@ export default class ZnConfirm extends ZincElement {
|
|
|
153
221
|
: ''}
|
|
154
222
|
|
|
155
223
|
<div class="confirm-dialog__content">
|
|
156
|
-
${
|
|
224
|
+
${this.loading ? html`Loading...` : this.failed ? html`
|
|
225
|
+
<strong>Failed</strong>
|
|
226
|
+
${this.failure ? html`<p>${this.failure}</p>` : ''}` : html`
|
|
157
227
|
${this.content ? html`${this.content}` : ''}
|
|
158
|
-
<slot></slot>`
|
|
159
|
-
Loading...`}
|
|
228
|
+
<slot></slot>`}
|
|
160
229
|
</div>
|
|
161
230
|
|
|
162
231
|
<zn-button outline color="${this.type}" slot="footer" dialog-closer disabled=${this.loading || nothing}>
|
|
@@ -196,12 +265,18 @@ export default class ZnConfirm extends ZincElement {
|
|
|
196
265
|
document.dispatchEvent(new CustomEvent('zn-register-element', {
|
|
197
266
|
detail: {element: form}
|
|
198
267
|
}))
|
|
268
|
+
if (this.showLoading) {
|
|
269
|
+
this.failed = false;
|
|
270
|
+
this.failure = '';
|
|
271
|
+
this.loading = true;
|
|
272
|
+
this.dialog.closer.disabled = true;
|
|
273
|
+
this.submitted = form;
|
|
274
|
+
form.addEventListener('complete', this.settle);
|
|
275
|
+
form.addEventListener('error', this.settle);
|
|
276
|
+
}
|
|
199
277
|
form.requestSubmit();
|
|
200
278
|
if (!this.showLoading) {
|
|
201
279
|
this.hide();
|
|
202
|
-
} else {
|
|
203
|
-
this.loading = true;
|
|
204
|
-
this.dialog.closer.disabled = true;
|
|
205
280
|
}
|
|
206
281
|
}
|
|
207
282
|
}
|
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import '../../../dist/zn.min.js';
|
|
2
2
|
import { expect, fixture, html } from '@open-wc/testing';
|
|
3
3
|
|
|
4
|
+
const click = (el: Element) => el.dispatchEvent(new MouseEvent('click', {bubbles: true, composed: true, cancelable: true}));
|
|
5
|
+
|
|
6
|
+
const settle = (form: HTMLFormElement, type: string, detail: unknown) =>
|
|
7
|
+
form.dispatchEvent(new CustomEvent(type, {detail}));
|
|
8
|
+
|
|
9
|
+
async function submittedConfirm() {
|
|
10
|
+
const el = await fixture<HTMLElement>(html`
|
|
11
|
+
<div>
|
|
12
|
+
<zn-confirm caption="Deploy" show-loading>
|
|
13
|
+
<form action="/deploy" method="post"></form>
|
|
14
|
+
</zn-confirm>
|
|
15
|
+
</div>
|
|
16
|
+
`);
|
|
17
|
+
const confirm = el.querySelector('zn-confirm')!;
|
|
18
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
19
|
+
|
|
20
|
+
const form = confirm.querySelector('form')!;
|
|
21
|
+
form.addEventListener('submit', e => e.preventDefault());
|
|
22
|
+
|
|
23
|
+
(confirm as never as {show: () => void}).show();
|
|
24
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
25
|
+
|
|
26
|
+
const dialog = confirm.shadowRoot!.querySelector('zn-dialog')!;
|
|
27
|
+
const buttons = confirm.shadowRoot!.querySelectorAll('zn-button[slot="footer"]');
|
|
28
|
+
click(buttons[1]);
|
|
29
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
30
|
+
|
|
31
|
+
return {confirm, form, dialog, cancel: buttons[0] as HTMLElement & {disabled: boolean}};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const closer = (dialog: Element) => dialog.shadowRoot!.querySelector('.dialog__close')!;
|
|
35
|
+
|
|
4
36
|
describe('<zn-confirm-modal>', () => {
|
|
5
37
|
it('should render a component', async () => {
|
|
6
38
|
const el = await fixture(html` <zn-confirm-modal></zn-confirm-modal> `);
|
|
@@ -22,4 +54,56 @@ describe('<zn-confirm-modal>', () => {
|
|
|
22
54
|
// An unescaped `#id` selector throws a SyntaxError here, leaving the component unrendered.
|
|
23
55
|
expect(confirm.shadowRoot!.querySelector('zn-dialog')).to.exist;
|
|
24
56
|
});
|
|
57
|
+
|
|
58
|
+
it('shows loading until the submitted request settles', async () => {
|
|
59
|
+
const {confirm} = await submittedConfirm();
|
|
60
|
+
|
|
61
|
+
expect(confirm.shadowRoot!.textContent).to.contain('Loading');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('reports failure when the response carries a danger alert', async () => {
|
|
65
|
+
const {confirm, form, dialog, cancel} = await submittedConfirm();
|
|
66
|
+
|
|
67
|
+
settle(form, 'complete', {
|
|
68
|
+
response: {status: 200, actions: [{action: 'alert', style: 'error', title: 'Deployment stopped'}]}
|
|
69
|
+
});
|
|
70
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
71
|
+
|
|
72
|
+
expect(confirm.shadowRoot!.textContent).to.contain('Failed');
|
|
73
|
+
expect(confirm.shadowRoot!.textContent).to.contain('Deployment stopped');
|
|
74
|
+
expect(confirm.shadowRoot!.textContent).to.not.contain('Loading');
|
|
75
|
+
expect(cancel.disabled).to.be.false;
|
|
76
|
+
expect(closer(dialog).disabled).to.be.false;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('reports failure when the request errors', async () => {
|
|
80
|
+
const {confirm, form, dialog} = await submittedConfirm();
|
|
81
|
+
|
|
82
|
+
settle(form, 'error', {});
|
|
83
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
84
|
+
|
|
85
|
+
expect(confirm.shadowRoot!.textContent).to.contain('Failed');
|
|
86
|
+
expect(closer(dialog).disabled).to.be.false;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('closes when the submitted request succeeds', async () => {
|
|
90
|
+
const {confirm, form, dialog} = await submittedConfirm();
|
|
91
|
+
|
|
92
|
+
settle(form, 'complete', {response: {status: 200, actions: [{action: 'alert', style: 'success', title: 'Deployed'}]}});
|
|
93
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
94
|
+
|
|
95
|
+
expect((dialog as HTMLElement & {open: boolean}).open).to.be.false;
|
|
96
|
+
expect(closer(dialog).disabled).to.be.false;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('clears a previous failure when reopened', async () => {
|
|
100
|
+
const {confirm, form} = await submittedConfirm();
|
|
101
|
+
settle(form, 'error', {});
|
|
102
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
103
|
+
|
|
104
|
+
(confirm as never as {show: () => void}).show();
|
|
105
|
+
await (confirm as never as {updateComplete: Promise<void>}).updateComplete;
|
|
106
|
+
|
|
107
|
+
expect(confirm.shadowRoot!.textContent).to.not.contain('Failed');
|
|
108
|
+
});
|
|
25
109
|
});
|