Skip to content

Commit

Permalink
Merge branch 'main' into shadow-selectors
Browse files Browse the repository at this point in the history
  • Loading branch information
Jym77 authored Jan 15, 2024
2 parents 49dc9f1 + 698761c commit 8f00748
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 72 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-lizards-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@siteimprove/alfa-dom": patch
---

**Fixed:** Parents of `Comment` inside a shadow tree now correctly skip over the shadow root when traversing the flat tree.
4 changes: 0 additions & 4 deletions docs/review/api/alfa-dom.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,6 @@ export class Element<N extends string = string> extends Node<"element"> implemen
// (undocumented)
static of<N extends string = string>(namespace: Option<Namespace>, prefix: Option<string>, name: N, attributes?: Iterable_2<Attribute>, children?: Iterable_2<Node>, style?: Option<Block>, box?: Option<Rectangle>, device?: Option<Device>, externalId?: string, extraData?: any): Element<N>;
// (undocumented)
parent(options?: Node.Traversal): Option<Node>;
// (undocumented)
get prefix(): Option<string>;
// (undocumented)
get qualifiedName(): string;
Expand Down Expand Up @@ -1149,8 +1147,6 @@ export class Text extends Node<"text"> implements Slotable {
// (undocumented)
static of(data: string, externalId?: string, extraData?: any): Text;
// (undocumented)
parent(options?: Node.Traversal): Option<Node>;
// (undocumented)
toJSON(): Text.JSON;
// (undocumented)
toString(): string;
Expand Down
28 changes: 28 additions & 0 deletions packages/alfa-dom/src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
Fragment,
Shadow,
Slot,
Slotable,
Text,
Type,
} from ".";
Expand Down Expand Up @@ -135,6 +136,33 @@ export abstract class Node<T extends string = string>
});
}

public parent(options: Node.Traversal = Node.Traversal.empty): Option<Node> {
const parent = this._parent as Option<Node>;

// If we traverse the flat tree, we want to jump over shadow roots.
if (options.isSet(Node.Traversal.flattened)) {
return parent.flatMap((parent) => {
if (Shadow.isShadow(parent)) {
return parent.host;
}

// Additionally, if this is a slottable light child of a shadow host, we want
// to search for where it is slotted, and return that parent instead.
if (
Element.isElement(parent) &&
parent.shadow.isSome() &&
Slotable.isSlotable(this)
) {
return this.assignedSlot().flatMap((slot) => slot.parent(options));
}

return Option.of(parent);
});
}

return parent;
}

private _path: Array<string> = [];

