diff --git a/libs/components/src/lib/date-picker/date-picker.spec.ts b/libs/components/src/lib/date-picker/date-picker.spec.ts index 7f446a0e1b..e38a42c725 100644 --- a/libs/components/src/lib/date-picker/date-picker.spec.ts +++ b/libs/components/src/lib/date-picker/date-picker.spec.ts @@ -6,6 +6,9 @@ import { setupDelegatesFocusPolyfill, } from '@vivid-nx/shared'; import { FoundationElementRegistry } from '@microsoft/fast-foundation'; +import enUS from '@vonage/vivid/locales/en-US'; +import deDE from '../../locales/de-DE'; +import { setLocale } from '../../shared/localization'; import { TextField } from '../text-field/text-field'; import { Button } from '../button/button'; import { DatePicker } from './date-picker'; @@ -309,6 +312,31 @@ describe('vwc-date-picker', () => { }); }); + describe('localization', () => { + afterEach(() => { + setLocale(enUS); + }); + + it('should format the date according to the locale', async () => { + setLocale(deDE); + + element.value = '2021-01-21'; + await elementUpdated(element); + + expect(textField.currentValue).toBe('21.01.2021'); + }); + + it('should update the text field when the locale changes', async () => { + element.value = '2021-01-21'; + await elementUpdated(element); + + setLocale(deDE); + await elementUpdated(element); + + expect(textField.currentValue).toBe('21.01.2021'); + }); + }); + describe('a11y', () => { it('should pass html a11y test', async () => { element.value = '2012-12-12'; diff --git a/libs/components/src/lib/date-picker/date-picker.ts b/libs/components/src/lib/date-picker/date-picker.ts index 1cbc5d6a02..0bb8f650b0 100644 --- a/libs/components/src/lib/date-picker/date-picker.ts +++ b/libs/components/src/lib/date-picker/date-picker.ts @@ -39,11 +39,17 @@ export class DatePicker extends DatePickerBase { return; } + this._adjustSelectedMonthToEnsureVisibilityOf(this.value); + } + this._updatePresentationValue(); + } + + protected override _updatePresentationValue() { + if (this.value) { this._presentationValue = formatPresentationDate( this.value, this.locale.datePicker ); - this._adjustSelectedMonthToEnsureVisibilityOf(this.value); } else { this._presentationValue = ''; } diff --git a/libs/components/src/lib/date-range-picker/date-range-picker.spec.ts b/libs/components/src/lib/date-range-picker/date-range-picker.spec.ts index 5420507a1b..f41ae91a9d 100644 --- a/libs/components/src/lib/date-range-picker/date-range-picker.spec.ts +++ b/libs/components/src/lib/date-range-picker/date-range-picker.spec.ts @@ -5,6 +5,9 @@ import { setupDelegatesFocusPolyfill, } from '@vivid-nx/shared'; import { FoundationElementRegistry } from '@microsoft/fast-foundation'; +import { setLocale } from '../../shared/localization'; +import deDE from '../../locales/de-DE'; +import enUS from '../../locales/en-US'; import { TextField } from '../text-field/text-field'; import { Popup } from '../popup/popup'; import { Button } from '../button/button'; @@ -610,6 +613,33 @@ describe('vwc-date-range-picker', () => { ); }); + describe('localization', () => { + afterEach(() => { + setLocale(enUS); + }); + + it('should format the date according to the locale', async () => { + setLocale(deDE); + + element.start = '2021-01-21'; + element.end = '2021-01-22'; + await elementUpdated(element); + + expect(textField.value).toBe('21.01.2021 – 22.01.2021'); + }); + + it('should update the text field when the locale changes', async () => { + element.start = '2021-01-21'; + element.end = '2021-01-22'; + await elementUpdated(element); + + setLocale(deDE); + await elementUpdated(element); + + expect(textField.value).toBe('21.01.2021 – 22.01.2021'); + }); + }); + describe('a11y', () => { it('should pass html a11y test', async () => { element.start = '2012-12-12'; diff --git a/libs/components/src/lib/date-range-picker/date-range-picker.ts b/libs/components/src/lib/date-range-picker/date-range-picker.ts index 2235fe0986..225c3bd2bf 100644 --- a/libs/components/src/lib/date-range-picker/date-range-picker.ts +++ b/libs/components/src/lib/date-range-picker/date-range-picker.ts @@ -184,20 +184,14 @@ export class DateRangePicker extends DatePickerBase { return; } - this._presentationValue = formatPresentationDateRange( - { - start: this.start, - end: this.end, - }, - this.locale.datePicker - ); // Set a dummy value for required validation this.value = formatRange(this.start, this.end); } else { this.value = ''; - this._presentationValue = ''; } + this._updatePresentationValue(); + const dateToEnsureVisibilityOf = this.start || this.end; if (dateToEnsureVisibilityOf) { this._adjustSelectedMonthToEnsureVisibilityOf(dateToEnsureVisibilityOf); @@ -206,6 +200,23 @@ export class DateRangePicker extends DatePickerBase { this.#updateFormValue(); } + /** + * @internal + */ + protected override _updatePresentationValue() { + if (this.start && this.end) { + this._presentationValue = formatPresentationDateRange( + { + start: this.start, + end: this.end, + }, + this.locale.datePicker + ); + } else { + this._presentationValue = ''; + } + } + /** * @internal */ diff --git a/libs/components/src/lib/time-picker/time-picker.spec.ts b/libs/components/src/lib/time-picker/time-picker.spec.ts index 72756611f6..0f8259217e 100644 --- a/libs/components/src/lib/time-picker/time-picker.spec.ts +++ b/libs/components/src/lib/time-picker/time-picker.spec.ts @@ -8,7 +8,7 @@ import { setupDelegatesFocusPolyfill, } from '@vivid-nx/shared'; import { FoundationElementRegistry } from '@microsoft/fast-foundation'; -import { setLocale } from '@vonage/vivid'; +import { setLocale } from '../../shared/localization'; import { Popup } from '../popup/popup.ts'; import { Button } from '../button/button.ts'; import { TextField } from '../text-field/text-field.ts'; @@ -349,6 +349,27 @@ describe('vwc-time-picker', () => { ]); }); }); + + it('should update the text field when the clock is changed', async () => { + element.clock = '12h'; + element.value = '13:45:00'; + await elementUpdated(element); + + element.clock = '24h'; + await elementUpdated(element); + + expect(textField.currentValue).toBe('13:45'); + }); + + it('should update the text field when the locale is changed', async () => { + element.value = '13:45:00'; + await elementUpdated(element); + + setLocale(enGB); + await elementUpdated(element); + + expect(textField.currentValue).toBe('13:45'); + }); }); describe('min', () => { diff --git a/libs/components/src/lib/time-picker/time-picker.ts b/libs/components/src/lib/time-picker/time-picker.ts index df4d002791..d8d1c135d6 100644 --- a/libs/components/src/lib/time-picker/time-picker.ts +++ b/libs/components/src/lib/time-picker/time-picker.ts @@ -1,9 +1,13 @@ import { attr, + type BindingObserver, + defaultExecutionContext, DOM, nullableNumberConverter, + Observable, observable, type ValueConverter, + volatile, } from '@microsoft/fast-element'; import { type ErrorText, @@ -175,12 +179,28 @@ export class TimePicker extends FormAssociatedTimePicker { /** * @internal */ + @volatile get _use12hClock() { return this.clock ? this.clock === '12h' : this.locale.timePicker.defaultTo12HourClock; } + // Reformat the presentation value when the clock changes + #clockChangeHandler = { + handleChange: () => { + if (this.value) { + this._presentationValue = formatPresentationTime( + this.value, + this._displaySeconds, + this._use12hClock + ); + } + }, + }; + + #clockChangeObserver!: BindingObserver; + #getFocusableEls = () => this.shadowRoot!.querySelectorAll(` .dialog [tabindex="0"], @@ -229,6 +249,12 @@ export class TimePicker extends FormAssociatedTimePicker { document.addEventListener('click', this.#dismissOnClickOutside); this.addEventListener('focusin', this.#onFocusIn); this.addEventListener('focusout', this.#onFocusOut); + + this.#clockChangeObserver = Observable.binding( + () => this._use12hClock, + this.#clockChangeHandler + ); + this.#clockChangeObserver.observe(this, defaultExecutionContext); } override disconnectedCallback() { @@ -237,6 +263,8 @@ export class TimePicker extends FormAssociatedTimePicker { document.removeEventListener('click', this.#dismissOnClickOutside); this.removeEventListener('focusin', this.#onFocusIn); this.removeEventListener('focusout', this.#onFocusOut); + + this.#clockChangeObserver.disconnect(); } #onFocusIn = () => { diff --git a/libs/components/src/shared/date-picker/date-picker-base.ts b/libs/components/src/shared/date-picker/date-picker-base.ts index f2174cd8e5..a089c1440e 100644 --- a/libs/components/src/shared/date-picker/date-picker-base.ts +++ b/libs/components/src/shared/date-picker/date-picker-base.ts @@ -1,6 +1,9 @@ import { attr, + type BindingObserver, + defaultExecutionContext, DOM, + Observable, observable, type ValueConverter, volatile, @@ -232,6 +235,12 @@ export abstract class DatePickerBase extends FormAssociatedDatePickerBase { document.addEventListener('click', this.#dismissOnClickOutside); this.addEventListener('focusin', this.#onFocusIn); this.addEventListener('focusout', this.#onFocusOut); + + this.#localeChangeObserver = Observable.binding( + () => this.locale, + this.#localeChangeHandler + ); + this.#localeChangeObserver.observe(this, defaultExecutionContext); } override disconnectedCallback() { @@ -240,6 +249,8 @@ export abstract class DatePickerBase extends FormAssociatedDatePickerBase { document.removeEventListener('click', this.#dismissOnClickOutside); this.removeEventListener('focusin', this.#onFocusIn); this.removeEventListener('focusout', this.#onFocusOut); + + this.#localeChangeObserver.disconnect(); } #onFocusIn = () => { @@ -272,6 +283,15 @@ export abstract class DatePickerBase extends FormAssociatedDatePickerBase { protected abstract _getCustomValidationError(): string | null; + // Reformat the presentation value when the locale changes + #localeChangeHandler = { + handleChange: () => { + this._updatePresentationValue(); + }, + }; + + #localeChangeObserver!: BindingObserver; + // --- Popup --- /** @@ -353,6 +373,8 @@ export abstract class DatePickerBase extends FormAssociatedDatePickerBase { this.validate(); } + protected abstract _updatePresentationValue(): void; + /** * @internal */