Skip to content

Conversation

@varun-srinivasa
Copy link
Collaborator

💡 What is the current behavior?

GitHub Issue Number: #2091

🆕 What is the new behavior?

🏁 Checklist

A pull request can only be merged if all of these conditions are met (where applicable):

  • 🦮 Accessibility (a11y) features were implemented
  • 🗺️ Internationalization (i18n) - no hard coded strings
  • 📲 Responsiveness - components handle viewport changes and content overflow gracefully
  • 📕 Add or update a Storybook story
  • 📄 Documentation was reviewed/updated siemens/ix-docs
  • 🧪 Unit tests were added/updated and pass (pnpm test)
  • 📸 Visual regression tests were added/updated and pass (Guide)
  • 🧐 Static code analysis passes (pnpm lint)
  • 🏗️ Successful compilation (pnpm build, changes pushed)

👨‍💻 Help & support

@changeset-bot
Copy link

changeset-bot bot commented Nov 20, 2025

🦋 Changeset detected

Latest commit: 40cfbe0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@siemens/ix Patch
@siemens/ix-angular Patch
@siemens/ix-docs Patch
@siemens/ix-react Patch
@siemens/ix-vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 5, 2025

@varun-srinivasa
Copy link
Collaborator Author

/gemini summary

@gemini-code-assist
Copy link
Contributor

Summary of Changes

This pull request implements the functionality to disable individual items within the 'ix-tree' component. This feature provides a clear visual indication and prevents user interaction with specific tree nodes, enhancing the control and clarity of hierarchical data displays. The changes span across the core component, its styling, and its integrations with Angular, React, and Vue, ensuring consistent behavior and appearance.

Highlights

  • New 'disabled' property for ix-tree-item: A new 'disabled' boolean property has been introduced across Angular, Core, React, and Vue components, allowing individual tree items to be explicitly disabled.
  • Visual and interactive disabling: Disabled tree items now display with reduced opacity, a 'not-allowed' cursor, and are non-interactive, preventing 'toggle' and 'itemClick' events.
  • Comprehensive testing: New regression tests have been added to verify the visual appearance and interaction behavior of disabled tree items, ensuring robustness and correct functionality.
Changelog
  • packages/angular/src/components.ts
    • Added 'disabled' as an input property for the 'IxTreeItem' Angular component.
  • packages/angular/standalone/src/components.ts
    • Added 'disabled' as an input property for the 'IxTreeItem' standalone Angular component.
  • packages/core/src/components.d.ts
    • Updated the 'IxTreeItem' interface to include a 'disabled' boolean property and 'TreeItemContext' with an optional 'isDisabled' property.
  • packages/core/src/components/tree-item/default-tree-item.ts
    • Modified the 'renderDefaultItem' function to correctly apply the 'disabled' state to tree items.
  • packages/core/src/components/tree-item/tree-item.scss
    • Introduced CSS rules to visually style disabled tree items with reduced opacity and a 'not-allowed' cursor.
  • packages/core/src/components/tree-item/tree-item.tsx
    • Implemented the 'disabled' property, added a 'disabled' class to the host element when applicable, and prevented click and toggle events on disabled items.
  • packages/core/src/components/tree/test/tree.ct.ts
    • Added multiple regression tests to validate the behavior and styling of disabled tree items, including their class, opacity, cursor, and ensuring enabled items remain selectable.
  • packages/core/src/components/tree/tree-model.ts
    • Extended the 'TreeItem' interface with an optional 'disabled' boolean property and the 'TreeItemContext' with an optional 'isDisabled' property.
  • packages/core/src/components/tree/tree.tsx
    • Updated the tree's context management to include the 'isDisabled' state and added logic to prevent toggling or clicking on disabled tree items.
  • packages/react/src/tree/tree.tsx
    • Modified the React wrapper to correctly pass the 'disabled' property to the underlying 'ix-tree-item' component.
  • packages/vue/src/components.ts
    • Added 'disabled' to the list of properties for the 'IxTreeItem' Vue component.
Activity
  • changeset-bot[bot] noted that no changeset was found, indicating no version bump would occur upon merge.
  • sonarqubecloud[bot] reported that the Quality Gate passed with no new issues, security hotspots, or duplication on new code.
  • varun-srinivasa requested a summary of the pull request.

@varun-srinivasa
Copy link
Collaborator Author

/gemini review

@varun-srinivasa varun-srinivasa marked this pull request as ready for review December 5, 2025 03:47
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a disabled property for the ix-tree-item component, making it available across Angular, React, and Vue frameworks. The changes include updating component interfaces, properties, and rendering logic to reflect the disabled state. Styling was added to visually indicate disabled items with reduced opacity and a 'not-allowed' cursor, and pointer-events: none; was applied to prevent mouse interactions. Event handlers for toggling and clicking tree items were updated to prevent actions on disabled items. New regression tests were added to verify the functionality and styling of disabled tree items. Review comments suggest adding a clarifying comment regarding the onClick checks for disabled items, as pointer-events: none; already prevents mouse events, though the checks remain valuable for programmatic or keyboard interactions. Additionally, it was recommended to refactor the repetitive test setup logic into a helper function for improved maintainability.