/**
Expand Down
20 changes: 0 additions & 20 deletions packages/alfa-dom/src/node/element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,26 +161,6 @@ export class Element<N extends string = string>
return this._boxes.get(device);
}

public parent(options: Node.Traversal = Node.Traversal.empty): Option<Node> {
const parent = this._parent as Option<Node>;

if (options.isSet(Node.Traversal.flattened)) {
return parent.flatMap((parent) => {
if (Shadow.isShadow(parent)) {
return parent.host;
}

if (Element.isElement(parent) && parent.shadow.isSome()) {
return this.assignedSlot().flatMap((slot) => slot.parent(options));
}

return Option.of(parent);
});
}

return parent;
}

public children(
options: Node.Traversal = Node.Traversal.empty,
): Sequence<Node> {
Expand Down
2 changes: 2 additions & 0 deletions packages/alfa-dom/src/node/shadow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export class Shadow extends Node<"shadow"> {
}

public parent(options: Node.Traversal = Node.Traversal.empty): Option<Node> {
// We only "land" on Shadow roots when traversing the composed tree.
// Notably, flattening the tree "jumps" over them.
if (options.isSet(Node.Traversal.composed)) {
return this._host;
}
Expand Down
22 changes: 0 additions & 22 deletions packages/alfa-dom/src/node/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import { Option } from "@siteimprove/alfa-option";
import { Trampoline } from "@siteimprove/alfa-trampoline";

import { Node } from "../node";
import { Element } from "./element";
import { Shadow } from "./shadow";
import { Slot } from "./slot";
import { Slotable } from "./slotable";

Expand Down Expand Up @@ -31,26 +29,6 @@ export class Text extends Node<"text"> implements Slotable {
return this._data;
}

public parent(options: Node.Traversal = Node.Traversal.empty): Option<Node> {
const parent = this._parent as Option<Node>;

if (options.isSet(Node.Traversal.flattened)) {
return parent.flatMap((parent) => {
if (Shadow.isShadow(parent)) {
return parent.host;
}

if (Element.isElement(parent) && parent.shadow.isSome()) {
return this.assignedSlot().flatMap((slot) => slot.parent(options));
}

return Option.of(parent);
});
}

return parent;
}

public assignedSlot(): Option<Slot> {
return Slotable.findSlot(this);
}
Expand Down
5 changes: 2 additions & 3 deletions packages/alfa-dom/src/node/traversal/get-nodes-between.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Node } from "../..";
import { Predicate } from "@siteimprove/alfa-predicate";
import { Sequence } from "@siteimprove/alfa-sequence";
import { Node } from "../..";

import { lowestCommonAncestor } from "./lowest-common-ancestor";

Expand Down Expand Up @@ -43,7 +43,7 @@ export function getNodesBetween(
// of the closest ancestor having one.
between = first
// Closest ancestor with a next sibling.
.closest((ancestor) => ancestor.next(treeOptions).isSome())
.closest((ancestor) => ancestor.next(treeOptions).isSome(), treeOptions)
// Get that sibling.
.flatMap((node) => node.next(treeOptions))
// Skip everything until next.
Expand Down Expand Up @@ -71,7 +71,6 @@ function getNodesInclusivelyBetween(
): Sequence<Node> {
const isFrontier = or(equals(node1), equals(node2));

// Get descendants of the LCA, and skip everything before and after both nodes.
return lowestCommonAncestor(node1, node2, treeOptions)
.map((context) =>
context
Expand Down
66 changes: 66 additions & 0 deletions packages/alfa-dom/test/traversal.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { None } from "@siteimprove/alfa-option";
import { test } from "@siteimprove/alfa-test";

import { h, Comment, Node } from "../src";

const targets = [<span />, h.text("hello"), Comment.of("world")];
const shadow = h.shadow(targets);
const div = <div>{shadow}</div>;

// Start with no flag.
// For each of the three flags, duplicate existing list and add the flag to one side.
// This ends up giving the eight possible options.
const flags = [
Node.Traversal.composed,
Node.Traversal.flattened,
Node.Traversal.nested,
].reduce(
(old, cur) => old.flatMap((flag) => [flag, flag.add(cur)]),
[Node.Traversal.of(Node.Traversal.none)],
);

test(".parent() of a shadow host returns None in composed traversal, the shadow root otherwise", (t) => {
for (const traversal of flags) {
if (traversal.has(Node.Traversal.composed)) {
t.deepEqual(shadow.parent(traversal).getUnsafe(), div);
} else {
t.deepEqual(shadow.parent(traversal), None);
}
}
});

test(".parent() of the children of a shadow root returns the shadow host in flat traversal, the shadow root otherwise", (t) => {
for (const target of targets) {
for (const traversal of flags) {
if (traversal.has(Node.Traversal.flattened)) {
t.deepEqual(target.parent(traversal).getUnsafe(), div);
} else {
t.deepEqual(target.parent(traversal).getUnsafe(), shadow);
}
}
}
});

test(".parent() of a slottable child of a shadow host returns the slot's parent in flat traversal, the light parent otherwise", (t) => {
const target = <span slot="foo" />;
const shadowDiv = (
<div>
<slot name="foo"></slot>
</div>
);
const shadow = h.shadow([shadowDiv]);
const lightDiv = (
<div>
{shadow}
{target}
</div>
);

for (const traversal of flags) {
if (traversal.has(Node.Traversal.flattened)) {
t.deepEqual(target.parent(traversal).getUnsafe(), shadowDiv);
} else {
t.deepEqual(target.parent(traversal).getUnsafe(), lightDiv);
}
}
});
5 changes: 3 additions & 2 deletions packages/alfa-dom/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@
"test/node/traversal/get-nodes-between.spec.tsx",
"test/node/traversal/lowest-common-ancestor.spec.tsx",
"test/node/query/element-descendants.spec.tsx",
"test/node/query/element-id-map.spec.tsx"
"test/node/query/element-id-map.spec.tsx",
"test/traversal.spec.tsx"
],
"references": [
{ "path": "../alfa-array" },
Expand All @@ -103,7 +104,7 @@
{ "path": "../alfa-map" },
{ "path": "../alfa-option" },
{ "path": "../alfa-predicate" },
{ "path": "../alfa-rectangle"},
{ "path": "../alfa-rectangle" },
{ "path": "../alfa-refinement" },
{ "path": "../alfa-sarif" },
{ "path": "../alfa-selective" },
Expand Down
35 changes: 14 additions & 21 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -406,9 +406,9 @@ __metadata:
linkType: hard

"@mdn/browser-compat-data@npm:^5.0.0":
version: 5.5.4
resolution: "@mdn/browser-compat-data@npm:5.5.4"
checksum: bdef5c85cc6e90bbf6e873033bfcf92dba2df699fb9fdd7390f5d1dc0e6d14fba9b8b3585370248a82913585eb1792c1afd20900efb6604c9e59122ba98060d7
version: 5.5.6
resolution: "@mdn/browser-compat-data@npm:5.5.6"
checksum: b22f1d9e69451c12295b0ead856dd5d12b08f43a068047bc8e40f365fd8b41207a96e2a5383048a0b6e87daa0edc4e8cbdaf70e1e593b653e347178a85b30530
languageName: node
linkType: hard

Expand Down Expand Up @@ -2092,11 +2092,11 @@ __metadata:
linkType: hard

"@types/node@npm:*, @types/node@npm:^20.5.9":
version: 20.10.7
resolution: "@types/node@npm:20.10.7"
version: 20.11.1
resolution: "@types/node@npm:20.11.1"
dependencies:
undici-types: "npm:~5.26.4"
checksum: d626cea1b7da4784ee7b335dcc54e64adba9725dab7ca51a690167de502ef89fec07b05ad8e25845d188d7ad7f72c192ec92964d456321ed5b9452113bf9351f
checksum: f665cdce28b0b6e57338d1f74e0599ee9b10eac74cff729921c8f473807398e9aba2f8cf74c74a4d3dfbc2d616c73267da7de3003ed3c8152ea366bf4c96a91a
languageName: node
linkType: hard

Expand Down Expand Up @@ -2663,13 +2663,6 @@ __metadata:
languageName: node
linkType: hard

"chalk@npm:^5.3.0":
version: 5.3.0
resolution: "chalk@npm:5.3.0"
checksum: 8297d436b2c0f95801103ff2ef67268d362021b8210daf8ddbe349695333eb3610a71122172ff3b0272f1ef2cf7cc2c41fdaa4715f52e49ffe04c56340feed09
languageName: node
linkType: hard

"chardet@npm:^0.7.0":
version: 0.7.0
resolution: "chardet@npm:0.7.0"
Expand Down Expand Up @@ -4593,8 +4586,8 @@ __metadata:
linkType: hard

"knip@npm:^3.0.0":
version: 3.12.0
resolution: "knip@npm:3.12.0"
version: 3.13.2
resolution: "knip@npm:3.13.2"
dependencies:
"@ericcornelissen/bash-parser": "npm:0.5.2"
"@npmcli/map-workspaces": "npm:3.0.4"
Expand All @@ -4603,14 +4596,14 @@ __metadata:
"@pnpm/logger": "npm:5.0.0"
"@pnpm/workspace.pkgs-graph": "npm:^2.0.13"
"@snyk/github-codeowners": "npm:1.1.0"
chalk: "npm:^5.3.0"
easy-table: "npm:1.2.0"
fast-glob: "npm:3.3.2"
globby: "npm:^14.0.0"
jiti: "npm:1.21.0"
js-yaml: "npm:4.1.0"
micromatch: "npm:4.0.5"
minimist: "npm:1.2.8"
picocolors: "npm:1.0.0"
pretty-ms: "npm:8.0.0"
strip-json-comments: "npm:5.0.1"
summary: "npm:2.1.0"
Expand All @@ -4621,7 +4614,7 @@ __metadata:
typescript: ">=5.0.4"
bin:
knip: bin/knip.js
checksum: 29599ebb99d369154320aa6ab9bb0d24a1eb3ff8386593e8bef6fb5481eb1ed59a00bf490d43495a07a5164d21c8bbd3c5a79c24a74cdb1e12ae2855918c8901
checksum: 9221fa8d863d298ad642fd379e0e8df7f160663e773591e226b46d73219e9a3679528fdc405071b8a3741f7b87491017b496331179db272df2db1945ea91c40d
languageName: node
linkType: hard

Expand Down Expand Up @@ -5645,7 +5638,7 @@ __metadata:
languageName: node
linkType: hard

"picocolors@npm:^1.0.0":
"picocolors@npm:1.0.0, picocolors@npm:^1.0.0":
version: 1.0.0
resolution: "picocolors@npm:1.0.0"
checksum: 20a5b249e331c14479d94ec6817a182fd7a5680debae82705747b2db7ec50009a5f6648d0621c561b0572703f84dbef0858abcbd5856d3c5511426afcb1961f7
Expand Down Expand Up @@ -5697,11 +5690,11 @@ __metadata:
linkType: hard

"prettier@npm:^3.0.0":
version: 3.1.1
resolution: "prettier@npm:3.1.1"
version: 3.2.2
resolution: "prettier@npm:3.2.2"
bin:
prettier: bin/prettier.cjs
checksum: facc944ba20e194ff4db765e830ffbcb642803381f0d2033ed397e79904fa4ccc877dc25ad68f42d36985c01d051c990ca1b905fb83d2d7d65fe69e4386fa1a3
checksum: e84d0d2a4ce2b88ee1636904effbdf68b59da63d9f887128f2ed5382206454185432e7c0a9578bc4308bc25d099cfef47fd0b9c211066777854e23e65e34044d
languageName: node
linkType: hard

Expand Down

0 comments on commit 8f00748

Please sign in to comment.