From f3d6f90a02cbf387b5c3028a76e5c76d0a2a3f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rolf=20Christian=20J=C3=B8rgensen?= <114920418+rcj-siteimprove@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:57:06 +0100 Subject: [PATCH] Implement mask-position --- .../alfa-style/src/property/mask-position.ts | 13 +- .../test/property/mask-position.spec.tsx | 195 ++++++++++++++++++ 2 files changed, 206 insertions(+), 2 deletions(-) diff --git a/packages/alfa-style/src/property/mask-position.ts b/packages/alfa-style/src/property/mask-position.ts index eececb931c..7e49961ae8 100644 --- a/packages/alfa-style/src/property/mask-position.ts +++ b/packages/alfa-style/src/property/mask-position.ts @@ -7,9 +7,10 @@ import { import { Longhand } from "../longhand.js"; import { matchLayers } from "./mask.js"; +import { Resolver } from "../resolver.js"; type Specified = List; -type Computed = Specified; +type Computed = List; /** * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/mask-position} @@ -27,5 +28,13 @@ export default Longhand.of( ", ", ), List.parseCommaSeparated(Position.parse(/* legacySyntax */ true)), - (value, style) => value.map((positions) => matchLayers(positions, style)), + (value, style) => + value.map((positions) => + matchLayers( + positions.map((position) => + position.partiallyResolve(Resolver.length(style)), + ), + style, + ), + ), ); diff --git a/packages/alfa-style/test/property/mask-position.spec.tsx b/packages/alfa-style/test/property/mask-position.spec.tsx index f1b18740fb..c24726391e 100644 --- a/packages/alfa-style/test/property/mask-position.spec.tsx +++ b/packages/alfa-style/test/property/mask-position.spec.tsx @@ -1,4 +1,5 @@ import { test } from "@siteimprove/alfa-test"; +import { h } from "@siteimprove/alfa-dom"; import { Device } from "@siteimprove/alfa-device"; @@ -46,3 +47,197 @@ test("initial value is 0% 0%", (t) => { source: null, }); }); + +// TODO: The spec requires the computed value to be two lengths or percentages, not a keyword value. +// E.g. the keyword `left` should be computes to `0% 50%` in Chrome and Firefox. +test("#computed parses single keywords", (t) => { + for (const kw of ["top", "bottom"] as const) { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.computed("mask-position").toJSON(), { + value: { + type: "list", + separator: ", ", + values: [ + { + type: "position", + horizontal: { + type: "keyword", + value: "center", + }, + vertical: { + type: "side", + offset: null, + side: { + type: "keyword", + value: kw, + }, + }, + }, + ], + }, + source: h.declaration("mask-position", kw).toJSON(), + }); + } + + for (const kw of ["left", "right"] as const) { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.computed("mask-position").toJSON(), { + value: { + type: "list", + separator: ", ", + values: [ + { + type: "position", + vertical: { + type: "keyword", + value: "center", + }, + horizontal: { + type: "side", + offset: null, + side: { + type: "keyword", + value: kw, + }, + }, + }, + ], + }, + source: h.declaration("mask-position", kw).toJSON(), + }); + } + + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.computed("mask-position").toJSON(), { + value: { + type: "list", + separator: ", ", + values: [ + { + type: "position", + vertical: { + type: "keyword", + value: "center", + }, + horizontal: { + type: "keyword", + value: "center", + }, + }, + ], + }, + source: h.declaration("mask-position", "center").toJSON(), + }); +}); + +test("#computed parses lengths and percentages", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.computed("mask-position").toJSON(), { + value: { + type: "list", + separator: ", ", + values: [ + { + type: "position", + horizontal: { + type: "side", + offset: { + type: "percentage", + value: 0.1, + }, + side: { + type: "keyword", + value: "left", + }, + }, + vertical: { + type: "side", + offset: { + type: "length", + unit: "px", + value: 48, + }, + side: { + type: "keyword", + value: "top", + }, + }, + }, + ], + }, + source: h.declaration("mask-position", "10% 3em").toJSON(), + }); +}); + +test("#computed parses multiple layers", (t) => { + const element = ( +
+ ); + + const style = Style.from(element, device); + + t.deepEqual(style.computed("mask-position").toJSON(), { + value: { + type: "list", + separator: ", ", + values: [ + { + type: "position", + horizontal: { + type: "side", + offset: { + type: "length", + unit: "px", + value: 16, + }, + side: { + type: "keyword", + value: "left", + }, + }, + vertical: { + type: "side", + offset: { + type: "length", + unit: "px", + value: 16, + }, + side: { + type: "keyword", + value: "top", + }, + }, + }, + { + type: "position", + horizontal: { + type: "keyword", + value: "center", + }, + vertical: { + type: "keyword", + value: "center", + }, + }, + ], + }, + source: h.declaration("mask-position", "1rem 1rem, center").toJSON(), + }); +});