Skip to content
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

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

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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/material-experimental/mdc-checkbox/_checkbox-theme.scss
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
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
@@ -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
@@ -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
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
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();
mmalerba marked this conversation as resolved.
Show resolved Hide resolved

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
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