Skip to content

Commit

Permalink
fix(material-experimental/theming): Add more tests for M3 theme tokens (
Browse files Browse the repository at this point in the history
#28561)

Additional tests that help ensure our M3 theme conforms to the following
rules:
- Does not add any additional selector specificity
- Emits only CSS variable definitions
- Emits each token for one and only one theme dimension

Also fixes several existing bugs that were discovered by the new tests.

(cherry picked from commit acf72e1)
  • Loading branch information
mmalerba committed Feb 9, 2024
1 parent 0edc476 commit 4742a17
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 39 deletions.
48 changes: 24 additions & 24 deletions src/material-experimental/theming/_custom-tokens.scss
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,15 @@
small-size-container-size: _hardcode(6px, $exclude-hardcoded),
container-padding: _hardcode(0 4px, $exclude-hardcoded),
small-size-container-padding: _hardcode(0, $exclude-hardcoded),
container-offset: -12px 0,
small-size-container-offset: -6px 0,
container-overlap-offset: -12px,
small-size-container-overlap-offset: -6px,
container-offset: _hardcode(-12px 0, $exclude-hardcoded),
small-size-container-offset: _hardcode(-6px 0, $exclude-hardcoded),
container-overlap-offset: _hardcode(-12px, $exclude-hardcoded),
small-size-container-overlap-offset: _hardcode(-6px, $exclude-hardcoded),

// This size isn't in the M3 spec so we emit the same values as for `medium`.
large-size-container-size: _hardcode(16px, $exclude-hardcoded),
large-size-container-offset: -12px 0,
large-size-container-overlap-offset: -12px,
large-size-container-offset: _hardcode(-12px 0, $exclude-hardcoded),
large-size-container-overlap-offset: _hardcode(-12px, $exclude-hardcoded),
large-size-text-size: map.get($systems, md-sys-typescale, label-small-size),
large-size-container-padding: _hardcode(0 4px, $exclude-hardcoded),
legacy-large-size-container-size: _hardcode(unset, $exclude-hardcoded),
Expand Down Expand Up @@ -423,7 +423,7 @@
@return (
container-max-width: _hardcode(560px, $exclude-hardcoded),
container-small-max-width: _hardcode(calc(100vw - 32px), $exclude-hardcoded),
container-min-width: _harcode(280px, $exclude-hardcoded),
container-min-width: _hardcode(280px, $exclude-hardcoded),
actions-alignment: _hardcode(flex-end, $exclude-hardcoded),
content-padding: _hardcode(20px 24px, $exclude-hardcoded),
with-actions-content-padding: _hardcode(20px 24px 0, $exclude-hardcoded),
Expand Down Expand Up @@ -1169,28 +1169,28 @@
/// @param {Boolean} $exclude-hardcoded Whether to exclude hardcoded token values
/// @return {Map} A set of custom tokens for the mat-slide-toggle
@function switch($systems, $exclude-hardcoded) {
@return ((
unselected-handle-size: 16px,
selected-handle-size: 24px,
with-icon-handle-size: 24px,
pressed-handle-size: 28px,
selected-handle-horizontal-margin: 0 24px,
selected-with-icon-handle-horizontal-margin: 0 24px,
selected-pressed-handle-horizontal-margin: 0 22px,
unselected-handle-horizontal-margin: 0 8px,
unselected-with-icon-handle-horizontal-margin: 0 4px,
unselected-pressed-handle-horizontal-margin: 0 2px,
@return (
unselected-handle-size: _hardcode(16px, $exclude-hardcoded),
selected-handle-size: _hardcode(24px, $exclude-hardcoded),
with-icon-handle-size: _hardcode(24px, $exclude-hardcoded),
pressed-handle-size: _hardcode(28px, $exclude-hardcoded),
selected-handle-horizontal-margin: _hardcode(0 24px, $exclude-hardcoded),
selected-with-icon-handle-horizontal-margin: _hardcode(0 24px, $exclude-hardcoded),
selected-pressed-handle-horizontal-margin: _hardcode(0 22px, $exclude-hardcoded),
unselected-handle-horizontal-margin: _hardcode(0 8px, $exclude-hardcoded),
unselected-with-icon-handle-horizontal-margin: _hardcode(0 4px, $exclude-hardcoded),
unselected-pressed-handle-horizontal-margin: _hardcode(0 2px, $exclude-hardcoded),
// The hidden and visible track represent whichever track is visible or
// hidden in the ui. This could be the .mdc-switch__track :before or
// :after depending on whether the switch is selected or unselected.
//
// The m2 slide-toggle uses transforms to hide & show the tracks while
// the m3 slide-toggle uses opacity.
visible-track-opacity: 1,
hidden-track-opacity: 0,
visible-track-transition: opacity 75ms,
hidden-track-transition: opacity 75ms,
), ());
visible-track-opacity: _hardcode(1, $exclude-hardcoded),
hidden-track-opacity: _hardcode(0, $exclude-hardcoded),
visible-track-transition: _hardcode(opacity 75ms, $exclude-hardcoded),
hidden-track-transition: _hardcode(opacity 75ms, $exclude-hardcoded),
);
}

/// Generates custom tokens for the mat-snack-bar.
Expand Down Expand Up @@ -1291,7 +1291,7 @@
_generate-typography-tokens($systems, label-text, title-small),
(
divider-color: map.get($systems, md-sys-color, surface-variant),
divider-height: 1px,
divider-height: _hardcode(1px, $exclude-hardcoded),
disabled-ripple-color: null, // TODO(mmalerba): Figure out correct value.
pagination-icon-color: map.get($systems, md-sys-color, on-surface),
inactive-label-text-color: map.get($systems, md-sys-color, on-surface),
Expand Down
2 changes: 2 additions & 0 deletions src/material-experimental/theming/_m3-density.scss
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ $_density-tokens: (
(mat, minimal-pseudo-checkbox): (),
(mat, paginator): (
container-size: (56px, 52px, 48px, 40px),
form-field-container-height: (40px, 40px, 40px, 40px, 40px, 36px),
form-field-container-vertical-padding: (8px, 8px, 8px, 8px, 8px, 6px),
),
(mat, radio): (
touch-target-display: (block, block, none, none),
Expand Down
14 changes: 11 additions & 3 deletions src/material-experimental/theming/_m3-tokens.scss
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@
headline-weight: subhead-weight,
));

@if (map.has-key($tokens, container-elevation)) {
@if map.get($tokens, container-elevation) != null {
$tokens: map.merge($tokens, (
// The spec has the dialog at an elevation of 3 which is consistent with the current
// version of the tokens (0_161), however both the designs and MDC's implementation
Expand All @@ -472,7 +472,10 @@
// However, this interferes with the use case of placing a list on other components. For example,
// the bottom sheet's container color is `md.sys.color.surface-container-low`. Instead, allow the
// list to just display the colors for its background.
@return map.set($tokens, list-item-container-color, transparent);
@if map.get($tokens, list-item-container-color) != null {
$tokens: map.set($tokens, list-item-container-color, transparent);
}
@return $tokens;
}

/// Generates a set of namespaced tokens for all components.
Expand Down Expand Up @@ -976,7 +979,13 @@
mdc-tokens.md-sys-color-values-light($ref));
@return _generate-tokens(map.merge($ref, (
md-sys-color: $sys-color,
// Because the elevation values are always combined with color values to create the box shadow,
// elevation needs to be part of the color dimension.
md-sys-elevation: mdc-tokens.md-sys-elevation-values(),
// Because the state values are sometimes combined with color values to create rgba colors,
// state needs to be part of color dimension.
// TODO(mmalerba): If at some point we remove the need for these combined values, we can move
// state to the base dimension.
md-sys-state: mdc-tokens.md-sys-state-values(),
)));
}
Expand Down Expand Up @@ -1012,6 +1021,5 @@
@return _generate-tokens((
md-sys-motion: mdc-tokens.md-sys-motion-values(),
md-sys-shape: mdc-tokens.md-sys-shape-values(),
md-sys-state: mdc-tokens.md-sys-state-values(),
), $include-non-systemized: true);
}
2 changes: 1 addition & 1 deletion src/material/button/_button-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
/// @param {Map} $theme The theme to generate base styles for.
@mixin base($theme) {
@if inspection.get-theme-version($theme) == 1 {
@include theme-from-tokens(inspection.get-theme-tokens($theme, base));
@include _theme-from-tokens(inspection.get-theme-tokens($theme, base));
}
@else {
@include sass-utils.current-selector-or-root() {
Expand Down
1 change: 1 addition & 0 deletions src/material/core/theming/_inspection.scss
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ $_typography-properties: (font, font-family, line-height, font-size, letter-spac
}
@else if $system == density {
$theme: map.deep-remove($theme, $_internals, density-scale);
$theme: map.deep-remove($theme, $_internals, density-tokens);
}
}
@return $theme;
Expand Down
57 changes: 56 additions & 1 deletion src/material/core/theming/tests/m3-theme.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,27 @@ function transpile(content: string) {
).css.toString();
}

function union<T>(...sets: Set<T>[]): Set<T> {
return new Set(sets.flatMap(s => [...s]));
}

function intersection<T>(set: Set<T>, ...sets: Set<T>[]): Set<T> {
return new Set([...set].filter(i => sets.every(s => s.has(i))));
}

describe('M3 theme', () => {
it('should emit all styles under the given selector', () => {
const root = parse(transpile(`html { @include mat.all-component-themes($theme); }`));
const root = parse(
transpile(`
html {
@include mat.all-component-themes($theme);
@include mat.all-component-bases($theme);
@include mat.all-component-colors($theme);
@include mat.all-component-typographies($theme);
@include mat.all-component-densities($theme);
}
`),
);
const selectors: string[] = [];
root.walkRules(rule => {
selectors.push(rule.selector);
Expand All @@ -64,4 +82,41 @@ describe('M3 theme', () => {
});
expect(nonVarProps).toEqual([]);
});

it('should not have overlapping tokens between theme dimensions', () => {
const css = transpile(`
$theme: matx.define-theme();
base {
@include mat.all-component-bases($theme);
}
color {
@include mat.all-component-colors($theme);
}
typography {
@include mat.all-component-typographies($theme);
}
density {
@include mat.all-component-densities($theme);
}
`);
const root = parse(css);
const propSets: {[key: string]: Set<string>} = {};
root.walkRules(rule => {
rule.walkDecls(decl => {
propSets[rule.selector] = propSets[rule.selector] || new Set();
propSets[rule.selector].add(decl.prop);
});
});
let overlap = new Set();
for (const [dimension1, props1] of Object.entries(propSets)) {
for (const [dimension2, props2] of Object.entries(propSets)) {
if (dimension1 !== dimension2) {
overlap = union(overlap, intersection(props1, props2));
}
}
}
expect([...overlap])
.withContext('Did you forget to wrap these in `_hardcode()`?')
.toEqual([]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -466,5 +466,14 @@ describe('theming inspection api', () => {
`),
).toThrowError(/Density information is not available on this theme/);
});

it('should not emit styles for removed theme dimensions', () => {
const css = transpile(`
$theme: mat.theme-remove(matx.define-theme(), base, color, typography, density);
div {
@include mat.all-component-themes($theme);
}`);
expect(css).toBe('');
});
});
});
24 changes: 22 additions & 2 deletions src/material/core/tokens/m2/mat/_paginator.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
@use 'sass:math';
@use 'sass:map';
@use '@material/textfield' as mdc-textfield;
@use '@material/density' as mdc-density;
@use '../../token-utils';
@use '../../../theming/theming';
@use '../../../theming/inspection';
Expand Down Expand Up @@ -39,16 +42,33 @@ $prefix: (mat, paginator);

// Tokens that can be configured through Angular Material's density theming API.
@function get-density-tokens($theme) {
$density-scale: theming.clamp-density(inspection.get-theme-density($theme), -3);
$density-scale: theming.clamp-density(inspection.get-theme-density($theme), -5);
$size-scale: (
0: 56px,
-1: 52px,
-2: 48px,
-3: 40px,
-4: 40px,
-5: 40px,
);
$form-field-density-scale: if($density-scale > -4, -4, $density-scale);
$form-field-height: mdc-density.prop-value(
$density-config: mdc-textfield.$density-config,
$density-scale: $form-field-density-scale,
$property-name: height,
);
// We computed the desired height of the form-field using the density configuration. The
// spec only describes vertical spacing/alignment in non-dense mode. This means that we
// cannot update the spacing to explicit numbers based on the density scale. Instead, we
// determine the height reduction and equally subtract it from the default `top` and `bottom`
// padding that is provided by the Material Design specification.
$form-field-vertical-deduction: math.div(mdc-textfield.$height - $form-field-height, 2);
$form-field-vertical-padding: 16px - $form-field-vertical-deduction;

@return (
container-size: map.get($size-scale, $density-scale)
container-size: map.get($size-scale, $density-scale),
form-field-container-height: $form-field-height,
form-field-container-vertical-padding: $form-field-vertical-padding,
);
}

Expand Down
7 changes: 6 additions & 1 deletion src/material/core/typography/_all-typography.scss
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@
// mixin that is transitively loaded by the `all-theme` file, imports `all-typography` which
// would then load `all-theme` again. This ultimately results a circular dependency.
@include badge-theme.typography($theme);
@include typography.typography-hierarchy($theme);
// Historically the typography hierarchy styles were included as part of this. We maintain this
// behavior for M2, but from M3 forward this is not included and should be explicitly @included
// by the user if desired.
@if (inspection.get-theme-version($theme) < 1) {
@include typography.typography-hierarchy($theme);
}
@include bottom-sheet-theme.typography($theme);
@include button-toggle-theme.typography($theme);
@include divider-theme.typography($theme);
Expand Down
7 changes: 0 additions & 7 deletions src/material/paginator/_paginator-theme.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
@use 'sass:map';
@use 'sass:meta';
@use '../core/tokens/m2/mat/paginator' as tokens-mat-paginator;
@use '../form-field/form-field-theme';
@use '../core/style/sass-utils';
@use '../core/typography/typography';
@use '../core/theming/theming';
Expand Down Expand Up @@ -53,12 +52,6 @@
tokens-mat-paginator.get-density-tokens($theme));
}
}

// We need the form field to be narrower in order to fit into the paginator,
// so we set its density to be -4 or denser.
.mat-mdc-paginator {
@include form-field-theme.density((density: $form-field-density));
}
}

@mixin theme($theme) {
Expand Down
6 changes: 6 additions & 0 deletions src/material/paginator/paginator.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ $button-icon-size: 28px;
@include token-utils.create-token-slot(font-weight, container-text-weight);
@include token-utils.create-token-slot(letter-spacing, container-text-tracking);

// Apply custom form-field density for paginator.
@include token-utils.create-token-slot(
--mat-form-field-container-height, form-field-container-height);
@include token-utils.create-token-slot(
--mat-form-field-container-vertical-padding, form-field-container-vertical-padding);

.mat-mdc-select-value {
@include token-utils.create-token-slot(font-size, select-trigger-text-size);
}
Expand Down

0 comments on commit 4742a17

Please sign in to comment.