• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

adobe / spectrum-web-components / 12802187570

16 Jan 2025 04:36AM CUT coverage: 98.206% (-0.003%) from 98.209%
12802187570

Pull #5019

github

web-flow
Merge 012b0d7d4 into cee6892e5
Pull Request #5019: chore: fix update-spectrum-css script

5151 of 5424 branches covered (94.97%)

Branch coverage included in aggregate %.

33006 of 33430 relevant lines covered (98.73%)

380.51 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.33
/packages/dialog/src/DialogBase.ts
1
/*
14✔
2
Copyright 2020 Adobe. All rights reserved.
14✔
3
This file is licensed to you under the Apache License, Version 2.0 (the "License");
14✔
4
you may not use this file except in compliance with the License. You may obtain a copy
14✔
5
of the License at http://www.apache.org/licenses/LICENSE-2.0
14✔
6

14✔
7
Unless required by applicable law or agreed to in writing, software distributed under
14✔
8
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
14✔
9
OF ANY KIND, either express or implied. See the License for the specific language
14✔
10
governing permissions and limitations under the License.
14✔
11
*/
14✔
12

14✔
13
import {
14✔
14
    CSSResultArray,
14✔
15
    html,
14✔
16
    nothing,
14✔
17
    PropertyValues,
14✔
18
    SpectrumElement,
14✔
19
    TemplateResult,
14✔
20
} from '@spectrum-web-components/base';
14✔
21
import { property } from '@spectrum-web-components/base/src/decorators.js';
14✔
22

14✔
23
import '@spectrum-web-components/underlay/sp-underlay.js';
14✔
24
import '@spectrum-web-components/button/sp-button.js';
14✔
25

14✔
26
// Leveraged in build systems that use aliasing to prevent multiple registrations: https://github.com/adobe/spectrum-web-components/pull/3225
14✔
27
import '@spectrum-web-components/dialog/sp-dialog.js';
14✔
28
import modalWrapperStyles from '@spectrum-web-components/modal/src/modal-wrapper.css.js';
14✔
29
import modalStyles from '@spectrum-web-components/modal/src/modal.css.js';
14✔
30
import { Dialog } from './Dialog.js';
14✔
31
import { FocusVisiblePolyfillMixin } from '@spectrum-web-components/shared';
14✔
32
import { firstFocusableIn } from '@spectrum-web-components/shared/src/first-focusable-in.js';
14✔
33

14✔
34
/**
14✔
35
 * @element sp-dialog-base
14✔
36
 *
14✔
37
 * @slot - A Dialog element to display.
14✔
38
 * @fires close - Announces that the dialog has been closed.
14✔
39
 */
