Skip to content

Commit

Permalink
feat(core/tree): migrate to shadow dom (#672)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielleroux committed Aug 7, 2023
1 parent d228212 commit 955180a
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 40 deletions.
4 changes: 2 additions & 2 deletions packages/core/component-doc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10628,7 +10628,7 @@
"usage": {},
"docs": "",
"docsTags": [],
"encapsulation": "scoped",
"encapsulation": "shadow",
"dependents": [],
"dependencies": [
"ix-tree-item"
Expand Down Expand Up @@ -10770,7 +10770,7 @@
"usage": {},
"docs": "",
"docsTags": [],
"encapsulation": "scoped",
"encapsulation": "shadow",
"dependents": [
"ix-tree"
],
Expand Down
55 changes: 27 additions & 28 deletions packages/core/src/components/tree-item/tree-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
* LICENSE file in the root directory of this source tree.
*/

@import 'mixins/hover';
@import 'common-variables';
@import 'mixins/hover';
@import 'mixins/text-truncation';
@import 'mixins/shadow-dom/component';

:host {
@include ix-component;

display: flex;
align-items: center;

Expand All @@ -21,32 +22,6 @@

cursor: pointer;

&:not(.disabled):not(:disabled):not(.selected) {
&.hover,
&:hover {
background-color: var(--theme-tree-item--background--hover);
}

&.active,
&:active {
background-color: var(--theme-tree-item--background--active);
}
}

&.selected {
background-color: var(--theme-tree-item--background--selected);

&.hover,
&:hover {
background-color: var(--theme-tree-item--background--selected-hover);
}

&.active,
&:active {
background-color: var(--theme-tree-item--background--selected-active);
}
}

.tree-node-container {
display: flex;
align-items: center;
Expand All @@ -71,3 +46,27 @@
}
}
}

:host(:not(.disabled):not(:disabled):not(.selected).hover),
:host(:not(.disabled):not(:disabled):not(.selected):hover) {
background-color: var(--theme-tree-item--background--hover);
}

:host(:not(.disabled):not(:disabled):not(.selected).active),
:host(:not(.disabled):not(:disabled):not(.selected):active) {
background-color: var(--theme-tree-item--background--active);
}

:host(.selected) {
background-color: var(--theme-tree-item--background--selected);
}

:host(.selected.hover),
:host(.selected:hover) {
background-color: var(--theme-tree-item--background--selected-hover);
}

:host(.selected.active),
:host(.selected:active) {
background-color: var(--theme-tree-item--background--selected-active);
}
2 changes: 1 addition & 1 deletion packages/core/src/components/tree-item/tree-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { TreeItemContext } from '../tree/tree-model';
@Component({
tag: 'ix-tree-item',
styleUrl: 'tree-item.scss',
scoped: true,
shadow: true,
})
export class TreeItem {
/**
Expand Down
173 changes: 173 additions & 0 deletions packages/core/src/components/tree/test/tree.ct.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* SPDX-FileCopyrightText: 2023 Siemens AG
*
* SPDX-License-Identifier: MIT
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { expect, Locator, Page } from '@playwright/test';
import { test } from '@utils/test';

const defaultModel = {
root: {
id: 'root',
data: {
name: '',
},
hasChildren: true,
children: ['sample'],
},
sample: {
id: 'sample',
data: {
name: 'Sample',
},
hasChildren: true,
children: ['sample-child-1', 'sample-child-2', 'sample-child-3'],
},
'sample-child-1': {
id: 'sample-child-1',
data: {
name: 'Sample Child 1',
},
hasChildren: false,
children: [],
},
'sample-child-2': {
id: 'sample-child-2',
data: {
name: 'Sample Child 2',
},
hasChildren: true,
children: ['sample-child-4'],
},
'sample-child-3': {
id: 'sample-child-3',
data: {
name: 'Sample Child 3',
},
hasChildren: false,
children: [],
},
'sample-child-4': {
id: 'sample-child-4',
data: {
name: 'Sample Child 4',
},
hasChildren: false,
children: [],
},
};

const initializeTree = async (mount, page: Page) => {
await mount(`
<div style=" height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);
const tree = page.locator('ix-tree');

await tree.evaluate(
(element: HTMLIxTreeElement, [model]) => {
element.model = model;
},
[defaultModel]
);

const item = tree.locator('ix-tree-item').nth(0);
await expect(tree).toHaveClass(/hydrated/);
await expect(item).toBeVisible();

return tree;
};

const updateModel = async (tree: Locator, updatedModel: any) => {
await tree.evaluate(
(element: HTMLIxTreeElement, [model]) => {
element.model = model;
},
[updatedModel]
);
};

test('renders', async ({ mount, page }) => {
const tree = await initializeTree(mount, page);
const item = tree.locator('ix-tree-item').nth(0);
await expect(tree).toHaveClass(/hydrated/);
await expect(item).toBeVisible();
});

test('update tree', async ({ mount, page }) => {
const tree = await initializeTree(mount, page);

const item = tree.locator('ix-tree-item').nth(0);
await item.locator('.icon-toggle-container').click();

const item2 = tree.locator('ix-tree-item').nth(2);
await item2.locator('.icon-toggle-container').click();

const item3 = tree.locator('ix-tree-item').nth(4);
await expect(item3).toBeVisible();
await expect(item3).toHaveText('Sample Child 3');
await expect(item3).toHaveCSS('padding-left', '16px');

await updateModel(tree, {
root: {
id: 'root',
data: {
name: '',
},
hasChildren: true,
children: ['sample'],
},
sample: {
id: 'sample',
data: {
name: 'Sample',
},
hasChildren: true,
children: ['sample-child-1', 'sample-child-2'],
},
'sample-child-1': {
id: 'sample-child-1',
data: {
name: 'Sample Child 1',
},
hasChildren: false,
children: [],
},
'sample-child-2': {
id: 'sample-child-2',
data: {
name: 'Sample Child 2',
},
hasChildren: true,
children: ['sample-child-3', 'sample-child-4'],
},
'sample-child-3': {
id: 'sample-child-3',
data: {
name: 'Sample Child 3',
},
hasChildren: false,
children: [],
},
'sample-child-4': {
id: 'sample-child-4',
data: {
name: 'Sample Child 4',
},
hasChildren: false,
children: [],
},
});

await expect(tree).toHaveClass(/hydrated/);
await expect(item).toBeVisible();
await expect(item2).toBeVisible();

const newChildItem = tree.locator('ix-tree-item').nth(3);
await expect(newChildItem).toBeVisible();
await expect(newChildItem).toHaveCSS('padding-left', '32px');
});
26 changes: 17 additions & 9 deletions packages/core/src/components/tree/tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import {
@Component({
tag: 'ix-tree',
styleUrl: 'tree.css',
scoped: true,
shadow: true,
})
export class Tree {
@Element() host!: HTMLIxTreeElement;
@Element() hostElement!: HTMLIxTreeElement;

/**
* Initial root element will not be rendered
Expand Down Expand Up @@ -89,11 +89,13 @@ export class Tree {
private toggleListener = new Map<HTMLElement, Function>();
private itemClickListener = new Map<HTMLElement, Function>();
private updates = new Map<string, UpdateCallback>();

private observer: MutationObserver;

private hasFirstRender = false;

private updatePadding(element: HTMLElement, item: TreeItemVisual<unknown>) {
element.style.paddingLeft = item.level + 'rem';
}

private getVirtualizerOptions() {
const list = this.buildTreeList(this.model[this.root]);

Expand Down Expand Up @@ -121,7 +123,8 @@ export class Tree {
total: list.length,
generate: (index: number) => {
const item = list[index];
const renderedTreeItem = this.host.querySelector(

const renderedTreeItem = this.hostElement.querySelector(
`[data-tree-node-id="${item.id}"]`
) as HTMLIxTreeItemElement;

Expand All @@ -138,6 +141,7 @@ export class Tree {
doUpdate(item, { ...this.context });
}

this.updatePadding(renderedTreeItem, item);
return renderedTreeItem;
}

Expand All @@ -162,8 +166,8 @@ export class Tree {

const el = innerElement;
el.setAttribute('data-tree-node-id', item.id);
el.style.paddingLeft = item.level + 'rem';
el.style.paddingRight = '1rem';
this.updatePadding(el, item);

if (!this.itemClickListener.has(el)) {
const itemClickCallback = (e: Event) => {
Expand Down Expand Up @@ -252,7 +256,7 @@ export class Tree {
this.nodeRemoved.emit(removed);
});

this.observer.observe(this.host, {
this.observer.observe(this.hostElement, {
childList: true,
});
}
Expand Down Expand Up @@ -293,14 +297,18 @@ export class Tree {

private refreshList() {
if (this.hyperlist) {
this.hyperlist.refresh(this.host, this.getVirtualizerOptions());
this.hyperlist.refresh(this.hostElement, this.getVirtualizerOptions());
}
}

private initList() {
if (!this.model) {
return;
}

this.hyperlist?.destroy();
const config = this.getVirtualizerOptions();
this.hyperlist = new Hyperlist(this.host, config);
this.hyperlist = new Hyperlist(this.hostElement, config);
}

render() {
Expand Down

0 comments on commit 955180a

Please sign in to comment.