Skip to content

Commit 8dcc94d

Browse files
authored
feat(material-experimental/mdc-slider): implement the mdc slider (#22596)
* feat(material-experimental/mdc-slider): delete old code * clearing the outdated code from mdc-slider * this will also make future commit messages easier to read * build: add mdc-slider to the mdc exports configs skipped packages * feat(material-experimental/mdc-slider): add skeleton code for MatSliderAdapter (#21645) * create slider-adapter.ts * add method stubs for MDCSliderAdapter implementation * add MDCSliderFoundation class variable to MatSlider * feat(material-experimental/mdc-slider): add skeleton code for MatSliderThumb (#21655) * created slider-thumb.ts * created MatSliderThumb directive for the mdc-slider input * feat(material-experimental/mdc-slider): implement MatSlider (#21680) * feat(material-experimental/mdc-slider): implement the SliderAdapter (#21844) * feat(material-experimental/mdc-slider): implement the SliderAdapter * complete the core logic for MatSliderThumb and MatSlider * collapse slider-thumb.ts and slider-adapter.ts into slider.ts * fix(material-experimental/mdc-slider): init step on thumb inputs (#21971) * fix(material-experimental/mdc-slider): init step on thumb inputs * feat(material-experimental/mdc-slider): add slider styles (#21934) * feat(material-experimental/mdc-slider): add slider styles * implement _MatSliderMixinBase * add color input to MatSlider * extend _MatSliderMixinBase from MatSlider * use without-ripple mixin for slider.scss * @include all other mdc-slider mixins except thumb-ripple-color in _slider-theme.scss * implement primary, accent, and warn colors in _slider-theme.scss * feat(material-experimental/mdc-slider): implement slider thumb ripples (#21979) * create MatSliderVisualThumb * create slider-thumb.html & slider-thumb.scss * feat(material-experimental/mdc-slider): implement control value accessor (#22016) * feat(material-experimental/mdc-slider): implement control value accessor * feat(material-experimental/mdc-slider): implement some basic unit tests (#22072) * feat(material-experimental/mdc-slider): implement some basic unit tests * implement unit tests for the standard slider, standard range slider, and for the slider ripple states * add mdc-slider theme to all-theme * use #waitForAsync to wait for foundation to finish initializing & layout * use forwardRef to avoid injection errors that only throw on ci * disable the mat ripple on the slider thumbs to prevent the automatic launch that happens on click/touch the problem is easily reproduced if you undo this change and test it out on a mobile device. * note: we use touch events instead of pointer events when testing on ios because pointerdown, pointerup, and pointermove are not supported * feat(material-experimental/mdc-slider): add unit tests for disabled slider (#22168) * test(material-experimental/mdc-slider): add unit tests for sliders with set min and max * add support for changing the min, max, or step after the component has already been initialized * fix(material-experimental/mdc-slider): fix VE bug * in view engine, MatSliders inputs are not initialized before MatSliderThumbs constructor is called. This means we cannot initialize the slider value attribute in the constructor. To fix this, we are initializing the value attribute in ngOnInit which is still before ngAfterViewInit but after MatSliders inputs are initialized * test(material-experimental/mdc-slider): add unit tests for sliders with values (#22193) * fix(material-experimental/mdc-slider): dedup mdc-slider styles (#22195) * remove mdc-slider theme from theme.scss since it is already included in all-theme now * avoid using deprecated mdc-theme prop-value function in _slider-theme.scss * feat(material-experimental/mdc-slider): add support for rtl/ltr toggle (#22196) * feat(material-experimental/mdc-slider): add support for rtl/ltr toggle * feat(material-experimental/mdc-slider): add support for disabling rip… (#22199) * feat(material-experimental/mdc-slider): add support for disabling ripples * fix(material-experimental/mdc-slider): add slider to mdc_scss_deps_lib (#22216) * fix(material-experimental/mdc-slider): add slider to mdc_scss_deps_lib * test(material-experimental/mdc-slider): add tests for sliders with se… (#22214) * test(material-experimental/mdc-slider): add tests for sliders with set steps * test(material-experimental/mdc-slider): add tests for slider with set… (#22238) * test(material-experimental/mdc-slider): add tests for slider with set displayWith * fix(material-experimental/mdc-slider): trigger change detection when the value indicator text changes * test(material-experimental/mdc-slider): add tests for slider with one-way value binding (#22242) * fix(material-experimental/mdc-slider): fix change events on slider in… (#22286) * fix(material-experimental/mdc-slider): fix change events on slider inputs * create GlobalChangeAndInputListener to handle listening for change events that occur on the document * stop all of the slider inputs change events from reaching users * dispatch our own fake change events from #emitChangeEvent in the slider adapter * use the GlobalChangeAndInputListener for change events instead of adding our own event listener in #registerInputEventHandler * keep track of and unsubscribe from the GlobalChangeAndInputListener in #deregisterInputEventHandler * fix(material-experimental/mdc-slider): fix change and input events on the mdc slider * use #fromEvent to simplify the global change and input listener * go back to dispatching real events instead of using Angular's event emitter system * fix how the global change and input listener is used in the slider adapter * Rename fake event indicator boolean * Simplified change & input event logic to make things more readable * feat(material-experimental/mdc-slider): rebuild the mdc-slider demo (#22445) * add exportAs: matSliderThumb to MatSliderThumb * create focus and blur methods in MatSliderThumb to match the old demo * test(material-experimental/mdc-slider): create e2e tests (#22463) * create basic tests for standard, disabled, and range sliders * test(material-experimental/mdc-slider): add two-way binding unit tests (#22470) * test(material-experimental/mdc-slider): add ngModel unit tests (#22474) * test(material-experimental/mdc-slider): add change handler tests (#22478) * test(material-experimental/mdc-slider): add input handler tests (#22481) * test(material-experimental/mdc-slider): add custom form control tests (#22546) * test(material-experimental/mdc-slider): add custom form control tests * fix bug where setting the disabled state on the overall slider was not disabling the individual slider thumbs control value accessor disabled state. * fix(material-experimental/mdc-slider): avoid using whenStable (#22571) * whenStable was causing tests to pass even when they should have been failing * test(material-experimental/mdc-slider): add directionality tests (#22572) * fix(material-experimental/mdc-slider): keep slider ui in sync with foundation (#22579) * The MDC Foundation stores the bounding client rect when layout is first called. This means that if the position of the slider changes after the initial layout, the slider will break. To fix this broken behavior, we have to keep calling layout. * Added a unit test to ensure layout changes does not break the slider. * fix(material-experimental/mdc-slider): fix unit tests after rebasing * define the width for the unit test component styles * fix decimal step tests that were failing because of the firefox decimal imprecision bug
1 parent 65358a9 commit 8dcc94d

25 files changed

+2851
-1505
lines changed

scripts/check-mdc-exports-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export const config = {
22
// The MDC sidenav hasn't been implemented yet.
3-
skippedPackages: ['mdc-sidenav'],
3+
skippedPackages: ['mdc-sidenav', 'mdc-slider'],
44
skippedExports: {
55
'mdc-chips': [
66
// These components haven't been implemented for MDC due to a different accessibility pattern.

src/cdk/testing/testbed/fake-events/dispatch-events.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export function dispatchPointerEvent(node: Node, type: string, clientX = 0, clie
6565
* Shorthand to dispatch a touch event on the specified coordinates.
6666
* @docs-private
6767
*/
68-
export function dispatchTouchEvent(node: Node, type: string, x = 0, y = 0) {
69-
return dispatchEvent(node, createTouchEvent(type, x, y));
68+
export function dispatchTouchEvent(node: Node, type: string, pageX = 0, pageY = 0, clientX = 0,
69+
clientY = 0) {
70+
return dispatchEvent(node, createTouchEvent(type, pageX, pageY, clientX, clientY));
7071
}

src/cdk/testing/testbed/fake-events/event-objects.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ export function createPointerEvent(type: string, clientX = 0, clientY = 0,
7979
* Creates a browser TouchEvent with the specified pointer coordinates.
8080
* @docs-private
8181
*/
82-
export function createTouchEvent(type: string, pageX = 0, pageY = 0) {
82+
export function createTouchEvent(type: string, pageX = 0, pageY = 0, clientX = 0, clientY = 0) {
8383
// In favor of creating events that work for most of the browsers, the event is created
8484
// as a basic UI Event. The necessary details for the event will be set manually.
8585
const event = document.createEvent('UIEvent');
86-
const touchDetails = {pageX, pageY};
86+
const touchDetails = {pageX, pageY, clientX, clientY};
8787

8888
// TS3.6 removes the initUIEvent method and suggests porting to "new UIEvent()".
8989
(event as any).initUIEvent(type, true, true, window, 0);
Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,88 @@
11
<h1>Default Slider</h1>
2-
Label <mat-slider #slidey aria-label="Basic slider"></mat-slider>
2+
<span>Label</span>
3+
<mat-slider>
4+
<input #slidey matSliderThumb>
5+
</mat-slider>
36
{{slidey.value}}
47

58
<h1>Colors</h1>
6-
<mat-slider color="primary" value="50" thumbLabel aria-label="Primary color slider"></mat-slider>
7-
<mat-slider color="accent" value="50" thumbLabel aria-label="Accent color slider"></mat-slider>
8-
<mat-slider color="warn" value="50" thumbLabel aria-label="Warn color slider"></mat-slider>
9+
<mat-slider color="primary" discrete>
10+
<input value="50" matSliderThumb>
11+
</mat-slider>
12+
<mat-slider color="accent" discrete>
13+
<input value="50" matSliderThumb>
14+
</mat-slider>
15+
<mat-slider color="warn">
16+
<input value="50" matSliderThumb>
17+
</mat-slider>
918

1019
<h1>Slider with Min and Max</h1>
1120
<input [(ngModel)]="min" type="number">
12-
<mat-slider [min]="min" [max]="max" tickInterval="5" #slider2 aria-label="Min & max slider">
21+
<mat-slider [min]="min" [max]="max" step="5" discrete showTickMarks>
22+
<input #slider2 matSliderThumb>
1323
</mat-slider>
1424
{{slider2.value}}
1525
<input [(ngModel)]="max" type="number">
1626

1727
<h1>Disabled Slider</h1>
18-
<mat-slider disabled [(ngModel)]="disabledValue" [tickInterval]="1" aria-label="Disabled slider">
28+
<mat-slider disabled [step]="1">
29+
<input [(ngModel)]="disabledValue" matSliderThumb>
1930
</mat-slider>
2031
<input [(ngModel)]="disabledValue" type="number">
2132

2233
<h1>Slider with set value</h1>
23-
<mat-slider value="43" aria-label="Initial value slider"></mat-slider>
34+
<mat-slider>
35+
<input value="43" matSliderThumb>
36+
</mat-slider>
2437

2538
<h1>Slider with step defined</h1>
26-
<mat-slider min="1" max="100" step="20" #slider5 aria-label="Slider with step"></mat-slider>
39+
<mat-slider min="1" max="100" step="20">
40+
<input #slider5 matSliderThumb>
41+
</mat-slider>
2742
{{slider5.value}}
2843

2944
<h1>Slider with set tick interval</h1>
30-
<mat-slider tickInterval="auto" aria-label="Slider with auto ticks"></mat-slider>
31-
<mat-slider tickInterval="9" aria-label="Slider with ticks"></mat-slider>
45+
<mat-slider discrete showTickMarks>
46+
<input matSliderThumb>
47+
</mat-slider>
48+
<mat-slider step="9" discrete showTickMarks>
49+
<input matSliderThumb>
50+
</mat-slider>
3251

3352
<h1>Slider with Thumb Label</h1>
34-
<mat-slider thumbLabel aria-label="Slider with thumb label"></mat-slider>
53+
<mat-slider discrete>
54+
<input matSliderThumb>
55+
</mat-slider>
3556

3657
<h1>Slider with one-way binding</h1>
37-
<mat-slider [value]="val" step="40" aria-label="Slider with value binding"></mat-slider>
58+
<mat-slider step="10">
59+
<input [value]="val" matSliderThumb>
60+
</mat-slider>
3861
<input [(ngModel)]="val" type="number">
3962

4063
<h1>Slider with two-way binding</h1>
41-
<mat-slider [(ngModel)]="demo" step="40" aria-label="Slider with ngModel"></mat-slider>
64+
<mat-slider step="40">
65+
<input [(ngModel)]="demo" matSliderThumb>
66+
</mat-slider>
4267
<input [(ngModel)]="demo" type="number">
4368

4469
<h1>Set/lost focus to show thumblabel programmatically</h1>
45-
<mat-slider #demoSlider="matSlider" thumbLabel aria-label="Slider with thumb label"></mat-slider>
70+
<mat-slider discrete>
71+
<input #demoSlider="matSliderThumb" matSliderThumb>
72+
</mat-slider>
4673
<button (click)="demoSlider.focus()">Focus Slider</button>
4774
<button (click)="demoSlider.blur()">Blur Slider</button>
4875

4976
<mat-tab-group>
5077
<mat-tab label="One">
51-
<mat-slider min="1" max="5" value="3" aria-label="Slider in a tab"></mat-slider>
78+
<mat-slider min="1" max="5">
79+
<input value="3" matSliderThumb>
80+
</mat-slider>
5281
</mat-tab>
5382
</mat-tab-group>
83+
84+
<h1>Range slider</h1>
85+
<mat-slider min="200" max="500" step="100" discrete showTickMarks>
86+
<input value="300" matSliderStartThumb>
87+
<input value="400" matSliderEndThumb>
88+
</mat-slider>

src/dev-app/mdc-slider/mdc-slider-demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {Component} from '@angular/core';
1212
@Component({
1313
selector: 'mdc-slider-demo',
1414
templateUrl: 'mdc-slider-demo.html',
15+
styles: ['.mat-mdc-slider { width: 300px; }'],
1516
})
1617
export class MdcSliderDemo {
1718
demo: number;

src/dev-app/theme.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@ $candy-app-theme: mat.define-light-theme((
3232
@include experimental.all-mdc-component-themes($candy-app-theme);
3333
@include experimental.column-resize-theme($candy-app-theme);
3434
@include experimental.popover-edit-theme($candy-app-theme);
35-
// We add this in manually for now, because it isn't included in `all-mdc-component-themes`.
36-
@include mdc-slider-theme.theme($candy-app-theme);
3735

3836
.demo-strong-focus {
3937
// Include base styles for strong focus indicators.

src/e2e-app/mdc-slider/mdc-slider-e2e.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,20 @@ import {Component} from '@angular/core';
1010

1111
@Component({
1212
selector: 'mdc-slider-e2e',
13-
template: `<mat-slider></mat-slider>`,
13+
template: `
14+
<mat-slider id="standard-slider">
15+
<input aria-label="Standard slider" matSliderThumb>
16+
</mat-slider>
17+
18+
<mat-slider id="disabled-slider" disabled>
19+
<input aria-label="Disabled slider" matSliderThumb>
20+
</mat-slider>
21+
22+
<mat-slider id="range-slider">
23+
<input aria-label="Range slider start thumb" matSliderStartThumb>
24+
<input aria-label="Range slider end thumb" matSliderEndThumb>
25+
</mat-slider>
26+
`,
1427
})
1528
export class MdcSliderE2e {
1629
}

src/material-experimental/mdc-helpers/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ npm_sass_library(
2323
"@npm//@material/list",
2424
"@npm//@material/menu-surface",
2525
"@npm//@material/radio",
26+
"@npm//@material/slider",
2627
"@npm//@material/snackbar",
2728
"@npm//@material/switch",
2829
"@npm//@material/tab",

src/material-experimental/mdc-slider/BUILD.bazel

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ ng_module(
1717
["**/*.ts"],
1818
exclude = ["**/*.spec.ts"],
1919
),
20-
assets = [":slider_scss"] + glob(["**/*.html"]),
20+
assets = [
21+
":slider_scss",
22+
":slider_thumb_scss",
23+
] + glob(["**/*.html"]),
2124
module_name = "@angular/material-experimental/mdc-slider",
2225
deps = [
2326
"//src/cdk/bidi",
@@ -35,6 +38,7 @@ sass_library(
3538
deps = [
3639
"//src/cdk/a11y:a11y_scss_lib",
3740
"//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
41+
"//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
3842
],
3943
)
4044

@@ -50,6 +54,11 @@ sass_binary(
5054
],
5155
)
5256

57+
sass_binary(
58+
name = "slider_thumb_scss",
59+
src = "slider-thumb.scss",
60+
)
61+
5362
###########
5463
# Testing
5564
###########
@@ -66,8 +75,11 @@ ng_test_library(
6675
"//src/cdk/keycodes",
6776
"//src/cdk/platform",
6877
"//src/cdk/testing/private",
78+
"//src/material/core",
6979
"@npm//@angular/forms",
7080
"@npm//@angular/platform-browser",
81+
"@npm//@material/slider",
82+
"@npm//rxjs",
7183
],
7284
)
7385

@@ -84,7 +96,9 @@ ng_e2e_test_library(
8496
name = "e2e_test_sources",
8597
srcs = glob(["**/*.e2e.spec.ts"]),
8698
deps = [
99+
":mdc-slider",
87100
"//src/cdk/testing/private/e2e",
101+
"@npm//@material/slider",
88102
],
89103
)
90104

src/material-experimental/mdc-slider/_slider-theme.scss

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
1-
// TODO: disabled until we implement the new MDC slider.
2-
// @use '@material/slider' as mdc-slider;
1+
@use 'sass:map';
2+
3+
@use '@material/slider/slider' as mdc-slider;
4+
@use '@material/slider/slider-theme';
5+
@use '@material/theme/variables' as theme-variables;
36
@use '../mdc-helpers/mdc-helpers';
47
@use '../../material/core/typography/typography';
8+
@use '../../material/core/ripple/ripple-theme';
59
@use '../../material/core/theming/theming';
610

711
@mixin color($config-or-theme) {
812
$config: theming.get-color-config($config-or-theme);
913
@include mdc-helpers.mat-using-mdc-theme($config) {
10-
// TODO: disabled until we implement the new MDC slider.
11-
// @include mdc-slider-core-styles($query: $mat-theme-styles-query);
14+
@include mdc-slider.without-ripple($query: mdc-helpers.$mat-theme-styles-query);
1215

1316
.mat-mdc-slider {
17+
&.mat-primary, &.mat-accent, &.mat-warn {
18+
$is-dark: map-get($config, is-dark);
19+
$indicator-color: if($is-dark, white, black);
20+
$indicator-text-color: if($is-dark, black, white);
21+
$indicator-opacity: if($is-dark, 0.9, 0.6);
22+
23+
@include slider-theme.value-indicator-color(
24+
$color: $indicator-color,
25+
$opacity: $indicator-opacity,
26+
$query: mdc-helpers.$mat-theme-styles-query
27+
);
28+
@include slider-theme.value-indicator-text-color(
29+
$color: $indicator-text-color,
30+
$query: mdc-helpers.$mat-theme-styles-query
31+
);
32+
}
33+
1434
&.mat-primary {
15-
// TODO: disabled until we implement the new MDC slider.
16-
// @include mdc-slider-color-accessible(primary, $mat-theme-styles-query);
35+
@include _custom-slider-color(primary, on-primary);
36+
}
37+
38+
&.mat-accent {
39+
@include _custom-slider-color(secondary, on-secondary);
1740
}
1841

1942
&.mat-warn {
20-
// TODO: disabled until we implement the new MDC slider.
21-
// @include mdc-slider-color-accessible(error, $mat-theme-styles-query);
43+
@include _custom-slider-color(error, on-error);
2244
}
2345
}
2446
}
@@ -28,8 +50,7 @@
2850
$config: typography.private-typography-to-2018-config(
2951
theming.get-typography-config($config-or-theme));
3052
@include mdc-helpers.mat-using-mdc-typography($config) {
31-
// TODO: disabled until we implement the new MDC slider.
32-
// @include mdc-slider-core-styles($query: $mat-typography-styles-query);
53+
@include mdc-slider.without-ripple($query: mdc-helpers.$mat-typography-styles-query);
3354
}
3455
}
3556

@@ -54,3 +75,52 @@
5475
}
5576
}
5677

78+
@mixin _custom-slider-color($color, $on-color) {
79+
@include slider-theme.thumb-color(
80+
$color-or-map: (
81+
default: $color,
82+
disabled: on-surface,
83+
),
84+
$query: mdc-helpers.$mat-theme-styles-query
85+
);
86+
@include slider-theme.track-active-color(
87+
$color-or-map: (
88+
default: $color,
89+
disabled: on-surface,
90+
),
91+
$query: mdc-helpers.$mat-theme-styles-query
92+
);
93+
@include slider-theme.track-inactive-color(
94+
$color-or-map: (
95+
default: $color,
96+
disabled: on-surface,
97+
),
98+
$query: mdc-helpers.$mat-theme-styles-query
99+
);
100+
@include slider-theme.tick-mark-active-color(
101+
$color-or-map: (
102+
default: $on-color,
103+
disabled: surface,
104+
),
105+
$query: mdc-helpers.$mat-theme-styles-query
106+
);
107+
@include slider-theme.tick-mark-inactive-color(
108+
$color-or-map: (
109+
default: $color,
110+
disabled: on-surface,
111+
),
112+
$query: mdc-helpers.$mat-theme-styles-query
113+
);
114+
$ripple-color: map.get(theme-variables.$property-values, $color);
115+
@include ripple-theme.color((
116+
foreground: (
117+
base: $ripple-color
118+
),
119+
));
120+
.mat-mdc-slider-hover-ripple {
121+
background-color: rgba($ripple-color, 0.05);
122+
}
123+
.mat-mdc-slider-focus-ripple, .mat-mdc-slider-active-ripple {
124+
background-color: rgba($ripple-color, 0.2);
125+
}
126+
}

0 commit comments

Comments
 (0)