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

adobe / spectrum-web-components / 14094674923

26 Mar 2025 10:27PM UTC coverage: 86.218% (-11.8%) from 98.002%
14094674923

Pull #5221

github

web-flow
Merge 2a1ea92e7 into 3184c1e6a
Pull Request #5221: RFC | leverage css module imports in components

1737 of 2032 branches covered (85.48%)

Branch coverage included in aggregate %.

14184 of 16434 relevant lines covered (86.31%)

85.29 hits per line

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

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

1✔
7
Unless required by applicable law or agreed to in writing, software distributed under
1✔
8
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
1✔
9
OF ANY KIND, either express or implied. See the License for the specific language
1✔
10
governing permissions and limitations under the License.
1✔
11
*/
1✔
12
import {
1✔
13
    CSSResultArray,
1✔
14
    html,
1✔
15
    SpectrumElement,
1✔
16
    TemplateResult,
1✔
17
} from '@spectrum-web-components/base';
1✔
18
import {
1✔
19
    property,
1✔
20
    query,
1✔
21
} from '@spectrum-web-components/base/src/decorators.js';
1✔
22
import '@spectrum-web-components/button/sp-button.js';
1✔
23
import { FocusVisiblePolyfillMixin } from '@spectrum-web-components/shared/src/focus-visible.js';
1✔
24
import { randomID } from '@spectrum-web-components/shared/src/random-id.js';
1✔
25
import { conditionAttributeWithId } from '@spectrum-web-components/base/src/condition-attribute-with-id.js';
1✔
26
import { ResizeController } from '@lit-labs/observers/resize-controller.js';
1✔
27
import alertStyles from './alert-dialog.css.js';
1✔
28

1✔
29
export type AlertDialogVariants =
1✔
30
    | 'confirmation'
1✔
31
    | 'information'
1✔
32
    | 'warning'
1✔
33
    | 'error'
1✔
34
    | 'destructive'
1✔
35
    | 'secondary'
1✔
36
    | '';
1✔
37

1✔
38
export const alertDialogVariants: AlertDialogVariants[] = [
1✔
39
    'confirmation',
1✔
40
    'information',
1✔
41
    'warning',
1✔
42
    'error',
1✔
43
    'destructive',
1✔
44
    'secondary',
1✔
45
];
1✔
46