Comment on lines +467 to +623
regressionTest(
'disabled item should have disabled class',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

await expect(tree).toHaveClass(/hydrated/);

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const disabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 1',
});

await expect(disabledItem).toBeVisible();
await expect(disabledItem).toHaveClass(/disabled/);
}
);

regressionTest(
'disabled item should have reduced opacity',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const disabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 1',
});

await expect(disabledItem).toBeVisible();
await expect(disabledItem).toHaveCSS('opacity', '0.5');
}
);

regressionTest(
'disabled item should have not-allowed cursor',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const disabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 1',
});

await expect(disabledItem).toBeVisible();
await expect(disabledItem).toHaveCSS('cursor', 'not-allowed');
}
);

regressionTest(
'enabled items should still be selectable when other items are disabled',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const enabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 2',
});

await expect(enabledItem).toBeVisible();
await expect(enabledItem).not.toHaveClass(/disabled/);

await enabledItem.click();

await expect(enabledItem).toHaveClass(/selected/);
}
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve maintainability and reduce code duplication, you could extract the test setup logic into a helper function. All the new tests share the same setup code.

Here's an example of how you could refactor it:

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

  const tree = page.locator('ix-tree');

  const modelWithDisabled = {
    ...defaultModel,
    'sample-child-1': {
      ...defaultModel['sample-child-1'],
      disabled: true,
    },
  };

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

  await expect(tree).toHaveClass(/hydrated/);

  const sampleItem = tree.locator('ix-tree-item').first();
  await sampleItem.locator('ix-icon').click();

  const disabledItem = tree.locator('ix-tree-item', {
    hasText: 'Sample Child 1',
  });

  await expect(disabledItem).toBeVisible();

  return { tree, disabledItem };
};

regressionTest(
  'disabled item should have disabled class',
  async ({ mount, page }) => {
    const { disabledItem } = await setupTreeWithDisabledItem(mount, page);
    await expect(disabledItem).toHaveClass(/disabled/);
  }
);

This would make the tests cleaner and easier to maintain.

Add property **disabled** to **ix-tree-item**.

:host(.disabled),
:host(:disabled) {
cursor: not-allowed;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cursor: not-allowed;

@Prop() context?: TreeItemContext;

/**
* Disable tree item
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since tag missing

);

regressionTest(
'disabled item should have reduced opacity',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case should be part of visual regression

Comment on lines +545 to +582
regressionTest(
'disabled item should have not-allowed cursor',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const disabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 1',
});

await expect(disabledItem).toBeVisible();
await expect(disabledItem).toHaveCSS('cursor', 'not-allowed');
}
);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
regressionTest(
'disabled item should have not-allowed cursor',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);
const tree = page.locator('ix-tree');
const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};
await tree.evaluate(
(element: HTMLIxTreeElement, [model]) => {
element.model = model;
},
[modelWithDisabled]
);
const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();
const disabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 1',
});
await expect(disabledItem).toBeVisible();
await expect(disabledItem).toHaveCSS('cursor', 'not-allowed');
}
);

Comment on lines +583 to +623
regressionTest(
'enabled items should still be selectable when other items are disabled',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);

const tree = page.locator('ix-tree');

const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};

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

const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();

const enabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 2',
});

await expect(enabledItem).toBeVisible();
await expect(enabledItem).not.toHaveClass(/disabled/);

await enabledItem.click();

await expect(enabledItem).toHaveClass(/selected/);
}
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
regressionTest(
'enabled items should still be selectable when other items are disabled',
async ({ mount, page }) => {
await mount(`
<div style="height: 20rem; width: 100%;">
<ix-tree root="root"></ix-tree>
</div>
`);
const tree = page.locator('ix-tree');
const modelWithDisabled = {
...defaultModel,
'sample-child-1': {
...defaultModel['sample-child-1'],
disabled: true,
},
};
await tree.evaluate(
(element: HTMLIxTreeElement, [model]) => {
element.model = model;
},
[modelWithDisabled]
);
const sampleItem = tree.locator('ix-tree-item').first();
await sampleItem.locator('ix-icon').click();
const enabledItem = tree.locator('ix-tree-item', {
hasText: 'Sample Child 2',
});
await expect(enabledItem).toBeVisible();
await expect(enabledItem).not.toHaveClass(/disabled/);
await enabledItem.click();
await expect(enabledItem).toHaveClass(/selected/);
}
);

@sonarqubecloud
Copy link

@nuke-ellington nuke-ellington added this to the 4.4.0 milestone Jan 26, 2026
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A11y support is missing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants