diff --git a/packages/slider/README.md b/packages/slider/README.md index baafbad05b..61666537a3 100644 --- a/packages/slider/README.md +++ b/packages/slider/README.md @@ -153,6 +153,25 @@ for a multi-handle slider, you can format the combined value label for all handles by passing a formatting function to the `getAriaValueText` property on the parent `sp-slider`. +### Units not included in `Intl.NumberFormatOptions` + +While `Intl.NumberFormatOptions` does support a [wide range of units](https://tc39.es/proposal-unified-intl-numberformat/section6/locales-currencies-tz_proposed_out.html#sec-issanctionedsimpleunitidentifier), it is possible to encounter units (e.g. the graphics units of `pixel`, `pixels`, `points`, etc.) that are not supported therein. When this occurs, an `` element will attempt to polyfill support for this unit. See the following example delivering `{ style: "unit", unit: "px" }` below: + +```html + + Document width in pixels + +``` + +Note: the polyfilling done here is very simplistic and is triggered by supplying options that would otherwise cause the `Intl.NumberFormat()` call to throw an error. Once the unsupporting unit of `px` causes the construction of the object to throw, a back up formatter/parser pair will be created without the supplied unit data. When the `style` is set to `unit`, the `unit` value of will be adopted as the _static_ unit display. This means that neither pluralization or translation will be handled within the `` element itself. If pluralization or translation is important to the delivered interface, please be sure to handle passing those strings into to element via the `formatOptions` property reactively to the value of the element or locale of that page in question. + ### Label Visibility Be default an `` element has both a "text" label and a "value" label. Either or both of these can be surpressed visually as needed by your application UI. This delivery is controlled by the `label-visibility` attribute (or `labelVisibility` property) which accepts `text`, `value`, or `none` as values. diff --git a/packages/slider/src/Slider.ts b/packages/slider/src/Slider.ts index eeac446b7b..8fe339149f 100644 --- a/packages/slider/src/Slider.ts +++ b/packages/slider/src/Slider.ts @@ -114,13 +114,13 @@ export class Slider extends ObserveSlotText(SliderHandle, '') { ) => { const valueArray = [...values.values()]; if (valueArray.length === 2) - return `${valueArray[0]} - ${valueArray[1]}`; - return valueArray.join(', '); + return `${valueArray[0]}${this._forcedUnit} - ${valueArray[1]}${this._forcedUnit}`; + return valueArray.join(`${this._forcedUnit}, `) + this._forcedUnit; }; public get ariaValueText(): string { if (!this.getAriaValueText) { - return `${this.value}`; + return `${this.value}${this._forcedUnit}`; } return this.getAriaValueText(this.handleController.formattedValues); } diff --git a/packages/slider/src/SliderHandle.ts b/packages/slider/src/SliderHandle.ts index 1ff0a00ba7..6d509dd53b 100644 --- a/packages/slider/src/SliderHandle.ts +++ b/packages/slider/src/SliderHandle.ts @@ -82,6 +82,8 @@ export class SliderHandle extends Focusable { return this.handleController?.inputForHandle(this) ?? this; } + _forcedUnit = ''; + @property({ type: Number }) value = 10; @@ -161,12 +163,33 @@ export class SliderHandle extends Focusable { !this._numberFormatCache || this.resolvedLanguage !== this._numberFormatCache.language ) { - this._numberFormatCache = { - language: this.resolvedLanguage, - numberFormat: new NumberFormatter( + let numberFormatter: NumberFormatter; + try { + numberFormatter = new NumberFormatter( this.resolvedLanguage, this.formatOptions - ), + ); + this._forcedUnit = ''; + // numberFormatter.format(1); + } catch (error) { + const { + style, + unit, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + unitDisplay, + ...formatOptionsNoUnit + } = this.formatOptions || {}; + if (style === 'unit') { + this._forcedUnit = unit as string; + } + numberFormatter = new NumberFormatter( + this.resolvedLanguage, + formatOptionsNoUnit + ); + } + this._numberFormatCache = { + language: this.resolvedLanguage, + numberFormat: numberFormatter, }; } /* c8 ignore next */ diff --git a/packages/slider/stories/slider.stories.ts b/packages/slider/stories/slider.stories.ts index b7bcb833a5..77b61c34d8 100644 --- a/packages/slider/stories/slider.stories.ts +++ b/packages/slider/stories/slider.stories.ts @@ -186,6 +186,34 @@ noVisibleLabels.args = { labelVisibility: 'none', }; +export const px = (args: StoryArgs): TemplateResult => { + const handleEvent = (event: Event): void => { + const target = event.target as Slider; + if (target.value != null) { + action(event.type)(target.value.toString()); + } + }; + return html` +
+ + Angle + +
+ `; +}; + class NumberFieldDefined extends HTMLElement { constructor() { super(); @@ -471,6 +499,87 @@ TwoHandles.args = { tickStep: 10, }; +export const TwoHandlesPt = (args: StoryArgs): TemplateResult => { + const handleEvent = (event: Event): void => { + const target = event.target as SliderHandle; + if (target.value != null) { + if (typeof target.value === 'object') { + action(event.type)(target.value); + } else { + action(event.type)(`${target.name}: ${target.value}`); + } + } + }; + return html` +
+ + Output Levels + + + +
+ `; +}; +TwoHandlesPt.args = { + variant: 'range', + tickStep: 10, +}; + +export const ThreeHandlesPc = (args: StoryArgs): TemplateResult => { + const handleEvent = (event: Event): void => { + const target = event.target as SliderHandle; + if (target.value != null) { + if (typeof target.value === 'object') { + action(event.type)(target.value); + } else { + action(event.type)(`${target.name}: ${target.value}`); + } + } + }; + return html` +
+ + Output Levels + + + + +
+ `; +}; + export const ThreeHandlesOrdered = (args: StoryArgs): TemplateResult => { const handleEvent = (event: Event): void => { const target = event.target as SliderHandle;