Skip to content

Commit

Permalink
feat(material-experimental/*): Add focus indicators to all MDC except…
Browse files Browse the repository at this point in the history
… mdc-chips. (#18175)
  • Loading branch information
zelliott authored and mmalerba committed Feb 12, 2020
1 parent 6886447 commit 02db4ba
Show file tree
Hide file tree
Showing 21 changed files with 200 additions and 41 deletions.
4 changes: 4 additions & 0 deletions src/dev-app/theme.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@import '../material/core/theming/all-theme';
@import '../material/core/focus-indicator/focus-indicator';
@import '../material-experimental/mdc-helpers/mdc-helpers';
@import '../material-experimental/mdc-slider/mdc-slider';
@import '../material-experimental/mdc-theming/all-theme';
@import '../material-experimental/mdc-typography/all-typography';
Expand All @@ -17,6 +18,7 @@
// Include base styles for strong focus indicators.
.demo-strong-focus {
@include mat-strong-focus-indicators();
@include mat-mdc-strong-focus-indicators();
}

// Define the default theme (same as the example above).
Expand All @@ -39,6 +41,7 @@ $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
// Include the default theme for focus indicators.
.demo-strong-focus {
@include mat-strong-focus-indicators-theme($candy-app-theme);
@include mat-mdc-strong-focus-indicators-theme($candy-app-theme);
}

// Include the alternative theme styles inside of a block with a CSS class. You can make this
Expand All @@ -55,4 +58,5 @@ $dark-theme: mat-dark-theme($dark-primary, $dark-accent, $dark-warn);
// Include the dark theme for focus indicators.
.demo-unicorn-dark-theme.demo-strong-focus {
@include mat-strong-focus-indicators-theme($dark-theme);
@include mat-mdc-strong-focus-indicators-theme($dark-theme);
}
2 changes: 2 additions & 0 deletions src/material-experimental/mdc-button/button-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const MAT_BUTTON_HOST = {
// an unthemed version. If color is undefined, apply a CSS class that makes it easy to
// select and style this "theme".
'[class.mat-unthemed]': '!color',
'class': 'mat-mdc-focus-indicator',
};

/** List of classes to add to buttons instances based on host attribute selector. */
Expand Down Expand Up @@ -147,6 +148,7 @@ export const MAT_ANCHOR_HOST = {
// an unthemed version. If color is undefined, apply a CSS class that makes it easy to
// select and style this "theme".
'[class.mat-unthemed]': '!color',
'class': 'mat-mdc-focus-indicator',
};

/**
Expand Down
9 changes: 9 additions & 0 deletions src/material-experimental/mdc-button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ describe('MDC-based MatButton', () => {
);
});
});

it('should have a focus indicator', () => {
const fixture = TestBed.createComponent(TestApp);
const buttonNativeElements =
[...fixture.debugElement.nativeElement.querySelectorAll('a, button')];

expect(buttonNativeElements
.every(element => element.classList.contains('mat-mdc-focus-indicator'))).toBe(true);
});
});

/** Test component that contains an MatButton. */
Expand Down
8 changes: 6 additions & 2 deletions src/material-experimental/mdc-button/fab.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@
.mat-mdc-fab, .mat-mdc-mini-fab {
@include _mat-button-interactive();
@include _mat-button-disabled();
}

.mat-mdc-fab, .mat-mdc-mini-fab {
// MDC adds some styles to fab and mini-fab that conflict with some of our focus indicator
// styles and don't actually do anything. This undoes those conflicting styles.
&:not(.mdc-ripple-upgraded):focus::before {
background: transparent;
opacity: 1;
}
}

// MDC expects the fab icon to contain this HTML content:
Expand Down
12 changes: 12 additions & 0 deletions src/material-experimental/mdc-button/icon-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,16 @@
border-radius: 50%;

@include _mat-button-disabled();

// MDC adds some styles to icon buttons that conflict with some of our focus indicator styles
// and don't actually do anything. This undoes those conflicting styles.
&.mat-unthemed,
&.mat-primary,
&.mat-accent,
&.mat-warn {
&:not(.mdc-ripple-upgraded):focus::before {
background: transparent;
opacity: 1;
}
}
}
2 changes: 1 addition & 1 deletion src/material-experimental/mdc-checkbox/checkbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</svg>
<div class="mdc-checkbox__mixedmark"></div>
</div>
<div class="mat-mdc-checkbox-ripple" mat-ripple
<div class="mat-mdc-checkbox-ripple mat-mdc-focus-indicator" mat-ripple
[matRippleTrigger]="checkbox"
[matRippleDisabled]="disableRipple || disabled"
[matRippleCentered]="true"
Expand Down
7 changes: 7 additions & 0 deletions src/material-experimental/mdc-checkbox/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,13 @@ describe('MDC-based MatCheckbox', () => {
expect(inputElement.indeterminate).toBe(true, 'indeterminate should not change');
}));
});

it('should have a focus indicator', () => {
const checkboxRippleNativeElement =
checkboxNativeElement.querySelector('.mat-mdc-checkbox-ripple')!;

expect(checkboxRippleNativeElement.classList.contains('mat-mdc-focus-indicator')).toBe(true);
});
});

describe('with change event and no initial value', () => {
Expand Down
72 changes: 72 additions & 0 deletions src/material-experimental/mdc-helpers/_mdc-helpers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@import '@material/theme/functions.import';
@import '@material/theme/variables.import';
@import '@material/typography/variables.import';
@import '../../material/core/style/layout-common';
@import '../../material/core/theming/theming';
@import '../../material/core/typography/typography';

Expand Down Expand Up @@ -211,3 +212,74 @@ $mat-typography-level-mappings: (
// Reset the original values.
$mdc-typography-styles: $orig-mdc-typography-styles !global;
}

/// Mixin that turns on strong focus indicators.
///
/// @example
/// .my-app {
/// @include mat-mdc-strong-focus-indicators();
/// }
@mixin mat-mdc-strong-focus-indicators() {
// Base styles for the focus indicators.
.mat-mdc-focus-indicator::before {
@include mat-fill();

border-radius: $mat-focus-indicator-border-radius;
border: $mat-focus-indicator-border-width $mat-focus-indicator-border-style transparent;
box-sizing: border-box;
pointer-events: none;
}

// By default, all focus indicators are flush with the bounding box of their
// host element. For particular elements (listed below), default inset/offset
// values are necessary to ensure that the focus indicator is sufficiently
// contrastive and renders appropriately.

.mat-mdc-focus-indicator.mdc-button::before,
.mat-mdc-focus-indicator.mdc-fab::before,
.mat-mdc-focus-indicator.mdc-icon-button::before {
margin: $mat-focus-indicator-border-width * -2;
}

.mat-mdc-focus-indicator.mat-mdc-tab::before,
.mat-mdc-focus-indicator.mat-mdc-tab-link::before {
margin: $mat-focus-indicator-border-width * 2;
}

// Render the focus indicator on focus. Defining a pseudo element's
// content will cause it to render.

// For checkboxes and slide toggles, render the focus indicator when we know the hidden input
// is focused (slightly different for each control).
.mdc-checkbox__native-control:focus ~ .mat-mdc-focus-indicator::before,
.mat-mdc-slide-toggle-focused .mat-mdc-focus-indicator::before,

// For all other components, render the focus indicator on focus.
.mat-mdc-focus-indicator:focus::before {
content: '';
}
}

/// Mixin that sets the color of the focus indicators.
///
/// @param {color|map} $themeOrMap
/// If theme, focus indicators are set to the primary color of the theme. If
/// color, focus indicators are set to that color.
///
/// @example
/// .demo-dark-theme {
/// @include mat-mdc-strong-focus-indicators-theme($darkThemeMap);
/// }
///
/// @example
/// .demo-red-theme {
/// @include mat-mdc-strong-focus-indicators-theme(#F00);
/// }
@mixin mat-mdc-strong-focus-indicators-theme($themeOrColor) {
.mat-mdc-focus-indicator::before {
border-color: if(
type-of($themeOrColor) == 'map',
mat-color(map_get($themeOrColor, primary)),
$themeOrColor);
}
}
7 changes: 4 additions & 3 deletions src/material-experimental/mdc-menu/menu-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ import {MatMenuItem as BaseMatMenuItem} from '@angular/material/menu';
inputs: ['disabled', 'disableRipple'],
host: {
'[attr.role]': 'role',
// The MatMenuItem parent class adds `mat-menu-item` to the CSS classlist, but this should
// not be added for this MDC equivalent menu item.
// The MatMenuItem parent class adds `mat-menu-item` and `mat-focus-indicator` to the CSS
// classlist, but these should not be added for this MDC equivalent menu item.
'[class.mat-menu-item]': 'false',
'class': 'mat-mdc-menu-item',
'[class.mat-focus-indicator]': 'false',
'class': 'mat-mdc-menu-item mat-mdc-focus-indicator',
'[class.mat-mdc-menu-item-highlighted]': '_highlighted',
'[class.mat-mdc-menu-item-submenu-trigger]': '_triggersSubmenu',
'[attr.tabindex]': '_getTabIndex()',
Expand Down
12 changes: 12 additions & 0 deletions src/material-experimental/mdc-menu/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2160,6 +2160,18 @@ describe('MDC-based MatMenu', () => {

});

it('should have a focus indicator', () => {
const fixture = createComponent(SimpleMenu, [], [FakeIcon]);
fixture.detectChanges();
fixture.componentInstance.trigger.openMenu();
fixture.detectChanges();
const menuItemNativeElements =
Array.from(overlayContainerElement.querySelectorAll('.mat-mdc-menu-item'));

expect(menuItemNativeElements
.every(element => element.classList.contains('mat-mdc-focus-indicator'))).toBe(true);
});

});

describe('MatMenu default overrides', () => {
Expand Down
35 changes: 18 additions & 17 deletions src/material-experimental/mdc-slide-toggle/slide-toggle.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,29 @@
<div class="mdc-switch" #switch>
<div class="mdc-switch__track"></div>
<div class="mdc-switch__thumb-underlay">
<div class="mat-mdc-slide-toggle-ripple" mat-ripple
<div class="mat-mdc-slide-toggle-ripple mat-mdc-focus-indicator" mat-ripple
[matRippleTrigger]="switch"
[matRippleDisabled]="disableRipple || disabled"
[matRippleCentered]="true"
[matRippleRadius]="24"
[matRippleAnimation]="_rippleAnimation"></div>
<div class="mdc-switch__thumb"></div>
<input #input class="mdc-switch__native-control" type="checkbox"
role="switch"
[id]="inputId"
[required]="required"
[tabIndex]="tabIndex"
[checked]="checked"
[disabled]="disabled"
[attr.name]="name"
[attr.aria-checked]="checked.toString()"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
(change)="_onChangeEvent($event)"
(click)="_onInputClick($event)"
(blur)="_onBlur()"
(focus)="_focused = true">
<div class="mdc-switch__thumb">
<input #input class="mdc-switch__native-control" type="checkbox"
role="switch"
[id]="inputId"
[required]="required"
[tabIndex]="tabIndex"
[checked]="checked"
[disabled]="disabled"
[attr.name]="name"
[attr.aria-checked]="checked.toString()"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="ariaLabelledby"
(change)="_onChangeEvent($event)"
(click)="_onInputClick($event)"
(blur)="_onBlur()"
(focus)="_focused = true">
</div>
</div>
</div>

Expand Down
5 changes: 5 additions & 0 deletions src/material-experimental/mdc-slide-toggle/slide-toggle.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
// The ripple needs extra specificity so the base ripple styling doesn't override its `position`.
.mat-mdc-slide-toggle-ripple, .mdc-switch__thumb-underlay::before {
@include mat-fill;

// Disable pointer events for the ripple container so that it doesn't eat the mouse events meant
// for the input. Pointer events can be safely disabled because the ripple trigger element is
// the host element.
pointer-events: none;
}

// The MDC switch styles related to the hover state are intertwined with the MDC ripple styles.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,14 @@ describe('MDC-based MatSlideToggle without forms', () => {

expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0);
});