14✔
40
export class DialogBase extends FocusVisiblePolyfillMixin(SpectrumElement) {
14✔
41
    public static override get styles(): CSSResultArray {
19✔
42
        return [modalWrapperStyles, modalStyles];
19✔
43
    }
19✔
44

19✔
45
    @property({ type: Boolean, reflect: true })
19✔
46
    public dismissable = false;
19✔
47

19✔
48
    @property({ type: Boolean, reflect: true })
19✔
49
    public open = false;
19✔
50

19✔
51
    @property({ type: String, reflect: true })
19✔
52
    public mode?: 'fullscreen' | 'fullscreenTakeover';
19✔
53

19✔
54
    /**
19✔
55
     * When set to true, fills screens smaller than 350px high and 400px wide with the full dialog.
19✔
56
     */
19✔
57
    @property({ type: Boolean })
19✔
58
    public responsive = false;
19✔
59

19✔
60
    private transitionPromise = Promise.resolve();
19✔
61

19✔
62
    private resolveTransitionPromise = (): void => {
19✔
63
        return;
×
64
    };
×
65

19✔
66
    @property({ type: Boolean })
19✔
67
    public underlay = false;
19✔
68

19✔
69
    protected get dialog(): Dialog {
19✔
70
        const dialog = (
8✔
71
            this.shadowRoot.querySelector('slot') as HTMLSlotElement
8✔
72
        ).assignedElements()[0] as Dialog;
8✔
73
        if (window.__swc.DEBUG) {
8✔
74
            if (!dialog) {
8✔
75
                window.__swc.warn(
×
76
                    this,
×
77
                    `<${this.localName}> expects to be provided dialog content via its default slot.`,
×
78
                    'https://opensource.adobe.com/spectrum-web-components/components/dialog-base/#dialog'
×
79
                );
×
80
            }
×
81
        }
8✔
82
        return dialog || this;
8✔
83
    }
8✔
84

19✔
85
    public override async focus(): Promise<void> {
19✔
86
        if (this.shadowRoot) {
1✔
87
            const firstFocusable = firstFocusableIn(this.dialog);
1✔
88
            if (firstFocusable) {
1✔
89
                if ((firstFocusable as SpectrumElement).updateComplete) {
1✔
90
                    await firstFocusable.updateComplete;
1✔
91
                }
1✔
92
                firstFocusable.focus();
1✔
93
            } else {
1✔
94
                this.dialog.focus();
×
95
            }
×
96
            /* c8 ignore next 3 */
14✔
97
        } else {
14✔
98
            super.focus();
14✔
99
        }
14✔
100
    }
1✔
101

19✔
102
    private animating = false;
19✔
103

14✔
104
    public overlayWillCloseCallback(): boolean {
14✔
105
        if (!this.open) return this.animating;
×
106
        this.close();
×
107
        return true;
×
108
    }
×
109

14✔
110
    private dismiss(): void {
14✔
111
        if (!this.dismissable) {
2✔
112
            return;
1✔
113
        }
1✔
114
        this.close();
1✔
115
    }
2✔
116

14✔
117
    protected handleClose(event: Event): void {
14✔
118
        event.stopPropagation();
1✔
119
        this.close();
1✔
120
    }
1✔
121

14✔
122
    public close(): void {
14✔
123
        this.open = false;
2✔
124
    }
2✔
125

14✔
126
    private dispatchClosed(): void {
14✔
127
        this.dispatchEvent(
10✔
128
            new Event('close', {
10✔
129
                bubbles: true,
10✔
130
            })
10✔
131
        );
10✔
132
    }
10✔
133

14✔
134
    private handleTransitionEvent(event: TransitionEvent): void {
14✔
135
        this.dispatchEvent(
194✔
136
            new TransitionEvent(event.type, {
194✔
137
                bubbles: true,
194✔
138
                composed: true,
194✔
139
                propertyName: event.propertyName,
194✔
140
            })
194✔
141
        );
194✔
142
    }
194✔
143

14✔
144
    protected handleUnderlayTransitionend(event: TransitionEvent): void {
14✔
145
        if (!this.open && event.propertyName === 'visibility') {
14✔
146
            this.resolveTransitionPromise();
5✔
147
        }
5✔
148
        this.handleTransitionEvent(event);
14✔
149
    }
14✔
150

14✔
151
    protected handleModalTransitionend(event: TransitionEvent): void {
14✔
152
        if (this.open || !this.underlay) {
83✔
153
            this.resolveTransitionPromise();
54✔
154
        }
54✔
155
        this.handleTransitionEvent(event);
83✔
156
    }
83✔
157

14✔
158
    private get hasTransitionDuration(): boolean {
14✔
159
        const modal = this.shadowRoot.querySelector('.modal') as HTMLElement;
14✔
160

14✔
161
        const modalTransitionDurations =
14✔
162
            window.getComputedStyle(modal).transitionDuration;
14✔
163
        for (const duration of modalTransitionDurations.split(','))
14✔
164
            if (parseFloat(duration) > 0) return true;
14✔
165

2✔
166
        const underlay = this.shadowRoot.querySelector(
2✔
167
            'sp-underlay'
2✔
168
        ) as HTMLElement;
2✔
169

2✔
170
        if (underlay) {
10!
171
            const underlayTransitionDurations =
×
172
                window.getComputedStyle(underlay).transitionDuration;
×
173
            for (const duration of underlayTransitionDurations.split(','))
×
174
                if (parseFloat(duration) > 0) return true;
×
175
        }
✔
176

2✔
177
        return false;
2✔
178
    }
14✔
179

14✔
180
    protected override update(changes: PropertyValues<this>): void {
14✔
181
        if (changes.has('open') && changes.get('open') !== undefined) {
34✔
182
            const hasTransitionDuration = this.hasTransitionDuration;
14✔
183
            this.animating = true;
14✔
184
            this.transitionPromise = new Promise((res) => {
14✔
185
                this.resolveTransitionPromise = () => {
14✔
186
                    this.animating = false;
59✔
187
                    if (!this.open && hasTransitionDuration)
59✔
188
                        this.dispatchClosed();
59✔
189
                    res();
59✔
190
                };
59✔
191
            });
14✔
192
            if (!this.open && !hasTransitionDuration) this.dispatchClosed();
14✔
193
        }
14✔
194
        super.update(changes);
34✔
195
    }
34✔
196

14✔
197
    protected renderDialog(): TemplateResult {
14✔
198
        return html`
6✔
199
            <slot></slot>
6✔
200
        `;
6✔
201
    }
6✔
202

14✔
203
    protected override render(): TemplateResult {
14✔
204
        return html`
34✔
205
            ${this.underlay
34✔
206
                ? html`
15✔
207
                      <sp-underlay
15✔
208
                          ?open=${this.open}
15✔
209
                          @close=${this.dismiss}
15✔
210
                          @transitionrun=${this.handleTransitionEvent}
15✔
211
                          @transitionend=${this.handleUnderlayTransitionend}
15✔
212
                          @transitioncancel=${this.handleTransitionEvent}
15✔
213
                      ></sp-underlay>
19✔
214
                  `
19✔
215
                : nothing}
34✔
216
            <div
34✔
217
                class="modal ${this.mode}"
34✔
218
                @transitionrun=${this.handleTransitionEvent}
34✔
219
                @transitionend=${this.handleModalTransitionend}
34✔
220
                @transitioncancel=${this.handleTransitionEvent}
34✔
221
                @close=${this.handleClose}
34✔
222
            >
34✔
223
                ${this.renderDialog()}
34✔
224
            </div>
34✔
225
        `;
34✔
226
    }
34✔
227

14✔
228
    protected override updated(changes: PropertyValues<this>): void {
14✔
229
        if (changes.has('open')) {
34✔
230
            if (this.open) {
33✔
231
                if (
18✔
232
                    'updateComplete' in this.dialog &&
18✔
233
                    'shouldManageTabOrderForScrolling' in this.dialog
18✔
234
                ) {
18✔
235
                    this.dialog.updateComplete.then(() => {
18✔
236
                        this.dialog.shouldManageTabOrderForScrolling();
18✔
237
                    });
18✔
238
                }
18✔
239
            }
18✔
240
        }
33✔
241
    }
34✔
242

14✔
243
    /**
14✔
244
     * Bind the open/close transition into the update complete lifecycle so
14✔
245
     * that the overlay system can wait for it to be "visibly ready" before
14✔
246
     * attempting to throw focus into the content contained herein. Not
14✔
247
     * waiting for this can cause small amounts of page scroll to happen
14✔
248
     * while opening the Tray when focusable content is included: e.g. Menu
14✔
249
     * elements whose selected Menu Item is not the first Menu Item.
14✔
250
     */
14✔
251
    protected override async getUpdateComplete(): Promise<boolean> {
14✔
252
        const complete = (await super.getUpdateComplete()) as boolean;
25✔
253
        await this.transitionPromise;
25✔
254
        return complete;
25✔
255
    }
25✔
256
}
14✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc