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

adobe / spectrum-web-components / 14094674923

26 Mar 2025 10:27PM CUT 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

33.04
/packages/slider/src/SliderHandle.ts
1
/*
1✔
2
Copyright 2021 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

1✔
13
import { PropertyValues } from '@spectrum-web-components/base';
1✔
14
import { property } from '@spectrum-web-components/base/src/decorators.js';
1✔
15
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js';
1✔
16
import {
1✔
17
    LanguageResolutionController,
1✔
18
    languageResolverUpdatedSymbol,
1✔
19
} from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js';
1✔
20
import {
1✔
21
    NumberFormatOptions,
1✔
22
    NumberFormatter,
1✔
23
} from '@internationalized/number';
1✔
24
import { HandleController } from './HandleController.js';
1✔
25

1✔
26
export type HandleMin = number | 'previous';
1✔
27
export type HandleMax = number | 'next';
1✔
28

1✔
29
export type HandleValues = {
1✔
30
    name: string;
1✔
31
    value: number;
1✔
32
}[];
1✔
33

1✔
34
export type SliderNormalization = {
1✔
35
    toNormalized: (value: number, min: number, max: number) => number;
1✔
36
    fromNormalized: (value: number, min: number, max: number) => number;
1✔
37
};
1✔
38

1✔
39
export const defaultNormalization: SliderNormalization = {
1✔
40
    toNormalized(value: number, min: number, max: number) {
1✔
41
        return (value - min) / (max - min);
×
42
    },
×
43
    fromNormalized(value: number, min: number, max: number) {
1✔
44
        return value * (max - min) + min;
×
45
    },
×
46
};
1✔
47

1✔
48
const MinConverter = {
1✔
49
    fromAttribute: (value: string): number | 'previous' => {
1✔
50
        if (value === 'previous') return value;
×
51
        return parseFloat(value);
×
52
    },
×
53
    toAttribute: (value: 'previous' | number): string => {
1✔
54
        return value.toString();
×
55
    },
×
56
};
1✔
57

1✔
58
const MaxConverter = {
1✔
59
    fromAttribute: (value: string): number | 'next' => {
1✔
60
        if (value === 'next') return value;
×
61
        return parseFloat(value);
×
62
    },
×
63
    toAttribute: (value: 'next' | number): string => {
1✔
64
        return value.toString();
×
65
    },
×
66
};
1✔
67

1✔
68
/**
1✔
69
 * @element sp-slider-handle
1✔
70
 *
1✔
71
 * @fires input - The value of the element has changed.
1✔
72
 * @fires change - An alteration to the value of the element has been committed by the user.
1✔
73
 */
1✔
74
export class SliderHandle extends Focusable {
1✔
75
    public handleController?: HandleController;
×
76

×
77
    public get handleName(): string {
×
78
        return this.name;
×
79
    }
×
80

×
81
    public override get focusElement(): HTMLElement {
×
82
        /* c8 ignore next */
1✔
83
        return this.handleController?.inputForHandle(this) ?? this;
1✔
84
    }
×
85

×
86
    _forcedUnit = '';
×
87

×
88
    /**
×
89
     * By default, the value of a Slider Handle will be halfway between its
×
90
     * `min` and `max` values, or the `min` value when `max` is less than `min`.
×
91
     */
×
92
    @property({ type: Number })
×
93
    public value!: number;
×
94

×
95
    /**
×
96
     * Set the default value of the handle. Setting this property will cause the
×
97
     * handle to reset to the default value on double click or pressing the `escape` key.
×
98
     */
×
99
    @property({ type: Number, attribute: 'default-value' })
×
100
    public defaultValue!: number;
×
101

×
102
    @property({ type: Boolean, reflect: true })
×
103
    public dragging = false;
×
104

×
105
    @property({ type: Boolean })
×
106
    public highlight = false;
×
107

×
108
    @property({ type: String })
×
109
    public name = '';
×
110

×
111
    @property({ reflect: true, converter: MinConverter })
×
112
    public min?: number | 'previous';
×
113

×
114
    @property({ reflect: true, converter: MaxConverter })
×
115
    public max?: number | 'next';
×
116

×
117
    @property({ type: Number, reflect: true })
×
118
    public step?: number;
×
119

×
120
    @property({ type: Object, attribute: 'format-options' })
×
121
    public formatOptions?: NumberFormatOptions;
×
122

×
123
    @property({ type: String })
×
124
    public label = '';
×
125

×
126
    @property({ attribute: false })
×
127
    public getAriaHandleText: (
×
128
        value: number,
×
129
        numberFormat: NumberFormatter
×
130
    ) => string = (value, numberFormat) => {
×
131
        return numberFormat.format(value);
×
132
    };
×
133

×
134
    private languageResolver = new LanguageResolutionController(this);
×
135

×
136
    protected override update(changes: PropertyValues): void {
×
137
        if (!this.hasUpdated) {
×
138
            const { max, min } = this as { max: number; min: number };
×
139
            if (this.value == null) {
×
140
                if (!isNaN(max) && !isNaN(min)) {
×
141
                    this.value = max < min ? min : min + (max - min) / 2;
×
142
                    this.handleController?.hostUpdate();
×
143
                }
×
144
            }
×
145
        }
×
146

×
147
        if (
×
148
            changes.has('formatOptions') ||
×
149
            changes.has(languageResolverUpdatedSymbol)
×
150
        ) {
×
151
            delete this._numberFormatCache;
×
152
        }
×
153
        if (changes.has('value')) {
×
154
            const oldValue = changes.get('value');
×
155
            if (oldValue != null) {
×
156
                this.updateComplete.then(() => {
×
157
                    this.handleController?.setValueFromHandle(this);
×
158
                });
×
159
            }
×
160
        }
×
161
        this.handleController?.handleHasChanged(this);
×
162
        super.update(changes);
×
163
    }
×
164

×
165
    protected override firstUpdated(
×
166
        changedProperties: PropertyValues<this>
×
167
    ): void {
×
168
        super.firstUpdated(changedProperties);
×
169
        this.dispatchEvent(new CustomEvent('sp-slider-handle-ready'));
×
170
    }
×
171

×
172
    @property({ attribute: false })
×
173
    public normalization: SliderNormalization = defaultNormalization;
×
174

1✔
175
    public dispatchInputEvent(): void {
1✔
176
        const inputEvent = new Event('input', {
×
177
            bubbles: true,
×
178
            composed: true,
×
179
        });
×
180

×
181
        this.dispatchEvent(inputEvent);
×
182
    }
×
183

1✔
184
    protected _numberFormatCache:
1✔
185
        | { numberFormat: NumberFormatter; language: string }
1✔
186
        | undefined;
1✔
187
    protected getNumberFormat(): NumberFormatter {
1✔
188
        /* c8 ignore next */
1✔
189
        if (
1✔
190
            !this._numberFormatCache ||
×
191
            this.languageResolver.language !== this._numberFormatCache.language
×
192
        ) {
×
193
            let numberFormatter: NumberFormatter;
×
194
            try {
×
195
                numberFormatter = new NumberFormatter(
×
196
                    this.languageResolver.language,
×
197
                    this.formatOptions
×
198
                );
×
199
                this._forcedUnit = '';
×
200
                // numberFormatter.format(1);
×
201
            } catch (error) {
×
202
                const {
×
203
                    style,
×
204
                    unit,
×
205
                    // eslint-disable-next-line @typescript-eslint/no-unused-vars
×
206
                    unitDisplay,
×
207
                    ...formatOptionsNoUnit
×
208
                } = this.formatOptions || {};
×
209
                if (style === 'unit') {
×
210
                    this._forcedUnit = unit as string;
×
211
                }
×
212
                numberFormatter = new NumberFormatter(
×
213
                    this.languageResolver.language,
×
214
                    formatOptionsNoUnit
×
215
                );
×
216
            }
×
217
            this._numberFormatCache = {
×
218
                language: this.languageResolver.language,
×
219
                numberFormat: numberFormatter,
×
220
            };
×
221
        }
×
222
        /* c8 ignore next */
1✔
223
        return this._numberFormatCache?.numberFormat;
1✔
224
    }
×
225

1✔
226
    public get numberFormat(): NumberFormatter | undefined {
1✔
227
        if (!this.formatOptions) return;
×
228
        return this.getNumberFormat();
×
229
    }
×
230
}
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