Skip to content

fix(material-experimental/mdc-radio): add accessible touch targets #22994

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/material-experimental/mdc-checkbox/_checkbox-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@
$query: mdc-helpers.$mat-base-styles-query
);
}

@if ($density-scale == -2 or $density-scale == 'minimum') {
.mat-mdc-checkbox-touch-target {
display: none;
}
}
}

@mixin theme($theme-or-color-config) {
Expand Down
6 changes: 6 additions & 0 deletions src/material-experimental/mdc-radio/_radio-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@
.mat-mdc-radio-button .mdc-radio {
@include mdc-radio-theme.density($density-scale, $query: mdc-helpers.$mat-base-styles-query);
}

@if ($density-scale == -2 or $density-scale == 'minimum') {
.mat-mdc-radio-touch-target {
display: none;
}
}
}

@mixin theme($theme-or-color-config) {
Expand Down
4 changes: 3 additions & 1 deletion src/material-experimental/mdc-radio/radio.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<div class="mdc-form-field" #formField
[class.mdc-form-field--align-end]="labelPosition == 'before'">
<div class="mdc-radio" [ngClass]="_classes">
<!-- Render this element first so the input is on top. -->
<div class="mat-mdc-radio-touch-target" (click)="_onInputInteraction($event)"></div>
<input #input class="mdc-radio__native-control" type="radio"
[id]="inputId"
[checked]="checked"
Expand All @@ -12,7 +14,7 @@
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
[attr.aria-describedby]="ariaDescribedby"
(change)="_onInputChange($event)">
(change)="_onInputInteraction($event)">
<div class="mdc-radio__background">
<div class="mdc-radio__outer-circle"></div>
<div class="mdc-radio__inner-circle"></div>
Expand Down
14 changes: 14 additions & 0 deletions src/material-experimental/mdc-radio/radio.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@use '@material/radio/radio' as mdc-radio;
@use '@material/radio/radio-theme' as mdc-radio-theme;
@use '@material/form-field' as mdc-form-field;
@use '@material/touch-target' as mdc-touch-target;
@use '../mdc-helpers/mdc-helpers';
@use '../../cdk/a11y';
@use '../../material/core/style/layout-common';
Expand All @@ -25,6 +26,19 @@
@include mdc-radio.without-ripple($query: animation);
}

// Element used to provide a larger tap target for users on touch devices.
.mat-mdc-radio-touch-target {
@include mdc-touch-target.touch-target(
$set-width: true,
$query: mdc-helpers.$mat-base-styles-query);

[dir='rtl'] & {
left: 0;
right: 50%;
transform: translate(50%, -50%);
}
}

// Note that this creates a square box around the circle, however it's consistent with
// how IE/Edge treat native radio buttons in high contrast mode. We can't turn the border
// into a dotted one, because it's too thick which causes the circles to look off.
Expand Down
2 changes: 1 addition & 1 deletion src/material/radio/radio.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
[attr.aria-describedby]="ariaDescribedby"
(change)="_onInputChange($event)"
(change)="_onInputInteraction($event)"
(click)="_onInputClick($event)">

<!-- The ripple comes after the input so that we can target it with a CSS
Expand Down
23 changes: 11 additions & 12 deletions src/material/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -585,24 +585,23 @@ export abstract class _MatRadioButtonBase extends _MatRadioButtonMixinBase imple
event.stopPropagation();
}

/**
* Triggered when the radio button received a click or the input recognized any change.
* Clicking on a label element, will trigger a change event on the associated input.
*/
_onInputChange(event: Event) {
/** Triggered when the radio button receives an interaction from the user. */
_onInputInteraction(event: Event) {
// We always have to stop propagation on the change event.
// Otherwise the change event, from the input element, will bubble up and
// emit its event object to the `change` output.
event.stopPropagation();

const groupValueChanged = this.radioGroup && this.value !== this.radioGroup.value;
this.checked = true;
this._emitChangeEvent();
if (!this.checked && !this.disabled) {
const groupValueChanged = this.radioGroup && this.value !== this.radioGroup.value;
this.checked = true;
this._emitChangeEvent();

if (this.radioGroup) {
this.radioGroup._controlValueAccessorChangeFn(this.value);
if (groupValueChanged) {
this.radioGroup._emitChangeEvent();
if (this.radioGroup) {
this.radioGroup._controlValueAccessorChangeFn(this.value);
if (groupValueChanged) {
this.radioGroup._emitChangeEvent();
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/material/radio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export declare abstract class _MatRadioButtonBase extends _MatRadioButtonMixinBa
constructor(radioGroup: _MatRadioGroupBase<_MatRadioButtonBase>, elementRef: ElementRef, _changeDetector: ChangeDetectorRef, _focusMonitor: FocusMonitor, _radioDispatcher: UniqueSelectionDispatcher, animationMode?: string, _providerOverride?: MatRadioDefaultOptions | undefined, tabIndex?: string);
_isRippleDisabled(): boolean;
_markForCheck(): void;
_onInputChange(event: Event): void;
_onInputClick(event: Event): void;
_onInputInteraction(event: Event): void;
protected _setDisabled(value: boolean): void;
focus(options?: FocusOptions, origin?: FocusOrigin): void;
ngAfterViewInit(): void;
Expand Down