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

adobe / spectrum-web-components / 14135130673

28 Mar 2025 06:05PM UTC coverage: 97.999% (-0.003%) from 98.002%
14135130673

Pull #5235

github

web-flow
Merge a7b657067 into 54e4c93de
Pull Request #5235: chore: update dependency @spectrum-css/meter to v1.1.0 and @spectrum-css/progressbar to v6.1.0

5322 of 5619 branches covered (94.71%)

Branch coverage included in aggregate %.

33711 of 34211 relevant lines covered (98.54%)

648.53 hits per line

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

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

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

4✔
13
import {
4✔
14
    html,
4✔
15
    PropertyValues,
4✔
16
    TemplateResult,
4✔
17
} from '@spectrum-web-components/base';
4✔
18
import { query } from '@spectrum-web-components/base/src/decorators.js';
4✔
19

4✔
20
import { Iconset } from './iconset.js';
4✔
21

4✔
22
export abstract class IconsetSVG extends Iconset {
4✔
23
    private iconMap: Map<string, SVGSymbolElement> = new Map();
12✔
24

4✔
25
    @query('slot')
4✔
26
    private slotContainer?: HTMLSlotElement;
4✔
27

4✔
28
    /**
4✔
29
     * First updated handler just ensures we've processed any slotted symbols
4✔
30
     */
4✔
31
    public override updated(changedProperties: PropertyValues): void {
4✔
32
        if (!this.slotContainer) {
12!
33
            return;
×
34
        }
×
35
        const currentSVGNodes = this.getSVGNodes(this.slotContainer);
12✔
36
        this.updateSVG(currentSVGNodes);
12✔
37
        super.updated(changedProperties);
12✔
38
    }
12✔
39
    /**
4✔
40
     * Applies the requested icon from this iconset instance to the given element.
4✔
41
     *
4✔
42
     * @param el - the element to apply the icon to
4✔
43
     * @param icon - the name of the icon within this set to apply.
4✔
44
     */
4✔
45
    public async applyIconToElement(
4✔
46
        el: HTMLElement,
13✔
47
        icon: string,
13✔
48
        _size: string,
13✔
49
        label: string
13✔
50
    ): Promise<void> {
13✔
51
        await this.updateComplete;
13✔
52
        const iconSymbol = this.iconMap.get(icon);
13✔
53
        if (!iconSymbol) {
13✔
54
            throw new Error(`Unable to find icon ${icon}`);
1✔
55
        }
1✔
56
        // we cannot share a single SVG globally across shadowroot boundaries
12✔
57
        // so copy the template node so we can inject it where we need it
12✔
58
        const clonedNode = this.prepareSvgClone(iconSymbol);
12✔
59
        clonedNode.setAttribute('role', 'img');
12✔
60
        if (label) {
13✔
61
            clonedNode.setAttribute('aria-label', label);
3✔
62
        } else {
13✔
63
            clonedNode.setAttribute('aria-hidden', 'true');
9✔
64
        }
9✔
65
        // append the svg to the node either in its shadowroot or directly into its dom
12✔
66
        if (el.shadowRoot) {
13!
67
            el.shadowRoot.appendChild(clonedNode);
×
68
        } else {
13✔
69
            el.appendChild(clonedNode);
12✔
70
        }
12✔
71
    }
13✔
72

4✔
73
    /**
4✔
74
     * Returns a list of all icons in this iconset.
4✔
75
     */
4✔
76
    public getIconList(): string[] {
4✔
77
        return [...this.iconMap.keys()];
3✔
78
    }
3✔
79

4✔
80
    protected prepareSvgClone(sourceSvg: SVGSymbolElement): SVGSVGElement {
4✔
81
        const content = sourceSvg.cloneNode(true) as SVGSymbolElement;
12✔
82
        // we're going to create a new svg element that will have our symbol geometry inside
12✔
83
        const svg = document.createElementNS(
12✔
84
            'http://www.w3.org/2000/svg',
12✔
85
            'svg'
12✔
86
        );
12✔
87
        const viewBox = content.getAttribute('viewBox') || '';
12!
88
        // inline style isn't ideal but will work in all cases and means our icons don't need to know
12✔
89
        // if they are svg or spritesheet provided
12✔
90
        const cssText =
12✔
91
            'pointer-events: none; display: block; width: 100%; height: 100%;';
12✔
92
        svg.style.cssText = cssText;
12✔
93
        // copy the viewbox and other properties into the svg
12✔
94
        svg.setAttribute('viewBox', viewBox);
12✔
95
        svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
12✔
96
        svg.setAttribute('focusable', 'false');
12✔
97
        // move all the child nodes over to the svg
12✔
98
        while (content.childNodes.length > 0) {
12✔
99
            svg.appendChild(content.childNodes[0]);
12✔
100
        }
12✔
101
        return svg;
12✔
102
    }
12✔
103
    protected getSVGIconName(icon: string): string {
4✔
104
        return icon;
×
105
    }
×
106
    protected getSanitizedIconName(icon: string): string {
4✔
107
        return icon;
×
108
    }
×
109
    protected renderDefaultContent(): TemplateResult {
4✔
110
        return html``;
×
111
    }
×
112

4✔
113
    protected override render(): TemplateResult {
4✔
114
        return html`
12✔
115
            <slot @slotchange=${this.onSlotChange}>
12✔
116
                ${this.renderDefaultContent()}
12✔
117
            </slot>
12✔
118
        `;
12✔
119
    }
12✔
120

4✔
121
    protected updateSVG(nodes: SVGElement[]): void {
4✔
122
        // iterate over the nodes that were passed in, and find all the top level symbols
15✔
123
        const symbols = nodes.reduce((prev, svgNode) => {
15✔
124
            const containedSymbols = svgNode.querySelectorAll('symbol');
15✔
125
            prev.push(...containedSymbols);
15✔
126
            return prev;
15✔
127
        }, [] as SVGSymbolElement[]);
15✔
128
        symbols.forEach((symbol) => {
15✔
129
            this.iconMap.set(this.getSanitizedIconName(symbol.id), symbol);
720✔
130
        });
15✔
131
    }
15✔
132

4✔
133
    protected getSVGNodes(slotTarget: HTMLSlotElement): SVGElement[] {
4✔
134
        const nodes = slotTarget.assignedNodes({ flatten: true });
15✔
135
        // find all the svg nodes
15✔
136
        const svgNodes = nodes.filter((node) => {
15✔
137
            return node.nodeName === 'svg';
33✔
138
        }) as SVGElement[];
15✔
139
        return svgNodes;
15✔
140
    }
15✔
141

4✔
142
    private onSlotChange(event: Event): void {
4✔
143
        const slotTarget = event.target as HTMLSlotElement;
3✔
144
        const svgNodes = this.getSVGNodes(slotTarget);
3✔
145
        this.updateSVG(svgNodes);
3✔
146
    }
3✔
147
}
4✔
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