Skip to content

Commit 1c4d95b

Browse files
authored
fix(material/timepicker): allow timepicker to opt out of opening on click (#31492)
Currently any click on an enabled timepicker input will open the associated timepicker. In some cases that might not be desirable so these changes add an input that allows users to opt out of it. Fixes #31398.
1 parent 9fb7b1b commit 1c4d95b

File tree

3 files changed

+24
-2
lines changed

3 files changed

+24
-2
lines changed

goldens/material/timepicker/index.api.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
9494
readonly min: InputSignalWithTransform<D | null, unknown>;
9595
// (undocumented)
9696
ngOnDestroy(): void;
97+
readonly openOnClick: InputSignalWithTransform<boolean, unknown>;
9798
registerOnChange(fn: (value: any) => void): void;
9899
registerOnTouched(fn: () => void): void;
99100
registerOnValidatorChange(fn: () => void): void;
@@ -103,7 +104,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
103104
readonly value: ModelSignal<D | null>;
104105
writeValue(value: any): void;
105106
// (undocumented)
106-
static ɵdir: i0.ɵɵDirectiveDeclaration<MatTimepickerInput<any>, "input[matTimepicker]", ["matTimepickerInput"], { "value": { "alias": "value"; "required": false; "isSignal": true; }; "timepicker": { "alias": "matTimepicker"; "required": true; "isSignal": true; }; "min": { "alias": "matTimepickerMin"; "required": false; "isSignal": true; }; "max": { "alias": "matTimepickerMax"; "required": false; "isSignal": true; }; "disabledInput": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
107+
static ɵdir: i0.ɵɵDirectiveDeclaration<MatTimepickerInput<any>, "input[matTimepicker]", ["matTimepickerInput"], { "value": { "alias": "value"; "required": false; "isSignal": true; }; "timepicker": { "alias": "matTimepicker"; "required": true; "isSignal": true; }; "min": { "alias": "matTimepickerMin"; "required": false; "isSignal": true; }; "max": { "alias": "matTimepickerMax"; "required": false; "isSignal": true; }; "openOnClick": { "alias": "matTimepickerOpenOnClick"; "required": false; "isSignal": true; }; "disabledInput": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "value": "valueChange"; }, never, never, true, never>;
107108
// (undocumented)
108109
static ɵfac: i0.ɵɵFactoryDeclaration<MatTimepickerInput<any>, never>;
109110
}

src/material/timepicker/timepicker-input.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
140140
transform: (value: unknown) => this._transformDateInput<D>(value),
141141
});
142142

143+
/**
144+
* Whether to open the timepicker overlay when clicking on the input. Enabled by default.
145+
* Note that when disabling this option, you'll have to provide your own logic for opening
146+
* the overlay.
147+
*/
148+
readonly openOnClick: InputSignalWithTransform<boolean, unknown> = input(true, {
149+
alias: 'matTimepickerOpenOnClick',
150+
transform: booleanAttribute,
151+
});
152+
143153
/** Whether the input is disabled. */
144154
readonly disabled: Signal<boolean> = computed(
145155
() => this.disabledInput() || this._accessorDisabled(),
@@ -254,7 +264,7 @@ export class MatTimepickerInput<D> implements ControlValueAccessor, Validator, O
254264

255265
/** Handles clicks on the input or the containing form field. */
256266
private _handleClick = (): void => {
257-
if (!this.disabled()) {
267+
if (!this.disabled() && this.openOnClick()) {
258268
this.timepicker().open();
259269
}
260270
};

src/material/timepicker/timepicker.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,15 @@ describe('MatTimepicker', () => {
480480
fixture.detectChanges();
481481
expect(getPanel()).toBeTruthy();
482482
}));
483+
484+
it('should be able to opt out of opening on click', () => {
485+
const fixture = TestBed.createComponent(StandaloneTimepicker);
486+
fixture.componentInstance.openOnClick.set(false);
487+
fixture.detectChanges();
488+
getInput(fixture).click();
489+
fixture.detectChanges();
490+
expect(getPanel()).toBeFalsy();
491+
});
483492
});
484493

485494
// Note: these tests intentionally don't cover the full option generation logic
@@ -1313,6 +1322,7 @@ describe('MatTimepicker', () => {
13131322
[disabled]="disabled()"
13141323
[matTimepickerMin]="min()"
13151324
[matTimepickerMax]="max()"
1325+
[matTimepickerOpenOnClick]="openOnClick()"
13161326
[value]="value()"/>
13171327
<mat-timepicker
13181328
#picker
@@ -1345,6 +1355,7 @@ class StandaloneTimepicker {
13451355
readonly toggleDisabled = signal<boolean>(false);
13461356
readonly toggleTabIndex = signal<number>(0);
13471357
readonly customOptions = signal<MatTimepickerOption<Date>[] | null>(null);
1358+
readonly openOnClick = signal(true);
13481359
readonly openedSpy = jasmine.createSpy('opened');
13491360
readonly closedSpy = jasmine.createSpy('closed');
13501361
readonly selectedSpy = jasmine.createSpy('selected');

0 commit comments

Comments
 (0)