Skip to content

feat(material-experimental/mdc-slide-toggle): switch to non-deprecated styles #23143

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
Aug 6, 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
3 changes: 3 additions & 0 deletions scripts/check-mdc-tests-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ export const config = {
'should re-add margin if label is added asynchronously',
'should properly update margin if label content is projected',

// The MDC slide toggle uses a `button` which isn't able to block form submission.
'should prevent the form from submit when being required',

// TODO: the focus origin functionality has to be implemeted for the MDC slide toggle.
'should not change focus origin if origin not specified'
],
Expand Down
5 changes: 4 additions & 1 deletion src/e2e-app/protractor.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ exports.config = {
{id: 'region', enabled: false},

// Don't require at least one `<h1>` since we don't have any content.
{id: 'page-has-heading-one', enabled: false}
{id: 'page-has-heading-one', enabled: false},

// Axe incorrectly picks up that `aria-required` is not allowed on the MDC slide toggle.
{id: 'aria-allowed-attr', selector: '*:not(.mdc-switch)'}
]
}
],
Expand Down
3 changes: 2 additions & 1 deletion src/material-experimental/mdc-helpers/_focus-indicators.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
// which will clip a square focus indicator so we have to turn it into a circle.
.mat-mdc-checkbox-ripple.mat-mdc-focus-indicator::before,
.mat-radio-ripple.mat-mdc-focus-indicator::before,
.mat-mdc-slider .mat-mdc-focus-indicator::before {
.mat-mdc-slider .mat-mdc-focus-indicator::before,
.mat-mdc-slide-toggle .mat-mdc-focus-indicator::before {
border-radius: 50%;
}

Expand Down
128 changes: 73 additions & 55 deletions src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.scss
Original file line number Diff line number Diff line change
@@ -1,103 +1,121 @@
@use '@material/theme/theme-color' as mdc-theme-color;
@use '@material/switch/deprecated' as mdc-switch with ($deprecated-suffix: '');
@use '@material/form-field' as mdc-form-field;
@use 'sass:map';
@use 'sass:color';
@use '@material/switch/switch-theme' as mdc-switch-theme;
@use '@material/theme/color-palette' as mdc-color-palette;
@use '@material/form-field' as mdc-form-field;
@use '../mdc-helpers/mdc-helpers';
@use '../../material/core/typography/typography';
@use '../../material/core/theming/theming';
@use '../../material/core/theming/palette';

// Generates all color mapping for the properties that only change based on the theme.
@function _get-theme-base-map($is-dark) {
$on-surface: if($is-dark, mdc-color-palette.$grey-100, mdc-color-palette.$grey-800);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a comment that explains where these color choices come from? E.g. why is $on-surface gray-100/gray-800?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is the part where I had to make some assumptions. The light theme values come from MDC's $light-theme variable one-to-one, but since MDC didn't provide a dark theme, I had to come up with the values myself. I did try to follow a pattern (e.g. 800 and 100 are on different ends of the palette), but it's not foolproof.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chatted with the MDC team about the status/plan here. Summary is:

  • For the new version of Material Design, there will be a set of token values OSS we can use, but they're not ready yet.
  • We have a set of token values internally for Google's specific brand configuration, but those aren't public
  • The MDC team is picking as reasonable values as they can for 2018-era Material Design (M2) tokens, but they didn't do dark theme values since we're the only ones using them, so it's reasonable for us to decide them on our own.

The tokens are split into two parts: system tokens (used across multiple components) and component-specific tokens. I think we should create a Sass file in material/core for the baseline token values, and then create a another file per component with the tokens for that specific spec version. So, something like

├── material/core/theming
│   ├── _system-tokens-2018.scss

├── material/slide-toggle
│   ├── _slide-toggle-tokens-2018.scss

We should make sure to comment which values we're just picking ourselves and which we're sourcing from somewhere else.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know which ones are system tokens and which ones are slide toggle-specific? I can make some reasonable guesses, but it would be better if there was a list.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The format is a little rough, but this is the stuff currently considered system styles that we care about (there was also some shape stuff that we don't care about yet)
/** COLOR */

// Background
$background;

// Error
$error;

// Error state content
$error-state-content;

// Error state layer
$error-state-layer;

// Hairline
$hairline;

// Inverse on surface
$inverse-on-surface;

// Inverse primary
$inverse-primary;

// Inverse primary state content
$inverse-primary-state-content;

// Inverse primary state layer
$inverse-primary-state-layer;

// Inverse surface
$inverse-surface;

// On background
$on-background;

// On error
$on-error;

// On error state content
$on-error-state-content;

// On error state layer
$on-error-state-layer;

// On primary
$on-primary;

// On primary state content
$on-primary-state-content;

// On primary state layer
$on-primary-state-layer;

// On secondary
$on-secondary;

// On secondary state content
$on-secondary-state-content;

// On secondary state layer
$on-secondary-state-layer;

// On surface
$on-surface;

// On surface state content
$on-surface-state-content;

// On surface state layer
$on-surface-state-layer;

// On surface variant
$on-surface-variant;

// Primary
$primary;

// Primary state content
$primary-state-content;

// Primary state layer
$primary-state-layer;

// Primary variant
$primary-variant;

// Secondary
$secondary;

// Secondary variant
$secondary-variant;

// Shadow color
$shadow;

// Surface
$surface;

// Textfield error
$textfield-error;

// Textfield hairline
$textfield-hairline;

// Textfield on surface variant
$textfield-on-surface-variant;

// Textfield primary
$textfield-primary;

// Textfield state layer
$textfield-state-layer;

// Textfield surface
$textfield-surface;

/** ANIMATION */

// Casual duration
$duration-long1;

// Slow duration
$duration-long2;

// Quick duration
$duration-medium1;

// Standard duration
$duration-medium2;

// Flash duration
$duration-short1;

// Rapid duration
$duration-short2;

// Accelerated easing (in)
$easing-accelerated;

// Decelerated easing (out)
$easing-decelerated;

// Emphasized
$easing-emphasized;

// Linear easing
$easing-linear;

// Standard easing (in and out)
$easing-standard;

/** TYPOGRAPHY */

// Body 1
$body1: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Body 1 font name
$body1-font;

// Body 1 line height
$body1-line-height;

// Body 1 font size
$body1-size;

// Body 1 font tracking
$body1-tracking;

// Body 1 font weight
$body1-weight;

// Body 2
$body2: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Body 2 font name
$body2-font;

// Body 2 line height
$body2-line-height;

// Body 2 font size
$body2-size;

// Body 2 font tracking
$body2-tracking;

// Body 2 font weight
$body2-weight;

// Button
$button: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Button font name
$button-font;

// Button line height
$button-line-height;

// Button font size
$button-size;

// Button font tracking
$button-tracking;

// Button font weight
$button-weight;

// Caption
$caption: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Caption font name
$caption-font;

// Caption line height
$caption-line-height;

// Caption font size
$caption-size;

// Caption font tracking
$caption-tracking;

// Caption font weight
$caption-weight;

// Display 1
$display1: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Display 1 font name
$display1-font;

// Display 1 line height
$display1-line-height;

// Display 1 font size
$display1-size;

// Display 1 font tracking
$display1-tracking;

// Display 1 font weight
$display1-weight;

// Display 2
$display2: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Display 2 font name
$display2-font;

// Display 2 line height
$display2-line-height;

// Display 2 font size
$display2-size;

// Display 2 font tracking
$display2-tracking;

// Display 2 font weight
$display2-weight;

// Display 3
$display3: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Display 3 font name
$display3-font;

// Display 3 line height
$display3-line-height;

// Display 3 font size
$display3-size;

// Display 3 font tracking
$display3-tracking;

// Display 3 font weight
$display3-weight;

// Headline 1
$headline1: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 1 font name
$headline1-font;

// Headline 1 line height
$headline1-line-height;

// Headline 1 font size
$headline1-size;

// Headline 1 font tracking
$headline1-tracking;

// Headline 1 font weight
$headline1-weight;

// Headline 2
$headline2: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 2 font name
$headline2-font;

// Headline 2 line height
$headline2-line-height;

// Headline 2 font size
$headline2-size;

// Headline 2 font tracking
$headline2-tracking;

// Headline 2 font weight
$headline2-weight;

// Headline 3
$headline3: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 3 font name
$headline3-font;

// Headline 3 line height
$headline3-line-height;

// Headline 3 font size
$headline3-size;

// Headline 3 font tracking
$headline3-tracking;

// Headline 3 font weight
$headline3-weight;

// Headline 4
$headline4: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 4 font name
$headline4-font;

// Headline 4 line height
$headline4-line-height;

// Headline 4 font size
$headline4-size;

// Headline 4 font tracking
$headline4-tracking;

// Headline 4 font weight
$headline4-weight;

// Headline 5
$headline5: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 5 font name
$headline5-font;

// Headline 5 line height
$headline5-line-height;

// Headline 5 font size
$headline5-size;

// Headline 5 font tracking
$headline5-tracking;

// Headline 5 font weight
$headline5-weight;

// Headline 6
$headline6: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Headline 6 font name
$headline6-font;

// Headline 6 line height
$headline6-line-height;

// Headline 6 font size
$headline6-size;

// Headline 6 font tracking
$headline6-tracking;

// Headline 6 font weight
$headline6-weight;

// Overline
$overline: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Overline font name
$overline-font;

// Overline line height
$overline-line-height;

// Overline font size
$overline-size;

// Overline text transform
$overline-text-transform;

// Overline font tracking
$overline-tracking;

// Overline font weight
$overline-weight;

// Subhead 1
$subhead1: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Subhead 1 font name
$subhead1-font;

// Subhead 1 line height
$subhead1-line-height;

// Subhead 1 font size
$subhead1-size;

// Subhead 1 font tracking
$subhead1-tracking;

// Subhead 1 font weight
$subhead1-weight;

// Subhead 2
$subhead2: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Subhead 2 font name
$subhead2-font;

// Subhead 2 line height
$subhead2-line-height;

// Subhead 2 font size
$subhead2-size;

// Subhead 2 font tracking
$subhead2-tracking;

// Subhead 2 font weight
$subhead2-weight;

// Subtitle 1
$subtitle1: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Subtitle 1 font name
$subtitle1-font;

// Subtitle 1 line height
$subtitle1-line-height;

// Subtitle 1 font size
$subtitle1-size;

// Subtitle 1 font tracking
$subtitle1-tracking;

// Subtitle 1 font weight
$subtitle1-weight;

// Subtitle 2
$subtitle2: (
font-family,
font-size,
font-weight,
letter-spacing,
line-height,
text-transform,
);

// Subtitle 2 font name
$subtitle2-font;

// Subtitle 2 line height
$subtitle2-line-height;

// Subtitle 2 font size
$subtitle2-size;

// Subtitle 2 font tracking
$subtitle2-tracking;

// Subtitle 2 font weight
$subtitle2-weight;

/** ELEVATION */

// 0
$level0;

// +1
$level1;

// +2
$level2;

// +3
$level3;

// +4
$level4;

// +5
$level5;

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for listing it out. Since this PR is pretty large already, would it be ok if I added these in a follow-up?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that's reasonable, I just want to make sure we do this before adding another component on top of the tokens system

$hairline: if($is-dark, mdc-color-palette.$grey-500, mdc-color-palette.$grey-300);
$on-surface-variant: if($is-dark, mdc-color-palette.$grey-200, mdc-color-palette.$grey-700);
$on-surface-state-content: if($is-dark, mdc-color-palette.$grey-50, mdc-color-palette.$grey-900);
$disabled-handle-color: mdc-color-palette.$grey-800;
$selected-icon-color: mdc-color-palette.$grey-100;
$icon-color: if($is-dark, mdc-color-palette.$grey-800, mdc-color-palette.$grey-100);

@return (
disabled-selected-handle-color: $disabled-handle-color,
disabled-unselected-handle-color: $disabled-handle-color,

disabled-selected-track-color: $on-surface,
disabled-unselected-track-color: $on-surface,
unselected-focus-state-layer-color: $on-surface,
unselected-pressed-state-layer-color: $on-surface,
unselected-hover-state-layer-color: $on-surface,

unselected-focus-track-color: $hairline,
unselected-hover-track-color: $hairline,
unselected-pressed-track-color: $hairline,
unselected-track-color: $hairline,

unselected-focus-handle-color: $on-surface-state-content,
unselected-hover-handle-color: $on-surface-state-content,
unselected-pressed-handle-color: $on-surface-state-content,

handle-surface-color: surface,
unselected-handle-color: $on-surface-variant,

selected-icon-color: $selected-icon-color,
disabled-selected-icon-color: $icon-color,
disabled-unselected-icon-color: $icon-color,
unselected-icon-color: $icon-color,
);
}

// Generates the mapping for the properties that change based on the slide toggle color.
@function _get-theme-color-map($color-palette) {
$state-content: color.scale($color-palette, $blackness: 50%);
$inverse: color.scale($color-palette, $lightness: 75%);

@return (
selected-focus-state-layer-color: $color-palette,
selected-handle-color: $color-palette,
selected-hover-state-layer-color: $color-palette,
selected-pressed-state-layer-color: $color-palette,

selected-focus-handle-color: $state-content,
selected-hover-handle-color: $state-content,
selected-pressed-handle-color: $state-content,

selected-focus-track-color: $inverse,
selected-hover-track-color: $inverse,
selected-pressed-track-color: $inverse,
selected-track-color: $inverse,
);
}

@mixin color($config-or-theme) {
$config: theming.get-color-config($config-or-theme);
$primary: theming.get-color-from-palette(map.get($config, primary));
$accent: theming.get-color-from-palette(map.get($config, accent));
$warn: theming.get-color-from-palette(map.get($config, warn));

// Save original values of MDC global variables. We need to save these so we can restore the
// variables to their original values and prevent unintended side effects from using this mixin.
$orig-baseline-theme-color: mdc-switch.$baseline-theme-color;
$orig-toggled-off-thumb-color: mdc-switch.$toggled-off-thumb-color;
$orig-toggled-off-track-color: mdc-switch.$toggled-off-track-color;
$orig-disabled-thumb-color: mdc-switch.$disabled-thumb-color;
$orig-disabled-track-color: mdc-switch.$disabled-track-color;
$is-dark: map.get($config, is-dark);

@include mdc-helpers.mat-using-mdc-theme($config) {
mdc-switch.$baseline-theme-color: primary;
mdc-switch.$toggled-off-thumb-color: mdc-theme-color.prop-value(surface);
mdc-switch.$toggled-off-track-color: mdc-theme-color.prop-value(on-surface);
mdc-switch.$disabled-thumb-color: mdc-theme-color.prop-value(surface);
mdc-switch.$disabled-track-color: mdc-theme-color.prop-value(on-surface);

// MDC's switch doesn't support a `color` property. We add support
// for it by adding a CSS class for accent and warn style.
.mat-mdc-slide-toggle {
@include mdc-form-field.core-styles($query: mdc-helpers.$mat-theme-styles-query);

.mdc-switch__thumb-underlay::after, .mat-ripple-element {
background: mdc-switch.$toggled-off-ripple-color;
}
@include mdc-switch-theme.theme(_get-theme-base-map($is-dark));

&.mat-primary {
@include mdc-switch.without-ripple($query: mdc-helpers.$mat-theme-styles-query);
@include mdc-switch-theme.theme(_get-theme-color-map($primary));
}

&.mat-accent {
mdc-switch.$baseline-theme-color: secondary;
@include mdc-switch.without-ripple($query: mdc-helpers.$mat-theme-styles-query);
@include mdc-switch-theme.theme(_get-theme-color-map($accent));
}

&.mat-warn {
mdc-switch.$baseline-theme-color: error;
@include mdc-switch.without-ripple($query: mdc-helpers.$mat-theme-styles-query);
}
}

// The ripple color matches the palette only when it's checked.
.mat-mdc-slide-toggle-checked {
.mdc-switch__thumb-underlay::after, .mat-ripple-element {
background: $primary;
}

&.mat-accent {
.mdc-switch__thumb-underlay::after, .mat-ripple-element {
background: $accent;
}
}

&.mat-warn {
.mdc-switch__thumb-underlay::after, .mat-ripple-element {
background: $warn;
}
@include mdc-switch-theme.theme(_get-theme-color-map($warn));
}
}
}

// Restore original values of MDC global variables.
mdc-switch.$baseline-theme-color: $orig-baseline-theme-color;
mdc-switch.$toggled-off-thumb-color: $orig-toggled-off-thumb-color;
mdc-switch.$toggled-off-track-color: $orig-toggled-off-track-color;
mdc-switch.$disabled-thumb-color: $orig-disabled-thumb-color;
mdc-switch.$disabled-track-color: $orig-disabled-track-color;
}

@mixin typography($config-or-theme) {
$config: typography.private-typography-to-2018-config(
theming.get-typography-config($config-or-theme));
@include mdc-helpers.mat-using-mdc-typography($config) {
@include mdc-switch.without-ripple($query: mdc-helpers.$mat-typography-styles-query);
@include mdc-form-field.core-styles($query: mdc-helpers.$mat-typography-styles-query);
}
}

@mixin density($config-or-theme) {
$density-scale: theming.get-density-config($config-or-theme);
.mat-mdc-slide-toggle .mdc-switch {
@include mdc-switch.density($density-scale, $query: mdc-helpers.$mat-base-styles-query);
.mat-mdc-slide-toggle {
@include mdc-switch-theme.theme(mdc-switch-theme.density($density-scale));
}
}

@mixin theme($theme-or-color-config) {
$theme: theming.private-legacy-get-theme($theme-or-color-config);

@include theming.private-check-duplicate-theme-styles($theme, 'mat-mdc-slide-toggle') {
$color: theming.get-color-config($theme);
$density: theming.get-density-config($theme);
Expand Down
46 changes: 23 additions & 23 deletions src/material-experimental/mdc-slide-toggle/slide-toggle.e2e.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {expectToExist} from '../../cdk/testing/private/e2e';


describe('MDC-based slide-toggle', () => {
const getInput = () => element(by.css('#normal-slide-toggle input'));
const getButton = () => element(by.css('#normal-slide-toggle button'));
const getNormalToggle = () => element(by.css('#normal-slide-toggle'));

beforeEach(async () => await browser.get('mdc-slide-toggle'));
Expand All @@ -13,44 +13,44 @@ describe('MDC-based slide-toggle', () => {
});

it('should change the checked state on click', async () => {
const inputEl = getInput();
const buttonEl = getButton();

expect(await inputEl.getAttribute('checked'))
.toBeFalsy('Expect slide-toggle to be unchecked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('false', 'Expect slide-toggle to be unchecked');

await getNormalToggle().click();

expect(await inputEl.getAttribute('checked'))
.toBeTruthy('Expect slide-toggle to be checked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('true', 'Expect slide-toggle to be checked');
});

it('should change the checked state on click', async () => {
const inputEl = getInput();
const buttonEl = getButton();

expect(await inputEl.getAttribute('checked'))
.toBeFalsy('Expect slide-toggle to be unchecked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('false', 'Expect slide-toggle to be unchecked');

await getNormalToggle().click();

expect(await inputEl.getAttribute('checked'))
.toBeTruthy('Expect slide-toggle to be checked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('true', 'Expect slide-toggle to be checked');
});

it('should not change the checked state on click when disabled', async () => {
const inputEl = getInput();
const buttonEl = getButton();

expect(await inputEl.getAttribute('checked'))
.toBeFalsy('Expect slide-toggle to be unchecked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('false', 'Expect slide-toggle to be unchecked');

await element(by.css('#disabled-slide-toggle')).click();

expect(await inputEl.getAttribute('checked'))
.toBeFalsy('Expect slide-toggle to be unchecked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('false', 'Expect slide-toggle to be unchecked');
});

it('should move the thumb on state change', async () => {
const slideToggleEl = getNormalToggle();
const thumbEl = element(by.css('#normal-slide-toggle .mdc-switch__thumb-underlay'));
const thumbEl = element(by.css('#normal-slide-toggle .mdc-switch__handle'));
const previousPosition = await thumbEl.getLocation();

await slideToggleEl.click();
Expand All @@ -61,15 +61,15 @@ describe('MDC-based slide-toggle', () => {
});

it('should toggle the slide-toggle on space key', async () => {
const inputEl = getInput();
const buttonEl = getButton();

expect(await inputEl.getAttribute('checked'))
.toBeFalsy('Expect slide-toggle to be unchecked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('false', 'Expect slide-toggle to be unchecked');

await inputEl.sendKeys(Key.SPACE);
await buttonEl.sendKeys(Key.SPACE);

expect(await inputEl.getAttribute('checked'))
.toBeTruthy('Expect slide-toggle to be checked');
expect(await buttonEl.getAttribute('aria-checked'))
.toBe('true', 'Expect slide-toggle to be checked');
});

});
66 changes: 42 additions & 24 deletions src/material-experimental/mdc-slide-toggle/slide-toggle.html
Original file line number Diff line number Diff line change
@@ -1,33 +1,51 @@
<div class="mdc-form-field"
[class.mdc-form-field--align-end]="labelPosition == 'before'">
<div class="mdc-switch mat-mdc-switch" #switch>
<button
class="mdc-switch"
role="switch"
type="button"
[class.mdc-switch--selected]="checked"
[class.mdc-switch--unselected]="!checked"
[tabIndex]="tabIndex"
[disabled]="disabled"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tested the demo (looking at aria-required), I was able to toggle the disabled slide-toggles with the keyboard.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried it and I couldn't reproduce. Are you sure that you weren't interacting with the top slide toggle which also changes the value of the disabled one?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see what happened. With VoiceOver on, screen-reader navigation moved onto the disabled toggle, but browser focus stayed on the enabled one. Nvm.

[attr.id]="buttonId"
[attr.name]="name"
[attr.aria-label]="ariaLabel"
[attr.aria-labelledby]="_getAriaLabelledBy()"
[attr.aria-describedby]="ariaDescribedby"
[attr.aria-required]="required"
(click)="_handleClick($event)"
#switch>
<div class="mdc-switch__track"></div>
<div class="mdc-switch__thumb-underlay mat-mdc-focus-indicator">
<div class="mat-mdc-slide-toggle-ripple" mat-ripple
[matRippleTrigger]="switch"
[matRippleDisabled]="disableRipple || disabled"
[matRippleCentered]="true"
[matRippleAnimation]="_rippleAnimation"></div>
<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"
[attr.aria-describedby]="ariaDescribedby"
(change)="_onChangeEvent($event)"
(click)="_onInputClick($event)">
<div class="mdc-switch__handle-track">
<div class="mdc-switch__handle">
<div class="mdc-switch__shadow">
<div class="mdc-elevation-overlay"></div>
</div>
<div class="mdc-switch__ripple">
<div class="mat-mdc-slide-toggle-ripple mat-mdc-focus-indicator" mat-ripple
[matRippleTrigger]="switch"
[matRippleDisabled]="disableRipple || disabled"
[matRippleCentered]="true"
[matRippleAnimation]="_rippleAnimation"></div>
</div>
<div class="mdc-switch__icons">
<svg class="mdc-switch__icon mdc-switch__icon--on" viewBox="0 0 24 24">
<path d="M19.69,5.23L8.96,15.96l-4.23-4.23L2.96,13.5l6,6L21.46,7L19.69,5.23z" />
</svg>
<svg class="mdc-switch__icon mdc-switch__icon--off" viewBox="0 0 24 24">
<path d="M20 13H4v-2h16v2z" />
</svg>
</div>
</div>
</div>
</div>
</button>

<label [for]="inputId">
<!--
Clicking on the label will trigger another click event from the button.
Stop propagation here so other listeners further up in the DOM don't execute twice.
-->
<label [for]="buttonId" [attr.id]="_labelId" (click)="$event.stopPropagation()">
<ng-content></ng-content>
</label>
</div>
Loading