-
Notifications
You must be signed in to change notification settings - Fork 125
Disable tree item #2279
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Disable tree item #2279
Conversation
🦋 Changeset detectedLatest commit: 40cfbe0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 5 packages
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 |
|
|
/gemini summary |
Summary of ChangesThis 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
Changelog
Activity
|
|
/gemini review |
There was a problem hiding this 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.
| 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/); | ||
| } | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| cursor: not-allowed; |
| @Prop() context?: TreeItemContext; | ||
|
|
||
| /** | ||
| * Disable tree item |
There was a problem hiding this comment.
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', |
There was a problem hiding this comment.
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
| 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'); | ||
| } | ||
| ); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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/); | ||
| } | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| 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/); | |
| } | |
| ); |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A11y support is missing



💡 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):
pnpm test)pnpm lint)pnpm build, changes pushed)👨💻 Help & support