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

feat: add ripples to button-toggle #9891

Merged
merged 2 commits into from
Feb 13, 2018
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
16 changes: 12 additions & 4 deletions src/demo-app/button-toggle/button-toggle-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ <h1>Exclusive Selection</h1>

<section class="demo-section">
<mat-button-toggle-group name="alignment" [vertical]="isVertical">
<mat-button-toggle value="left"><mat-icon>format_align_left</mat-icon></mat-button-toggle>
<mat-button-toggle value="center"><mat-icon>format_align_center</mat-icon></mat-button-toggle>
<mat-button-toggle value="right"><mat-icon>format_align_right</mat-icon></mat-button-toggle>
<mat-button-toggle value="justify" [disabled]="isDisabled"><mat-icon>format_align_justify</mat-icon></mat-button-toggle>
<mat-button-toggle value="left"[disabled]="isDisabled">
<mat-icon>format_align_left</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="center" [disabled]="isDisabled">
<mat-icon>format_align_center</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="right" [disabled]="isDisabled">
<mat-icon>format_align_right</mat-icon>
</mat-button-toggle>
<mat-button-toggle value="justify" [disabled]="isDisabled">
<mat-icon>format_align_justify</mat-icon>
</mat-button-toggle>
</mat-button-toggle-group>
</section>

Expand Down
13 changes: 2 additions & 11 deletions src/lib/button-toggle/_button-toggle-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,15 @@
@import '../core/theming/theming';
@import '../core/typography/typography-utils';

// Applies a focus style to an mat-button-toggle element for each of the supported palettes.
@mixin _mat-button-toggle-focus-color($theme) {
$background: map-get($theme, background);

.mat-button-toggle-focus-overlay {
background-color: mat-color($background, focused-button);
}
}

@mixin mat-button-toggle-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);

.mat-button-toggle {
color: mat-color($foreground, hint-text);

&.cdk-focused {
@include _mat-button-toggle-focus-color($theme);
.mat-button-toggle-focus-overlay {
background-color: mat-color($background, focused-button);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib/button-toggle/button-toggle-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

import {NgModule} from '@angular/core';
import {MatButtonToggleGroup, MatButtonToggleGroupMultiple, MatButtonToggle} from './button-toggle';
import {MatCommonModule} from '@angular/material/core';
import {MatCommonModule, MatRippleModule} from '@angular/material/core';
import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections';
import {A11yModule} from '@angular/cdk/a11y';


@NgModule({
imports: [MatCommonModule, A11yModule],
imports: [MatCommonModule, MatRippleModule, A11yModule],
exports: [
MatButtonToggleGroup,
MatButtonToggleGroupMultiple,
Expand Down
7 changes: 6 additions & 1 deletion src/lib/button-toggle/button-toggle.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<label [attr.for]="inputId" class="mat-button-toggle-label">
<label [attr.for]="inputId" class="mat-button-toggle-label" #label>
<input #input class="mat-button-toggle-input cdk-visually-hidden"
[type]="_type"
[id]="inputId"
Expand All @@ -15,3 +15,8 @@
</div>
</label>
<div class="mat-button-toggle-focus-overlay"></div>

<div class="mat-button-toggle-ripple" matRipple
[matRippleTrigger]="label"
[matRippleDisabled]="this.disableRipple || this.disabled">
</div>
17 changes: 14 additions & 3 deletions src/lib/button-toggle/button-toggle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ $mat-button-toggle-border-radius: 2px !default;
}
}


.mat-button-toggle-disabled .mat-button-toggle-label-content {
cursor: default;
}
Expand All @@ -39,8 +38,11 @@ $mat-button-toggle-border-radius: 2px !default;
white-space: nowrap;
position: relative;

&.cdk-keyboard-focused,
&.cdk-program-focused {
// Similar to components like the checkbox, slide-toggle and radio, we cannot show the focus
// overlay for `.cdk-program-focused` because mouse clicks on the <label> element would be always
// treated as programmatic focus.
// TODO(paul): support `program` as well. See https://github.com/angular/material2/issues/9889
&.cdk-keyboard-focused {
.mat-button-toggle-focus-overlay {
opacity: 1;
}
Expand Down Expand Up @@ -68,3 +70,12 @@ $mat-button-toggle-border-radius: 2px !default;
opacity: 0;
@include mat-fill;
}

.mat-button-toggle-ripple {
@include mat-fill;

// Disable pointer events for the ripple container, because the container will overlay the user
// content and we don't want to prevent mouse clicks that should toggle the state.
// Pointer events can be safely disabled because the ripple trigger element is the label element.
pointer-events: none;
}
31 changes: 29 additions & 2 deletions src/lib/button-toggle/button-toggle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ComponentFixture,
TestBed,
} from '@angular/core/testing';
import {dispatchMouseEvent} from '@angular/cdk/testing';
import {NgModel, FormsModule, ReactiveFormsModule, FormControl} from '@angular/forms';
import {Component, DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
Expand Down Expand Up @@ -177,8 +178,32 @@ describe('MatButtonToggle with forms', () => {

expect(testComponent.modelValue).toBe('green');
}));
});

it('should show a ripple on label click', () => {
const groupElement = groupDebugElement.nativeElement;

expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

dispatchMouseEvent(buttonToggleLabels[0], 'mousedown');
dispatchMouseEvent(buttonToggleLabels[0], 'mouseup');

expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(1);
});

it('should allow ripples to be disabled', () => {
const groupElement = groupDebugElement.nativeElement;

testComponent.disableRipple = true;
fixture.detectChanges();

expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);

dispatchMouseEvent(buttonToggleLabels[0], 'mousedown');
dispatchMouseEvent(buttonToggleLabels[0], 'mouseup');

expect(groupElement.querySelectorAll('.mat-ripple-element').length).toBe(0);
});
});
});