1✔
47
function gatherAppliedIdsFromSlottedChildren(
10✔
48
    slot: HTMLSlotElement,
10✔
49
    idBase: string
10✔
50
): string[] {
10✔
51
    const assignedElements = slot.assignedElements();
10✔
52
    const ids: string[] = [];
10✔
53
    assignedElements.forEach((el) => {
10✔
54
        if (el.id) {
7✔
55
            ids.push(el.id);
1✔
56
        } else {
7✔
57
            const id = idBase + `-${randomID()}`;
6✔
58
            el.id = id;
6✔
59
            ids.push(id);
6✔
60
        }
6✔
61
    });
10✔
62
    return ids;
10✔
63
}
10✔
64
export class AlertDialog extends FocusVisiblePolyfillMixin(SpectrumElement) {
1✔
65
    public static override get styles(): CSSResultArray {
5✔
66
        return [alertStyles];
5✔
67
    }
5✔
68

5✔
69
    @query('.content')
5✔
70
    private contentElement!: HTMLDivElement;
5✔
71

5✔
72
    private resizeController = new ResizeController(this, {
5✔
73
        callback: () => {
5✔
74
            this.shouldManageTabOrderForScrolling();
17✔
75
        },
17✔
76
    });
5✔
77

5✔
78
    public _variant: AlertDialogVariants = '';
5✔
79

5✔
80
    @property({ type: String, reflect: true })
5✔
81
    public set variant(variant: AlertDialogVariants) {
5✔
82
        if (variant === this.variant) {
7✔
83
            return;
1✔
84
        }
1✔
85
        const oldValue = this.variant;
6✔
86
        if (alertDialogVariants.includes(variant)) {
7✔
87
            this.setAttribute('variant', variant);
5✔
88
            this._variant = variant;
5✔
89
        } else {
7✔
90
            this.removeAttribute('variant');
1✔
91
            this._variant = '';
1✔
92
        }
1✔
93
        this.requestUpdate('variant', oldValue);
6✔
94
    }
7✔
95

5✔
96
    public get variant(): AlertDialogVariants {
5✔
97
        return this._variant;
61✔
98
    }
61✔
99

5✔
100
    protected renderIcon(): TemplateResult {
5✔
101
        switch (this.variant) {
18✔
102
            case 'warning':
18✔
103
            case 'error':
18✔
104
                return html`
3✔
105
                    <sp-icon-alert class="icon"></sp-icon-alert>
18✔
106
                `;
18✔
107

18✔
108
            default:
18✔
109
                return html``;
15✔
110
        }
18✔
111
    }
18✔
112

5✔
113
    protected renderHeading(): TemplateResult {
5✔
114
        return html`
18✔
115
            <slot name="heading" @slotchange=${this.onHeadingSlotchange}></slot>
18✔
116
        `;
18✔
117
    }
18✔
118

5✔
119
    protected renderContent(): TemplateResult {
5✔
120
        return html`
18✔
121
            <div class="content">
18✔
122
                <slot @slotchange=${this.onContentSlotChange}></slot>
18✔
123
            </div>
18✔
124
        `;
18✔
125
    }
18✔
126

5✔
127
    static instanceCount = 0;
5✔
128
    private labelledbyId = `sp-dialog-label-${AlertDialog.instanceCount++}`;
5✔
129
    private conditionLabelledby?: () => void;
5✔
130
    private conditionDescribedby?: () => void;
5✔
131

5✔
132
    private onHeadingSlotchange({
5✔
133
        target,
4✔
134
    }: Event & { target: HTMLSlotElement }): void {
4✔
135
        if (this.conditionLabelledby) {
4!
136
            this.conditionLabelledby();
×
137
            delete this.conditionLabelledby;
×
138
        }
×
139
        const ids = gatherAppliedIdsFromSlottedChildren(
4✔
140
            target,
4✔
141
            this.labelledbyId
4✔
142
        );
4✔
143
        if (ids.length) {
4✔
144
            this.conditionLabelledby = conditionAttributeWithId(
4✔
145
                this,
4✔
146
                'aria-labelledby',
4✔
147
                ids
4✔
148
            );
4✔
149
        }
4✔
150
    }
4✔
151

5✔
152
    public shouldManageTabOrderForScrolling = (): void => {
5✔
153
        if (!this.contentElement) return;
17!
154

17✔
155
        const { offsetHeight, scrollHeight } = this.contentElement;
17✔
156
        if (offsetHeight < scrollHeight) {
17!
157
            this.contentElement.tabIndex = 0;
×
158
        } else {
17✔
159
            this.contentElement.removeAttribute('tabindex');
17✔
160
        }
17✔
161
    };
17✔
162

5✔
163
    private describedbyId = `sp-dialog-description-${AlertDialog.instanceCount++}`;
5✔
164

1✔
165
    protected onContentSlotChange({
1✔
166
        target,
6✔
167
    }: Event & { target: HTMLSlotElement }): void {
6✔
168
        requestAnimationFrame(() => {
6✔
169
            // Can happen more than once. Take this.contentElement out
6✔
170
            // of the observer before adding it again.
6✔
171
            this.resizeController.unobserve(this.contentElement);
6✔
172
            this.resizeController.observe(this.contentElement);
6✔
173
        });
6✔
174
        if (this.conditionDescribedby) {
6✔
175
            this.conditionDescribedby();
1✔
176
            delete this.conditionDescribedby;
1✔
177
        }
1✔
178
        const ids = gatherAppliedIdsFromSlottedChildren(
6✔
179
            target,
6✔
180
            this.describedbyId
6✔
181
        );
6✔
182
        if (ids.length && ids.length < 4) {
6✔
183
            this.conditionDescribedby = conditionAttributeWithId(
2✔
184
                this,
2✔
185
                'aria-describedby',
2✔
186
                ids
2✔
187
            );
2✔
188
        } else if (!ids.length) {
6✔
189
            const idProvided = !!this.id;
4✔
190
            if (!idProvided) this.id = this.describedbyId;
4✔
191
            const conditionDescribedby = conditionAttributeWithId(
4✔
192
                this,
4✔
193
                'aria-describedby',
4✔
194
                this.id
4✔
195
            );
4✔
196
            this.conditionDescribedby = () => {
4✔
197
                conditionDescribedby();
×
198
                if (!idProvided) {
×
199
                    this.removeAttribute('id');
×
200
                }
×
201
            };
×
202
        }
4✔
203
    }
6✔
204

1✔
205
    protected renderButtons(): TemplateResult {
1✔
206
        return html`
18✔
207
            <sp-button-group class="button-group">
18✔
208
                <slot name="button"></slot>
18✔
209
            </sp-button-group>
18✔
210
        `;
18✔
211
    }
18✔
212

1✔
213
    protected override render(): TemplateResult {
1✔
214
        return html`
18✔
215
            <div class="grid">
18✔
216
                <div class="header">
18✔
217
                    ${this.renderHeading()} ${this.renderIcon()}
18✔
218
                </div>
18✔
219
                <sp-divider size="m" class="divider"></sp-divider>
18✔
220
                ${this.renderContent()} ${this.renderButtons()}
18✔
221
            </div>
18✔
222
        `;
18✔
223
    }
18✔
224
}
1✔
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