From 597c2a17c36e44f27c10c055b043f8211e7df8cd Mon Sep 17 00:00:00 2001 From: Jean-Yves Moyen Date: Tue, 4 May 2021 15:56:43 +0200 Subject: [PATCH] Fix background size (#788) * Add test cases for `background-size` * Accept percentages and two values for background-size * Use Tuple to store dimensions in background-size --- .../src/property/background-size.ts | 50 +++--- .../alfa-style/src/property/background.ts | 3 +- .../test/property/background-size.spec.tsx | 160 ++++++++++++++++++ packages/alfa-style/tsconfig.json | 1 + 4 files changed, 194 insertions(+), 20 deletions(-) create mode 100644 packages/alfa-style/test/property/background-size.spec.tsx diff --git a/packages/alfa-style/src/property/background-size.ts b/packages/alfa-style/src/property/background-size.ts index c98306873c..516cd4cd9f 100644 --- a/packages/alfa-style/src/property/background-size.ts +++ b/packages/alfa-style/src/property/background-size.ts @@ -1,13 +1,15 @@ import { Keyword, Length, Percentage, Token } from "@siteimprove/alfa-css"; import { Iterable } from "@siteimprove/alfa-iterable"; import { Parser } from "@siteimprove/alfa-parser"; +import { Slice } from "@siteimprove/alfa-slice"; import { Property } from "../property"; import { Resolver } from "../resolver"; import { List } from "./value/list"; +import { Tuple } from "./value/tuple"; -const { map, either, delimited, option, pair, separatedList } = Parser; +const { map, either, delimited, option, pair, right, separatedList } = Parser; declare module "../property" { interface Longhands { @@ -24,11 +26,10 @@ export type Specified = List; * @internal */ export namespace Specified { + export type Dimension = Length | Percentage | Keyword<"auto">; + export type Item = - | [ - Length | Percentage | Keyword<"auto">, - Length | Percentage | Keyword<"auto"> - ] + | Tuple<[Dimension, Dimension]> | Keyword<"cover"> | Keyword<"contain">; } @@ -42,24 +43,35 @@ export type Computed = List; * @internal */ export namespace Computed { + export type Dimension = Length<"px"> | Percentage | Keyword<"auto">; + export type Item = - | [ - Length<"px"> | Percentage | Keyword<"auto">, - Length<"px"> | Percentage | Keyword<"auto"> - ] + | Tuple<[Dimension, Dimension]> | Keyword<"cover"> | Keyword<"contain">; } +/** + * @internal + */ +const parseDimension = either, Specified.Dimension, string>( + Length.parse, + Percentage.parse, + Keyword.parse("auto") +); + /** * @internal */ export const parse = either( - pair( - either(Length.parse, Keyword.parse("auto")), - map(option(either(Length.parse, Keyword.parse("auto"))), (y) => - y.getOrElse(() => Keyword.of("auto")) - ) + map( + pair( + parseDimension, + map(option(right(Token.parseWhitespace, parseDimension)), (y) => + y.getOrElse(() => Keyword.of("auto")) + ) + ), + ([x, y]) => Tuple.of(x, y) ), Keyword.parse("contain", "cover") ); @@ -82,7 +94,7 @@ export const parseList = map( export default Property.register( "background-size", Property.of( - List.of([[Keyword.of("auto"), Keyword.of("auto")]], ", "), + List.of([Tuple.of(Keyword.of("auto"), Keyword.of("auto"))], ", "), parseList, (value, style) => value.map((sizes) => @@ -92,12 +104,12 @@ export default Property.register( return size; } - const [x, y] = size; + const [x, y] = size.values; - return [ + return Tuple.of( x.type === "length" ? Resolver.length(x, style) : x, - y.type === "length" ? Resolver.length(y, style) : y, - ]; + y.type === "length" ? Resolver.length(y, style) : y + ); }), ", " ) diff --git a/packages/alfa-style/src/property/background.ts b/packages/alfa-style/src/property/background.ts index ec88e9f114..d8ac64e387 100644 --- a/packages/alfa-style/src/property/background.ts +++ b/packages/alfa-style/src/property/background.ts @@ -6,6 +6,7 @@ import { Slice } from "@siteimprove/alfa-slice"; import { Property } from "../property"; import { List } from "./value/list"; +import { Tuple } from "./value/tuple"; import * as Attachment from "./background-attachment"; import * as Clip from "./background-clip"; @@ -253,7 +254,7 @@ export default Property.registerShorthand( image.push(layer[1] ?? Keyword.of("none")); positionX.push(layer[2] ?? Percentage.of(0)); positionY.push(layer[3] ?? Percentage.of(0)); - size.push(layer[4] ?? [Keyword.of("auto"), Keyword.of("auto")]); + size.push(layer[4] ?? Tuple.of(Keyword.of("auto"), Keyword.of("auto"))); repeatX.push(layer[5] ?? Keyword.of("repeat")); repeatY.push(layer[6] ?? Keyword.of("repeat")); attachment.push(layer[7] ?? Keyword.of("scroll")); diff --git a/packages/alfa-style/test/property/background-size.spec.tsx b/packages/alfa-style/test/property/background-size.spec.tsx new file mode 100644 index 0000000000..5920772501 --- /dev/null +++ b/packages/alfa-style/test/property/background-size.spec.tsx @@ -0,0 +1,160 @@ +import { Device } from "@siteimprove/alfa-device"; +import { h } from "@siteimprove/alfa-dom"; +import { test } from "@siteimprove/alfa-test"; +import { Style } from "../../src"; + +const device = Device.standard(); + +test("#cascaded() parses `background-size: cover`", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("background-size").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "keyword", + value: "cover", + }, + ], + separator: ", ", + }, + source: h.declaration("background-size", "cover").toJSON(), + }); +}); + +test("#cascaded() parses `background-size: 10px`", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("background-size").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "tuple", + values: [ + { + type: "length", + value: 10, + unit: "px", + }, + { + type: "keyword", + value: "auto", + }, + ], + }, + ], + separator: ", ", + }, + source: h.declaration("background-size", "10px").toJSON(), + }); +}); + +test("#cascaded() parses `background-size: 10%`", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("background-size").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "tuple", + values: [ + { + type: "percentage", + value: 0.1, + }, + { + type: "keyword", + value: "auto", + }, + ], + }, + ], + separator: ", ", + }, + source: h.declaration("background-size", "10%").toJSON(), + }); +}); + +test("#cascaded() parses `background-size: 10px 20px`", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("background-size").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "tuple", + values: [ + { + type: "length", + value: 10, + unit: "px", + }, + { + type: "length", + value: 20, + unit: "px", + }, + ], + }, + ], + separator: ", ", + }, + source: h.declaration("background-size", "10px 20px").toJSON(), + }); +}); + +test("#cascaded() parses `background-size: 10px, 20px`", (t) => { + const element =
; + + const style = Style.from(element, device); + + t.deepEqual(style.cascaded("background-size").get().toJSON(), { + value: { + type: "list", + values: [ + { + type: "tuple", + values: [ + { + type: "length", + value: 10, + unit: "px", + }, + { + type: "keyword", + value: "auto", + }, + ], + }, + { + type: "tuple", + values: [ + { + type: "length", + value: 20, + unit: "px", + }, + { + type: "keyword", + value: "auto", + }, + ], + }, + ], + separator: ", ", + }, + source: h.declaration("background-size", "10px, 20px").toJSON(), + }); +}); diff --git a/packages/alfa-style/tsconfig.json b/packages/alfa-style/tsconfig.json index dcc9cccbda..dda9f4dc68 100644 --- a/packages/alfa-style/tsconfig.json +++ b/packages/alfa-style/tsconfig.json @@ -138,6 +138,7 @@ "test/property/background-color.spec.tsx", "test/property/background-image.spec.tsx", "test/property/background-position.spec.tsx", + "test/property/background-size.spec.tsx", "test/property/border.spec.tsx", "test/property/border-[block,inline].spec.tsx", "test/property/border-[block,inline]-[end,start].spec.tsx",