Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions docs/pages/components/tree-item.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,66 @@ const App = () => (
</SlTree>
);
```

### Lazy Loading

Use the `lazy` attribute on a tree item to indicate that the content is not yet present and will be loaded later. When the user tries to expand the node, the `loading` state is set to `true` and the `sl-lazy-load` event will be emitted to allow you to load data asynchronously. The item will remain in a loading state until its content is changed.

If you want to disable this behavior after the first load, simply remove the `lazy` attribute and, on the next expand, the existing content will be shown instead.

```html:preview
<sl-tree>
<sl-tree-item lazy>Available Trees</sl-tree-item>
</sl-tree>

<script type="module">
const lazyItem = document.querySelector('sl-tree-item[lazy]');

lazyItem.addEventListener('sl-lazy-load', () => {
// Simulate asynchronous loading
setTimeout(() => {
const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];

for (const item of subItems) {
const treeItem = document.createElement('sl-tree-item');
treeItem.innerText = item;
lazyItem.append(treeItem);
}

// Disable lazy mode once the content has been loaded
lazyItem.lazy = false;
}, 1000);
});
</script>
```

```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';

const App = () => {
const [childItems, setChildItems] = useState([]);
const [lazy, setLazy] = useState(true);

const handleLazyLoad = () => {
// Simulate asynchronous loading
setTimeout(() => {
setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']);

// Disable lazy mode once the content has been loaded
setLazy(false);
}, 1000);
};

return (
<SlTree>
<SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
Available Trees
{childItems.map(item => (
<SlTreeItem>{item}</SlTreeItem>
))}
</SlTreeItem>
</SlTree>
);
};
```
63 changes: 0 additions & 63 deletions docs/pages/components/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,69 +235,6 @@ const App = () => (

{% endraw %}

### Lazy Loading

Use the `lazy` attribute on a tree item to indicate that the content is not yet present and will be loaded later. When the user tries to expand the node, the `loading` state is set to `true` and the `sl-lazy-load` event will be emitted to allow you to load data asynchronously. The item will remain in a loading state until its content is changed.

If you want to disable this behavior after the first load, simply remove the `lazy` attribute and, on the next expand, the existing content will be shown instead.

```html:preview
<sl-tree>
<sl-tree-item lazy>Available Trees</sl-tree-item>
</sl-tree>

<script type="module">
const lazyItem = document.querySelector('sl-tree-item[lazy]');

lazyItem.addEventListener('sl-lazy-load', () => {
// Simulate asynchronous loading
setTimeout(() => {
const subItems = ['Birch', 'Cedar', 'Maple', 'Pine'];

for (const item of subItems) {
const treeItem = document.createElement('sl-tree-item');
treeItem.innerText = item;
lazyItem.append(treeItem);
}

// Disable lazy mode once the content has been loaded
lazyItem.lazy = false;
}, 1000);
});
</script>
```

```jsx:react
import SlTree from '@shoelace-style/shoelace/dist/react/tree';
import SlTreeItem from '@shoelace-style/shoelace/dist/react/tree-item';

const App = () => {
const [childItems, setChildItems] = useState([]);
const [lazy, setLazy] = useState(true);

const handleLazyLoad = () => {
// Simulate asynchronous loading
setTimeout(() => {
setChildItems(['Birch', 'Cedar', 'Maple', 'Pine']);

// Disable lazy mode once the content has been loaded
setLazy(false);
}, 1000);
};

return (
<SlTree>
<SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
Available Trees
{childItems.map(item => (
<SlTreeItem>{item}</SlTreeItem>
))}
</SlTreeItem>
</SlTree>
);
};
```

### Customizing the Expand and Collapse Icons

Use the `expand-icon` and `collapse-icon` slots to change the expand and collapse icons, respectively. To disable the animation, override the `rotate` property on the `expand-button` part as shown below.
Expand Down
3 changes: 3 additions & 0 deletions src/components/tree-item/tree-item.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ export default class SlTreeItem extends ShoelaceElement {
this.setAttribute('aria-busy', this.loading ? 'true' : 'false');

if (!this.loading) {
this.getChildrenItems().forEach(item => {
item.selected = this.selected;
});
this.animateExpand();
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/components/tree-item/tree-item.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,4 +177,25 @@ describe('<sl-tree-item>', () => {
expect(lazyChangeSpy).to.have.been.calledTwice;
});
});

it('should update children select state after loading', async () => {
// Arrange
const lazyLoadSpy = sinon.spy();

parentItem.addEventListener('sl-lazy-load', lazyLoadSpy);
parentItem.selected = true;
parentItem.lazy = true;

// Act
parentItem.expanded = true;
await waitUntil(() => lazyLoadSpy.calledOnce);
parentItem.loading = false;
await parentItem.updateComplete;

// Assert
expect(parentItem.childElementCount).to.be.greaterThan(0);
parentItem.getChildrenItems().forEach(child => {
expect(child.selected).to.be.true;
});
});
});