• 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

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

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

3✔
13
import { html, TemplateResult } from '@spectrum-web-components/base';
3✔
14
import { ifDefined } from '@spectrum-web-components/base/src/directives.js';
3✔
15
import { conditionAttributeWithId } from '@spectrum-web-components/base/src/condition-attribute-with-id.js';
3✔
16
import { randomID } from '@spectrum-web-components/shared/src/random-id.js';
3✔
17
import type { HelpText } from './HelpText';
3✔
18

3✔
19
export class HelpTextManager {
3✔
20
    private conditionId?: () => void;
3✔
21
    private host!: HTMLElement;
3✔
22
    public id!: string;
3✔
23
    private mode: 'internal' | 'external' = 'internal';
3✔
24
    private previousTabindex?: -1 | 0 | undefined;
3✔
25
    private helpTextElement!: Element;
3✔
26
    private get isInternal(): boolean {
3✔
27
        return this.mode === 'internal';
3✔
28
    }
3✔
29

3✔
30
    constructor(
3✔
31
        host: HTMLElement,
21✔
32
        { mode }: { mode: 'internal' | 'external' } = { mode: 'internal' }
21✔
33
    ) {
21✔
34
        this.host = host;
21✔
35
        this.id = `sp-help-text-${randomID()}`;
21✔
36
        this.mode = mode;
21✔
37
    }
21✔
38

3✔
39
    public render(negative?: boolean): TemplateResult {
3✔
40
        // `pass-through-help-text-${this.instanceCount}` makes the slot effectively unreachable from
42✔
41
        // the outside allowing the `help-text` slot to be preferred while `negative === false`.
42✔
42
        return html`
42✔
43
            <div
42✔
44
                id=${ifDefined(this.isInternal ? this.id : undefined)}
42!
45
                aria-live="assertive"
42✔
46
            >
42✔
47
                <slot
42✔
48
                    name=${negative
42✔
49
                        ? 'negative-help-text'
7✔
50
                        : `pass-through-help-text-${randomID()}`}
42✔
51
                    @slotchange=${this.handleSlotchange}
42✔
52
                >
42✔
53
                    <slot name="help-text"></slot>
42✔
54
                </slot>
42✔
55
            </div>
42✔
56
        `;
42✔
57
    }
42✔
58

3✔
59
    private addId(): void {
3✔
60
        const id = this.helpTextElement ? this.helpTextElement.id : this.id;
×
61
        this.conditionId = conditionAttributeWithId(
×
62
            this.host,
×
63
            'aria-describedby',
×
64
            id
×
65
        );
×
66
        if (this.host.hasAttribute('tabindex')) {
×
67
            this.previousTabindex = parseFloat(
×
68
                this.host.getAttribute('tabindex') as string
×
69
            ) as -1 | 0;
×
70
        }
×
71
        this.host.tabIndex = 0;
×
72
    }
×
73

3✔
74
    private removeId(): void {
3✔
75
        if (this.conditionId) {
×
76
            this.conditionId();
×
77
            delete this.conditionId;
×
78
        }
×
79
        if (this.helpTextElement) return;
×
80
        if (this.previousTabindex) {
×
81
            this.host.tabIndex = this.previousTabindex;
×
82
        } else {
×
83
            this.host.removeAttribute('tabindex');
×
84
        }
×
85
    }
×
86

3✔
87
    private handleSlotchange = ({
3✔
88
        target,
1✔
89
    }: Event & { target: HTMLSlotElement }): void => {
1✔
90
        this.handleHelpText(target);
1✔
91
        this.handleNegativeHelpText(target);
1✔
92
    };
1✔
93

3✔
94
    private handleHelpText(target: HTMLSlotElement): void {
3✔
95
        if (this.isInternal) return;
×
96

×
97
        if (this.helpTextElement && this.helpTextElement.id === this.id) {
×
98
            this.helpTextElement.removeAttribute('id');
×
99
        }
×
100
        this.removeId();
×
101
        const assignedElements = target.assignedElements();
×
102
        const nextHelpTextElement = assignedElements[0];
×
103
        this.helpTextElement = nextHelpTextElement;
×
104
        if (nextHelpTextElement) {
×
105
            if (!nextHelpTextElement.id) {
×
106
                nextHelpTextElement.id = this.id;
×
107
            }
×
108
            this.addId();
×
109
        }
×
110
    }
×
111

3✔
112
    private handleNegativeHelpText(target: HTMLSlotElement): void {
3✔
113
        if (target.name !== 'negative-help-text') return;
×
114

×
115
        const assignedElements = target.assignedElements();
×
116
        assignedElements.forEach(
×
117
            (el) => ((el as unknown as HelpText).variant = 'negative')
×
118
        );
×
119
    }
×
120
}
3✔
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

© 2026 Coveralls, Inc