it('should have a focus indicator', () => {
const slideToggleRippleNativeElement =
slideToggleElement.querySelector('.mat-mdc-slide-toggle-ripple')!;

expect(slideToggleRippleNativeElement.classList.contains('mat-mdc-focus-indicator'))
.toBe(true);
});
});

describe('custom template', () => {
Expand Down
4 changes: 4 additions & 0 deletions src/material-experimental/mdc-slider/slider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ describe('MDC-based MatSlider', () => {
expect(sliderInstance.value).toBe(100);
});

it('should have a focus indicator', () => {
expect(sliderNativeElement.classList.contains('mat-mdc-focus-indicator')).toBe(true);
});

});

describe('disabled slider', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/material-experimental/mdc-slider/slider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class MatSliderChange {
templateUrl: 'slider.html',
styleUrls: ['slider.css'],
host: {
'class': 'mat-mdc-slider mdc-slider',
'class': 'mat-mdc-slider mdc-slider mat-mdc-focus-indicator',
'role': 'slider',
'aria-orientation': 'horizontal',
// The tabindex if the slider turns disabled is managed by the MDC foundation which
Expand Down
2 changes: 1 addition & 1 deletion src/material-experimental/mdc-tabs/tab-group.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
(indexFocused)="_focusChanged($event)"
(selectFocusedIndex)="selectedIndex = $event">

<div class="mdc-tab mat-mdc-tab"
<div class="mdc-tab mat-mdc-tab mat-mdc-focus-indicator"
#tabNode
role="tab"
matTabLabelWrapper
Expand Down
8 changes: 8 additions & 0 deletions src/material-experimental/mdc-tabs/tab-group.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,14 @@ describe('MDC-based MatTabGroup', () => {
subscription.unsubscribe();
});

it('should have a focus indicator', () => {
const tabLabelNativeElements =
[...fixture.debugElement.nativeElement.querySelectorAll('.mat-mdc-tab')];

expect(tabLabelNativeElements.every(el => el.classList.contains('mat-mdc-focus-indicator')))
.toBe(true);
});

});

describe('aria labelling', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,14 @@ describe('MDC-based MatTabNavBar', () => {
expect(fixture.componentInstance.tabLinks.toArray().every(tabLink => tabLink.rippleDisabled))
.toBe(true, 'Expected every tab link to have ripples disabled');
});

it('should have a focus indicator', () => {
const tabLinkNativeElements =
[...fixture.debugElement.nativeElement.querySelectorAll('.mat-mdc-tab-link')];

expect(tabLinkNativeElements
.every(element => element.classList.contains('mat-mdc-focus-indicator'))).toBe(true);
});
});

describe('with the ink bar fit to content', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export class MatTabNav extends _MatTabNavBase implements AfterContentInit {
templateUrl: 'tab-link.html',
styleUrls: ['tab-link.css'],
host: {
'class': 'mdc-tab mat-mdc-tab-link',
'class': 'mdc-tab mat-mdc-tab-link mat-mdc-focus-indicator',
'[attr.aria-current]': 'active ? "page" : null',
'[attr.aria-disabled]': 'disabled',
'[attr.tabIndex]': 'tabIndex',
Expand Down
6 changes: 4 additions & 2 deletions src/material/button/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,11 @@ describe('MatButton', () => {

it('should have a focus indicator', () => {
const fixture = TestBed.createComponent(TestApp);
const buttonNativeElement = fixture.debugElement.nativeElement.querySelector('button');
const buttonNativeElements =
[...fixture.debugElement.nativeElement.querySelectorAll('a, button')];

expect(buttonNativeElement.classList.contains('mat-focus-indicator')).toBe(true);
expect(buttonNativeElements.every(element => element.classList.contains('mat-focus-indicator')))
.toBe(true);
});
});

Expand Down
Loading

0 comments on commit 02db4ba

Please sign in to comment.