diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 70fdb7e15027..264aa6fcbbd1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -28,7 +28,7 @@ /src/material/legacy-select/** @crisbeto /src/material/select/** @crisbeto /src/material/sidenav/** @mmalerba -/src/material/slide-toggle/** @devversion +/src/material/legacy-slide-toggle/** @devversion /src/material/slider/** @mmalerba /src/material/snack-bar/** @andrewseguin @crisbeto /src/material/sort/** @andrewseguin @@ -126,7 +126,7 @@ /src/material/progress-bar/** @andrewseguin /src/material-experimental/mdc-radio/** @mmalerba /src/material-experimental/mdc-snack-bar/** @andrewseguin -/src/material-experimental/mdc-slide-toggle/** @crisbeto +/src/material/slide-toggle/** @crisbeto /src/material-experimental/mdc-slider/** @devversion /src/material-experimental/mdc-tabs/** @crisbeto /src/material-experimental/mdc-tooltip/** @crisbeto @@ -341,7 +341,7 @@ /tools/public_api_guard/material/radio** @andrewseguin @devversion /tools/public_api_guard/material/legacy-select** @crisbeto /tools/public_api_guard/material/sidenav** @mmalerba -/tools/public_api_guard/material/slide-toggle** @devversion +/tools/public_api_guard/material/legacy-slide-toggle** @devversion /tools/public_api_guard/material/slider** @mmalerba /tools/public_api_guard/material/snack-bar** @andrewseguin @crisbeto /tools/public_api_guard/material/sort** @andrewseguin diff --git a/.ng-dev/commit-message.mts b/.ng-dev/commit-message.mts index 77908cd46f93..57ae590dafa1 100644 --- a/.ng-dev/commit-message.mts +++ b/.ng-dev/commit-message.mts @@ -53,7 +53,7 @@ export const commitMessage: CommitMessageConfig = { 'material/progress-bar', 'material-experimental/mdc-progress-spinner', 'material-experimental/mdc-radio', - 'material-experimental/mdc-slide-toggle', + 'material/slide-toggle', 'material-experimental/mdc-slider', 'material-experimental/mdc-snack-bar', 'material-experimental/mdc-table', @@ -99,7 +99,7 @@ export const commitMessage: CommitMessageConfig = { 'material/select', 'material/legacy-select', 'material/sidenav', - 'material/slide-toggle', + 'material/legacy-slide-toggle', 'material/slider', 'material/snack-bar', 'material/sort', diff --git a/goldens/tsec-exemption.json b/goldens/tsec-exemption.json index dabf1efe6262..035ce976b9dd 100644 --- a/goldens/tsec-exemption.json +++ b/goldens/tsec-exemption.json @@ -6,7 +6,7 @@ "../src/material/checkbox/checkbox.ts", "../src/material-experimental/mdc-list/interactive-list-base.ts", "../src/material-experimental/mdc-progress-spinner/progress-spinner.ts", - "../src/material-experimental/mdc-slide-toggle/slide-toggle.ts", + "../src/material/slide-toggle/slide-toggle.ts", "../src/material/icon/icon-registry.ts", "../src/material/icon/icon.ts" ] diff --git a/integration/mdc-migration/golden/src/app/app.module.ts b/integration/mdc-migration/golden/src/app/app.module.ts index fe9f79aa97fb..d308aa731bbb 100644 --- a/integration/mdc-migration/golden/src/app/app.module.ts +++ b/integration/mdc-migration/golden/src/app/app.module.ts @@ -19,7 +19,7 @@ import {MatProgressBarModule} from '@angular/material-experimental/mdc-progress- import {MatProgressSpinnerModule} from '@angular/material-experimental/mdc-progress-spinner'; import {MatRadioModule} from '@angular/material-experimental/mdc-radio'; import {MatSelectModule} from '@angular/material-experimental/mdc-select'; -import {MatSlideToggleModule} from '@angular/material-experimental/mdc-slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {MatSliderModule} from '@angular/material-experimental/mdc-slider'; import {MatSnackBarModule} from '@angular/material/snack-bar'; import {MatTableModule} from '@angular/material-experimental/mdc-table'; diff --git a/integration/mdc-migration/golden/src/app/components/slide-toggle/slide-toggle.component.spec.ts b/integration/mdc-migration/golden/src/app/components/slide-toggle/slide-toggle.component.spec.ts index 0bbebb232f69..f0dd9f1f7996 100644 --- a/integration/mdc-migration/golden/src/app/components/slide-toggle/slide-toggle.component.spec.ts +++ b/integration/mdc-migration/golden/src/app/components/slide-toggle/slide-toggle.component.spec.ts @@ -1,6 +1,6 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {SlideToggleComponent} from './slide-toggle.component'; describe('SlideToggleComponent', () => { diff --git a/integration/mdc-migration/golden/src/styles.scss b/integration/mdc-migration/golden/src/styles.scss index e001d8d7a66b..a1d51198c7ec 100644 --- a/integration/mdc-migration/golden/src/styles.scss +++ b/integration/mdc-migration/golden/src/styles.scss @@ -62,8 +62,8 @@ $sample-project-theme: mat.define-light-theme(( @include mat.mdc-select-typography($sample-project-theme); @include mat.mdc-core-theme($sample-project-theme); @include mat.mdc-core-typography($sample-project-theme); -@include mat.mdc-slide-toggle-theme($sample-project-theme); -@include mat.mdc-slide-toggle-typography($sample-project-theme); +@include mat.slide-toggle-theme($sample-project-theme); +@include mat.slide-toggle-typography($sample-project-theme); @include mat.mdc-slider-theme($sample-project-theme); @include mat.mdc-slider-typography($sample-project-theme); @include mat.mdc-snack-bar-theme($sample-project-theme); diff --git a/integration/mdc-migration/sample-project/src/app/app.module.ts b/integration/mdc-migration/sample-project/src/app/app.module.ts index aa2071676861..f339776508c7 100644 --- a/integration/mdc-migration/sample-project/src/app/app.module.ts +++ b/integration/mdc-migration/sample-project/src/app/app.module.ts @@ -19,7 +19,7 @@ import {MatProgressBarModule} from '@angular/material/progress-bar'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; import {MatRadioModule} from '@angular/material/radio'; import {MatSelectModule} from '@angular/material/select'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {MatSliderModule} from '@angular/material/slider'; import {MatSnackBarModule} from '@angular/material/snack-bar'; import {MatTableModule} from '@angular/material/table'; diff --git a/integration/mdc-migration/sample-project/src/app/components/slide-toggle/slide-toggle.component.spec.ts b/integration/mdc-migration/sample-project/src/app/components/slide-toggle/slide-toggle.component.spec.ts index 0bbebb232f69..f0dd9f1f7996 100644 --- a/integration/mdc-migration/sample-project/src/app/components/slide-toggle/slide-toggle.component.spec.ts +++ b/integration/mdc-migration/sample-project/src/app/components/slide-toggle/slide-toggle.component.spec.ts @@ -1,6 +1,6 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {SlideToggleComponent} from './slide-toggle.component'; describe('SlideToggleComponent', () => { diff --git a/integration/mdc-migration/sample-project/src/styles.scss b/integration/mdc-migration/sample-project/src/styles.scss index 067b638b2b55..a6250d83c064 100644 --- a/integration/mdc-migration/sample-project/src/styles.scss +++ b/integration/mdc-migration/sample-project/src/styles.scss @@ -42,7 +42,7 @@ $sample-project-theme: mat.define-light-theme(( @include mat.progress-spinner-theme($sample-project-theme); @include mat.radio-theme($sample-project-theme); @include mat.select-theme($sample-project-theme); -@include mat.slide-toggle-theme($sample-project-theme); +@include mat.legacy-slide-toggle-theme($sample-project-theme); @include mat.slider-theme($sample-project-theme); @include mat.snack-bar-theme($sample-project-theme); @include mat.table-theme($sample-project-theme); diff --git a/integration/size-test/material/chips/BUILD.bazel b/integration/size-test/material/chips/BUILD.bazel index 4a2dc13e56c0..52540a8a6b99 100644 --- a/integration/size-test/material/chips/BUILD.bazel +++ b/integration/size-test/material/chips/BUILD.bazel @@ -3,5 +3,5 @@ load("//integration/size-test:index.bzl", "size_test") size_test( name = "basic", file = "basic.ts", - deps = ["//src/material/chips"], + deps = ["//src/material/legacy-chips"], ) diff --git a/src/components-examples/material/autocomplete/BUILD.bazel b/src/components-examples/material/autocomplete/BUILD.bazel index e29af751ecff..0bec19fe9795 100644 --- a/src/components-examples/material/autocomplete/BUILD.bazel +++ b/src/components-examples/material/autocomplete/BUILD.bazel @@ -20,7 +20,7 @@ ng_module( "//src/material/legacy-autocomplete/testing", "//src/material/legacy-form-field", "//src/material/legacy-input", - "//src/material/slide-toggle", + "//src/material/legacy-slide-toggle", "@npm//@angular/forms", "@npm//@angular/platform-browser", "@npm//@angular/platform-browser-dynamic", diff --git a/src/components-examples/material/autocomplete/index.ts b/src/components-examples/material/autocomplete/index.ts index 0b235db1cb99..34c6dbf2473b 100644 --- a/src/components-examples/material/autocomplete/index.ts +++ b/src/components-examples/material/autocomplete/index.ts @@ -4,7 +4,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatLegacyAutocompleteModule} from '@angular/material/legacy-autocomplete'; import {MatLegacyFormFieldModule} from '@angular/material/legacy-form-field'; import {MatLegacyInputModule} from '@angular/material/legacy-input'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {AutocompleteAutoActiveFirstOptionExample} from './autocomplete-auto-active-first-option/autocomplete-auto-active-first-option-example'; import {AutocompleteDisplayExample} from './autocomplete-display/autocomplete-display-example'; import {AutocompleteFilterExample} from './autocomplete-filter/autocomplete-filter-example'; @@ -42,7 +42,7 @@ const EXAMPLES = [ MatLegacyAutocompleteModule, MatLegacyFormFieldModule, MatLegacyInputModule, - MatSlideToggleModule, + MatLegacySlideToggleModule, FormsModule, ReactiveFormsModule, ], diff --git a/src/components-examples/material/slide-toggle/BUILD.bazel b/src/components-examples/material/slide-toggle/BUILD.bazel index 61e277aa0da5..8632f94df056 100644 --- a/src/components-examples/material/slide-toggle/BUILD.bazel +++ b/src/components-examples/material/slide-toggle/BUILD.bazel @@ -18,9 +18,9 @@ ng_module( "//src/material/button", "//src/material/legacy-card", "//src/material/legacy-checkbox", + "//src/material/legacy-slide-toggle", + "//src/material/legacy-slide-toggle/testing", "//src/material/radio", - "//src/material/slide-toggle", - "//src/material/slide-toggle/testing", "@npm//@angular/forms", "@npm//@angular/platform-browser", "@npm//@angular/platform-browser-dynamic", @@ -44,8 +44,8 @@ ng_test_library( ":slide-toggle", "//src/cdk/testing", "//src/cdk/testing/testbed", - "//src/material/slide-toggle", - "//src/material/slide-toggle/testing", + "//src/material/legacy-slide-toggle", + "//src/material/legacy-slide-toggle/testing", "@npm//@angular/forms", "@npm//@angular/platform-browser-dynamic", ], diff --git a/src/components-examples/material/slide-toggle/index.ts b/src/components-examples/material/slide-toggle/index.ts index a97c2153116f..9eadeb7f6802 100644 --- a/src/components-examples/material/slide-toggle/index.ts +++ b/src/components-examples/material/slide-toggle/index.ts @@ -4,7 +4,7 @@ import {MatButtonModule} from '@angular/material/button'; import {MatLegacyCardModule} from '@angular/material/legacy-card'; import {MatLegacyCheckboxModule} from '@angular/material/legacy-checkbox'; import {MatRadioModule} from '@angular/material/radio'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {SlideToggleConfigurableExample} from './slide-toggle-configurable/slide-toggle-configurable-example'; import {SlideToggleFormsExample} from './slide-toggle-forms/slide-toggle-forms-example'; import {SlideToggleOverviewExample} from './slide-toggle-overview/slide-toggle-overview-example'; @@ -31,7 +31,7 @@ const EXAMPLES = [ MatLegacyCardModule, MatLegacyCheckboxModule, MatRadioModule, - MatSlideToggleModule, + MatLegacySlideToggleModule, ReactiveFormsModule, ], declarations: EXAMPLES, diff --git a/src/components-examples/material/slide-toggle/slide-toggle-harness/slide-toggle-harness-example.spec.ts b/src/components-examples/material/slide-toggle/slide-toggle-harness/slide-toggle-harness-example.spec.ts index ca7d37bfd678..e57da2bc73bc 100644 --- a/src/components-examples/material/slide-toggle/slide-toggle-harness/slide-toggle-harness-example.spec.ts +++ b/src/components-examples/material/slide-toggle/slide-toggle-harness/slide-toggle-harness-example.spec.ts @@ -1,8 +1,8 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; -import {MatSlideToggleHarness} from '@angular/material/slide-toggle/testing'; +import {MatLegacySlideToggleHarness} from '@angular/material/legacy-slide-toggle/testing'; import {HarnessLoader} from '@angular/cdk/testing'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {SlideToggleHarnessExample} from './slide-toggle-harness-example'; import {ReactiveFormsModule} from '@angular/forms'; @@ -12,7 +12,7 @@ describe('SlideToggleHarnessExample', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [MatSlideToggleModule, ReactiveFormsModule], + imports: [MatLegacySlideToggleModule, ReactiveFormsModule], declarations: [SlideToggleHarnessExample], }).compileComponents(); fixture = TestBed.createComponent(SlideToggleHarnessExample); @@ -21,33 +21,37 @@ describe('SlideToggleHarnessExample', () => { }); it('should load all slide-toggle harnesses', async () => { - const slideToggles = await loader.getAllHarnesses(MatSlideToggleHarness); + const slideToggles = await loader.getAllHarnesses(MatLegacySlideToggleHarness); expect(slideToggles.length).toBe(2); }); it('should load slide-toggle with name', async () => { const slideToggles = await loader.getAllHarnesses( - MatSlideToggleHarness.with({name: 'first-name'}), + MatLegacySlideToggleHarness.with({name: 'first-name'}), ); expect(slideToggles.length).toBe(1); expect(await slideToggles[0].getLabelText()).toBe('First'); }); it('should get disabled state', async () => { - const [enabledToggle, disabledToggle] = await loader.getAllHarnesses(MatSlideToggleHarness); + const [enabledToggle, disabledToggle] = await loader.getAllHarnesses( + MatLegacySlideToggleHarness, + ); expect(await enabledToggle.isDisabled()).toBe(false); expect(await disabledToggle.isDisabled()).toBe(true); }); it('should get label text', async () => { - const [firstToggle, secondToggle] = await loader.getAllHarnesses(MatSlideToggleHarness); + const [firstToggle, secondToggle] = await loader.getAllHarnesses(MatLegacySlideToggleHarness); expect(await firstToggle.getLabelText()).toBe('First'); expect(await secondToggle.getLabelText()).toBe('Second'); }); it('should toggle slide-toggle', async () => { fixture.componentInstance.disabled = false; - const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses(MatSlideToggleHarness); + const [checkedToggle, uncheckedToggle] = await loader.getAllHarnesses( + MatLegacySlideToggleHarness, + ); await checkedToggle.toggle(); await uncheckedToggle.toggle(); expect(await checkedToggle.isChecked()).toBe(false); diff --git a/src/dev-app/expansion/BUILD.bazel b/src/dev-app/expansion/BUILD.bazel index 498bb219c9e0..64c31e0e74b6 100644 --- a/src/dev-app/expansion/BUILD.bazel +++ b/src/dev-app/expansion/BUILD.bazel @@ -16,8 +16,8 @@ ng_module( "//src/material/legacy-checkbox", "//src/material/legacy-form-field", "//src/material/legacy-input", + "//src/material/legacy-slide-toggle", "//src/material/radio", - "//src/material/slide-toggle", "@npm//@angular/forms", ], ) diff --git a/src/dev-app/expansion/expansion-demo.ts b/src/dev-app/expansion/expansion-demo.ts index 946a6721a847..8bb972fca8bf 100644 --- a/src/dev-app/expansion/expansion-demo.ts +++ b/src/dev-app/expansion/expansion-demo.ts @@ -21,7 +21,7 @@ import { import {MatLegacyFormFieldModule} from '@angular/material/legacy-form-field'; import {MatLegacyInputModule} from '@angular/material/legacy-input'; import {MatRadioModule} from '@angular/material/radio'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; @Component({ selector: 'expansion-demo', @@ -38,7 +38,7 @@ import {MatSlideToggleModule} from '@angular/material/slide-toggle'; MatLegacyFormFieldModule, MatLegacyInputModule, MatRadioModule, - MatSlideToggleModule, + MatLegacySlideToggleModule, ], }) export class ExpansionDemo { diff --git a/src/dev-app/mdc-paginator/BUILD.bazel b/src/dev-app/mdc-paginator/BUILD.bazel index ee46edc32da8..c1509ffa79db 100644 --- a/src/dev-app/mdc-paginator/BUILD.bazel +++ b/src/dev-app/mdc-paginator/BUILD.bazel @@ -11,10 +11,10 @@ ng_module( ], deps = [ "//src/material-experimental/mdc-paginator", - "//src/material-experimental/mdc-slide-toggle", "//src/material/card", "//src/material/form-field", "//src/material/input", + "//src/material/slide-toggle", "@npm//@angular/forms", ], ) diff --git a/src/dev-app/mdc-paginator/mdc-paginator-demo.ts b/src/dev-app/mdc-paginator/mdc-paginator-demo.ts index 364463ab0892..e1210c310936 100644 --- a/src/dev-app/mdc-paginator/mdc-paginator-demo.ts +++ b/src/dev-app/mdc-paginator/mdc-paginator-demo.ts @@ -12,7 +12,7 @@ import {MatPaginatorModule, PageEvent} from '@angular/material-experimental/mdc- import {FormsModule} from '@angular/forms'; import {MatCardModule} from '@angular/material/card'; import {MatInputModule} from '@angular/material/input'; -import {MatSlideToggleModule} from '@angular/material-experimental/mdc-slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; @Component({ selector: 'mdc-paginator-demo', diff --git a/src/dev-app/mdc-slide-toggle/BUILD.bazel b/src/dev-app/mdc-slide-toggle/BUILD.bazel index d639bf9b7375..f3166ac18a68 100644 --- a/src/dev-app/mdc-slide-toggle/BUILD.bazel +++ b/src/dev-app/mdc-slide-toggle/BUILD.bazel @@ -10,7 +10,7 @@ ng_module( ":mdc_slide_toggle_demo_scss", ], deps = [ - "//src/material-experimental/mdc-slide-toggle", + "//src/material/slide-toggle", "@npm//@angular/forms", ], ) diff --git a/src/dev-app/mdc-slide-toggle/mdc-slide-toggle-demo.ts b/src/dev-app/mdc-slide-toggle/mdc-slide-toggle-demo.ts index a89bfb61adda..c8c50a16cda4 100644 --- a/src/dev-app/mdc-slide-toggle/mdc-slide-toggle-demo.ts +++ b/src/dev-app/mdc-slide-toggle/mdc-slide-toggle-demo.ts @@ -7,7 +7,7 @@ */ import {Component} from '@angular/core'; -import {MatSlideToggleModule} from '@angular/material-experimental/mdc-slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {FormsModule} from '@angular/forms'; @Component({ diff --git a/src/dev-app/paginator/BUILD.bazel b/src/dev-app/paginator/BUILD.bazel index 73faed899384..1aa35a0ed572 100644 --- a/src/dev-app/paginator/BUILD.bazel +++ b/src/dev-app/paginator/BUILD.bazel @@ -13,8 +13,8 @@ ng_module( "//src/material/legacy-card", "//src/material/legacy-form-field", "//src/material/legacy-input", + "//src/material/legacy-slide-toggle", "//src/material/paginator", - "//src/material/slide-toggle", "@npm//@angular/forms", ], ) diff --git a/src/dev-app/paginator/paginator-demo.ts b/src/dev-app/paginator/paginator-demo.ts index c4584b5c1b1a..7a9e34aad68e 100644 --- a/src/dev-app/paginator/paginator-demo.ts +++ b/src/dev-app/paginator/paginator-demo.ts @@ -13,7 +13,7 @@ import {FormsModule} from '@angular/forms'; import {MatLegacyCardModule} from '@angular/material/legacy-card'; import {MatLegacyFormFieldModule} from '@angular/material/legacy-form-field'; import {MatLegacyInputModule} from '@angular/material/legacy-input'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; @Component({ selector: 'paginator-demo', @@ -27,7 +27,7 @@ import {MatSlideToggleModule} from '@angular/material/slide-toggle'; MatLegacyFormFieldModule, MatLegacyInputModule, MatPaginatorModule, - MatSlideToggleModule, + MatLegacySlideToggleModule, ], }) export class PaginatorDemo { diff --git a/src/dev-app/slide-toggle/BUILD.bazel b/src/dev-app/slide-toggle/BUILD.bazel index f94359a79da1..947fd95475e0 100644 --- a/src/dev-app/slide-toggle/BUILD.bazel +++ b/src/dev-app/slide-toggle/BUILD.bazel @@ -11,7 +11,7 @@ ng_module( ], deps = [ "//src/material/button", - "//src/material/slide-toggle", + "//src/material/legacy-slide-toggle", "@npm//@angular/forms", ], ) diff --git a/src/dev-app/slide-toggle/slide-toggle-demo.ts b/src/dev-app/slide-toggle/slide-toggle-demo.ts index 958917bddd00..b472994ffca6 100644 --- a/src/dev-app/slide-toggle/slide-toggle-demo.ts +++ b/src/dev-app/slide-toggle/slide-toggle-demo.ts @@ -9,14 +9,14 @@ import {Component} from '@angular/core'; import {FormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; @Component({ selector: 'slide-toggle-demo', templateUrl: 'slide-toggle-demo.html', styleUrls: ['slide-toggle-demo.css'], standalone: true, - imports: [FormsModule, MatButtonModule, MatSlideToggleModule], + imports: [FormsModule, MatButtonModule, MatLegacySlideToggleModule], }) export class SlideToggleDemo { firstToggle: boolean; diff --git a/src/e2e-app/BUILD.bazel b/src/e2e-app/BUILD.bazel index 0fe872e74bed..94b6f38e3f72 100644 --- a/src/e2e-app/BUILD.bazel +++ b/src/e2e-app/BUILD.bazel @@ -46,7 +46,6 @@ ng_module( "//src/material-experimental/mdc-menu", "//src/material-experimental/mdc-progress-spinner", "//src/material-experimental/mdc-radio", - "//src/material-experimental/mdc-slide-toggle", "//src/material-experimental/mdc-slider", "//src/material-experimental/mdc-table", "//src/material-experimental/mdc-tabs", @@ -64,6 +63,7 @@ ng_module( "//src/material/legacy-form-field", "//src/material/legacy-input", "//src/material/legacy-progress-bar", + "//src/material/legacy-slide-toggle", "//src/material/list", "//src/material/menu", "//src/material/progress-bar", diff --git a/src/e2e-app/mdc-slide-toggle/mdc-slide-toggle-e2e-module.ts b/src/e2e-app/mdc-slide-toggle/mdc-slide-toggle-e2e-module.ts index f63918f917dd..e83745aee7dd 100644 --- a/src/e2e-app/mdc-slide-toggle/mdc-slide-toggle-e2e-module.ts +++ b/src/e2e-app/mdc-slide-toggle/mdc-slide-toggle-e2e-module.ts @@ -7,7 +7,7 @@ */ import {NgModule} from '@angular/core'; -import {MatSlideToggleModule} from '@angular/material-experimental/mdc-slide-toggle'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; import {MdcSlideToggleE2e} from './mdc-slide-toggle-e2e'; @NgModule({ diff --git a/src/e2e-app/slide-toggle/slide-toggle-e2e-module.ts b/src/e2e-app/slide-toggle/slide-toggle-e2e-module.ts index 88399587225b..4a8ac6294143 100644 --- a/src/e2e-app/slide-toggle/slide-toggle-e2e-module.ts +++ b/src/e2e-app/slide-toggle/slide-toggle-e2e-module.ts @@ -7,11 +7,11 @@ */ import {NgModule} from '@angular/core'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; import {SlideToggleE2E} from './slide-toggle-e2e'; @NgModule({ - imports: [MatSlideToggleModule], + imports: [MatLegacySlideToggleModule], declarations: [SlideToggleE2E], }) export class SlideToggleE2eModule {} diff --git a/src/material-experimental/_index.scss b/src/material-experimental/_index.scss index e1a73627801e..82d2a04bb6bf 100644 --- a/src/material-experimental/_index.scss +++ b/src/material-experimental/_index.scss @@ -34,8 +34,6 @@ mdc-progress-spinner-theme; @forward './mdc-radio/radio-theme' as mdc-radio-* show mdc-radio-color, mdc-radio-typography, mdc-radio-density, mdc-radio-theme; -@forward './mdc-slide-toggle/slide-toggle-theme' as mdc-slide-toggle-* show mdc-slide-toggle-color, - mdc-slide-toggle-typography, mdc-slide-toggle-density, mdc-slide-toggle-theme; @forward './mdc-slider/slider-theme' as mdc-slider-* show mdc-slider-color, mdc-slider-typography, mdc-slider-density, mdc-slider-theme; @forward './mdc-snack-bar/snack-bar-theme' as mdc-snack-bar-* show mdc-snack-bar-color, diff --git a/src/material-experimental/config.bzl b/src/material-experimental/config.bzl index 2f77bb3ef170..c31213ea186b 100644 --- a/src/material-experimental/config.bzl +++ b/src/material-experimental/config.bzl @@ -13,8 +13,6 @@ entryPoints = [ "mdc-progress-spinner/testing", "mdc-radio", "mdc-radio/testing", - "mdc-slide-toggle", - "mdc-slide-toggle/testing", "mdc-slider", "mdc-slider/testing", "mdc-snack-bar", diff --git a/src/material-experimental/mdc-core/color/_all-color.import.scss b/src/material-experimental/mdc-core/color/_all-color.import.scss index 75e8678b5850..85e7776804c1 100644 --- a/src/material-experimental/mdc-core/color/_all-color.import.scss +++ b/src/material-experimental/mdc-core/color/_all-color.import.scss @@ -11,13 +11,6 @@ $mat-mdc-button-mat-button-state-target; @forward '../../mdc-radio/radio-theme' as mat-mdc-radio-* hide $mat-mdc-radio-mdc-radio-baseline-theme-color, $mat-mdc-radio-mdc-radio-disabled-circle-color, $mat-mdc-radio-mdc-radio-unchecked-color; -@forward '../../mdc-slide-toggle/slide-toggle-theme' hide color, density, theme, typography; -@forward '../../mdc-slide-toggle/slide-toggle-theme' as mat-mdc-slide-toggle-* hide -$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; @forward '../../mdc-snack-bar/snack-bar-theme' hide color, density, theme, typography; @forward '../../mdc-snack-bar/snack-bar-theme' as mat-mdc-snack-bar-* hide $mat-mdc-snack-bar-mdc-snackbar-dismiss-ink-color, $mat-mdc-snack-bar-mdc-snackbar-fill-color, diff --git a/src/material-experimental/mdc-core/density/_all-density.import.scss b/src/material-experimental/mdc-core/density/_all-density.import.scss index 5da8a9a5ad14..432b7c18c573 100644 --- a/src/material-experimental/mdc-core/density/_all-density.import.scss +++ b/src/material-experimental/mdc-core/density/_all-density.import.scss @@ -11,13 +11,6 @@ $mat-mdc-button-mat-button-state-target; @forward '../../mdc-radio/radio-theme' as mat-mdc-radio-* hide $mat-mdc-radio-mdc-radio-baseline-theme-color, $mat-mdc-radio-mdc-radio-disabled-circle-color, $mat-mdc-radio-mdc-radio-unchecked-color; -@forward '../../mdc-slide-toggle/slide-toggle-theme' hide color, density, theme, typography; -@forward '../../mdc-slide-toggle/slide-toggle-theme' as mat-mdc-slide-toggle-* hide -$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; @forward '../../mdc-snack-bar/snack-bar-theme' hide color, density, theme, typography; @forward '../../mdc-snack-bar/snack-bar-theme' as mat-mdc-snack-bar-* hide $mat-mdc-snack-bar-mdc-snackbar-dismiss-ink-color, $mat-mdc-snack-bar-mdc-snackbar-fill-color, diff --git a/src/material-experimental/mdc-core/theming/BUILD.bazel b/src/material-experimental/mdc-core/theming/BUILD.bazel index 64334900a6f1..51066e02bbb4 100644 --- a/src/material-experimental/mdc-core/theming/BUILD.bazel +++ b/src/material-experimental/mdc-core/theming/BUILD.bazel @@ -27,7 +27,6 @@ sass_library( "//src/material-experimental/mdc-paginator:mdc_paginator_scss_lib", "//src/material-experimental/mdc-progress-spinner:mdc_progress_spinner_scss_lib", "//src/material-experimental/mdc-radio:mdc_radio_scss_lib", - "//src/material-experimental/mdc-slide-toggle:mdc_slide_toggle_scss_lib", "//src/material-experimental/mdc-slider:mdc_slider_scss_lib", "//src/material-experimental/mdc-snack-bar:mdc_snack_bar_scss_lib", "//src/material-experimental/mdc-table:mdc_table_scss_lib", @@ -40,6 +39,7 @@ sass_library( "//src/material/form-field:form_field_scss_lib", "//src/material/input:input_scss_lib", "//src/material/progress-bar:progress_bar_scss_lib", + "//src/material/slide-toggle:slide_toggle_scss_lib", "//src/material/tooltip:tooltip_scss_lib", ], ) diff --git a/src/material-experimental/mdc-core/theming/_all-theme.import.scss b/src/material-experimental/mdc-core/theming/_all-theme.import.scss index 856ecd893b5c..5b2ab2bceed7 100644 --- a/src/material-experimental/mdc-core/theming/_all-theme.import.scss +++ b/src/material-experimental/mdc-core/theming/_all-theme.import.scss @@ -11,13 +11,6 @@ $mat-mdc-button-mat-button-state-target; @forward '../../mdc-radio/radio-theme' as mat-mdc-radio-* hide $mat-mdc-radio-mdc-radio-baseline-theme-color, $mat-mdc-radio-mdc-radio-disabled-circle-color, $mat-mdc-radio-mdc-radio-unchecked-color; -@forward '../../mdc-slide-toggle/slide-toggle-theme' hide color, density, theme, typography; -@forward '../../mdc-slide-toggle/slide-toggle-theme' as mat-mdc-slide-toggle-* hide -$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; @forward '../../mdc-snack-bar/snack-bar-theme' hide color, density, theme, typography; @forward '../../mdc-snack-bar/snack-bar-theme' as mat-mdc-snack-bar-* hide $mat-mdc-snack-bar-mdc-snackbar-dismiss-ink-color, $mat-mdc-snack-bar-mdc-snackbar-fill-color, @@ -47,7 +40,6 @@ $mat-mdc-table-mdc-data-table-stroke-color, $mat-mdc-table-mdc-data-table-table- @import '../../mdc-list/list-theme'; @import '../../mdc-menu/menu-theme'; @import '../../mdc-radio/radio-theme'; -@import '../../mdc-slide-toggle/slide-toggle-theme'; @import '../../mdc-snack-bar/snack-bar-theme'; @import '../../mdc-tabs/tabs-theme'; @import '../../mdc-table/table-theme'; diff --git a/src/material-experimental/mdc-core/theming/_all-theme.scss b/src/material-experimental/mdc-core/theming/_all-theme.scss index f56413c08d8a..b93793eadb75 100644 --- a/src/material-experimental/mdc-core/theming/_all-theme.scss +++ b/src/material-experimental/mdc-core/theming/_all-theme.scss @@ -7,7 +7,6 @@ @use '../../mdc-list/list-theme'; @use '../../mdc-menu/menu-theme'; @use '../../mdc-radio/radio-theme'; -@use '../../mdc-slide-toggle/slide-toggle-theme'; @use '../../mdc-slider/slider-theme'; @use '../../mdc-snack-bar/snack-bar-theme'; @use '../../mdc-tabs/tabs-theme'; @@ -34,7 +33,7 @@ @include progress-spinner-theme.theme($theme-or-color-config); @include radio-theme.theme($theme-or-color-config); @include mat.select-theme($theme-or-color-config); - @include slide-toggle-theme.theme($theme-or-color-config); + @include mat.slide-toggle-theme($theme-or-color-config); @include slider-theme.theme($theme-or-color-config); @include snack-bar-theme.theme($theme-or-color-config); @include table-theme.theme($theme-or-color-config); diff --git a/src/material-experimental/mdc-core/typography/_all-typography.import.scss b/src/material-experimental/mdc-core/typography/_all-typography.import.scss index 0625b478d0bc..90c2e4646090 100644 --- a/src/material-experimental/mdc-core/typography/_all-typography.import.scss +++ b/src/material-experimental/mdc-core/typography/_all-typography.import.scss @@ -11,13 +11,6 @@ $mat-mdc-button-mat-button-state-target; @forward '../../mdc-radio/radio-theme' as mat-mdc-radio-* hide $mat-mdc-radio-mdc-radio-baseline-theme-color, $mat-mdc-radio-mdc-radio-disabled-circle-color, $mat-mdc-radio-mdc-radio-unchecked-color; -@forward '../../mdc-slide-toggle/slide-toggle-theme' hide color, density, theme, typography; -@forward '../../mdc-slide-toggle/slide-toggle-theme' as mat-mdc-slide-toggle-* hide -$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; @forward '../../mdc-snack-bar/snack-bar-theme' hide color, density, theme, typography; @forward '../../mdc-snack-bar/snack-bar-theme' as mat-mdc-snack-bar-* hide $mat-mdc-snack-bar-mdc-snackbar-dismiss-ink-color, $mat-mdc-snack-bar-mdc-snackbar-fill-color, diff --git a/src/material-experimental/mdc-slide-toggle/README.md b/src/material-experimental/mdc-slide-toggle/README.md deleted file mode 100644 index 5eff83ec8a1c..000000000000 --- a/src/material-experimental/mdc-slide-toggle/README.md +++ /dev/null @@ -1,87 +0,0 @@ -This is prototype of an alternate version of `` built on top of -[MDC Web](https://github.com/material-components/material-components-web). It demonstrates how -Angular Material could use MDC Web under the hood while still exposing the same API Angular users as -the existing ``. This component is experimental and should not be used in production. - -## How to use -Assuming your application is already up and running using Angular Material, you can add this -component by following these steps: - -1. Install Angular Material Experimental & MDC WEB: - - ```bash - npm i material-components-web @angular/material-experimental - ``` - -2. In your `angular.json`, make sure `node_modules/` is listed as a Sass include path. This is - needed for the Sass compiler to be able to find the MDC Web Sass files. - - ```json - ... - "styles": [ - "src/styles.scss" - ], - "stylePreprocessorOptions": { - "includePaths": [ - "node_modules/" - ] - }, - ... - ``` - -3. Import the experimental `MatSlideToggleModule` and add it to the module that declares your - component: - - ```ts - import {MatSlideToggleModule} from '@angular/material-experimental/mdc-slide-toggle'; - - @NgModule({ - declarations: [MyComponent], - imports: [MatSlideToggleModule], - }) - export class MyModule {} - ``` - -4. Add use `` in your component's template, just like you would the normal - ``: - - ```html - Toggle me - ``` - -5. Add the theme and typography mixins to your Sass. (There is currently no pre-built CSS option for - the experimental ``): - - ```scss - @use '@angular/material' as mat; - @use '@angular/material-experimental' as mat-experimental; - - $my-primary: mat.define-palette(mat.$indigo-palette); - $my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); - $my-theme: mat.define-light-theme(( - color: ( - primary: $my-primary, - accent: $my-accent - ) - )); - - @include mat-experimental.mdc-slide-toggle-theme($my-theme); - @include mat-experimental.mdc-slide-toggle-typography($my-theme); - ``` - -## Replacing the standard slide toggle in an existing app -Because the experimental API mirrors the API for the standard slide toggle, it can easily be swapped -in by just changing the import paths. There is currently no schematic for this, but you can run the -following string replace across your TypeScript files: - -```bash -grep -lr --include="*.ts" --exclude-dir="node_modules" \ - --exclude="*.d.ts" "['\"]@angular/material/slide-toggle['\"]" | xargs sed -i \ - "s/['\"]@angular\/material\/slide-toggle['\"]/'@angular\/material-experimental\/mdc-slide-toggle'/g" -``` - -CSS styles and tests that depend on implementation details of mat-slide-toggle (such as getting -elements from the template by class name) will need to be manually updated. - -There are some small visual differences between this slide and the standard mat-slide. This -slide has a slightly larger ripple and different spacing between the label and the toggle. diff --git a/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.import.scss b/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.import.scss deleted file mode 100644 index e5266b0680cf..000000000000 --- a/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.import.scss +++ /dev/null @@ -1,7 +0,0 @@ -@forward 'slide-toggle-theme' hide color, density, theme, typography; -@forward 'slide-toggle-theme' as mat-mdc-slide-toggle-* hide -$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, -$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; diff --git a/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.scss b/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.scss deleted file mode 100644 index c81c0668dc6c..000000000000 --- a/src/material-experimental/mdc-slide-toggle/_slide-toggle-theme.scss +++ /dev/null @@ -1,139 +0,0 @@ -@use 'sass:map'; -@use 'sass:color'; -@use '@angular/material' as mat; -@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; - - -// 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); - $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: mat.get-color-config($config-or-theme); - $primary: mat.get-color-from-palette(map.get($config, primary)); - $accent: mat.get-color-from-palette(map.get($config, accent)); - $warn: mat.get-color-from-palette(map.get($config, warn)); - $is-dark: map.get($config, is-dark); - $foreground: map.get($config, foreground); - - @include mat.private-using-mdc-theme($config) { - // 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: mat.$private-mdc-theme-styles-query); - @include mdc-switch-theme.theme(_get-theme-base-map($is-dark)); - - // MDC should set the disabled color on the label, but doesn't, so we do it here instead. - .mdc-switch--disabled + label { - color: mat.get-color-from-palette($foreground, disabled-text); - } - - &.mat-primary { - @include mdc-switch-theme.theme(_get-theme-color-map($primary)); - } - - &.mat-accent { - @include mdc-switch-theme.theme(_get-theme-color-map($accent)); - } - - &.mat-warn { - @include mdc-switch-theme.theme(_get-theme-color-map($warn)); - } - } - } -} - -@mixin typography($config-or-theme) { - $config: mat.private-typography-to-2018-config( - mat.get-typography-config($config-or-theme)); - @include mat.private-using-mdc-typography($config) { - @include mdc-form-field.core-styles($query: mat.$private-mdc-typography-styles-query); - } -} - -@mixin density($config-or-theme) { - $density-scale: mat.get-density-config($config-or-theme); - .mat-mdc-slide-toggle { - @include mdc-switch-theme.theme(mdc-switch-theme.density($density-scale)); - } -} - -@mixin theme($theme-or-color-config) { - $theme: mat.private-legacy-get-theme($theme-or-color-config); - - @include mat.private-check-duplicate-theme-styles($theme, 'mat-mdc-slide-toggle') { - $color: mat.get-color-config($theme); - $density: mat.get-density-config($theme); - $typography: mat.get-typography-config($theme); - - @if $color != null { - @include color($color); - } - @if $density != null { - @include density($density); - } - @if $typography != null { - @include typography($typography); - } - } -} - diff --git a/src/material-experimental/mdc-slide-toggle/public-api.ts b/src/material-experimental/mdc-slide-toggle/public-api.ts deleted file mode 100644 index c03f1fcf72a6..000000000000 --- a/src/material-experimental/mdc-slide-toggle/public-api.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -export * from './slide-toggle'; -export * from './slide-toggle-config'; -export * from './module'; - -export { - MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR, - MatSlideToggleRequiredValidator, -} from '@angular/material/slide-toggle'; diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.e2e.spec.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.e2e.spec.ts deleted file mode 100644 index 9e2fb8d72e28..000000000000 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.e2e.spec.ts +++ /dev/null @@ -1,89 +0,0 @@ -import {browser, element, by, Key} from 'protractor'; -import {expectToExist} from '../../cdk/testing/private/e2e'; - -describe('MDC-based slide-toggle', () => { - 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')); - - it('should render a slide-toggle', async () => { - await expectToExist('mat-slide-toggle'); - }); - - it('should change the checked state on click', async () => { - const buttonEl = getButton(); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'false', - 'Expect slide-toggle to be unchecked', - ); - - await getNormalToggle().click(); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'true', - 'Expect slide-toggle to be checked', - ); - }); - - it('should change the checked state on click', async () => { - const buttonEl = getButton(); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'false', - 'Expect slide-toggle to be unchecked', - ); - - await getNormalToggle().click(); - - 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 buttonEl = getButton(); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'false', - 'Expect slide-toggle to be unchecked', - ); - - await element(by.css('#disabled-slide-toggle')).click(); - - 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__handle')); - const previousPosition = await thumbEl.getLocation(); - - await slideToggleEl.click(); - - const position = await thumbEl.getLocation(); - - expect(position.x).not.toBe(previousPosition.x); - }); - - it('should toggle the slide-toggle on space key', async () => { - const buttonEl = getButton(); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'false', - 'Expect slide-toggle to be unchecked', - ); - - await buttonEl.sendKeys(Key.SPACE); - - expect(await buttonEl.getAttribute('aria-checked')).toBe( - 'true', - 'Expect slide-toggle to be checked', - ); - }); -}); diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.html b/src/material-experimental/mdc-slide-toggle/slide-toggle.html deleted file mode 100644 index ec29dc3c9c4b..000000000000 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.html +++ /dev/null @@ -1,53 +0,0 @@ -
- - - - -
diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.scss b/src/material-experimental/mdc-slide-toggle/slide-toggle.scss deleted file mode 100644 index 938784322b9a..000000000000 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.scss +++ /dev/null @@ -1,77 +0,0 @@ -@use 'sass:map'; -@use '@angular/material' as mat; -@use '@material/switch/switch' as mdc-switch; -@use '@material/switch/switch-theme' as mdc-switch-theme; -@use '@material/form-field' as mdc-form-field; -@use '@material/ripple' as mdc-ripple; - - -@include mat.private-disable-mdc-fallback-declarations { - @include mdc-form-field.core-styles($query: mat.$private-mdc-base-styles-query); - @include mdc-switch.static-styles-without-ripple; -} - -.mat-mdc-slide-toggle { - display: inline-block; - - // Remove the native outline since we use the ripple for focus indication. - outline: 0; - - .mdc-switch { - // MDC theme styles also include structural styles so we have to include the theme at least - // once here. The values will be overwritten by our own theme file afterwards. - @include mat.private-disable-mdc-fallback-declarations { - @include mdc-switch-theme.theme-styles(mdc-switch-theme.$light-theme); - } - } - - // The ripple needs extra specificity so the base ripple styling doesn't override its `position`. - .mat-mdc-slide-toggle-ripple, #{mdc-switch.$ripple-target}::after { - @include mat.private-fill; - border-radius: 50%; - // 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; - // Fixes the ripples not clipping to the border radius on Safari. Uses `:not(:empty)` - // in order to avoid creating extra layers when there aren't any ripples. - &:not(:empty) { - transform: translateZ(0); - } - } - - #{mdc-switch.$ripple-target}::after { - content: ''; - opacity: 0; - } - - .mdc-switch:hover #{mdc-switch.$ripple-target}::after { - opacity: map.get(mdc-ripple.$dark-ink-opacities, hover); - transition: mdc-switch-transition-enter(opacity, 0, 75ms); - } - - // Needs a little more specificity so the :hover styles don't override it. - &.mat-mdc-slide-toggle-focused { - .mdc-switch #{mdc-switch.$ripple-target}::after { - opacity: map.get(mdc-ripple.$dark-ink-opacities, focus); - } - - // For slide-toggles render the focus indicator when we know - // the hidden input is focused (slightly different for each control). - .mat-mdc-focus-indicator::before { - content: ''; - } - } - - // We use an Angular Material ripple rather than an MDC ripple due to size concerns, so we need to - // style it appropriately. - .mat-ripple-element { - opacity: map.get(mdc-ripple.$dark-ink-opacities, press); - } - - // Slide-toggle components have to set `border-radius: 50%` in order to support density scaling - // which will clip a square focus indicator so we have to turn it into a circle. - .mat-mdc-focus-indicator::before { - border-radius: 50%; - } -} diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts b/src/material-experimental/mdc-slide-toggle/slide-toggle.ts deleted file mode 100644 index e6e6e0b17686..000000000000 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import { - ChangeDetectionStrategy, - Component, - ViewEncapsulation, - forwardRef, - ViewChild, - ElementRef, - ChangeDetectorRef, - Attribute, - Inject, - Optional, -} from '@angular/core'; -import {NG_VALUE_ACCESSOR} from '@angular/forms'; -import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; -import {FocusMonitor} from '@angular/cdk/a11y'; -import {_MatSlideToggleBase} from '@angular/material/slide-toggle'; -import { - MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, - MatSlideToggleDefaultOptions, -} from './slide-toggle-config'; - -/** @docs-private */ -export const MAT_SLIDE_TOGGLE_VALUE_ACCESSOR = { - provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => MatSlideToggle), - multi: true, -}; - -/** Change event object emitted by a slide toggle. */ -export class MatSlideToggleChange { - constructor( - /** The source slide toggle of the event. */ - public source: MatSlideToggle, - /** The new `checked` value of the slide toggle. */ - public checked: boolean, - ) {} -} - -@Component({ - selector: 'mat-slide-toggle', - templateUrl: 'slide-toggle.html', - styleUrls: ['slide-toggle.css'], - inputs: ['disabled', 'disableRipple', 'color', 'tabIndex'], - host: { - 'class': 'mat-mdc-slide-toggle', - '[id]': 'id', - // Needs to be removed since it causes some a11y issues (see #21266). - '[attr.tabindex]': 'null', - '[attr.aria-label]': 'null', - '[attr.name]': 'null', - '[attr.aria-labelledby]': 'null', - '[class.mat-mdc-slide-toggle-focused]': '_focused', - '[class.mat-mdc-slide-toggle-checked]': 'checked', - '[class._mat-animation-noopable]': '_noopAnimations', - }, - exportAs: 'matSlideToggle', - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [MAT_SLIDE_TOGGLE_VALUE_ACCESSOR], -}) -export class MatSlideToggle extends _MatSlideToggleBase { - /** Unique ID for the label element. */ - _labelId: string; - - /** Returns the unique id for the visual hidden button. */ - get buttonId(): string { - return `${this.id || this._uniqueId}-button`; - } - - /** Reference to the MDC switch element. */ - @ViewChild('switch') _switchElement: ElementRef; - - constructor( - elementRef: ElementRef, - focusMonitor: FocusMonitor, - changeDetectorRef: ChangeDetectorRef, - @Attribute('tabindex') tabIndex: string, - @Inject(MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS) - defaults: MatSlideToggleDefaultOptions, - @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, - ) { - super( - elementRef, - focusMonitor, - changeDetectorRef, - tabIndex, - defaults, - animationMode, - 'mat-mdc-slide-toggle-', - ); - this._labelId = this._uniqueId + '-label'; - } - - /** Method being called whenever the underlying button is clicked. */ - _handleClick() { - this.toggleChange.emit(); - - if (!this.defaults.disableToggleValue) { - this.checked = !this.checked; - this._onChange(this.checked); - this.change.emit(new MatSlideToggleChange(this, this.checked)); - } - } - - /** Focuses the slide-toggle. */ - focus(): void { - this._switchElement.nativeElement.focus(); - } - - protected _createChangeEvent(isChecked: boolean) { - return new MatSlideToggleChange(this, isChecked); - } - - _getAriaLabelledBy() { - if (this.ariaLabelledby) { - return this.ariaLabelledby; - } - - // Even though we have a `label` element with a `for` pointing to the button, we need the - // `aria-labelledby`, because the button gets flagged as not having a label by tools like axe. - return this.ariaLabel ? null : this._labelId; - } -} diff --git a/src/material-experimental/mdc-slide-toggle/testing/BUILD.bazel b/src/material-experimental/mdc-slide-toggle/testing/BUILD.bazel deleted file mode 100644 index 0641d6595c59..000000000000 --- a/src/material-experimental/mdc-slide-toggle/testing/BUILD.bazel +++ /dev/null @@ -1,33 +0,0 @@ -load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library") - -package(default_visibility = ["//visibility:public"]) - -ts_library( - name = "testing", - srcs = glob( - ["**/*.ts"], - exclude = ["**/*.spec.ts"], - ), - deps = [ - "//src/cdk/coercion", - "//src/cdk/testing", - "//src/material/slide-toggle/testing", - ], -) - -ng_test_library( - name = "unit_tests_lib", - srcs = glob(["**/*.spec.ts"]), - deps = [ - ":testing", - "//src/material-experimental/mdc-slide-toggle", - "//src/material/slide-toggle/testing:harness_tests_lib", - ], -) - -ng_web_test_suite( - name = "unit_tests", - deps = [ - ":unit_tests_lib", - ], -) diff --git a/src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.spec.ts b/src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.spec.ts deleted file mode 100644 index 060147ff92ec..000000000000 --- a/src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import {runHarnessTests} from '@angular/material/slide-toggle/testing/shared.spec'; -import {MatSlideToggleModule} from '../index'; -import {MatSlideToggleHarness} from './slide-toggle-harness'; - -describe('MDC-based MatSlideToggleHarness', () => { - runHarnessTests(MatSlideToggleModule, MatSlideToggleHarness as any); -}); diff --git a/src/material/_index.scss b/src/material/_index.scss index 2887210352ec..9b8862f5fd85 100644 --- a/src/material/_index.scss +++ b/src/material/_index.scss @@ -65,9 +65,9 @@ @forward './legacy-core/option/option-theme' as legacy-option-* show legacy-option-color, legacy-option-typography, legacy-option-theme; @forward './core/option/option-theme' as option-* show option-color, option-typography, - option-theme; + option-theme, option-density; @forward './core/option/optgroup-theme' as optgroup-* show optgroup-color, optgroup-typography, - optgroup-theme; + optgroup-theme, optgroup-density; @forward './legacy-core/option/optgroup-theme' as legacy-optgroup-* show legacy-optgroup-color, legacy-optgroup-typography, legacy-optgroup-theme; @forward './core/selection/pseudo-checkbox/pseudo-checkbox-theme' as pseudo-checkbox-* show @@ -75,7 +75,7 @@ @forward './core/focus-indicators/focus-indicators-theme' as strong-focus-indicators-* show strong-focus-indicators-color, strong-focus-indicators-theme; @forward './autocomplete/autocomplete-theme' as autocomplete-* show autocomplete-theme, - autocomplete-color, autocomplete-typography; + autocomplete-color, autocomplete-typography, autocomplete-density; @forward './legacy-autocomplete/autocomplete-theme' as legacy-autocomplete-* show legacy-autocomplete-theme, legacy-autocomplete-color, legacy-autocomplete-typography; @forward './badge/badge-theme' as badge-* show badge-theme, badge-color, badge-typography; @@ -84,28 +84,30 @@ @forward './button/button-theme' as button-* show button-theme, button-color, button-typography; @forward './button-toggle/button-toggle-theme' as button-toggle-* show button-toggle-theme, button-toggle-color, button-toggle-typography; -@forward './card/card-theme' as card-* show card-theme, card-color, card-typography; +@forward './card/card-theme' as card-* show card-theme, card-color, card-typography, card-density; @forward './legacy-card/card-theme' as legacy-card-* show legacy-card-theme, legacy-card-color, legacy-card-typography; @forward './legacy-checkbox/checkbox-theme' as legacy-checkbox-* show checkbox-legacy-theme, checkbox-legacy-color, checkbox-legacy-typography; @forward './checkbox/checkbox-theme' as checkbox-* show checkbox-theme, checkbox-color, - checkbox-typography; + checkbox-typography, checkbox-density; @forward './legacy-chips/chips-theme' as legacy-chips-* show legacy-chips-theme, legacy-chips-color, legacy-chips-typography; -@forward './chips/chips-theme' as chips-* show chips-theme, chips-color, chips-typography; +@forward './chips/chips-theme' as chips-* show chips-theme, chips-color, chips-typography, + chips-density; @forward './datepicker/datepicker-theme' as datepicker-* show datepicker-theme, datepicker-color, datepicker-typography, datepicker-date-range-colors; @forward './legacy-dialog/dialog-theme' as legacy-dialog-* show legacy-dialog-theme, legacy-dialog-color, legacy-dialog-typography; -@forward './dialog/dialog-theme' as dialog-* show dialog-theme, dialog-color, dialog-typography; +@forward './dialog/dialog-theme' as dialog-* show dialog-theme, dialog-color, dialog-typography, + dialog-density; @forward './dialog/dialog-legacy-padding' as dialog-* show dialog-legacy-padding; @forward './divider/divider-theme' as divider-* show divider-theme, divider-color, divider-typography; @forward './expansion/expansion-theme' as expansion-* show expansion-theme, expansion-color, expansion-typography; @forward './form-field/form-field-theme' as form-field-* show form-field-theme, - form-field-color, form-field-typography; + form-field-color, form-field-typography, form-field-density; @forward './legacy-form-field/form-field-theme' as legacy-form-field-* show legacy-form-field-theme, legacy-form-field-color, legacy-form-field-typography; @forward './grid-list/grid-list-theme' as grid-list-* show grid-list-theme, grid-list-color, @@ -113,7 +115,8 @@ @forward './icon/icon-theme' as icon-* show icon-theme, icon-color, icon-typography; @forward './legacy-input/input-theme' as legacy-input-* show legacy-input-theme, legacy-input-color, legacy-input-typography; -@forward './input/input-theme' as input-* show input-theme, input-color, input-typography; +@forward './input/input-theme' as input-* show input-theme, input-color, input-typography, + input-density; @forward './list/list-theme' as list-* show list-theme, list-color, list-typography; @forward './menu/menu-theme' as menu-* show menu-theme, menu-color, menu-typography; @forward './paginator/paginator-theme' as paginator-* show paginator-theme, paginator-color, @@ -121,17 +124,20 @@ @forward './legacy-progress-bar/progress-bar-theme' as legacy-progress-bar-* show legacy-progress-bar-theme, legacy-progress-bar-color, legacy-progress-bar-typography; @forward './progress-bar/progress-bar-theme' as progress-bar-* show - progress-bar-theme, progress-bar-color, progress-bar-typography; + progress-bar-theme, progress-bar-color, progress-bar-typography, progress-bar-density; @forward './progress-spinner/progress-spinner-theme' as progress-spinner-* show progress-spinner-theme, progress-spinner-color, progress-spinner-typography; @forward './radio/radio-theme' as radio-* show radio-theme, radio-color, radio-typography; -@forward './select/select-theme' as select-* show select-theme, select-color, select-typography; +@forward './select/select-theme' as select-* show select-theme, select-color, select-typography, + select-density; @forward './legacy-select/select-theme' as legacy-select-* show legacy-select-theme, legacy-select-color, legacy-select-typography; @forward './sidenav/sidenav-theme' as sidenav-* show sidenav-theme, sidenav-color, sidenav-typography; -@forward './slide-toggle/slide-toggle-theme' as slide-toggle-* show slide-toggle-theme, - slide-toggle-color, slide-toggle-typography; +@forward './legacy-slide-toggle/slide-toggle-theme' as legacy-slide-toggle-* show + legacy-slide-toggle-theme, legacy-slide-toggle-color, legacy-slide-toggle-typography; +@forward './slide-toggle/slide-toggle-theme' as slide-toggle-* show +slide-toggle-theme, slide-toggle-color, slide-toggle-typography, slide-toggle-density; @forward './slider/slider-theme' as slider-* show slider-theme, slider-color, slider-typography; @forward './snack-bar/snack-bar-theme' as snack-bar-* show snack-bar-theme, snack-bar-color, snack-bar-typography; @@ -145,7 +151,7 @@ @forward './legacy-tooltip/tooltip-theme' as legacy-tooltip-* show legacy-tooltip-theme, legacy-tooltip-color, legacy-tooltip-typography; @forward './tooltip/tooltip-theme' as tooltip-* show tooltip-theme, tooltip-color, - tooltip-typography; + tooltip-typography, tooltip-density; @forward './tree/tree-theme' as tree-* show tree-theme, tree-color, tree-typography; // MDC Helpers diff --git a/src/material/_theming.scss b/src/material/_theming.scss index ad2d2306eb90..30090b074c07 100644 --- a/src/material/_theming.scss +++ b/src/material/_theming.scss @@ -31,7 +31,7 @@ @forward './radio/radio-legacy-index'; @forward './legacy-select/select-legacy-index'; @forward './sidenav/sidenav-legacy-index'; -@forward './slide-toggle/slide-toggle-legacy-index'; +@forward './legacy-slide-toggle/slide-toggle-legacy-index'; @forward './slider/slider-legacy-index'; @forward './snack-bar/snack-bar-legacy-index'; @forward './sort/sort-legacy-index'; diff --git a/src/material/config.bzl b/src/material/config.bzl index fbeab09e6c83..060f0eba3422 100644 --- a/src/material/config.bzl +++ b/src/material/config.bzl @@ -72,6 +72,8 @@ entryPoints = [ "sidenav/testing", "slide-toggle", "slide-toggle/testing", + "legacy-slide-toggle", + "legacy-slide-toggle/testing", "slider", "slider/testing", "snack-bar", diff --git a/src/material/core/density/private/_all-density.scss b/src/material/core/density/private/_all-density.scss index a1389d6a5d39..0edb9c071e63 100644 --- a/src/material/core/density/private/_all-density.scss +++ b/src/material/core/density/private/_all-density.scss @@ -17,6 +17,7 @@ @use '../../../select/select-theme'; @use '../../../dialog/dialog-theme'; @use '../../../chips/chips-theme'; +@use '../../../slide-toggle/slide-toggle-theme'; @mixin private-all-unmigrated-component-densities($config) { @include expansion-theme.density($config); @@ -55,6 +56,7 @@ @include autocomplete-theme.density($config); @include dialog-theme.density($config); @include chips-theme.density($config); + @include slide-toggle-theme.density($config); @include private-all-unmigrated-component-densities($config); } diff --git a/src/material/core/theming/_all-theme.scss b/src/material/core/theming/_all-theme.scss index 2ce73e19236a..f932fca9710e 100644 --- a/src/material/core/theming/_all-theme.scss +++ b/src/material/core/theming/_all-theme.scss @@ -54,7 +54,6 @@ @include progress-spinner-theme.theme($theme-or-color-config); @include radio-theme.theme($theme-or-color-config); @include sidenav-theme.theme($theme-or-color-config); - @include slide-toggle-theme.theme($theme-or-color-config); @include slider-theme.theme($theme-or-color-config); @include stepper-theme.theme($theme-or-color-config); @include sort-theme.theme($theme-or-color-config); @@ -78,6 +77,7 @@ @include autocomplete-theme.theme($theme-or-color-config); @include dialog-theme.theme($theme-or-color-config); @include chips-theme.theme($theme-or-color-config); + @include slide-toggle-theme.theme($theme-or-color-config); @include private-all-unmigrated-component-themes($theme-or-color-config); } } diff --git a/src/material/core/theming/tests/test-css-variables-theme.scss b/src/material/core/theming/tests/test-css-variables-theme.scss index db4d909fbe66..d0ace45ae0f9 100644 --- a/src/material/core/theming/tests/test-css-variables-theme.scss +++ b/src/material/core/theming/tests/test-css-variables-theme.scss @@ -19,7 +19,6 @@ @use '../../../progress-spinner/progress-spinner-theme'; @use '../../../radio/radio-theme'; @use '../../../sidenav/sidenav-theme'; -@use '../../../slide-toggle/slide-toggle-theme'; @use '../../../slider/slider-theme'; @use '../../../stepper/stepper-theme'; @use '../../../sort/sort-theme'; @@ -72,7 +71,6 @@ @include progress-spinner-theme.theme($css-var-theme); @include radio-theme.theme($css-var-theme); @include sidenav-theme.theme($css-var-theme); - @include slide-toggle-theme.theme($css-var-theme); @include slider-theme.theme($css-var-theme); @include stepper-theme.theme($css-var-theme); @include sort-theme.theme($css-var-theme); diff --git a/src/material/core/typography/_all-typography.scss b/src/material/core/typography/_all-typography.scss index 838e8ab3e53b..2d641d78101e 100644 --- a/src/material/core/typography/_all-typography.scss +++ b/src/material/core/typography/_all-typography.scss @@ -55,7 +55,6 @@ @include progress-spinner-theme.typography($config); @include radio-theme.typography($config); @include sidenav-theme.typography($config); - @include slide-toggle-theme.typography($config); @include slider-theme.typography($config); @include stepper-theme.typography($config); @include sort-theme.typography($config); @@ -94,6 +93,7 @@ @include autocomplete-theme.typography($config); @include dialog-theme.typography($config); @include chips-theme.typography($config); + @include slide-toggle-theme.typography($config); } // @deprecated Use `all-component-typographies`. diff --git a/src/material/legacy-core/theming/_all-theme.scss b/src/material/legacy-core/theming/_all-theme.scss index 53af7abca282..37eb8391a7f2 100644 --- a/src/material/legacy-core/theming/_all-theme.scss +++ b/src/material/legacy-core/theming/_all-theme.scss @@ -11,6 +11,7 @@ @use '../../legacy-autocomplete/autocomplete-theme'; @use '../../legacy-dialog/dialog-theme'; @use '../../legacy-chips/chips-theme'; +@use '../../legacy-slide-toggle/slide-toggle-theme'; // Create a theme. @mixin all-legacy-component-themes($theme-or-color-config) { @@ -27,6 +28,7 @@ @include autocomplete-theme.theme($theme-or-color-config); @include dialog-theme.theme($theme-or-color-config); @include chips-theme.theme($theme-or-color-config); + @include slide-toggle-theme.theme($theme-or-color-config); @include all-theme.private-all-unmigrated-component-themes($theme-or-color-config); } } diff --git a/src/material/legacy-core/typography/_all-typography.scss b/src/material/legacy-core/typography/_all-typography.scss index 0310fe007fbd..975030ce529d 100644 --- a/src/material/legacy-core/typography/_all-typography.scss +++ b/src/material/legacy-core/typography/_all-typography.scss @@ -13,6 +13,7 @@ @use '../../legacy-autocomplete/autocomplete-theme'; @use '../../legacy-dialog/dialog-theme'; @use '../../legacy-chips/chips-theme'; +@use '../../legacy-slide-toggle/slide-toggle-theme'; // Includes all of the typographic styles. @mixin all-legacy-component-typographies($config-or-theme: null) { @@ -43,6 +44,7 @@ @include autocomplete-theme.typography($config); @include dialog-theme.typography($config); @include chips-theme.typography($config); + @include slide-toggle-theme.typography($config); } // @deprecated Use `all-legacy-component-typographies`. diff --git a/src/material-experimental/mdc-slide-toggle/BUILD.bazel b/src/material/legacy-slide-toggle/BUILD.bazel similarity index 66% rename from src/material-experimental/mdc-slide-toggle/BUILD.bazel rename to src/material/legacy-slide-toggle/BUILD.bazel index 4048f6a51271..7bce68c56111 100644 --- a/src/material-experimental/mdc-slide-toggle/BUILD.bazel +++ b/src/material/legacy-slide-toggle/BUILD.bazel @@ -1,6 +1,7 @@ load("//src/e2e-app:test_suite.bzl", "e2e_test_suite") load( "//tools:defaults.bzl", + "markdown_to_html", "ng_e2e_test_library", "ng_module", "ng_test_library", @@ -12,59 +13,52 @@ load( package(default_visibility = ["//visibility:public"]) ng_module( - name = "mdc-slide-toggle", + name = "legacy-slide-toggle", srcs = glob( ["**/*.ts"], - exclude = [ - "**/*.spec.ts", - "harness/**", - ], + exclude = ["**/*.spec.ts"], ), - assets = [":slide_toggle_scss"] + glob(["**/*.html"]), + assets = [":slide-toggle.css"] + glob(["**/*.html"]), deps = [ + "//src/cdk/a11y", "//src/cdk/coercion", + "//src/cdk/observers", "//src/material/core", "//src/material/slide-toggle", "@npm//@angular/animations", - "@npm//@angular/common", "@npm//@angular/core", "@npm//@angular/forms", - "@npm//@material/switch", + "@npm//@angular/platform-browser", ], ) sass_library( - name = "mdc_slide_toggle_scss_lib", + name = "legacy_slide_toggle_scss_lib", srcs = glob(["**/_*.scss"]), - deps = [ - "//:mdc_sass_lib", - "//src/material:sass_lib", - "//src/material/core:core_scss_lib", - ], + deps = ["//src/material/core:core_scss_lib"], ) sass_binary( name = "slide_toggle_scss", src = "slide-toggle.scss", deps = [ - "//:mdc_sass_lib", - "//src/material:sass_lib", + "//src/cdk:sass_lib", "//src/material/core:core_scss_lib", ], ) ng_test_library( - name = "slide_toggle_tests_lib", + name = "unit_test_sources", srcs = glob( ["**/*.spec.ts"], exclude = ["**/*.e2e.spec.ts"], ), deps = [ - ":mdc-slide-toggle", + ":legacy-slide-toggle", "//src/cdk/a11y", - "//src/cdk/bidi", + "//src/cdk/observers", "//src/cdk/testing/private", - "//src/material/slide-toggle", + "//src/material/testing", "@npm//@angular/forms", "@npm//@angular/platform-browser", ], @@ -72,9 +66,7 @@ ng_test_library( ng_web_test_suite( name = "unit_tests", - deps = [ - ":slide_toggle_tests_lib", - ], + deps = [":unit_test_sources"], ) ng_e2e_test_library( @@ -92,3 +84,13 @@ e2e_test_suite( "//src/cdk/testing/private/e2e", ], ) + +markdown_to_html( + name = "overview", + srcs = [":slide-toggle.md"], +) + +filegroup( + name = "source-files", + srcs = glob(["**/*.ts"]), +) diff --git a/src/material/legacy-slide-toggle/README.md b/src/material/legacy-slide-toggle/README.md new file mode 100644 index 000000000000..a987b3625d68 --- /dev/null +++ b/src/material/legacy-slide-toggle/README.md @@ -0,0 +1 @@ +Please see the official documentation at https://material.angular.io/components/component/slide-toggle \ No newline at end of file diff --git a/src/material/legacy-slide-toggle/_slide-toggle-legacy-index.scss b/src/material/legacy-slide-toggle/_slide-toggle-legacy-index.scss new file mode 100644 index 000000000000..b7ac649abe73 --- /dev/null +++ b/src/material/legacy-slide-toggle/_slide-toggle-legacy-index.scss @@ -0,0 +1,3 @@ +@forward 'slide-toggle-theme' hide color, theme, typography; +@forward 'slide-toggle-theme' as mat-legacy-slide-toggle-* hide mat-legacy-slide-toggle-checked, +mat-legacy-slide-toggle-density; diff --git a/src/material/legacy-slide-toggle/_slide-toggle-theme.import.scss b/src/material/legacy-slide-toggle/_slide-toggle-theme.import.scss new file mode 100644 index 000000000000..9b6319dc29cf --- /dev/null +++ b/src/material/legacy-slide-toggle/_slide-toggle-theme.import.scss @@ -0,0 +1,11 @@ +@forward '../core/style/private.import'; +@forward '../core/theming/theming.import'; +@forward '../core/typography/typography-utils.import'; +@forward 'slide-toggle-theme' hide color, theme, typography; +@forward 'slide-toggle-theme' as mat-legacy-slide-toggle-* hide mat-legacy-slide-toggle-checked, +mat-legacy-slide-toggle-density; + +@import '../core/style/private'; +@import '../core/theming/palette'; +@import '../core/theming/theming'; +@import '../core/typography/typography-utils'; diff --git a/src/material/legacy-slide-toggle/_slide-toggle-theme.scss b/src/material/legacy-slide-toggle/_slide-toggle-theme.scss new file mode 100644 index 000000000000..e8a8427ebbd8 --- /dev/null +++ b/src/material/legacy-slide-toggle/_slide-toggle-theme.scss @@ -0,0 +1,105 @@ +@use 'sass:map'; +@use '../core/style/private'; +@use '../core/theming/palette'; +@use '../core/theming/theming'; +@use '../core/typography/typography'; +@use '../core/typography/typography-utils'; + +@mixin _checked-color($palette, $thumb-checked-hue) { + &.mat-checked { + .mat-slide-toggle-thumb { + background-color: theming.get-color-from-palette($palette, $thumb-checked-hue); + } + + .mat-slide-toggle-bar { + // Opacity is determined from the specs for the selection controls. + // See: https://material.io/design/components/selection-controls.html#specs + background-color: theming.get-color-from-palette($palette, $thumb-checked-hue, 0.54); + } + + .mat-ripple-element { + // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically + // based on the type of interaction with the slide-toggle (e.g. for hover, focus) + background-color: theming.get-color-from-palette($palette, $thumb-checked-hue); + } + } +} + +@mixin color($config-or-theme) { + $config: theming.get-color-config($config-or-theme); + $is-dark: map.get($config, is-dark); + $primary: map.get($config, primary); + $accent: map.get($config, accent); + $warn: map.get($config, warn); + $background: map.get($config, background); + $foreground: map.get($config, foreground); + + // Color hues are based on the specs which briefly show the hues that are applied to a switch. + // The 2018 specs no longer describe how dark switches should look like. Due to the lack of + // information for dark themed switches, we partially keep the old behavior that is based on + // the previous specifications. For the checked color we always use the `default` hue because + // that follows MDC and also makes it easier for people to create a custom theme without needing + // to specify each hue individually. + $thumb-unchecked-hue: if($is-dark, 400, 50); + $thumb-checked-hue: default; + + $bar-unchecked-color: theming.get-color-from-palette($foreground, disabled); + $ripple-unchecked-color: theming.get-color-from-palette($foreground, base); + + .mat-slide-toggle { + @include _checked-color($accent, $thumb-checked-hue); + + &.mat-primary { + @include _checked-color($primary, $thumb-checked-hue); + } + + &.mat-warn { + @include _checked-color($warn, $thumb-checked-hue); + } + + &:not(.mat-checked) .mat-ripple-element { + // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically + // based on the type of interaction with the slide-toggle (e.g. for hover, focus) + background-color: $ripple-unchecked-color; + } + } + + .mat-slide-toggle-thumb { + @include private.private-theme-elevation(1, $config); + background-color: theming.get-color-from-palette(palette.$grey-palette, $thumb-unchecked-hue); + } + + .mat-slide-toggle-bar { + background-color: $bar-unchecked-color; + } +} + +@mixin typography($config-or-theme) { + $config: typography.private-typography-to-2014-config( + theming.get-typography-config($config-or-theme)); + .mat-slide-toggle-content { + font-family: typography-utils.font-family($config); + } +} + +@mixin _density($config-or-theme) {} + +@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-legacy-slide-toggle') { + $color: theming.get-color-config($theme); + $density: theming.get-density-config($theme); + $typography: theming.get-typography-config($theme); + + @if $color != null { + @include color($color); + } + @if $density != null { + @include _density($density); + } + @if $typography != null { + @include typography($typography); + } + } +} + diff --git a/src/material-experimental/mdc-slide-toggle/index.ts b/src/material/legacy-slide-toggle/index.ts similarity index 100% rename from src/material-experimental/mdc-slide-toggle/index.ts rename to src/material/legacy-slide-toggle/index.ts diff --git a/src/material/legacy-slide-toggle/public-api.ts b/src/material/legacy-slide-toggle/public-api.ts new file mode 100644 index 000000000000..4702233a20d4 --- /dev/null +++ b/src/material/legacy-slide-toggle/public-api.ts @@ -0,0 +1,17 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './slide-toggle-module'; +export * from './slide-toggle'; +export * from './slide-toggle-config'; +export { + _MatSlideToggleBase as _MatLegacySlideToggleBase, + _MatSlideToggleRequiredValidatorModule as _MatLegacySlideToggleRequiredValidatorModule, + MAT_SLIDE_TOGGLE_REQUIRED_VALIDATOR as MAT_LEGACY_SLIDE_TOGGLE_REQUIRED_VALIDATOR, + MatSlideToggleRequiredValidator as MatLegacySlideToggleRequiredValidator, +} from '@angular/material/slide-toggle'; diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle-config.ts b/src/material/legacy-slide-toggle/slide-toggle-config.ts similarity index 77% rename from src/material-experimental/mdc-slide-toggle/slide-toggle-config.ts rename to src/material/legacy-slide-toggle/slide-toggle-config.ts index 30ae3cf64623..dbb699e60e79 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle-config.ts +++ b/src/material/legacy-slide-toggle/slide-toggle-config.ts @@ -9,7 +9,7 @@ import {InjectionToken} from '@angular/core'; import {ThemePalette} from '@angular/material/core'; /** Default `mat-slide-toggle` options that can be overridden. */ -export interface MatSlideToggleDefaultOptions { +export interface MatLegacySlideToggleDefaultOptions { /** Whether toggle action triggers value changes in slide toggle. */ disableToggleValue?: boolean; @@ -18,10 +18,8 @@ export interface MatSlideToggleDefaultOptions { } /** Injection token to be used to override the default options for `mat-slide-toggle`. */ -export const MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS = new InjectionToken( - 'mat-slide-toggle-default-options', - { +export const MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS = + new InjectionToken('mat-slide-toggle-default-options', { providedIn: 'root', factory: () => ({disableToggleValue: false}), - }, -); + }); diff --git a/src/material-experimental/mdc-slide-toggle/module.ts b/src/material/legacy-slide-toggle/slide-toggle-module.ts similarity index 50% rename from src/material-experimental/mdc-slide-toggle/module.ts rename to src/material/legacy-slide-toggle/slide-toggle-module.ts index f113cde81057..761ddc143f03 100644 --- a/src/material-experimental/mdc-slide-toggle/module.ts +++ b/src/material/legacy-slide-toggle/slide-toggle-module.ts @@ -6,15 +6,20 @@ * found in the LICENSE file at https://angular.io/license */ -import {CommonModule} from '@angular/common'; +import {ObserversModule} from '@angular/cdk/observers'; import {NgModule} from '@angular/core'; import {MatCommonModule, MatRippleModule} from '@angular/material/core'; +import {MatLegacySlideToggle} from './slide-toggle'; import {_MatSlideToggleRequiredValidatorModule} from '@angular/material/slide-toggle'; -import {MatSlideToggle} from './slide-toggle'; @NgModule({ - imports: [_MatSlideToggleRequiredValidatorModule, MatCommonModule, MatRippleModule, CommonModule], - exports: [_MatSlideToggleRequiredValidatorModule, MatSlideToggle, MatCommonModule], - declarations: [MatSlideToggle], + imports: [ + _MatSlideToggleRequiredValidatorModule, + MatRippleModule, + MatCommonModule, + ObserversModule, + ], + exports: [_MatSlideToggleRequiredValidatorModule, MatLegacySlideToggle, MatCommonModule], + declarations: [MatLegacySlideToggle], }) -export class MatSlideToggleModule {} +export class MatLegacySlideToggleModule {} diff --git a/src/material/legacy-slide-toggle/slide-toggle.e2e.spec.ts b/src/material/legacy-slide-toggle/slide-toggle.e2e.spec.ts new file mode 100644 index 000000000000..615590383467 --- /dev/null +++ b/src/material/legacy-slide-toggle/slide-toggle.e2e.spec.ts @@ -0,0 +1,65 @@ +import {browser, element, by, Key} from 'protractor'; +import {expectToExist} from '../../cdk/testing/private/e2e'; + +describe('slide-toggle', () => { + const getInput = () => element(by.css('#normal-slide-toggle input')); + const getNormalToggle = () => element(by.css('#normal-slide-toggle')); + + beforeEach(async () => await browser.get('slide-toggle')); + + it('should render a slide-toggle', async () => { + await expectToExist('mat-slide-toggle'); + }); + + it('should change the checked state on click', async () => { + const inputEl = getInput(); + + expect(await inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); + + await getNormalToggle().click(); + + expect(await inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); + }); + + it('should change the checked state on click', async () => { + const inputEl = getInput(); + + expect(await inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); + + await getNormalToggle().click(); + + expect(await inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); + }); + + it('should not change the checked state on click when disabled', async () => { + const inputEl = getInput(); + + expect(await inputEl.getAttribute('checked')).toBeFalsy('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'); + }); + + it('should move the thumb on state change', async () => { + const slideToggleEl = getNormalToggle(); + const thumbEl = element(by.css('#normal-slide-toggle .mat-slide-toggle-thumb-container')); + const previousPosition = await thumbEl.getLocation(); + + await slideToggleEl.click(); + + const position = await thumbEl.getLocation(); + + expect(position.x).not.toBe(previousPosition.x); + }); + + it('should toggle the slide-toggle on space key', async () => { + const inputEl = getInput(); + + expect(await inputEl.getAttribute('checked')).toBeFalsy('Expect slide-toggle to be unchecked'); + + await inputEl.sendKeys(Key.SPACE); + + expect(await inputEl.getAttribute('checked')).toBeTruthy('Expect slide-toggle to be checked'); + }); +}); diff --git a/src/material/legacy-slide-toggle/slide-toggle.html b/src/material/legacy-slide-toggle/slide-toggle.html new file mode 100644 index 000000000000..c7038930c12a --- /dev/null +++ b/src/material/legacy-slide-toggle/slide-toggle.html @@ -0,0 +1,40 @@ + diff --git a/src/material/legacy-slide-toggle/slide-toggle.md b/src/material/legacy-slide-toggle/slide-toggle.md new file mode 100644 index 000000000000..fb51508ef55c --- /dev/null +++ b/src/material/legacy-slide-toggle/slide-toggle.md @@ -0,0 +1,39 @@ +`` is an on/off control that can be toggled via clicking. + + + +The slide-toggle behaves similarly to a checkbox, though it does not support an `indeterminate` +state like ``. + +### Slide-toggle label +The slide-toggle label is provided as the content to the `` element. + +If you don't want the label to appear next to the slide-toggle, you can use +[`aria-label`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-label) or +[`aria-labelledby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby) to +specify an appropriate label. + +### Use with `@angular/forms` +`` is compatible with `@angular/forms` and supports both `FormsModule` +and `ReactiveFormsModule`. + +### Theming +The color of a `` can be changed by using the `color` property. By default, +slide-toggles use the theme's accent color. This can be changed to `'primary'` or `'warn'`. + +### Accessibility + +`MatSlideToggle` uses an internal `` with `role="switch"` to provide an +accessible experience. This internal checkbox receives focus and is automatically labelled by the +text content of the `` element. Avoid adding other interactive controls into the +content of ``, as this degrades the experience for users of assistive technology. + +Always provide an accessible label via `aria-label` or `aria-labelledby` for toggles without +descriptive text content. For dynamic labels, `MatSlideToggle` provides input properties for binding +`aria-label` and `aria-labelledby`. This means that you should not use the `attr.` prefix when +binding these properties, as demonstrated below. + +```html + + +``` diff --git a/src/material/legacy-slide-toggle/slide-toggle.scss b/src/material/legacy-slide-toggle/slide-toggle.scss new file mode 100644 index 000000000000..e420cb134285 --- /dev/null +++ b/src/material/legacy-slide-toggle/slide-toggle.scss @@ -0,0 +1,236 @@ +@use 'sass:math'; +@use '@angular/cdk'; + +@use '../core/style/vendor-prefixes'; +@use '../core/style/variables'; +@use '../core/ripple/ripple'; +@use '../core/style/list-common'; + +$thumb-size: 20px !default; +$bar-border-radius: 8px !default; +$height: 24px !default; +$spacing: 8px !default; +$ripple-radius: 20px !default; +$bar-width: 36px !default; +$bar-height: 14px !default; +$bar-track-width: $bar-width - $thumb-size; + + +.mat-slide-toggle { + display: inline-block; + height: $height; + max-width: 100%; + + line-height: $height; + white-space: nowrap; + outline: none; + + -webkit-tap-highlight-color: transparent; + + &.mat-checked { + .mat-slide-toggle-thumb-container { + transform: translate3d($bar-track-width, 0, 0); + + [dir='rtl'] & { + transform: translate3d(-$bar-track-width, 0, 0); + } + } + } + + &.mat-disabled { + // The value is based on MDC. + opacity: 0.38; + + .mat-slide-toggle-label, .mat-slide-toggle-thumb-container { + cursor: default; + } + } +} + +// The label element is our root container for the slide-toggle / switch indicator and label text. +// It has to be a label, to support accessibility for the visual hidden input. +.mat-slide-toggle-label { + @include vendor-prefixes.user-select(none); + display: flex; + flex: 1; + flex-direction: row; + align-items: center; + height: inherit; + cursor: pointer; +} + +.mat-slide-toggle-content { + @include list-common.truncate-line(); +} + +// If the label should be placed before the thumb then we just change the orders. +.mat-slide-toggle-label-before { + .mat-slide-toggle-label { order: 1; } + .mat-slide-toggle-bar { order: 2; } +} + +// Apply the margin for slide-toggles and revert it for RTL toggles with labelPosition before. +[dir='rtl'] .mat-slide-toggle-label-before .mat-slide-toggle-bar, +.mat-slide-toggle-bar { + margin-right: $spacing; + margin-left: 0; +} + +// Switch the margins in RTL mode and also switch it if the labelPosition is set to before. +[dir='rtl'], +.mat-slide-toggle-label-before { + .mat-slide-toggle-bar { + margin-left: $spacing; + margin-right: 0; + } +} + +.mat-slide-toggle-bar-no-side-margin { + margin-left: 0; + margin-right: 0; +} + +// The container includes the visual thumb and the ripple container element. +.mat-slide-toggle-thumb-container { + $thumb-bar-vertical-padding: math.div($thumb-size - $bar-height, 2); + + position: absolute; + z-index: 1; + + width: $thumb-size; + height: $thumb-size; + top: -$thumb-bar-vertical-padding; + left: 0; + + transform: translate3d(0, 0, 0); + transition: variables.$swift-linear; + transition-property: transform; + + ._mat-animation-noopable & { + transition: none; + } + + [dir='rtl'] & { + left: auto; + right: 0; + } +} + +// The visual thumb element that moves inside of the thumb bar. +// The parent thumb-container container is responsible for the movement of the visual thumb. +.mat-slide-toggle-thumb { + height: $thumb-size; + width: $thumb-size; + border-radius: 50%; + display: block; +} + +// Horizontal bar for the slide-toggle. +// The slide-toggle bar is shown behind the movable thumb element. +.mat-slide-toggle-bar { + position: relative; + + width: $bar-width; + height: $bar-height; + + // Prevent shrinking of the bar container. It can happen that the content is long enough to + // shrink the bar and the thumb. + flex-shrink: 0; + + border-radius: $bar-border-radius; +} + +// The slide toggle shows a visually hidden input inside of the component, which is used +// to take advantage of the native browser functionality. +.mat-slide-toggle-input { + // Move the input to the bottom and in the middle of the thumb. + // Visual improvement to properly show browser popups when being required. + $horizontal-offset: math.div($thumb-size, 2); + + bottom: 0; + left: $horizontal-offset; + + [dir='rtl'] & { + left: auto; + right: $horizontal-offset; + } +} + +.mat-slide-toggle-bar, +.mat-slide-toggle-thumb { + transition: variables.$swift-linear; + transition-property: background-color; + transition-delay: 50ms; + + ._mat-animation-noopable & { + transition: none; + } +} + +// Ripple positioning for the slide-toggle. Moves the ripple container into the center of the thumb. +// Increase specificity because ripple styles are part of the `mat-core` mixin and can +// potentially overwrite the absolute position of the container. +.mat-slide-toggle .mat-slide-toggle-ripple { + position: absolute; + top: calc(50% - #{$ripple-radius}); + left: calc(50% - #{$ripple-radius}); + height: $ripple-radius * 2; + width: $ripple-radius * 2; + z-index: 1; + pointer-events: none; + + .mat-ripple-element:not(.mat-slide-toggle-persistent-ripple) { + // Although the specs describe an opacity of 16% for ripples if the slide-toggle is being + // pressed, we need to reduce the opacity a bit because besides the transient ripples, + // the persistent ripple will still show up and blend with the transient ripple. + opacity: 0.12; + } +} + +.mat-slide-toggle-persistent-ripple { + width: 100%; + height: 100%; + transform: none; + + .mat-slide-toggle-bar:hover & { + opacity: 0.04; + } + + // As per specifications, the focus ripple should only show up if the slide-toggle has + // been focused through keyboard. We cannot account for `cdk-program-focused` because clicking + // on the label causes the focus origin to be `program` due to the focus redirection. + .mat-slide-toggle:not(.mat-disabled).cdk-keyboard-focused & { + opacity: 0.12; + } + + // We do this here, rather than having a `:not(.mat-slide-toggle-disabled)` + // above in the `:hover`, because the `:not` will bump the specificity + // a lot and will cause it to overide the focus styles. + &, .mat-slide-toggle.mat-disabled .mat-slide-toggle-bar:hover & { + opacity: 0; + } + + // Hover styles will be displayed after tapping on touch devices. + // Disable the hover styling if the user's device doesn't support hovering. + @media (hover: none) { + // Note that we only negate the `:hover` rather than setting it to always be `display: none`, + // in order to maintain the focus indication for hybrid touch + keyboard devices. + .mat-slide-toggle-bar:hover & { + display: none; + } + } +} + +// Slide-toggles render focus indicators when the +// associated visually-hidden input is focused. +.mat-slide-toggle-input:focus ~ .mat-slide-toggle-thumb-container .mat-focus-indicator::before { + content: ''; +} + +// Custom styling to make the slide-toggle usable in high contrast mode. +@include cdk.high-contrast(active, off) { + .mat-slide-toggle-thumb, + .mat-slide-toggle-bar { + border: 1px solid; + } +} diff --git a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts b/src/material/legacy-slide-toggle/slide-toggle.spec.ts similarity index 61% rename from src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts rename to src/material/legacy-slide-toggle/slide-toggle.spec.ts index f24cb8dc54a9..5a21ef7d2d7e 100644 --- a/src/material-experimental/mdc-slide-toggle/slide-toggle.spec.ts +++ b/src/material/legacy-slide-toggle/slide-toggle.spec.ts @@ -1,34 +1,53 @@ -import {BidiModule, Direction} from '@angular/cdk/bidi'; +import {MutationObserverFactory} from '@angular/cdk/observers'; import {dispatchFakeEvent} from '../../cdk/testing/private'; -import {Component} from '@angular/core'; +import {Component, DebugElement} from '@angular/core'; import { ComponentFixture, fakeAsync, flush, flushMicrotasks, - inject, TestBed, tick, + inject, } from '@angular/core/testing'; import {FormControl, FormsModule, NgModel, ReactiveFormsModule} from '@angular/forms'; import {By} from '@angular/platform-browser'; import {FocusMonitor} from '@angular/cdk/a11y'; -import {MatSlideToggle, MatSlideToggleChange, MatSlideToggleModule} from './index'; -import {MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS} from './slide-toggle-config'; +import { + MatLegacySlideToggle, + MatLegacySlideToggleChange, + MatLegacySlideToggleModule, +} from './index'; +import {MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS} from './slide-toggle-config'; + +describe('MatSlideToggle without forms', () => { + let mutationObserverCallbacks: Function[]; + let flushMutationObserver = () => mutationObserverCallbacks.forEach(callback => callback()); -describe('MDC-based MatSlideToggle without forms', () => { beforeEach(fakeAsync(() => { + mutationObserverCallbacks = []; + TestBed.configureTestingModule({ - imports: [MatSlideToggleModule, BidiModule], + imports: [MatLegacySlideToggleModule], declarations: [ SlideToggleBasic, - SlideToggleCheckedAndDisabledAttr, SlideToggleWithTabindexAttr, SlideToggleWithoutLabel, SlideToggleProjectedLabel, TextBindingComponent, SlideToggleWithStaticAriaAttributes, ], + providers: [ + { + provide: MutationObserverFactory, + useValue: { + create: (callback: Function) => { + mutationObserverCallbacks.push(callback); + return {observe: () => {}, disconnect: () => {}}; + }, + }, + }, + ], }); TestBed.compileComponents(); @@ -38,10 +57,10 @@ describe('MDC-based MatSlideToggle without forms', () => { let fixture: ComponentFixture; let testComponent: SlideToggleBasic; - let slideToggle: MatSlideToggle; + let slideToggle: MatLegacySlideToggle; let slideToggleElement: HTMLElement; let labelElement: HTMLLabelElement; - let buttonElement: HTMLButtonElement; + let inputElement: HTMLInputElement; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(SlideToggleBasic); @@ -59,7 +78,7 @@ describe('MDC-based MatSlideToggle without forms', () => { testComponent = fixture.debugElement.componentInstance; slideToggle = slideToggleDebug.componentInstance; slideToggleElement = slideToggleDebug.nativeElement; - buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement; + inputElement = fixture.debugElement.query(By.css('input'))!.nativeElement; labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement; })); @@ -76,74 +95,93 @@ describe('MDC-based MatSlideToggle without forms', () => { })); it('should correctly update the disabled property', fakeAsync(() => { - expect(buttonElement.disabled).toBeFalsy(); + expect(inputElement.disabled).toBeFalsy(); testComponent.isDisabled = true; fixture.detectChanges(); - expect(buttonElement.disabled).toBeTruthy(); + expect(inputElement.disabled).toBeTruthy(); })); it('should correctly update the checked property', fakeAsync(() => { expect(slideToggle.checked).toBeFalsy(); - expect(buttonElement.getAttribute('aria-checked')).toBe('false'); + expect(inputElement.getAttribute('aria-checked')).toBe('false'); testComponent.slideChecked = true; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-checked')).toBe('true'); + expect(inputElement.checked).toBeTruthy(); + expect(inputElement.getAttribute('aria-checked')).toBe('true'); })); it('should set the toggle to checked on click', fakeAsync(() => { expect(slideToggle.checked).toBe(false); - expect(buttonElement.getAttribute('aria-checked')).toBe('false'); - expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked'); + expect(inputElement.getAttribute('aria-checked')).toBe('false'); + expect(slideToggleElement.classList).not.toContain('mat-checked'); labelElement.click(); fixture.detectChanges(); flush(); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); expect(slideToggle.checked).toBe(true); - expect(buttonElement.getAttribute('aria-checked')).toBe('true'); + expect(inputElement.getAttribute('aria-checked')).toBe('true'); })); it('should not trigger the click event multiple times', fakeAsync(() => { // By default, when clicking on a label element, a generated click will be dispatched - // on the associated button element. - // Since we're using a label element and a visual hidden button, this behavior can led + // on the associated input element. + // Since we're using a label element and a visual hidden input, this behavior can led // to an issue, where the click events on the slide-toggle are getting executed twice. expect(slideToggle.checked).toBe(false); - expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).not.toContain('mat-checked'); labelElement.click(); fixture.detectChanges(); - tick(); + flush(); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); expect(slideToggle.checked).toBe(true); expect(testComponent.onSlideClick).toHaveBeenCalledTimes(1); })); it('should trigger the change event properly', fakeAsync(() => { - expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked'); + expect(inputElement.checked).toBe(false); + expect(slideToggleElement.classList).not.toContain('mat-checked'); labelElement.click(); fixture.detectChanges(); flush(); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(inputElement.checked).toBe(true); + expect(slideToggleElement.classList).toContain('mat-checked'); expect(testComponent.onSlideChange).toHaveBeenCalledTimes(1); })); it('should not trigger the change event by changing the native value', fakeAsync(() => { - expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked'); + expect(inputElement.checked).toBe(false); + expect(slideToggleElement.classList).not.toContain('mat-checked'); testComponent.slideChecked = true; fixture.detectChanges(); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(inputElement.checked).toBe(true); + expect(slideToggleElement.classList).toContain('mat-checked'); + tick(); + + expect(testComponent.onSlideChange).not.toHaveBeenCalled(); + })); + + it('should not trigger the change event on initialization', fakeAsync(() => { + expect(inputElement.checked).toBe(false); + expect(slideToggleElement.classList).not.toContain('mat-checked'); + + testComponent.slideChecked = true; + fixture.detectChanges(); + + expect(inputElement.checked).toBe(true); + expect(slideToggleElement.classList).toContain('mat-checked'); tick(); expect(testComponent.onSlideChange).not.toHaveBeenCalled(); @@ -154,92 +192,89 @@ describe('MDC-based MatSlideToggle without forms', () => { fixture.detectChanges(); expect(slideToggleElement.id).toBe('myId'); - expect(buttonElement.id).toBe(`${slideToggleElement.id}-button`); + expect(inputElement.id).toBe(`${slideToggleElement.id}-input`); testComponent.slideId = 'nextId'; fixture.detectChanges(); expect(slideToggleElement.id).toBe('nextId'); - expect(buttonElement.id).toBe(`${slideToggleElement.id}-button`); + expect(inputElement.id).toBe(`${slideToggleElement.id}-input`); testComponent.slideId = null; fixture.detectChanges(); // Once the id binding is set to null, the id property should auto-generate a unique id. - expect(buttonElement.id).toMatch(/mat-mdc-slide-toggle-\d+-button/); + expect(inputElement.id).toMatch(/mat-slide-toggle-\d+-input/); })); it('should forward the tabIndex to the underlying element', fakeAsync(() => { fixture.detectChanges(); - expect(buttonElement.tabIndex).toBe(0); + expect(inputElement.tabIndex).toBe(0); testComponent.slideTabindex = 4; fixture.detectChanges(); - expect(buttonElement.tabIndex).toBe(4); + expect(inputElement.tabIndex).toBe(4); })); it('should forward the specified name to the element', fakeAsync(() => { testComponent.slideName = 'myName'; fixture.detectChanges(); - expect(buttonElement.name).toBe('myName'); + expect(inputElement.name).toBe('myName'); testComponent.slideName = 'nextName'; fixture.detectChanges(); - expect(buttonElement.name).toBe('nextName'); + expect(inputElement.name).toBe('nextName'); testComponent.slideName = null; fixture.detectChanges(); - expect(buttonElement.name).toBe(''); + expect(inputElement.name).toBe(''); })); it('should forward the aria-label attribute to the element', fakeAsync(() => { testComponent.slideLabel = 'ariaLabel'; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-label')).toBe('ariaLabel'); + expect(inputElement.getAttribute('aria-label')).toBe('ariaLabel'); testComponent.slideLabel = null; fixture.detectChanges(); - expect(buttonElement.hasAttribute('aria-label')).toBeFalsy(); + expect(inputElement.hasAttribute('aria-label')).toBeFalsy(); })); it('should forward the aria-labelledby attribute to the element', fakeAsync(() => { testComponent.slideLabelledBy = 'ariaLabelledBy'; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-labelledby')).toBe('ariaLabelledBy'); + expect(inputElement.getAttribute('aria-labelledby')).toBe('ariaLabelledBy'); testComponent.slideLabelledBy = null; fixture.detectChanges(); - // We fall back to pointing to the label if a value isn't provided. - expect(buttonElement.getAttribute('aria-labelledby')).toMatch( - /mat-mdc-slide-toggle-\d+-label/, - ); + expect(inputElement.hasAttribute('aria-labelledby')).toBeFalsy(); })); it('should forward the aria-describedby attribute to the element', fakeAsync(() => { testComponent.slideAriaDescribedBy = 'some-element'; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-describedby')).toBe('some-element'); + expect(inputElement.getAttribute('aria-describedby')).toBe('some-element'); testComponent.slideAriaDescribedBy = null; fixture.detectChanges(); - expect(buttonElement.hasAttribute('aria-describedby')).toBe(false); + expect(inputElement.hasAttribute('aria-describedby')).toBe(false); })); it('should set the `for` attribute to the id of the element', fakeAsync(() => { expect(labelElement.getAttribute('for')).toBeTruthy(); - expect(buttonElement.getAttribute('id')).toBeTruthy(); - expect(labelElement.getAttribute('for')).toBe(buttonElement.getAttribute('id')); + expect(inputElement.getAttribute('id')).toBeTruthy(); + expect(labelElement.getAttribute('for')).toBe(inputElement.getAttribute('id')); })); it('should emit the new values properly', fakeAsync(() => { @@ -265,86 +300,84 @@ describe('MDC-based MatSlideToggle without forms', () => { subscription.unsubscribe(); })); - it('should forward the required attribute', () => { + it('should forward the required attribute', fakeAsync(() => { testComponent.isRequired = true; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-required')).toBe('true'); + expect(inputElement.required).toBe(true); testComponent.isRequired = false; fixture.detectChanges(); - expect(buttonElement.getAttribute('aria-required')).toBe(null); - }); + expect(inputElement.required).toBe(false); + })); it('should focus on underlying element when focus() is called', fakeAsync(() => { - expect(document.activeElement).not.toBe(buttonElement); + expect(document.activeElement).not.toBe(inputElement); slideToggle.focus(); fixture.detectChanges(); flush(); - expect(document.activeElement).toBe(buttonElement); + expect(document.activeElement).toBe(inputElement); })); - it('should not manually move focus to underlying when focus comes from mouse or touch', fakeAsync( - inject([FocusMonitor], (focusMonitor: FocusMonitor) => { - expect(document.activeElement).not.toBe(buttonElement); + it('should not manually move focus to underlying when focus comes from mouse or touch', inject( + [FocusMonitor], + (focusMonitor: FocusMonitor) => { + expect(document.activeElement).not.toBe(inputElement); focusMonitor.focusVia(slideToggleElement, 'mouse'); fixture.detectChanges(); - flush(); - expect(document.activeElement).not.toBe(buttonElement); + expect(document.activeElement).not.toBe(inputElement); focusMonitor.focusVia(slideToggleElement, 'touch'); fixture.detectChanges(); - flush(); - expect(document.activeElement).not.toBe(buttonElement); - }), + expect(document.activeElement).not.toBe(inputElement); + }, )); it('should set a element class if labelPosition is set to before', fakeAsync(() => { - const formField = slideToggleElement.querySelector('.mdc-form-field')!; - - expect(formField.classList).not.toContain('mdc-form-field--align-end'); + expect(slideToggleElement.classList).not.toContain('mat-slide-toggle-label-before'); testComponent.labelPosition = 'before'; fixture.detectChanges(); - expect(formField.classList).toContain('mdc-form-field--align-end'); + expect(slideToggleElement.classList).toContain('mat-slide-toggle-label-before'); })); it('should show ripples', fakeAsync(() => { - const rippleSelector = '.mat-ripple-element'; - const switchElement = slideToggleElement.querySelector('.mdc-switch')!; + const rippleSelector = '.mat-ripple-element:not(.mat-slide-toggle-persistent-ripple)'; expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0); - dispatchFakeEvent(switchElement, 'mousedown'); - dispatchFakeEvent(switchElement, 'mouseup'); + dispatchFakeEvent(labelElement, 'mousedown'); + dispatchFakeEvent(labelElement, 'mouseup'); expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(1); flush(); })); it('should not show ripples when disableRipple is set', fakeAsync(() => { - const switchElement = slideToggleElement.querySelector('.mdc-switch')!; - const rippleSelector = '.mat-ripple-element'; + const rippleSelector = '.mat-ripple-element:not(.mat-slide-toggle-persistent-ripple)'; testComponent.disableRipple = true; fixture.detectChanges(); expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0); - dispatchFakeEvent(switchElement, 'mousedown'); - dispatchFakeEvent(switchElement, 'mouseup'); + dispatchFakeEvent(labelElement, 'mousedown'); + dispatchFakeEvent(labelElement, 'mouseup'); expect(slideToggleElement.querySelectorAll(rippleSelector).length).toBe(0); flush(); })); it('should have a focus indicator', fakeAsync(() => { - const rippleElement = slideToggleElement.querySelector('.mat-mdc-slide-toggle-ripple')!; - expect(rippleElement.classList).toContain('mat-mdc-focus-indicator'); + const slideToggleRippleNativeElement = slideToggleElement.querySelector( + '.mat-slide-toggle-ripple', + )!; + + expect(slideToggleRippleNativeElement.classList.contains('mat-focus-indicator')).toBe(true); })); }); @@ -363,36 +396,22 @@ describe('MDC-based MatSlideToggle without forms', () => { fixture.detectChanges(); - const slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))! - .componentInstance as MatSlideToggle; + const slideToggle = fixture.debugElement.query(By.directive(MatLegacySlideToggle))! + .componentInstance as MatLegacySlideToggle; expect(slideToggle.tabIndex) .withContext('Expected tabIndex property to have been set based on the native attribute') .toBe(5); })); - it('should add the disabled class if disabled through attribute', fakeAsync(() => { - const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr); - fixture.detectChanges(); - - const switchEl = fixture.nativeElement.querySelector('.mdc-switch'); - expect(switchEl.classList).toContain('mdc-switch--disabled'); - })); - - it('should add the checked class if checked through attribute', fakeAsync(() => { - const fixture = TestBed.createComponent(SlideToggleCheckedAndDisabledAttr); - fixture.detectChanges(); - - const switchEl = fixture.nativeElement.querySelector('.mdc-switch'); - expect(switchEl.classList).toContain('mdc-switch--checked'); - })); - it('should remove the tabindex from the host node', fakeAsync(() => { const fixture = TestBed.createComponent(SlideToggleWithTabindexAttr); fixture.detectChanges(); - const slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.nativeElement; + const slideToggle = fixture.debugElement.query( + By.directive(MatLegacySlideToggle), + )!.nativeElement; expect(slideToggle.hasAttribute('tabindex')).toBe(false); })); @@ -402,68 +421,144 @@ describe('MDC-based MatSlideToggle without forms', () => { fixture.componentInstance.disabled = true; fixture.detectChanges(); - const slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.nativeElement; + const slideToggle = fixture.debugElement.query( + By.directive(MatLegacySlideToggle), + )!.nativeElement; expect(slideToggle.hasAttribute('tabindex')).toBe(false); })); }); - it( - 'should not change value on click when click action is noop when using custom a ' + - 'action configuration', - fakeAsync(() => { + describe('default options', () => { + it( + 'should not change value on click when click action is noop when using custom a ' + + 'action configuration', + fakeAsync(() => { + TestBed.resetTestingModule().configureTestingModule({ + imports: [MatLegacySlideToggleModule], + declarations: [SlideToggleBasic], + providers: [ + { + provide: MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS, + useValue: {disableToggleValue: true}, + }, + ], + }); + const fixture = TestBed.createComponent(SlideToggleBasic); + fixture.detectChanges(); + + const testComponent = fixture.debugElement.componentInstance; + const slideToggleDebug = fixture.debugElement.query(By.css('mat-slide-toggle'))!; + + const slideToggle = slideToggleDebug.componentInstance; + const inputElement = fixture.debugElement.query(By.css('input'))!.nativeElement; + const labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement; + + expect(testComponent.toggleTriggered).toBe(0); + expect(testComponent.dragTriggered).toBe(0); + expect(slideToggle.checked) + .withContext('Expect slide toggle value not changed') + .toBe(false); + + labelElement.click(); + fixture.detectChanges(); + flush(); + + expect(slideToggle.checked) + .withContext('Expect slide toggle value not changed') + .toBe(false); + expect(testComponent.toggleTriggered).withContext('Expect toggle once').toBe(1); + expect(testComponent.dragTriggered).toBe(0); + + inputElement.click(); + fixture.detectChanges(); + flush(); + + expect(slideToggle.checked) + .withContext('Expect slide toggle value not changed') + .toBe(false); + expect(testComponent.toggleTriggered).withContext('Expect toggle twice').toBe(2); + expect(testComponent.dragTriggered).toBe(0); + }), + ); + + it('should be able to change the default color', fakeAsync(() => { TestBed.resetTestingModule().configureTestingModule({ - imports: [MatSlideToggleModule], - declarations: [SlideToggleBasic], - providers: [ - { - provide: MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, - useValue: {disableToggleValue: true}, - }, - ], + imports: [MatLegacySlideToggleModule], + declarations: [SlideToggleWithForm], + providers: [{provide: MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS, useValue: {color: 'warn'}}], }); - const fixture = TestBed.createComponent(SlideToggleBasic); + const fixture = TestBed.createComponent(SlideToggleWithForm); fixture.detectChanges(); + const slideToggle = fixture.nativeElement.querySelector('.mat-slide-toggle'); + expect(slideToggle.classList).toContain('mat-warn'); + })); + }); - const testComponent = fixture.debugElement.componentInstance; - const slideToggleDebug = fixture.debugElement.query(By.css('mat-slide-toggle'))!; + describe('without label', () => { + let fixture: ComponentFixture; + let testComponent: SlideToggleWithoutLabel; + let slideToggleBarElement: HTMLElement; - const slideToggle = slideToggleDebug.componentInstance; - const buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement; - const labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement; + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(SlideToggleWithoutLabel); - expect(testComponent.toggleTriggered).toBe(0); - expect(testComponent.dragTriggered).toBe(0); - expect(slideToggle.checked).withContext('Expect slide toggle value not changed').toBe(false); + const slideToggleDebugEl = fixture.debugElement.query(By.directive(MatLegacySlideToggle))!; - labelElement.click(); + testComponent = fixture.componentInstance; + slideToggleBarElement = slideToggleDebugEl.query( + By.css('.mat-slide-toggle-bar'), + )!.nativeElement; + })); + + it('should remove margin for slide-toggle without a label', fakeAsync(() => { fixture.detectChanges(); - tick(); - expect(slideToggle.checked).withContext('Expect slide toggle value not changed').toBe(false); - expect(testComponent.toggleTriggered).withContext('Expect toggle once').toBe(1); - expect(testComponent.dragTriggered).toBe(0); + expect(slideToggleBarElement.classList).toContain('mat-slide-toggle-bar-no-side-margin'); + })); + + it('should not remove margin if initial label is set through binding', fakeAsync(() => { + testComponent.label = 'Some content'; + fixture.detectChanges(); - buttonElement.click(); + expect(slideToggleBarElement.classList).not.toContain('mat-slide-toggle-bar-no-side-margin'); + })); + + it('should re-add margin if label is added asynchronously', fakeAsync(() => { fixture.detectChanges(); - tick(); - expect(slideToggle.checked).withContext('Expect slide toggle value not changed').toBe(false); - expect(testComponent.toggleTriggered).withContext('Expect toggle twice').toBe(2); - expect(testComponent.dragTriggered).toBe(0); - }), - ); - - it('should be able to change the default color', fakeAsync(() => { - TestBed.resetTestingModule().configureTestingModule({ - imports: [MatSlideToggleModule], - declarations: [SlideToggleWithForm], - providers: [{provide: MAT_SLIDE_TOGGLE_DEFAULT_OPTIONS, useValue: {color: 'warn'}}], - }); - const fixture = TestBed.createComponent(SlideToggleWithForm); - fixture.detectChanges(); - const slideToggle = fixture.nativeElement.querySelector('.mat-mdc-slide-toggle'); - expect(slideToggle.classList).toContain('mat-warn'); - })); + expect(slideToggleBarElement.classList).toContain('mat-slide-toggle-bar-no-side-margin'); + + testComponent.label = 'Some content'; + fixture.detectChanges(); + flushMutationObserver(); + fixture.detectChanges(); + + expect(slideToggleBarElement.classList).not.toContain('mat-slide-toggle-bar-no-side-margin'); + })); + }); + + describe('label margin', () => { + let fixture: ComponentFixture; + let slideToggleBarElement: HTMLElement; + + beforeEach(fakeAsync(() => { + fixture = TestBed.createComponent(SlideToggleProjectedLabel); + slideToggleBarElement = fixture.debugElement.query( + By.css('.mat-slide-toggle-bar'), + )!.nativeElement; + + fixture.detectChanges(); + })); + + it('should properly update margin if label content is projected', fakeAsync(() => { + // Do not run the change detection for the fixture manually because we want to verify + // that the slide-toggle properly toggles the margin class even if the observe content + // output fires outside of the zone. + flushMutationObserver(); + + expect(slideToggleBarElement.classList).not.toContain('mat-slide-toggle-bar-no-side-margin'); + })); + }); it('should clear static aria attributes from the host node', fakeAsync(() => { const fixture = TestBed.createComponent(SlideToggleWithStaticAriaAttributes); @@ -475,10 +570,10 @@ describe('MDC-based MatSlideToggle without forms', () => { })); }); -describe('MDC-based MatSlideToggle with forms', () => { +describe('MatSlideToggle with forms', () => { beforeEach(fakeAsync(() => { TestBed.configureTestingModule({ - imports: [MatSlideToggleModule, FormsModule, ReactiveFormsModule], + imports: [MatLegacySlideToggleModule, FormsModule, ReactiveFormsModule], declarations: [ SlideToggleWithForm, SlideToggleWithModel, @@ -494,10 +589,10 @@ describe('MDC-based MatSlideToggle with forms', () => { let fixture: ComponentFixture; let testComponent: SlideToggleWithModel; - let slideToggle: MatSlideToggle; + let slideToggle: MatLegacySlideToggle; let slideToggleElement: HTMLElement; let slideToggleModel: NgModel; - let buttonElement: HTMLButtonElement; + let inputElement: HTMLInputElement; let labelElement: HTMLLabelElement; // This initialization is async() because it needs to wait for ngModel to set the initial value. @@ -505,13 +600,13 @@ describe('MDC-based MatSlideToggle with forms', () => { fixture = TestBed.createComponent(SlideToggleWithModel); fixture.detectChanges(); - const slideToggleDebug = fixture.debugElement.query(By.directive(MatSlideToggle))!; + const slideToggleDebug = fixture.debugElement.query(By.directive(MatLegacySlideToggle))!; testComponent = fixture.debugElement.componentInstance; slideToggle = slideToggleDebug.componentInstance; slideToggleElement = slideToggleDebug.nativeElement; slideToggleModel = slideToggleDebug.injector.get(NgModel); - buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement; + inputElement = fixture.debugElement.query(By.css('input'))!.nativeElement; labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement; })); @@ -521,7 +616,7 @@ describe('MDC-based MatSlideToggle with forms', () => { })); it('should update the model programmatically', fakeAsync(() => { - expect(slideToggleElement.classList).not.toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).not.toContain('mat-checked'); testComponent.modelValue = true; fixture.detectChanges(); @@ -530,7 +625,7 @@ describe('MDC-based MatSlideToggle with forms', () => { flushMicrotasks(); fixture.detectChanges(); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); })); it('should have the correct control state initially and after interaction', fakeAsync(() => { @@ -543,17 +638,17 @@ describe('MDC-based MatSlideToggle with forms', () => { // become dirty (not pristine), but remain untouched if focus is still there. slideToggle.checked = true; - dispatchFakeEvent(buttonElement, 'focus'); - buttonElement.click(); - flush(); + // Dispatch a change event on the input element to fake a user interaction that triggered + // the state change. + dispatchFakeEvent(inputElement, 'change'); expect(slideToggleModel.valid).toBe(true); expect(slideToggleModel.pristine).toBe(false); expect(slideToggleModel.touched).toBe(false); - // Once the button element loses focus, the control should remain dirty but should + // Once the input element loses focus, the control should remain dirty but should // also turn touched. - dispatchFakeEvent(buttonElement, 'blur'); + dispatchFakeEvent(inputElement, 'blur'); fixture.detectChanges(); flushMicrotasks(); @@ -564,14 +659,15 @@ describe('MDC-based MatSlideToggle with forms', () => { it('should not throw an error when disabling while focused', fakeAsync(() => { expect(() => { - // Focus the button element because after disabling, the `blur` event should automatically + // Focus the input element because after disabling, the `blur` event should automatically // fire and not result in a changed after checked exception. Related: #12323 - buttonElement.focus(); - tick(); + inputElement.focus(); - fixture.componentInstance.isDisabled = true; - fixture.detectChanges(); + // Flush the two nested timeouts from the FocusMonitor that are being created on `focus`. + flush(); + slideToggle.disabled = true; + fixture.detectChanges(); flushMicrotasks(); }).not.toThrow(); })); @@ -580,20 +676,20 @@ describe('MDC-based MatSlideToggle with forms', () => { // The control should start off with being untouched. expect(slideToggleModel.touched).toBe(false); - testComponent.isChecked = true; + slideToggle.checked = true; fixture.detectChanges(); expect(slideToggleModel.touched).toBe(false); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); - // Once the button element loses focus, the control should remain dirty but should + // Once the input element loses focus, the control should remain dirty but should // also turn touched. - dispatchFakeEvent(buttonElement, 'blur'); + dispatchFakeEvent(inputElement, 'blur'); fixture.detectChanges(); flushMicrotasks(); expect(slideToggleModel.touched).toBe(true); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); })); it('should not set the control to touched when changing the model', fakeAsync(() => { @@ -612,12 +708,14 @@ describe('MDC-based MatSlideToggle with forms', () => { expect(slideToggleModel.touched).toBe(false); expect(slideToggle.checked).toBe(true); - expect(slideToggleElement.classList).toContain('mat-mdc-slide-toggle-checked'); + expect(slideToggleElement.classList).toContain('mat-checked'); })); it('should update checked state on click if control is checked initially', fakeAsync(() => { fixture = TestBed.createComponent(SlideToggleWithModel); - slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.componentInstance; + slideToggle = fixture.debugElement.query( + By.directive(MatLegacySlideToggle), + )!.componentInstance; labelElement = fixture.debugElement.query(By.css('label'))!.nativeElement; fixture.componentInstance.modelValue = true; @@ -627,7 +725,7 @@ describe('MDC-based MatSlideToggle with forms', () => { flushMicrotasks(); // Now the new checked variable has been updated in the slide-toggle and the slide-toggle - // is marked for check because it still needs to update the underlying button. + // is marked for check because it still needs to update the underlying input. fixture.detectChanges(); expect(slideToggle.checked) @@ -649,7 +747,7 @@ describe('MDC-based MatSlideToggle with forms', () => { fixture.componentInstance.modelValue = true; fixture.detectChanges(); - const debugElement = fixture.debugElement.query(By.directive(MatSlideToggle))!; + const debugElement = fixture.debugElement.query(By.directive(MatLegacySlideToggle))!; const modelInstance = debugElement.injector.get(NgModel); // Flush the microtasks because the forms module updates the model state asynchronously. @@ -661,7 +759,7 @@ describe('MDC-based MatSlideToggle with forms', () => { it('should set the model value when toggling via the `toggle` method', fakeAsync(() => { expect(testComponent.modelValue).toBe(false); - fixture.debugElement.query(By.directive(MatSlideToggle))!.componentInstance.toggle(); + fixture.debugElement.query(By.directive(MatLegacySlideToggle))!.componentInstance.toggle(); fixture.detectChanges(); flushMicrotasks(); @@ -674,33 +772,46 @@ describe('MDC-based MatSlideToggle with forms', () => { let fixture: ComponentFixture; let testComponent: SlideToggleWithFormControl; - let slideToggle: MatSlideToggle; - let buttonElement: HTMLButtonElement; + let slideToggleDebug: DebugElement; + let slideToggle: MatLegacySlideToggle; + let inputElement: HTMLInputElement; beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(SlideToggleWithFormControl); fixture.detectChanges(); + slideToggleDebug = fixture.debugElement.query(By.directive(MatLegacySlideToggle))!; + testComponent = fixture.debugElement.componentInstance; - slideToggle = fixture.debugElement.query(By.directive(MatSlideToggle))!.componentInstance; - buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement; + slideToggle = slideToggleDebug.componentInstance; + inputElement = fixture.debugElement.query(By.css('input'))!.nativeElement; })); it('should toggle the disabled state', fakeAsync(() => { expect(slideToggle.disabled).toBe(false); - expect(buttonElement.disabled).toBe(false); + expect(inputElement.disabled).toBe(false); testComponent.formControl.disable(); fixture.detectChanges(); expect(slideToggle.disabled).toBe(true); - expect(buttonElement.disabled).toBe(true); + expect(inputElement.disabled).toBe(true); testComponent.formControl.enable(); fixture.detectChanges(); expect(slideToggle.disabled).toBe(false); - expect(buttonElement.disabled).toBe(false); + expect(inputElement.disabled).toBe(false); + })); + + it('should not change focus origin if origin not specified', fakeAsync(() => { + slideToggle.focus(undefined, 'mouse'); + slideToggle.focus(); + fixture.detectChanges(); + flush(); + + expect(slideToggleDebug.nativeElement.classList).toContain('cdk-focused'); + expect(slideToggleDebug.nativeElement.classList).toContain('cdk-mouse-focused'); })); }); @@ -708,6 +819,7 @@ describe('MDC-based MatSlideToggle with forms', () => { let fixture: ComponentFixture; let testComponent: SlideToggleWithForm; let buttonElement: HTMLButtonElement; + let inputElement: HTMLInputElement; // This initialization is async() because it needs to wait for ngModel to set the initial value. beforeEach(fakeAsync(() => { @@ -716,30 +828,48 @@ describe('MDC-based MatSlideToggle with forms', () => { testComponent = fixture.debugElement.componentInstance; buttonElement = fixture.debugElement.query(By.css('button'))!.nativeElement; + inputElement = fixture.debugElement.query(By.css('input'))!.nativeElement; })); - it('should not submit the form when clicked', fakeAsync(() => { - expect(testComponent.isSubmitted).toBe(false); + it('should prevent the form from submit when being required', fakeAsync(() => { + if (typeof (inputElement as any).reportValidity === 'undefined') { + // If the browser does not report the validity then the tests will break. + // e.g Safari 8 on Mobile. + return; + } + + testComponent.isRequired = true; + + fixture.detectChanges(); buttonElement.click(); fixture.detectChanges(); flush(); expect(testComponent.isSubmitted).toBe(false); + + testComponent.isRequired = false; + fixture.detectChanges(); + + buttonElement.click(); + fixture.detectChanges(); + flush(); + + expect(testComponent.isSubmitted).toBe(true); })); it('should have proper invalid state if unchecked', fakeAsync(() => { testComponent.isRequired = true; fixture.detectChanges(); - const slideToggleEl = fixture.nativeElement.querySelector('.mat-mdc-slide-toggle'); + const slideToggleEl = fixture.nativeElement.querySelector('.mat-slide-toggle'); expect(slideToggleEl.classList).toContain('ng-invalid'); expect(slideToggleEl.classList).not.toContain('ng-valid'); // The required slide-toggle will be checked and the form control // should become valid. - buttonElement.click(); + inputElement.click(); fixture.detectChanges(); flush(); @@ -748,7 +878,7 @@ describe('MDC-based MatSlideToggle with forms', () => { // The required slide-toggle will be unchecked and the form control // should become invalid. - buttonElement.click(); + inputElement.click(); fixture.detectChanges(); flush(); @@ -757,8 +887,8 @@ describe('MDC-based MatSlideToggle with forms', () => { })); it('should clear static name attribute from the slide toggle host node', () => { - const hostNode = fixture.nativeElement.querySelector('.mat-mdc-slide-toggle'); - expect(buttonElement.getAttribute('name')).toBeTruthy(); + const hostNode = fixture.nativeElement.querySelector('.mat-slide-toggle'); + expect(inputElement.getAttribute('name')).toBeTruthy(); expect(hostNode.hasAttribute('name')).toBe(false); }); }); @@ -786,7 +916,7 @@ describe('MDC-based MatSlideToggle with forms', () => { @Component({ template: ` - void = () => {}; - onSlideChange = (event: MatSlideToggleChange) => (this.lastEvent = event); + onSlideChange = (event: MatLegacySlideToggleChange) => (this.lastEvent = event); onSlideToggleChange = () => this.toggleTriggered++; onSlideDragChange = () => this.dragTriggered++; } @@ -842,20 +971,12 @@ class SlideToggleWithForm { } @Component({ - template: ``, + template: ``, }) class SlideToggleWithModel { modelValue = false; - isChecked = false; - isDisabled = false; } -@Component({ - template: `Label`, -}) -class SlideToggleCheckedAndDisabledAttr {} - @Component({ template: ` diff --git a/src/material/legacy-slide-toggle/slide-toggle.ts b/src/material/legacy-slide-toggle/slide-toggle.ts new file mode 100644 index 000000000000..4c5ce3543e88 --- /dev/null +++ b/src/material/legacy-slide-toggle/slide-toggle.ts @@ -0,0 +1,153 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {FocusMonitor, FocusOrigin} from '@angular/cdk/a11y'; +import { + Attribute, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + Inject, + Optional, + ViewChild, + ViewEncapsulation, +} from '@angular/core'; +import {NG_VALUE_ACCESSOR} from '@angular/forms'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; +import { + MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS, + MatLegacySlideToggleDefaultOptions, +} from './slide-toggle-config'; +import {_MatSlideToggleBase} from '@angular/material/slide-toggle'; + +/** @docs-private */ +export const MAT_LEGACY_SLIDE_TOGGLE_VALUE_ACCESSOR = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => MatLegacySlideToggle), + multi: true, +}; + +/** Change event object emitted by a slide toggle. */ +export class MatLegacySlideToggleChange { + constructor( + /** The source slide toggle of the event. */ + public source: MatLegacySlideToggle, + /** The new `checked` value of the slide toggle. */ + public checked: boolean, + ) {} +} + +/** Represents a slidable "switch" toggle that can be moved between on and off. */ +@Component({ + selector: 'mat-slide-toggle', + exportAs: 'matSlideToggle', + host: { + 'class': 'mat-slide-toggle', + '[id]': 'id', + // Needs to be removed since it causes some a11y issues (see #21266). + '[attr.tabindex]': 'null', + '[attr.aria-label]': 'null', + '[attr.aria-labelledby]': 'null', + '[attr.name]': 'null', + '[class.mat-checked]': 'checked', + '[class.mat-disabled]': 'disabled', + '[class.mat-slide-toggle-label-before]': 'labelPosition == "before"', + '[class._mat-animation-noopable]': '_noopAnimations', + }, + templateUrl: 'slide-toggle.html', + styleUrls: ['slide-toggle.css'], + providers: [MAT_LEGACY_SLIDE_TOGGLE_VALUE_ACCESSOR], + inputs: ['disabled', 'disableRipple', 'color', 'tabIndex'], + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MatLegacySlideToggle extends _MatSlideToggleBase { + /** Reference to the underlying input element. */ + @ViewChild('input') _inputElement: ElementRef; + + constructor( + elementRef: ElementRef, + focusMonitor: FocusMonitor, + changeDetectorRef: ChangeDetectorRef, + @Attribute('tabindex') tabIndex: string, + @Inject(MAT_LEGACY_SLIDE_TOGGLE_DEFAULT_OPTIONS) + defaults: MatLegacySlideToggleDefaultOptions, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + ) { + super( + elementRef, + focusMonitor, + changeDetectorRef, + tabIndex, + defaults, + animationMode, + 'mat-slide-toggle-', + ); + } + + protected _createChangeEvent(isChecked: boolean) { + return new MatLegacySlideToggleChange(this, isChecked); + } + + /** Method being called whenever the underlying input emits a change event. */ + _onChangeEvent(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 component's `change` output. + event.stopPropagation(); + this.toggleChange.emit(); + + // When the slide toggle's config disables toggle change event by setting + // `disableToggleValue: true`, the slide toggle's value does not change, and the + // checked state of the underlying input needs to be changed back. + if (this.defaults.disableToggleValue) { + this._inputElement.nativeElement.checked = this.checked; + return; + } + + // Sync the value from the underlying input element with the component instance. + this.checked = this._inputElement.nativeElement.checked; + + // Emit our custom change event only if the underlying input emitted one. This ensures that + // there is no change event, when the checked state changes programmatically. + this._emitChangeEvent(); + } + + /** Method being called whenever the slide-toggle has been clicked. */ + _onInputClick(event: Event) { + // We have to stop propagation for click events on the visual hidden input element. + // By default, when a user clicks on a label element, a generated click event will be + // dispatched on the associated input element. Since we are using a label element as our + // root container, the click event on the `slide-toggle` will be executed twice. + // The real click event will bubble up, and the generated click event also tries to bubble up. + // This will lead to multiple click events. + // Preventing bubbling for the second event will solve that issue. + event.stopPropagation(); + } + + /** Focuses the slide-toggle. */ + focus(options?: FocusOptions, origin?: FocusOrigin): void { + if (origin) { + this._focusMonitor.focusVia(this._inputElement, origin, options); + } else { + this._inputElement.nativeElement.focus(options); + } + } + + /** Method being called whenever the label text changes. */ + _onLabelTextChange() { + // Since the event of the `cdkObserveContent` directive runs outside of the zone, the + // slide-toggle component will be only marked for check, but no actual change detection runs + // automatically. Instead of going back into the zone in order to trigger a change detection + // which causes *all* components to be checked (if explicitly marked or not using OnPush), + // we only trigger an explicit change detection for the slide-toggle view and its children. + this._changeDetectorRef.detectChanges(); + } +} diff --git a/src/material/legacy-slide-toggle/testing/BUILD.bazel b/src/material/legacy-slide-toggle/testing/BUILD.bazel new file mode 100644 index 000000000000..210fb69acb19 --- /dev/null +++ b/src/material/legacy-slide-toggle/testing/BUILD.bazel @@ -0,0 +1,52 @@ +load("//tools:defaults.bzl", "ng_test_library", "ng_web_test_suite", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "testing", + srcs = glob( + ["**/*.ts"], + exclude = ["**/*.spec.ts"], + ), + deps = [ + "//src/cdk/coercion", + "//src/cdk/testing", + "//src/material/slide-toggle/testing", + ], +) + +filegroup( + name = "source-files", + srcs = glob(["**/*.ts"]), +) + +ng_test_library( + name = "harness_tests_lib", + srcs = ["shared.spec.ts"], + deps = [ + ":testing", + "//src/cdk/testing", + "//src/cdk/testing/testbed", + "//src/material/legacy-slide-toggle", + "@npm//@angular/forms", + "@npm//@angular/platform-browser", + ], +) + +ng_test_library( + name = "unit_tests_lib", + srcs = glob( + ["**/*.spec.ts"], + exclude = ["shared.spec.ts"], + ), + deps = [ + ":harness_tests_lib", + ":testing", + "//src/material/legacy-slide-toggle", + ], +) + +ng_web_test_suite( + name = "unit_tests", + deps = [":unit_tests_lib"], +) diff --git a/src/material-experimental/mdc-slide-toggle/testing/index.ts b/src/material/legacy-slide-toggle/testing/index.ts similarity index 100% rename from src/material-experimental/mdc-slide-toggle/testing/index.ts rename to src/material/legacy-slide-toggle/testing/index.ts diff --git a/src/material-experimental/mdc-slide-toggle/testing/public-api.ts b/src/material/legacy-slide-toggle/testing/public-api.ts similarity index 56% rename from src/material-experimental/mdc-slide-toggle/testing/public-api.ts rename to src/material/legacy-slide-toggle/testing/public-api.ts index c02821d601b0..35169062863f 100644 --- a/src/material-experimental/mdc-slide-toggle/testing/public-api.ts +++ b/src/material/legacy-slide-toggle/testing/public-api.ts @@ -7,4 +7,7 @@ */ export * from './slide-toggle-harness'; -export {SlideToggleHarnessFilters} from '@angular/material/slide-toggle/testing'; +export { + _MatSlideToggleHarnessBase as _MatLegacySlideToggleHarnessBase, + SlideToggleHarnessFilters as LegacySlideToggleHarnessFilters, +} from '@angular/material/slide-toggle/testing'; diff --git a/src/material/slide-toggle/testing/shared.spec.ts b/src/material/legacy-slide-toggle/testing/shared.spec.ts similarity index 95% rename from src/material/slide-toggle/testing/shared.spec.ts rename to src/material/legacy-slide-toggle/testing/shared.spec.ts index b4e05946eba5..5d006385b786 100644 --- a/src/material/slide-toggle/testing/shared.spec.ts +++ b/src/material/legacy-slide-toggle/testing/shared.spec.ts @@ -3,13 +3,13 @@ import {TestbedHarnessEnvironment} from '@angular/cdk/testing/testbed'; import {Component} from '@angular/core'; import {ComponentFixture, TestBed} from '@angular/core/testing'; import {FormControl, ReactiveFormsModule} from '@angular/forms'; -import {MatSlideToggleModule} from '@angular/material/slide-toggle'; -import {MatSlideToggleHarness} from '@angular/material/slide-toggle/testing/slide-toggle-harness'; +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; +import {MatLegacySlideToggleHarness} from '@angular/material/legacy-slide-toggle/testing/slide-toggle-harness'; /** Shared tests to run on both the original and MDC-based slide-toggles. */ export function runHarnessTests( - slideToggleModule: typeof MatSlideToggleModule, - slideToggleHarness: typeof MatSlideToggleHarness, + slideToggleModule: typeof MatLegacySlideToggleModule, + slideToggleHarness: typeof MatLegacySlideToggleHarness, ) { let fixture: ComponentFixture; let loader: HarnessLoader; diff --git a/src/material/legacy-slide-toggle/testing/slide-toggle-harness.spec.ts b/src/material/legacy-slide-toggle/testing/slide-toggle-harness.spec.ts new file mode 100644 index 000000000000..f9213494ba31 --- /dev/null +++ b/src/material/legacy-slide-toggle/testing/slide-toggle-harness.spec.ts @@ -0,0 +1,7 @@ +import {MatLegacySlideToggleModule} from '@angular/material/legacy-slide-toggle'; +import {runHarnessTests} from '@angular/material/legacy-slide-toggle/testing/shared.spec'; +import {MatLegacySlideToggleHarness} from './slide-toggle-harness'; + +describe('Non-MDC-based MatSlideToggleHarness', () => { + runHarnessTests(MatLegacySlideToggleModule, MatLegacySlideToggleHarness); +}); diff --git a/src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.ts b/src/material/legacy-slide-toggle/testing/slide-toggle-harness.ts similarity index 60% rename from src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.ts rename to src/material/legacy-slide-toggle/testing/slide-toggle-harness.ts index 2a94a56add5b..f62ee184f038 100644 --- a/src/material-experimental/mdc-slide-toggle/testing/slide-toggle-harness.ts +++ b/src/material/legacy-slide-toggle/testing/slide-toggle-harness.ts @@ -6,31 +6,32 @@ * found in the LICENSE file at https://angular.io/license */ -import {ComponentHarnessConstructor, HarnessPredicate} from '@angular/cdk/testing'; +import {HarnessPredicate} from '@angular/cdk/testing'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; import { _MatSlideToggleHarnessBase, SlideToggleHarnessFilters, } from '@angular/material/slide-toggle/testing'; -/** Harness for interacting with a MDC-based mat-slide-toggle in tests. */ -export class MatSlideToggleHarness extends _MatSlideToggleHarnessBase { - protected _nativeElement = this.locatorFor('button'); - static hostSelector = '.mat-mdc-slide-toggle'; +/** Harness for interacting with a standard mat-slide-toggle in tests. */ +export class MatLegacySlideToggleHarness extends _MatSlideToggleHarnessBase { + private _inputContainer = this.locatorFor('.mat-slide-toggle-bar'); + protected _nativeElement = this.locatorFor('input'); + + /** The selector for the host element of a `MatSlideToggle` instance. */ + static hostSelector = '.mat-slide-toggle'; /** - * Gets a `HarnessPredicate` that can be used to search for a slide-toggle w/ specific attributes. - * @param options Options for narrowing the search: - * - `selector` finds a slide-toggle whose host element matches the given selector. - * - `label` finds a slide-toggle with specific label text. + * Gets a `HarnessPredicate` that can be used to search for a `MatSlideToggleHarness` that meets + * certain criteria. + * @param options Options for filtering which slide toggle instances are considered a match. * @return a `HarnessPredicate` configured with the given options. */ - static with( - this: ComponentHarnessConstructor, + static with( options: SlideToggleHarnessFilters = {}, - ): HarnessPredicate { + ): HarnessPredicate { return ( - new HarnessPredicate(this, options) + new HarnessPredicate(MatLegacySlideToggleHarness, options) .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabelText(), label), ) @@ -55,17 +56,14 @@ export class MatSlideToggleHarness extends _MatSlideToggleHarnessBase { ); } + /** Toggle the checked state of the slide-toggle. */ async toggle(): Promise { - return (await this._nativeElement()).click(); - } - - override async isRequired(): Promise { - const ariaRequired = await (await this._nativeElement()).getAttribute('aria-required'); - return ariaRequired === 'true'; + return (await this._inputContainer()).click(); } + /** Whether the slide-toggle is checked. */ async isChecked(): Promise { - const checked = (await this._nativeElement()).getAttribute('aria-checked'); + const checked = (await this._nativeElement()).getProperty('checked'); return coerceBooleanProperty(await checked); } } diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.spec.ts b/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.spec.ts index c7b6741c3b78..8d1e6099d8af 100644 --- a/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.spec.ts +++ b/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.spec.ts @@ -23,13 +23,13 @@ describe('slide-toggle styles', () => { ` @use '@angular/material' as mat; $theme: (); - @include mat.slide-toggle-theme($theme); + @include mat.legacy-slide-toggle-theme($theme); `, ` @use '@angular/material' as mat; $theme: (); - @include mat.mdc-slide-toggle-theme($theme); - @include mat.mdc-slide-toggle-typography($theme); + @include mat.slide-toggle-theme($theme); + @include mat.slide-toggle-typography($theme); `, ); }); @@ -39,13 +39,13 @@ describe('slide-toggle styles', () => { ` @use '@angular/material' as arbitrary; $theme: (); - @include arbitrary.slide-toggle-theme($theme); + @include arbitrary.legacy-slide-toggle-theme($theme); `, ` @use '@angular/material' as arbitrary; $theme: (); - @include arbitrary.mdc-slide-toggle-theme($theme); - @include arbitrary.mdc-slide-toggle-typography($theme); + @include arbitrary.slide-toggle-theme($theme); + @include arbitrary.slide-toggle-typography($theme); `, ); }); @@ -56,17 +56,17 @@ describe('slide-toggle styles', () => { @use '@angular/material' as mat; $light-theme: (); $dark-theme: (); - @include mat.slide-toggle-theme($light-theme); - @include mat.slide-toggle-theme($dark-theme); + @include mat.legacy-slide-toggle-theme($light-theme); + @include mat.legacy-slide-toggle-theme($dark-theme); `, ` @use '@angular/material' as mat; $light-theme: (); $dark-theme: (); - @include mat.mdc-slide-toggle-theme($light-theme); - @include mat.mdc-slide-toggle-typography($light-theme); - @include mat.mdc-slide-toggle-theme($dark-theme); - @include mat.mdc-slide-toggle-typography($dark-theme); + @include mat.slide-toggle-theme($light-theme); + @include mat.slide-toggle-typography($light-theme); + @include mat.slide-toggle-theme($dark-theme); + @include mat.slide-toggle-typography($dark-theme); `, ); }); @@ -78,7 +78,7 @@ describe('slide-toggle styles', () => { $theme: (); - @include mat.slide-toggle-theme($theme); + @include mat.legacy-slide-toggle-theme($theme); `, @@ -87,8 +87,8 @@ describe('slide-toggle styles', () => { $theme: (); - @include mat.mdc-slide-toggle-theme($theme); - @include mat.mdc-slide-toggle-typography($theme); + @include mat.slide-toggle-theme($theme); + @include mat.slide-toggle-typography($theme); `, diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.ts b/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.ts index b5eb3bc27db5..59a2ed9ecaeb 100644 --- a/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.ts +++ b/src/material/schematics/ng-generate/mdc-migration/rules/components/slide-toggle/slide-toggle-styles.ts @@ -15,8 +15,8 @@ export class SlideToggleStylesMigrator extends StyleMigrator { mixinChanges = [ { - old: 'slide-toggle-theme', - new: ['mdc-slide-toggle-theme', 'mdc-slide-toggle-typography'], + old: 'legacy-slide-toggle-theme', + new: ['slide-toggle-theme', 'slide-toggle-typography'], }, ]; diff --git a/src/material/schematics/ng-generate/mdc-migration/rules/ts-migration/import-replacements.ts b/src/material/schematics/ng-generate/mdc-migration/rules/ts-migration/import-replacements.ts index aafdbc3cbfab..a9b1cac399db 100644 --- a/src/material/schematics/ng-generate/mdc-migration/rules/ts-migration/import-replacements.ts +++ b/src/material/schematics/ng-generate/mdc-migration/rules/ts-migration/import-replacements.ts @@ -72,8 +72,8 @@ export const IMPORT_REPLACEMENTS: {[component: string]: {old: string; new: strin new: '@angular/material-experimental/mdc-sidenav', }, 'slide-toggle': { - old: '@angular/material/slide-toggle', - new: '@angular/material-experimental/mdc-slide-toggle', + old: '@angular/material/legacy-slide-toggle', + new: '@angular/material/slide-toggle', }, 'slider': { old: '@angular/material/slider', diff --git a/src/material/slide-toggle/BUILD.bazel b/src/material/slide-toggle/BUILD.bazel index 9c987e51c8cf..89f7793ed75f 100644 --- a/src/material/slide-toggle/BUILD.bazel +++ b/src/material/slide-toggle/BUILD.bazel @@ -16,38 +16,43 @@ ng_module( name = "slide-toggle", srcs = glob( ["**/*.ts"], - exclude = ["**/*.spec.ts"], + exclude = [ + "**/*.spec.ts", + "harness/**", + ], ), - assets = [":slide-toggle.css"] + glob(["**/*.html"]), + assets = [":slide_toggle_scss"] + glob(["**/*.html"]), deps = [ - "//src/cdk/a11y", "//src/cdk/coercion", - "//src/cdk/observers", "//src/material/core", "@npm//@angular/animations", + "@npm//@angular/common", "@npm//@angular/core", "@npm//@angular/forms", - "@npm//@angular/platform-browser", + "@npm//@material/switch", ], ) sass_library( name = "slide_toggle_scss_lib", srcs = glob(["**/_*.scss"]), - deps = ["//src/material/core:core_scss_lib"], + deps = [ + "//:mdc_sass_lib", + "//src/material/core:core_scss_lib", + ], ) sass_binary( name = "slide_toggle_scss", src = "slide-toggle.scss", deps = [ - "//src/cdk:sass_lib", + "//:mdc_sass_lib", "//src/material/core:core_scss_lib", ], ) ng_test_library( - name = "unit_test_sources", + name = "slide_toggle_tests_lib", srcs = glob( ["**/*.spec.ts"], exclude = ["**/*.e2e.spec.ts"], @@ -55,9 +60,8 @@ ng_test_library( deps = [ ":slide-toggle", "//src/cdk/a11y", - "//src/cdk/observers", + "//src/cdk/bidi", "//src/cdk/testing/private", - "//src/material/testing", "@npm//@angular/forms", "@npm//@angular/platform-browser", ], @@ -65,7 +69,9 @@ ng_test_library( ng_web_test_suite( name = "unit_tests", - deps = [":unit_test_sources"], + deps = [ + ":slide_toggle_tests_lib", + ], ) ng_e2e_test_library( diff --git a/src/material/slide-toggle/README.md b/src/material/slide-toggle/README.md index a987b3625d68..29191ac0e63a 100644 --- a/src/material/slide-toggle/README.md +++ b/src/material/slide-toggle/README.md @@ -1 +1,86 @@ -Please see the official documentation at https://material.angular.io/components/component/slide-toggle \ No newline at end of file +This is prototype of an alternate version of `` built on top of +[MDC Web](https://github.com/material-components/material-components-web). It demonstrates how +Angular Material could use MDC Web under the hood while still exposing the same API Angular users as +the existing ``. This component is experimental and should not be used in production. + +## How to use +Assuming your application is already up and running using Angular Material, you can add this +component by following these steps: + +1. Install Angular Material Experimental & MDC WEB: + + ```bash + npm i material-components-web @angular/material + ``` + +2. In your `angular.json`, make sure `node_modules/` is listed as a Sass include path. This is + needed for the Sass compiler to be able to find the MDC Web Sass files. + + ```json + ... + "styles": [ + "src/styles.scss" + ], + "stylePreprocessorOptions": { + "includePaths": [ + "node_modules/" + ] + }, + ... + ``` + +3. Import the experimental `MatSlideToggleModule` and add it to the module that declares your + component: + + ```ts + import {MatSlideToggleModule} from '@angular/material/slide-toggle'; + + @NgModule({ + declarations: [MyComponent], + imports: [MatSlideToggleModule], + }) + export class MyModule {} + ``` + +4. Add use `` in your component's template, just like you would the normal + ``: + + ```html + Toggle me + ``` + +5. Add the theme and typography mixins to your Sass. (There is currently no pre-built CSS option for + the experimental ``): + + ```scss + @use '@angular/material' as mat; + + $my-primary: mat.define-palette(mat.$indigo-palette); + $my-accent: mat.define-palette(mat.$pink-palette, A200, A100, A400); + $my-theme: mat.define-light-theme(( + color: ( + primary: $my-primary, + accent: $my-accent + ) + )); + + @include mat.slide-toggle-theme($my-theme); + @include mat.slide-toggle-typography($my-theme); + ``` + +## Replacing the standard slide toggle in an existing app +Because the experimental API mirrors the API for the standard slide toggle, it can easily be swapped +in by just changing the import paths. There is currently no schematic for this, but you can run the +following string replace across your TypeScript files: + +```bash +grep -lr --include="*.ts" --exclude-dir="node_modules" \ + --exclude="*.d.ts" "['\"]@angular/material/legacy-slide-toggle['\"]" | xargs sed -i \ + "s/['\"]@angular\/material\/legacy-slide-toggle['\"]/'@angular\/material\/slide-toggle'/g" +``` + +CSS styles and tests that depend on implementation details of mat-slide-toggle (such as getting +elements from the template by class name) will need to be manually updated. + +There are some small visual differences between this slide and the standard mat-slide. This +slide has a slightly larger ripple and different spacing between the label and the toggle. diff --git a/src/material/slide-toggle/_slide-toggle-legacy-index.scss b/src/material/slide-toggle/_slide-toggle-legacy-index.scss deleted file mode 100644 index 4ed8c0b37161..000000000000 --- a/src/material/slide-toggle/_slide-toggle-legacy-index.scss +++ /dev/null @@ -1,3 +0,0 @@ -@forward 'slide-toggle-theme' hide color, theme, typography; -@forward 'slide-toggle-theme' as mat-slide-toggle-* hide mat-slide-toggle-checked, -mat-slide-toggle-density; diff --git a/src/material/slide-toggle/_slide-toggle-theme.import.scss b/src/material/slide-toggle/_slide-toggle-theme.import.scss index bfda867ea93d..e5266b0680cf 100644 --- a/src/material/slide-toggle/_slide-toggle-theme.import.scss +++ b/src/material/slide-toggle/_slide-toggle-theme.import.scss @@ -1,11 +1,7 @@ -@forward '../core/style/private.import'; -@forward '../core/theming/theming.import'; -@forward '../core/typography/typography-utils.import'; -@forward 'slide-toggle-theme' hide color, theme, typography; -@forward 'slide-toggle-theme' as mat-slide-toggle-* hide mat-slide-toggle-checked, -mat-slide-toggle-density; - -@import '../core/style/private'; -@import '../core/theming/palette'; -@import '../core/theming/theming'; -@import '../core/typography/typography-utils'; +@forward 'slide-toggle-theme' hide color, density, theme, typography; +@forward 'slide-toggle-theme' as mat-mdc-slide-toggle-* hide +$mat-mdc-slide-toggle-mdc-switch-baseline-theme-color, +$mat-mdc-slide-toggle-mdc-switch-disabled-thumb-color, +$mat-mdc-slide-toggle-mdc-switch-disabled-track-color, +$mat-mdc-slide-toggle-mdc-switch-toggled-off-thumb-color, +$mat-mdc-slide-toggle-mdc-switch-toggled-off-track-color; diff --git a/src/material/slide-toggle/_slide-toggle-theme.scss b/src/material/slide-toggle/_slide-toggle-theme.scss index e8ade454af1b..eab59ddcaeb2 100644 --- a/src/material/slide-toggle/_slide-toggle-theme.scss +++ b/src/material/slide-toggle/_slide-toggle-theme.scss @@ -1,91 +1,127 @@ @use 'sass:map'; -@use '../core/style/private'; -@use '../core/theming/palette'; +@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 '../core/theming/theming'; +@use '../core/mdc-helpers/mdc-helpers'; @use '../core/typography/typography'; -@use '../core/typography/typography-utils'; -@mixin _checked-color($palette, $thumb-checked-hue) { - &.mat-checked { - .mat-slide-toggle-thumb { - background-color: theming.get-color-from-palette($palette, $thumb-checked-hue); - } - .mat-slide-toggle-bar { - // Opacity is determined from the specs for the selection controls. - // See: https://material.io/design/components/selection-controls.html#specs - background-color: theming.get-color-from-palette($palette, $thumb-checked-hue, 0.54); - } +// 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); + $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, + ); +} - .mat-ripple-element { - // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically - // based on the type of interaction with the slide-toggle (e.g. for hover, focus) - background-color: theming.get-color-from-palette($palette, $thumb-checked-hue); - } - } +// 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)); $is-dark: map.get($config, is-dark); - $primary: map.get($config, primary); - $accent: map.get($config, accent); - $warn: map.get($config, warn); - $background: map.get($config, background); $foreground: map.get($config, foreground); - // Color hues are based on the specs which briefly show the hues that are applied to a switch. - // The 2018 specs no longer describe how dark switches should look like. Due to the lack of - // information for dark themed switches, we partially keep the old behavior that is based on - // the previous specifications. For the checked color we always use the `default` hue because - // that follows MDC and also makes it easier for people to create a custom theme without needing - // to specify each hue individually. - $thumb-unchecked-hue: if($is-dark, 400, 50); - $thumb-checked-hue: default; - - $bar-unchecked-color: theming.get-color-from-palette($foreground, disabled); - $ripple-unchecked-color: theming.get-color-from-palette($foreground, base); - - .mat-slide-toggle { - @include _checked-color($accent, $thumb-checked-hue); - - &.mat-primary { - @include _checked-color($primary, $thumb-checked-hue); - } - - &.mat-warn { - @include _checked-color($warn, $thumb-checked-hue); - } - - &:not(.mat-checked) .mat-ripple-element { - // Set no opacity for the ripples because the ripple opacity will be adjusted dynamically - // based on the type of interaction with the slide-toggle (e.g. for hover, focus) - background-color: $ripple-unchecked-color; + @include mdc-helpers.using-mdc-theme($config) { + // 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.$mdc-theme-styles-query); + @include mdc-switch-theme.theme(_get-theme-base-map($is-dark)); + + // MDC should set the disabled color on the label, but doesn't, so we do it here instead. + .mdc-switch--disabled + label { + color: theming.get-color-from-palette($foreground, disabled-text); + } + + &.mat-primary { + @include mdc-switch-theme.theme(_get-theme-color-map($primary)); + } + + &.mat-accent { + @include mdc-switch-theme.theme(_get-theme-color-map($accent)); + } + + &.mat-warn { + @include mdc-switch-theme.theme(_get-theme-color-map($warn)); + } } } - - .mat-slide-toggle-thumb { - @include private.private-theme-elevation(1, $config); - background-color: theming.get-color-from-palette(palette.$grey-palette, $thumb-unchecked-hue); - } - - .mat-slide-toggle-bar { - background-color: $bar-unchecked-color; - } } @mixin typography($config-or-theme) { - $config: typography.private-typography-to-2014-config( + $config: typography.private-typography-to-2018-config( theming.get-typography-config($config-or-theme)); - .mat-slide-toggle-content { - font-family: typography-utils.font-family($config); + @include mdc-helpers.using-mdc-typography($config) { + @include mdc-form-field.core-styles($query: mdc-helpers.$mdc-typography-styles-query); } } -@mixin _density($config-or-theme) {} +@mixin density($config-or-theme) { + $density-scale: theming.get-density-config($config-or-theme); + .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-slide-toggle') { $color: theming.get-color-config($theme); $density: theming.get-density-config($theme); @@ -95,7 +131,7 @@ @include color($color); } @if $density != null { - @include _density($density); + @include density($density); } @if $typography != null { @include typography($typography); diff --git a/src/material/slide-toggle/slide-toggle-module.ts b/src/material/slide-toggle/module.ts similarity index 82% rename from src/material/slide-toggle/slide-toggle-module.ts rename to src/material/slide-toggle/module.ts index 4b9163822a58..5bf9b4e24acb 100644 --- a/src/material/slide-toggle/slide-toggle-module.ts +++ b/src/material/slide-toggle/module.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {ObserversModule} from '@angular/cdk/observers'; +import {CommonModule} from '@angular/common'; import {NgModule} from '@angular/core'; import {MatCommonModule, MatRippleModule} from '@angular/material/core'; import {MatSlideToggle} from './slide-toggle'; @@ -20,12 +20,7 @@ import {MatSlideToggleRequiredValidator} from './slide-toggle-required-validator export class _MatSlideToggleRequiredValidatorModule {} @NgModule({ - imports: [ - _MatSlideToggleRequiredValidatorModule, - MatRippleModule, - MatCommonModule, - ObserversModule, - ], + imports: [_MatSlideToggleRequiredValidatorModule, MatCommonModule, MatRippleModule, CommonModule], exports: [_MatSlideToggleRequiredValidatorModule, MatSlideToggle, MatCommonModule], declarations: [MatSlideToggle], }) diff --git a/src/material/slide-toggle/public-api.ts b/src/material/slide-toggle/public-api.ts index 6f5078ec5988..e315bd9b0810 100644 --- a/src/material/slide-toggle/public-api.ts +++ b/src/material/slide-toggle/public-api.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -export * from './slide-toggle-module'; export * from './slide-toggle'; export * from './slide-toggle-config'; +export * from './module'; export * from './slide-toggle-required-validator'; diff --git a/src/material/slide-toggle/slide-toggle.e2e.spec.ts b/src/material/slide-toggle/slide-toggle.e2e.spec.ts index 615590383467..9e2fb8d72e28 100644 --- a/src/material/slide-toggle/slide-toggle.e2e.spec.ts +++ b/src/material/slide-toggle/slide-toggle.e2e.spec.ts @@ -1,49 +1,67 @@ import {browser, element, by, Key} from 'protractor'; import {expectToExist} from '../../cdk/testing/private/e2e'; -describe('slide-toggle', () => { - const getInput = () => element(by.css('#normal-slide-toggle input')); +describe('MDC-based slide-toggle', () => { + const getButton = () => element(by.css('#normal-slide-toggle button')); const getNormalToggle = () => element(by.css('#normal-slide-toggle')); - beforeEach(async () => await browser.get('slide-toggle')); + beforeEach(async () => await browser.get('mdc-slide-toggle')); it('should render a slide-toggle', async () => { await expectToExist('mat-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 .mat-slide-toggle-thumb-container')); + const thumbEl = element(by.css('#normal-slide-toggle .mdc-switch__handle')); const previousPosition = await thumbEl.getLocation(); await slideToggleEl.click(); @@ -54,12 +72,18 @@ describe('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', + ); }); }); diff --git a/src/material/slide-toggle/slide-toggle.html b/src/material/slide-toggle/slide-toggle.html index c7038930c12a..ec29dc3c9c4b 100644 --- a/src/material/slide-toggle/slide-toggle.html +++ b/src/material/slide-toggle/slide-toggle.html @@ -1,40 +1,53 @@ -