Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
11 changes: 11 additions & 0 deletions packages/core/src/components/tree-item/tree-item.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@use 'misc/common-variables' as vars;
@use 'mixins/text-truncation';
@use 'mixins/shadow-dom/component';
@use 'mixins/shadow-dom/focus-visible';

:host {
display: flex;
Expand All @@ -28,9 +29,14 @@
height: vars.$x-large-space;
flex-grow: 1;
align-items: center;
outline: none;

@include text-truncation.ellipsis;

&:focus-visible {
@include focus-visible.ix-focus-visible();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
}

.tree-node-text {
@include text-truncation.ellipsis;
}
Expand All @@ -46,10 +52,15 @@

ix-icon {
transition: transform var(--theme-default-time) ease-in-out;
outline: none;

&.icon-toggle-down {
transform: rotate(90deg);
}

&:focus-visible {
@include focus-visible.ix-focus-visible();
}
Comment thread
1307-Dev marked this conversation as resolved.
}
}
}
Expand Down
48 changes: 41 additions & 7 deletions packages/core/src/components/tree-item/tree-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,15 @@
* LICENSE file in the root directory of this source tree.
*/

import { Component, Event, EventEmitter, h, Host, Prop } from '@stencil/core';
import {
Component,
Element,
Event,
EventEmitter,
h,
Host,
Prop,
} from '@stencil/core';
import { TreeItemContext } from '../tree/tree-model';
import { iconChevronRightSmall } from '@siemens/ix-icons/icons';

Expand All @@ -20,6 +28,8 @@
shadow: true,
})
export class TreeItem {
@Element() hostElement!: HTMLIxTreeItemElement;

/**
* Text
*/
Expand Down Expand Up @@ -76,6 +86,14 @@
['icon-toggle-down']: !!this.context?.isExpanded,
}}
color="color-std-text"
tabIndex={isDisabled ? -1 : 0}
role="button"
aria-label={
this.ariaLabelChevronIcon ??
(this.context?.isExpanded
? 'Collapse tree item'
: 'Expand tree item')
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
onClick={(e: Event) => {
if (isDisabled) {
return;
Expand All @@ -84,24 +102,40 @@
e.stopPropagation();
this.toggle.emit();
}}
aria-label={
this.ariaLabelChevronIcon ??
(this.context?.isExpanded
? 'Collapse tree item'
: 'Expand tree item')
}
onKeyDown={(e: KeyboardEvent) => {
if (isDisabled) {
return;
}
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
this.toggle.emit();
}
}}
/>
) : null}
</div>
<div
class="tree-node-container"
role="button"
tabIndex={isDisabled ? -1 : 0}
Comment thread
1307-Dev marked this conversation as resolved.
Comment thread
1307-Dev marked this conversation as resolved.
onKeyDown={(e: KeyboardEvent) => {
if (isDisabled) {
return;
}
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
(e.currentTarget as HTMLElement).click();
}
Comment thread
1307-Dev marked this conversation as resolved.
}}
Comment thread
1307-Dev marked this conversation as resolved.
onClick={() => {
if (isDisabled) {
return;
}
this.itemClick.emit();
}}
>

Check warning on line 138 in packages/core/src/components/tree-item/tree-item.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <input type="button">, <input type="image">, <input type="reset">, <input type="submit">, or <button> instead of the "button" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=siemens_ix&issues=AZ-NFj5PgOV5hraTpHKa&open=AZ-NFj5PgOV5hraTpHKa&pullRequest=2665
<div class="tree-node-text">{this.text}</div>
<slot></slot>
</div>
Expand Down
44 changes: 44 additions & 0 deletions packages/core/src/components/tree/tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,17 +314,61 @@ export class Tree {
);
}

private getFocusedTreeNodeInfo(): {
nodeId: string;
target: 'chevron' | 'item';
} | null {
const activeEl = document.activeElement;
if (!(activeEl instanceof HTMLElement)) {
return null;
}

const nodeId = this.getTreeNodeId(activeEl);
if (!nodeId) {
return null;
}

const shadowActive = activeEl.shadowRoot?.activeElement;
if (shadowActive?.tagName === 'IX-ICON') {
Comment thread
1307-Dev marked this conversation as resolved.
return { nodeId, target: 'chevron' };
}

if (
shadowActive instanceof HTMLElement &&
shadowActive.classList.contains('tree-node-container')
) {
return { nodeId, target: 'item' };
}

return null;
}

/**
* Refresh the list.
* This will re-render the list with the current model and context.
*/
@Method()
async refreshTree(options: RefreshTreeOptions = defaultRefreshTreeOptions) {
if (this.hyperlist) {
const focusInfo = this.getFocusedTreeNodeInfo();

this.hyperlist.refresh(
this.hostElement,
this.getVirtualizerOptions(options)
);

if (focusInfo) {
const treeItem = this.hostElement.querySelector<HTMLElement>(
`[data-tree-node-id="${focusInfo.nodeId}"]`
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (focusInfo.target === 'chevron') {
treeItem?.shadowRoot?.querySelector<HTMLElement>('ix-icon')?.focus();
} else {
treeItem?.shadowRoot
?.querySelector<HTMLElement>('.tree-node-container')
?.focus();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
}
}

Expand Down
Loading