describe('MatButtonToggle without forms', () => {
Expand Down Expand Up @@ -647,7 +672,8 @@ class ButtonTogglesInsideButtonToggleGroup {
@Component({
template: `
<mat-button-toggle-group [(ngModel)]="modelValue" (change)="lastEvent = $event">
<mat-button-toggle *ngFor="let option of options" [value]="option.value">
<mat-button-toggle *ngFor="let option of options" [value]="option.value"
[disableRipple]="disableRipple">
{{option.label}}
</mat-button-toggle>
</mat-button-toggle-group>
Expand All @@ -661,6 +687,7 @@ class ButtonToggleGroupWithNgModel {
{label: 'Blue', value: 'blue'},
];
lastEvent: MatButtonToggleChange;
disableRipple = false;
}

@Component({
Expand Down
18 changes: 16 additions & 2 deletions src/lib/button-toggle/button-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ import {
ViewEncapsulation,
} from '@angular/core';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {CanDisable, mixinDisabled} from '@angular/material/core';
import {
CanDisable,
CanDisableRipple,
mixinDisabled,
mixinDisableRipple
} from '@angular/material/core';

/** Acceptable types for a button toggle. */
export type ToggleType = 'checkbox' | 'radio';
Expand Down Expand Up @@ -225,6 +230,11 @@ export class MatButtonToggleGroupMultiple extends _MatButtonToggleGroupMixinBase
private _vertical: boolean = false;
}

// Boilerplate for applying mixins to the MatButtonToggle class.
/** @docs-private */
export class MatButtonToggleBase {}
export const _MatButtonToggleMixinBase = mixinDisableRipple(MatButtonToggleBase);

/** Single button inside of a toggle group. */
@Component({
moduleId: module.id,
Expand All @@ -235,6 +245,7 @@ export class MatButtonToggleGroupMultiple extends _MatButtonToggleGroupMixinBase
preserveWhitespaces: false,
exportAs: 'matButtonToggle',
changeDetection: ChangeDetectionStrategy.OnPush,
inputs: ['disableRipple'],
host: {
'[class.mat-button-toggle-standalone]': '!buttonToggleGroup && !buttonToggleGroupMultiple',
'[class.mat-button-toggle-checked]': 'checked',
Expand All @@ -243,7 +254,9 @@ export class MatButtonToggleGroupMultiple extends _MatButtonToggleGroupMixinBase
'[attr.id]': 'id',
}
})
export class MatButtonToggle implements OnInit, OnDestroy {
export class MatButtonToggle extends _MatButtonToggleMixinBase
implements OnInit, OnDestroy, CanDisableRipple {

/**
* Attached to the aria-label attribute of the host element. In most cases, arial-labelledby will
* take precedence so this may be omitted.
Expand Down Expand Up @@ -331,6 +344,7 @@ export class MatButtonToggle implements OnInit, OnDestroy {
private _buttonToggleDispatcher: UniqueSelectionDispatcher,
private _elementRef: ElementRef,
private _focusMonitor: FocusMonitor) {
super();

this.buttonToggleGroup = toggleGroup;
this.buttonToggleGroupMultiple = toggleGroupMultiple;
Expand Down
1 change: 1 addition & 0 deletions src/lib/checkbox/checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ export class MatCheckbox extends _MatCheckboxMixinBase implements ControlValueAc

/** Function is called whenever the focus changes for the input element. */
private _onInputFocusChange(focusOrigin: FocusOrigin) {
// TODO(paul): support `program`. See https://github.com/angular/material2/issues/9889
if (!this._focusRipple && focusOrigin === 'keyboard') {
this._focusRipple = this.ripple.launch(0, 0, {persistent: true});
} else if (!focusOrigin) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/radio/radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ export class MatRadioButton extends _MatRadioButtonMixinBase

/** Function is called whenever the focus changes for the input element. */
private _onInputFocusChange(focusOrigin: FocusOrigin) {
// TODO(paul): support `program`. See https://github.com/angular/material2/issues/9889
if (!this._focusRipple && focusOrigin === 'keyboard') {
this._focusRipple = this._ripple.launch(0, 0, {persistent: true});
} else if (!focusOrigin) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/slide-toggle/slide-toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export class MatSlideToggle extends _MatSlideToggleMixinBase implements OnDestro

/** Function is called whenever the focus changes for the input element. */
private _onInputFocusChange(focusOrigin: FocusOrigin) {
// TODO(paul): support `program`. See https://github.com/angular/material2/issues/9889
if (!this._focusRipple && focusOrigin === 'keyboard') {
// For keyboard focus show a persistent ripple as focus indicator.
this._focusRipple = this._ripple.launch(0, 0, {persistent: true});
Expand Down