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

adobe / spectrum-web-components / 14135005473

28 Mar 2025 05:57PM CUT coverage: 98.009% (+0.007%) from 98.002%
14135005473

Pull #5282

github

web-flow
Merge 92b6077cf into 54e4c93de
Pull Request #5282: [test] full update for css packages (not for merging)

5324 of 5619 branches covered (94.75%)

Branch coverage included in aggregate %.

33713 of 34211 relevant lines covered (98.54%)

644.86 hits per line

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

93.24
/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 {
21โœ”
42
        return [modalWrapperStyles, modalStyles];
21โœ”
43
    }
21โœ”
44

21โœ”
45
    @property({ type: Boolean, reflect: true })
21โœ”
46
    public dismissable = false;
21โœ”
47

21โœ”
48
    @property({ type: Boolean, reflect: true })
21โœ”
49
    public open = false;
21โœ”
50

21โœ”
51
    @property({ type: String, reflect: true })
21โœ”
52
    public mode?: 'fullscreen' | 'fullscreenTakeover';
21โœ”
53

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

21โœ”
60
    private transitionPromise = Promise.resolve();
21โœ”
61

21โœ”
62
    private resolveTransitionPromise = (): void => {
21โœ”
63
        return;
ร—
64
    };
ร—
65

21โœ”
66
    @property({ type: Boolean })
21โœ”
67
    public underlay = false;
21โœ”
68

21โœ”
69
    protected get dialog(): Dialog {
21โœ”
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

21โœ”
85
    public override async focus(): Promise<void> {
21โœ”
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

21โœ”
102
    private animating = false;
21โœ”
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(
9โœ”
128
            new Event('close', {
9โœ”
129
                bubbles: true,
9โœ”
130
            })
9โœ”
131
        );
9โœ”
132
    }
9โœ”
133

14โœ”
134
    private handleTransitionEvent(event: TransitionEvent): void {
14โœ”
135
        this.dispatchEvent(
190โœ”
136
            new TransitionEvent(event.type, {
190โœ”
137
                bubbles: true,
190โœ”
138
                composed: true,
190โœ”
139
                propertyName: event.propertyName,
190โœ”
140
            })
190โœ”
141
        );
190โœ”
142
    }
190โœ”
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) {
81โœ”
153
            this.resolveTransitionPromise();
52โœ”
154
        }
52โœ”
155
        this.handleTransitionEvent(event);
81โœ”
156
    }
81โœ”
157

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

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

1โœ”
166
        const underlay = this.shadowRoot.querySelector(
1โœ”
167
            'sp-underlay'
1โœ”
168
        ) as HTMLElement;
1โœ”
169

1โœ”
170
        if (underlay) {
2!
171
            const underlayTransitionDurations =
ร—
172
                window.getComputedStyle(underlay).transitionDuration;
ร—
173
            for (const duration of underlayTransitionDurations.split(','))
ร—
174
                if (parseFloat(duration) > 0) return true;
ร—
175
        }
โœ”
176

1โœ”
177
        return false;
1โœ”
178
    }
12โœ”
179

14โœ”
180
    protected override update(changes: PropertyValues<this>): void {
14โœ”
181
        if (changes.has('open') && changes.get('open') !== undefined) {
33โœ”
182
            const hasTransitionDuration = this.hasTransitionDuration;
12โœ”
183
            this.animating = true;
12โœ”
184
            this.transitionPromise = new Promise((res) => {
12โœ”
185
                this.resolveTransitionPromise = () => {
12โœ”
186
                    this.animating = false;
57โœ”
187
                    if (!this.open && hasTransitionDuration)
57โœ”
188
                        this.dispatchClosed();
57โœ”
189
                    res();
57โœ”
190
                };
57โœ”
191
            });
12โœ”
192
            if (!this.open && !hasTransitionDuration) this.dispatchClosed();
12โœ”
193
        }
12โœ”
194
        super.update(changes);
33โœ”
195
    }
33โœ”
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`
33โœ”
205
            ${this.underlay
33โœ”
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>
18โœ”
214
                  `
18โœ”
215
                : nothing}
33โœ”
216
            <div
33โœ”
217
                class="modal ${this.mode}"
33โœ”
218
                @transitionrun=${this.handleTransitionEvent}
33โœ”
219
                @transitionend=${this.handleModalTransitionend}
33โœ”
220
                @transitioncancel=${this.handleTransitionEvent}
33โœ”
221
                @close=${this.handleClose}
33โœ”
222
            >
33โœ”
223
                ${this.renderDialog()}
33โœ”
224
            </div>
33โœ”
225
        `;
33โœ”
226
    }
33โœ”
227

14โœ”
228
    protected override updated(changes: PropertyValues<this>): void {
14โœ”
229
        if (changes.has('open')) {
33โœ”
230
            if (this.open) {
33โœ”
231
                if (
17โœ”
232
                    'updateComplete' in this.dialog &&
17โœ”
233
                    'shouldManageTabOrderForScrolling' in this.dialog
17โœ”
234
                ) {
17โœ”
235
                    this.dialog.updateComplete.then(() => {
17โœ”
236
                        this.dialog.shouldManageTabOrderForScrolling();
17โœ”
237
                    });
17โœ”
238
                }
17โœ”
239
            }
17โœ”
240
        }
33โœ”
241
    }
33โœ”
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;
23โœ”
253
        await this.transitionPromise;
23โœ”
254
        return complete;
23โœ”
255
    }
23โœ”
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