Skip to content

Commit ee44255

Browse files
calebkishCaleb Kish
and
Caleb Kish
authored
fix(material/timepicker): TimepickerInput component in shadow DOM (#30642)
Fixes an issue where the value of the input element referenced by a TimepickerInput component inside a shadow DOM was getting formatted too often due to `TimepickerInput._hasFocus()` assuming the component wasn't in the shadow DOM. Fixes #30641 Co-authored-by: Caleb Kish <j69674031@gmail.com>
1 parent 1ffe706 commit ee44255

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

src/material/timepicker/timepicker-input.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {MAT_INPUT_VALUE_ACCESSOR} from '../input';
4141
import {Subscription} from 'rxjs';
4242
import {DOWN_ARROW, ESCAPE, hasModifierKey, UP_ARROW} from '@angular/cdk/keycodes';
4343
import {validateAdapter} from './util';
44-
import {DOCUMENT} from '@angular/common';
44+
import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform';
4545

4646
/**
4747
* Input that can be used to enter time and connect to a `mat-timepicker`.
@@ -82,7 +82,6 @@ import {DOCUMENT} from '@angular/common';
8282
})
8383
export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, OnDestroy {
8484
private _elementRef = inject<ElementRef<HTMLInputElement>>(ElementRef);
85-
private _document = inject(DOCUMENT);
8685
private _dateAdapter = inject<DateAdapter<D>>(DateAdapter, {optional: true})!;
8786
private _dateFormats = inject(MAT_DATE_FORMATS, {optional: true})!;
8887
private _formField = inject(MAT_FORM_FIELD, {optional: true});
@@ -406,7 +405,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
406405

407406
/** Whether the input is currently focused. */
408407
private _hasFocus(): boolean {
409-
return this._document.activeElement === this._elementRef.nativeElement;
408+
return _getFocusedElementPierceShadowDom() === this._elementRef.nativeElement;
410409
}
411410

412411
/** Gets a function that can be used to validate the input. */

src/material/timepicker/timepicker.spec.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, inject, Provider, signal, ViewChild} from '@angular/core';
1+
import {Component, inject, Provider, signal, ViewChild, ViewEncapsulation} from '@angular/core';
22
import {ComponentFixture, fakeAsync, flush, TestBed} from '@angular/core/testing';
33
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
44
import {DateAdapter, provideNativeDateAdapter} from '../core';
@@ -299,6 +299,15 @@ describe('MatTimepicker', () => {
299299
fixture.detectChanges();
300300
}).toThrowError(/MatTimepicker can only be registered with one input at a time/);
301301
});
302+
303+
it('input should be properly formatted when in shadow DOM', () => {
304+
const fixture = TestBed.createComponent(TimepickerInShadowDom);
305+
fixture.detectChanges(); // So that TimepickerInput.timepicker gets set.
306+
const input = fixture.nativeElement.shadowRoot.querySelector('.mat-timepicker-input');
307+
typeInElement(input, '13:37');
308+
fixture.detectChanges();
309+
expect(input.value).toBe('13:37');
310+
});
302311
});
303312

304313
describe('opening and closing', () => {
@@ -1413,3 +1422,13 @@ class TimepickerWithMultipleInputs {}
14131422
class TimepickerWithoutInput {
14141423
@ViewChild(MatTimepicker) timepicker: MatTimepicker<Date>;
14151424
}
1425+
1426+
@Component({
1427+
template: `
1428+
<input [matTimepicker]="picker" />
1429+
<mat-timepicker #picker />
1430+
`,
1431+
imports: [MatTimepicker, MatTimepickerInput],
1432+
encapsulation: ViewEncapsulation.ShadowDom,
1433+
})
1434+
class TimepickerInShadowDom {}

0 commit comments

Comments
 (0)