Skip to content

Commit a85d031

Browse files
authored
fix(mock-doc): implement part API (#6423)
* fix(mock-doc): implement part API * refactor(mock-doc): unify classList and part into MockTokenList * fix: run lint --fix to sort imports * fix: run prettier to format code
1 parent b191267 commit a85d031

File tree

5 files changed

+147
-142
lines changed

5 files changed

+147
-142
lines changed

src/mock-doc/class-list.ts

Lines changed: 0 additions & 84 deletions
This file was deleted.

src/mock-doc/node.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createAttributeProxy, MockAttr, MockAttributeMap } from './attribute';
2-
import { MockClassList } from './class-list';
32
import { NODE_NAMES, NODE_TYPES } from './constants';
43
import { createCSSStyleDeclaration, MockCSSStyleDeclaration } from './css-style-declaration';
54
import { attributeChanged, checkAttributeChanged, connectNode, disconnectNode } from './custom-element-registry';
@@ -15,6 +14,7 @@ import {
1514
import { parseFragmentUtil } from './parse-util';
1615
import { matches, selectAll, selectOne } from './selector';
1716
import { NON_ESCAPABLE_CONTENT, serializeNodeToHtml, SerializeNodeToHtmlOptions } from './serialize-node';
17+
import { MockTokenList } from './token-list';
1818

1919
export class MockNode {
2020
private _nodeValue: string | null;
@@ -368,7 +368,11 @@ export class MockElement extends MockNode {
368368
}
369369

370370
get classList() {
371-
return new MockClassList(this as any);
371+
return new MockTokenList(this as any, 'class');
372+
}
373+
374+
get part() {
375+
return new MockTokenList(this as any, 'part');
372376
}
373377

374378
click() {

src/mock-doc/test/class-list.spec.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { MockTokenList } from '../token-list';
2+
import { MockDocument } from '../document';
3+
import { MockElement } from '../node';
4+
5+
describe('token-list', () => {
6+
let tokenList: MockTokenList;
7+
beforeEach(() => {
8+
const doc = new MockDocument();
9+
const el = new MockElement(doc, 'div');
10+
tokenList = new MockTokenList(el as any, 'class');
11+
});
12+
13+
it('add and remove tokens', () => {
14+
tokenList.add('one');
15+
tokenList.add('two', 'three');
16+
tokenList.add(null);
17+
tokenList.add(undefined);
18+
tokenList.add(1 as any, 2 as any);
19+
expect(tokenList.toString()).toEqual('one two three null undefined 1 2');
20+
21+
expect(tokenList.contains('one')).toBe(true);
22+
expect(tokenList.contains('two')).toBe(true);
23+
expect(tokenList.contains('three')).toBe(true);
24+
expect(tokenList.contains('null')).toBe(true);
25+
expect(tokenList.contains(null)).toBe(true);
26+
expect(tokenList.contains('undefined')).toBe(true);
27+
expect(tokenList.contains('1')).toBe(true);
28+
expect(tokenList.contains(2 as any)).toBe(true);
29+
30+
tokenList.remove('one');
31+
tokenList.remove('two', 'three');
32+
tokenList.remove(null);
33+
tokenList.remove(undefined);
34+
tokenList.remove(1 as any, 2 as any);
35+
36+
expect(tokenList.toString()).toEqual('');
37+
});
38+
39+
it('should throw if empty', () => {
40+
expect(() => {
41+
tokenList.add('');
42+
}).toThrow();
43+
expect(() => {
44+
tokenList.remove('');
45+
}).toThrow();
46+
});
47+
48+
it('should throw if has spaces', () => {
49+
expect(() => {
50+
tokenList.add('');
51+
}).toThrow();
52+
expect(() => {
53+
tokenList.remove(' ');
54+
}).toThrow();
55+
});
56+
});

src/mock-doc/token-list.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
export class MockTokenList {
2+
constructor(
3+
private elm: HTMLElement,
4+
private attr: string,
5+
) {}
6+
7+
add(...tokens: string[]) {
8+
const items = getItems(this.elm, this.attr);
9+
let updated = false;
10+
tokens.forEach((token) => {
11+
token = String(token);
12+
validateToken(token);
13+
if (items.includes(token) === false) {
14+
items.push(token);
15+
updated = true;
16+
}
17+
});
18+
if (updated) {
19+
this.elm.setAttributeNS(null, this.attr, items.join(' '));
20+
}
21+
}
22+
23+
remove(...tokens: string[]) {
24+
const items = getItems(this.elm, this.attr);
25+
let updated = false;
26+
tokens.forEach((token) => {
27+
token = String(token);
28+
validateToken(token);
29+
const index = items.indexOf(token);
30+
if (index > -1) {
31+
items.splice(index, 1);
32+
updated = true;
33+
}
34+
});
35+
if (updated) {
36+
this.elm.setAttributeNS(null, this.attr, items.filter((c) => c.length > 0).join(' '));
37+
}
38+
}
39+
40+
contains(token: string) {
41+
token = String(token);
42+
return getItems(this.elm, this.attr).includes(token);
43+
}
44+
45+
toggle(token: string) {
46+
token = String(token);
47+
if (this.contains(token) === true) {
48+
this.remove(token);
49+
} else {
50+
this.add(token);
51+
}
52+
}
53+
54+
get length() {
55+
return getItems(this.elm, this.attr).length;
56+
}
57+
58+
item(index: number) {
59+
return getItems(this.elm, this.attr)[index];
60+
}
61+
62+
toString() {
63+
return getItems(this.elm, this.attr).join(' ');
64+
}
65+
}
66+
67+
function validateToken(token: string) {
68+
if (token === '') {
69+
throw new Error('The token provided must not be empty.');
70+
}
71+
if (/\s/.test(token)) {
72+
throw new Error(`The token provided ('${token}') contains HTML space characters, which are not valid in tokens.`);
73+
}
74+
}
75+
76+
function getItems(elm: HTMLElement, attr: string) {
77+
const value = elm.getAttribute(attr);
78+
if (typeof value === 'string' && value.length > 0) {
79+
return value
80+
.trim()
81+
.split(' ')
82+
.filter((c) => c.length > 0);
83+
}
84+
return [];
85+
}

0 commit comments

Comments
 (0)