) => (
+
+ MOCK CONTENT COMPONENT
+
+ ));
+
+ const response = render({
+ items: [{ id: '1' }],
+ slotProps: { item: { ContentComponent } },
+ });
+
+ expect(response.getItemContent('1').textContent).to.equal('MOCK CONTENT COMPONENT');
+ });
+
+ it('should use the ContentProps prop when defined', function test() {
+ if (treeItemComponentName === 'TreeItem2') {
+ this.skip();
+ }
+
+ const ContentComponent = React.forwardRef((props: any, ref: React.Ref) => (
+
+ {props.customProp}
+
+ ));
+
+ const response = render({
+ items: [{ id: '1' }],
+ slotProps: { item: { ContentComponent, ContentProps: { customProp: 'ABCDEF' } as any } },
+ });
+
+ expect(response.getItemContent('1').textContent).to.equal('ABCDEF');
+ });
+ });
+});
+
describe('', () => {
const { render } = createRenderer();
@@ -71,7 +112,7 @@ describe('', () => {
skip: ['reactTestRenderer', 'componentProp', 'componentsProp', 'themeVariants'],
}));
- describe('warnings', () => {
+ describe('PropTypes warnings', () => {
beforeEach(() => {
PropTypes.resetWarningCache();
});
@@ -98,178 +139,4 @@ describe('', () => {
}).toErrorDev('Expected an element type that can hold a ref.');
});
});
-
- it('should call onClick when clicked', () => {
- const handleClick = spy();
-
- const { getByText } = render(
-
-
- ,
- );
-
- fireEvent.click(getByText('test'));
-
- expect(handleClick.callCount).to.equal(1);
- });
-
- it('should not call onClick when children are clicked', () => {
- const handleClick = spy();
-
- const { getByText } = render(
-
-
-
-
- ,
- );
-
- fireEvent.click(getByText('two'));
-
- expect(handleClick.callCount).to.equal(0);
- });
-
- describe('Accessibility', () => {
- it('should have the role `treeitem`', () => {
- const { getByTestId } = render(
-
-
- ,
- );
-
- expect(getByTestId('test')).to.have.attribute('role', 'treeitem');
- });
-
- it('should add the role `group` to a component containing children', () => {
- const { getByRole, getByText } = render(
-
-
-
-
- ,
- );
-
- expect(getByRole('group')).to.contain(getByText('test2'));
- });
- });
-
- describe('prop: disabled', () => {
- describe('event bindings', () => {
- it('should not prevent onClick being fired', () => {
- const handleClick = spy();
-
- const { getByText } = render(
-
-
- ,
- );
-
- fireEvent.click(getByText('test'));
-
- expect(handleClick.callCount).to.equal(1);
- });
- });
- });
-
- describe('content customisation', () => {
- it('should allow a custom ContentComponent', () => {
- const mockContent = React.forwardRef((props: {}, ref: React.Ref) => (
- MOCK CONTENT COMPONENT
- ));
- const { container } = render(
-
-
- ,
- );
- expect(container.textContent).to.equal('MOCK CONTENT COMPONENT');
- });
-
- it('should allow props to be passed to a custom ContentComponent', () => {
- const mockContent = React.forwardRef((props: any, ref: React.Ref) => (
- {props.customProp}
- ));
- const { container } = render(
-
-
- ,
- );
- expect(container.textContent).to.equal('ABCDEF');
- });
- });
-
- it('should be able to type in an child input', () => {
- const { getByRole } = render(
-
-
-
-
-
- }
- data-testid="two"
- />
-
- ,
- );
- const input = getByRole('textbox');
- const keydownEvent = createEvent.keyDown(input, {
- key: 'a',
- });
-
- const handlePreventDefault = spy();
- keydownEvent.preventDefault = handlePreventDefault;
- fireEvent(input, keydownEvent);
- expect(handlePreventDefault.callCount).to.equal(0);
- });
-
- it('should not focus steal', () => {
- let setActiveItemMounted;
- // a TreeItem whose mounted state we can control with `setActiveItemMounted`
- function ControlledTreeItem(props) {
- const [mounted, setMounted] = React.useState(true);
- setActiveItemMounted = setMounted;
-
- if (!mounted) {
- return null;
- }
- return ;
- }
- const { getByText, getByTestId, getByRole } = render(
-
-
-
-
-
-
- ,
- );
-
- fireEvent.click(getByText('two'));
- act(() => {
- getByTestId('two').focus();
- });
-
- expect(getByTestId('two')).toHaveFocus();
-
- act(() => {
- getByRole('button').focus();
- });
-
- expect(getByRole('button')).toHaveFocus();
-
- act(() => {
- setActiveItemMounted(false);
- });
- act(() => {
- setActiveItemMounted(true);
- });
-
- expect(getByRole('button')).toHaveFocus();
- });
});
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.test.tsx
index 0db6df32a57c0..6bd9dc2448937 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.test.tsx
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewFocus/useTreeViewFocus.test.tsx
@@ -1,3 +1,4 @@
+import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { act, fireEvent } from '@mui/internal-test-utils';
@@ -14,176 +15,231 @@ import {
*/
describeTreeView<
[UseTreeViewFocusSignature, UseTreeViewSelectionSignature, UseTreeViewItemsSignature]
->('useTreeViewFocus plugin', ({ render }) => {
- describe('basic behavior', () => {
- it('should allow to focus an item', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- });
+>(
+ 'useTreeViewFocus plugin',
+ ({ render, renderFromJSX, TreeItemComponent, treeViewComponentName, TreeViewComponent }) => {
+ describe('basic behavior', () => {
+ it('should allow to focus an item', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
- fireEvent.focus(response.getItemRoot('2'));
- expect(response.getFocusedItemId()).to.equal('2');
+ fireEvent.focus(response.getItemRoot('2'));
+ expect(response.getFocusedItemId()).to.equal('2');
- fireEvent.focus(response.getItemRoot('1'));
- expect(response.getFocusedItemId()).to.equal('1');
- });
-
- it('should move the focus when the focused item is removed', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
+ fireEvent.focus(response.getItemRoot('1'));
+ expect(response.getFocusedItemId()).to.equal('1');
});
- fireEvent.focus(response.getItemRoot('2'));
- expect(response.getFocusedItemId()).to.equal('2');
+ it('should move the focus when the focused item is removed', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
- response.setItems([{ id: '1' }]);
- expect(response.getFocusedItemId()).to.equal('1');
- });
- });
+ fireEvent.focus(response.getItemRoot('2'));
+ expect(response.getFocusedItemId()).to.equal('2');
- describe('tabIndex HTML attribute', () => {
- it('should set tabIndex={0} on the first item if none are selected', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
+ response.setItems([{ id: '1' }]);
+ expect(response.getFocusedItemId()).to.equal('1');
});
-
- expect(response.getItemRoot('1').tabIndex).to.equal(0);
- expect(response.getItemRoot('2').tabIndex).to.equal(-1);
});
- it('should set tabIndex={0} on the selected item (single selection)', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- selectedItems: '2',
- });
-
- expect(response.getItemRoot('1').tabIndex).to.equal(-1);
- expect(response.getItemRoot('2').tabIndex).to.equal(0);
- });
+ describe('tabIndex HTML attribute', () => {
+ it('should set tabIndex={0} on the first item if none are selected', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
- it('should set tabIndex={0} on the first selected item (multi selection)', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }, { id: '3' }],
- selectedItems: ['2', '3'],
- multiSelect: true,
+ expect(response.getItemRoot('1').tabIndex).to.equal(0);
+ expect(response.getItemRoot('2').tabIndex).to.equal(-1);
});
- expect(response.getItemRoot('1').tabIndex).to.equal(-1);
- expect(response.getItemRoot('2').tabIndex).to.equal(0);
- expect(response.getItemRoot('3').tabIndex).to.equal(-1);
- });
+ it('should set tabIndex={0} on the selected item (single selection)', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ selectedItems: '2',
+ });
- it('should set tabIndex={0} on the first item if the selected item is not visible', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }] }],
- selectedItems: '2.1',
+ expect(response.getItemRoot('1').tabIndex).to.equal(-1);
+ expect(response.getItemRoot('2').tabIndex).to.equal(0);
});
- expect(response.getItemRoot('1').tabIndex).to.equal(0);
- expect(response.getItemRoot('2').tabIndex).to.equal(-1);
- });
+ it('should set tabIndex={0} on the first selected item (multi selection)', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }, { id: '3' }],
+ selectedItems: ['2', '3'],
+ multiSelect: true,
+ });
- it('should set tabIndex={0} on the first item if the no selected item is visible', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }, { id: '2.2' }] }],
- selectedItems: ['2.1', '2.2'],
- multiSelect: true,
+ expect(response.getItemRoot('1').tabIndex).to.equal(-1);
+ expect(response.getItemRoot('2').tabIndex).to.equal(0);
+ expect(response.getItemRoot('3').tabIndex).to.equal(-1);
});
- expect(response.getItemRoot('1').tabIndex).to.equal(0);
- expect(response.getItemRoot('2').tabIndex).to.equal(-1);
- });
- });
-
- describe('focusItem api method', () => {
- it('should focus the item', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- });
+ it('should set tabIndex={0} on the first item if the selected item is not visible', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }] }],
+ selectedItems: '2.1',
+ });
- act(() => {
- response.apiRef.current.focusItem({} as any, '2');
+ expect(response.getItemRoot('1').tabIndex).to.equal(0);
+ expect(response.getItemRoot('2').tabIndex).to.equal(-1);
});
- expect(response.getFocusedItemId()).to.equal('2');
- });
-
- it('should not focus item if parent is collapsed', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }] }],
- });
+ it('should set tabIndex={0} on the first item if the no selected item is visible', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }, { id: '2.2' }] }],
+ selectedItems: ['2.1', '2.2'],
+ multiSelect: true,
+ });
- act(() => {
- response.apiRef.current.focusItem({} as any, '2.1');
+ expect(response.getItemRoot('1').tabIndex).to.equal(0);
+ expect(response.getItemRoot('2').tabIndex).to.equal(-1);
});
-
- expect(response.getFocusedItemId()).to.equal(null);
});
- });
- describe('onItemFocus prop', () => {
- it('should be called when an item is focused', () => {
- const onItemFocus = spy();
+ describe('focusItem api method', () => {
+ it('should focus the item', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
- const response = render({
- items: [{ id: '1' }],
- onItemFocus,
- });
+ act(() => {
+ response.apiRef.current.focusItem({} as any, '2');
+ });
- act(() => {
- response.getItemRoot('1').focus();
+ expect(response.getFocusedItemId()).to.equal('2');
});
- expect(onItemFocus.callCount).to.equal(1);
- expect(onItemFocus.lastCall.lastArg).to.equal('1');
- });
- });
-
- describe('disabledItemsFocusable prop', () => {
- describe('disabledItemFocusable={false}', () => {
- it('should prevent focus by mouse', () => {
+ it('should not focus item if parent is collapsed', () => {
const response = render({
- items: [{ id: '1', disabled: true }],
- disabledItemsFocusable: false,
+ items: [{ id: '1' }, { id: '2', children: [{ id: '2.1' }] }],
+ });
+
+ act(() => {
+ response.apiRef.current.focusItem({} as any, '2.1');
});
- fireEvent.click(response.getItemContent('1'));
expect(response.getFocusedItemId()).to.equal(null);
});
+ });
+
+ describe('onItemFocus prop', () => {
+ it('should be called when an item is focused', () => {
+ const onItemFocus = spy();
- it('should tab tabIndex={-1} on the disabled item and tabIndex={0} on the first non-disabled item', () => {
const response = render({
- items: [{ id: '1', disabled: true }, { id: '2' }, { id: '3' }],
- disabledItemsFocusable: false,
+ items: [{ id: '1' }],
+ onItemFocus,
});
- expect(response.getItemRoot('1').tabIndex).to.equal(-1);
- expect(response.getItemRoot('2').tabIndex).to.equal(0);
- expect(response.getItemRoot('3').tabIndex).to.equal(-1);
+ act(() => {
+ response.getItemRoot('1').focus();
+ });
+
+ expect(onItemFocus.callCount).to.equal(1);
+ expect(onItemFocus.lastCall.lastArg).to.equal('1');
});
});
- describe('disabledItemFocusable={true}', () => {
- it('should prevent focus by mouse', () => {
- const response = render({
- items: [{ id: '1', disabled: true }],
- disabledItemsFocusable: true,
+ describe('disabledItemsFocusable prop', () => {
+ describe('disabledItemFocusable={false}', () => {
+ it('should prevent focus by mouse', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }],
+ disabledItemsFocusable: false,
+ });
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.getFocusedItemId()).to.equal(null);
});
- fireEvent.click(response.getItemContent('1'));
- expect(response.getFocusedItemId()).to.equal(null);
+ it('should tab tabIndex={-1} on the disabled item and tabIndex={0} on the first non-disabled item', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }, { id: '2' }, { id: '3' }],
+ disabledItemsFocusable: false,
+ });
+
+ expect(response.getItemRoot('1').tabIndex).to.equal(-1);
+ expect(response.getItemRoot('2').tabIndex).to.equal(0);
+ expect(response.getItemRoot('3').tabIndex).to.equal(-1);
+ });
});
- it('should tab tabIndex={0} on the disabled item and tabIndex={-1} on the other items', () => {
- const response = render({
- items: [{ id: '1', disabled: true }, { id: '2' }, { id: '3' }],
- disabledItemsFocusable: true,
+ describe('disabledItemFocusable={true}', () => {
+ it('should prevent focus by mouse', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }],
+ disabledItemsFocusable: true,
+ });
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.getFocusedItemId()).to.equal(null);
});
- expect(response.getItemRoot('1').tabIndex).to.equal(0);
- expect(response.getItemRoot('2').tabIndex).to.equal(-1);
- expect(response.getItemRoot('3').tabIndex).to.equal(-1);
+ it('should tab tabIndex={0} on the disabled item and tabIndex={-1} on the other items', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }, { id: '2' }, { id: '3' }],
+ disabledItemsFocusable: true,
+ });
+
+ expect(response.getItemRoot('1').tabIndex).to.equal(0);
+ expect(response.getItemRoot('2').tabIndex).to.equal(-1);
+ expect(response.getItemRoot('3').tabIndex).to.equal(-1);
+ });
});
});
- });
-});
+
+ it('should not error when component state changes', () => {
+ const items = [{ id: '1', children: [{ id: '1.1' }] }];
+ const getItemLabel = (item) => item.id;
+
+ function MyComponent() {
+ const [, setState] = React.useState(1);
+
+ if (treeViewComponentName === 'SimpleTreeView') {
+ return (
+ {
+ setState(Math.random);
+ }}
+ >
+
+
+
+
+ );
+ }
+
+ return (
+ {
+ setState(Math.random);
+ }}
+ slotProps={{
+ item: (ownerState) => ({ 'data-testid': ownerState.itemId }) as any,
+ }}
+ getItemLabel={getItemLabel}
+ />
+ );
+ }
+
+ const response = renderFromJSX();
+
+ fireEvent.focus(response.getItemRoot('1'));
+ expect(response.getFocusedItemId()).to.equal('1');
+
+ fireEvent.keyDown(response.getItemRoot('1'), { key: 'ArrowDown' });
+ expect(response.getFocusedItemId()).to.equal('1.1');
+
+ fireEvent.keyDown(response.getItemRoot('1.1'), { key: 'ArrowUp' });
+ expect(response.getFocusedItemId()).to.equal('1');
+
+ fireEvent.keyDown(response.getItemRoot('1'), { key: 'ArrowDown' });
+ expect(response.getFocusedItemId()).to.equal('1.1');
+ });
+ },
+);
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx
index 96493e3e15666..790fc886213bd 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.test.tsx
@@ -1,5 +1,6 @@
import * as React from 'react';
import { expect } from 'chai';
+import { spy } from 'sinon';
import { act, fireEvent } from '@mui/internal-test-utils';
import { describeTreeView } from 'test/utils/tree-view/describeTreeView';
import {
@@ -1250,4 +1251,29 @@ describeTreeView<
expect(response.getFocusedItemId()).to.equal('1');
});
});
+
+ describe('onKeyDown prop', () => {
+ it('should call onKeyDown on the Tree View and the Tree Item when a key is pressed', () => {
+ const handleTreeViewKeyDown = spy();
+ const handleTreeItemKeyDown = spy();
+
+ const response = render({
+ items: [{ id: '1' }],
+ onKeyDown: handleTreeViewKeyDown,
+ slotProps: { item: { onKeyDown: handleTreeItemKeyDown } },
+ } as any);
+
+ const itemRoot = response.getItemRoot('1');
+ act(() => {
+ itemRoot.focus();
+ });
+
+ fireEvent.keyDown(itemRoot, { key: 'Enter' });
+ fireEvent.keyDown(itemRoot, { key: 'A' });
+ fireEvent.keyDown(itemRoot, { key: ']' });
+
+ expect(handleTreeViewKeyDown.callCount).to.equal(3);
+ expect(handleTreeItemKeyDown.callCount).to.equal(3);
+ });
+ });
});
diff --git a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.test.tsx b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.test.tsx
index 6af3f4b2ca00e..6bd4595d4c060 100644
--- a/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.test.tsx
+++ b/packages/x-tree-view/src/internals/plugins/useTreeViewSelection/useTreeViewSelection.test.tsx
@@ -2,765 +2,797 @@ import { expect } from 'chai';
import { spy } from 'sinon';
import { fireEvent } from '@mui/internal-test-utils';
import { describeTreeView } from 'test/utils/tree-view/describeTreeView';
-import { UseTreeViewSelectionSignature } from '@mui/x-tree-view/internals';
+import {
+ UseTreeViewExpansionSignature,
+ UseTreeViewSelectionSignature,
+} from '@mui/x-tree-view/internals';
/**
* All tests related to keyboard navigation (e.g.: selection using "Space")
* are located in the `useTreeViewKeyboardNavigation.test.tsx` file.
*/
-describeTreeView<[UseTreeViewSelectionSignature]>('useTreeViewSelection plugin', ({ render }) => {
- describe('model props (selectedItems, defaultSelectedItems, onSelectedItemsChange)', () => {
- it('should not select items when no default state and no control state are defined', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- });
-
- expect(response.isItemSelected('1')).to.equal(false);
- });
+describeTreeView<[UseTreeViewSelectionSignature, UseTreeViewExpansionSignature]>(
+ 'useTreeViewSelection plugin',
+ ({ render }) => {
+ describe('model props (selectedItems, defaultSelectedItems, onSelectedItemsChange)', () => {
+ it('should not select items when no default state and no control state are defined', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
- it('should use the default state when defined', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(true);
- });
+ it('should use the default state when defined', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1'],
+ });
- it('should use the controlled state when defined', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- selectedItems: ['1'],
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.isItemSelected('1')).to.equal(true);
- });
+ it('should use the controlled state when defined', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ selectedItems: ['1'],
+ });
- it('should use the controlled state instead of the default state when both are defined', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- selectedItems: ['1'],
- defaultSelectedItems: ['2'],
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.isItemSelected('1')).to.equal(true);
- });
+ it('should use the controlled state instead of the default state when both are defined', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ selectedItems: ['1'],
+ defaultSelectedItems: ['2'],
+ });
- it('should react to controlled state update', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- selectedItems: [],
+ expect(response.isItemSelected('1')).to.equal(true);
});
- response.setProps({ selectedItems: ['1'] });
- expect(response.isItemSelected('1')).to.equal(true);
- });
-
- it('should call the onSelectedItemsChange callback when the model is updated (single selection and add selected item)', () => {
- const onSelectedItemsChange = spy();
+ it('should react to controlled state update', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ selectedItems: [],
+ });
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- onSelectedItemsChange,
+ response.setProps({ selectedItems: ['1'] });
+ expect(response.isItemSelected('1')).to.equal(true);
});
- fireEvent.click(response.getItemContent('1'));
+ it('should call the onSelectedItemsChange callback when the model is updated (single selection and add selected item)', () => {
+ const onSelectedItemsChange = spy();
- expect(onSelectedItemsChange.callCount).to.equal(1);
- expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal('1');
- });
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ onSelectedItemsChange,
+ });
- // TODO: Re-enable this test if we have a way to un-select an item in single selection.
- // eslint-disable-next-line mocha/no-skipped-tests
- it.skip('should call onSelectedItemsChange callback when the model is updated (single selection and remove selected item', () => {
- const onSelectedItemsChange = spy();
+ fireEvent.click(response.getItemContent('1'));
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- onSelectedItemsChange,
- defaultSelectedItems: ['1'],
+ expect(onSelectedItemsChange.callCount).to.equal(1);
+ expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal('1');
});
- fireEvent.click(response.getItemContent('1'));
+ // TODO: Re-enable this test if we have a way to un-select an item in single selection.
+ // eslint-disable-next-line mocha/no-skipped-tests
+ it.skip('should call onSelectedItemsChange callback when the model is updated (single selection and remove selected item', () => {
+ const onSelectedItemsChange = spy();
- expect(onSelectedItemsChange.callCount).to.equal(1);
- expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal([]);
- });
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ onSelectedItemsChange,
+ defaultSelectedItems: ['1'],
+ });
- it('should call the onSelectedItemsChange callback when the model is updated (multi selection and add selected item to empty list)', () => {
- const onSelectedItemsChange = spy();
+ fireEvent.click(response.getItemContent('1'));
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- onSelectedItemsChange,
+ expect(onSelectedItemsChange.callCount).to.equal(1);
+ expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal([]);
});
- fireEvent.click(response.getItemContent('1'));
+ it('should call the onSelectedItemsChange callback when the model is updated (multi selection and add selected item to empty list)', () => {
+ const onSelectedItemsChange = spy();
- expect(onSelectedItemsChange.callCount).to.equal(1);
- expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['1']);
- });
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ onSelectedItemsChange,
+ });
- it('should call the onSelectedItemsChange callback when the model is updated (multi selection and add selected item to non-empty list)', () => {
- const onSelectedItemsChange = spy();
+ fireEvent.click(response.getItemContent('1'));
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- onSelectedItemsChange,
- defaultSelectedItems: ['1'],
+ expect(onSelectedItemsChange.callCount).to.equal(1);
+ expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['1']);
});
- fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
+ it('should call the onSelectedItemsChange callback when the model is updated (multi selection and add selected item to non-empty list)', () => {
+ const onSelectedItemsChange = spy();
- expect(onSelectedItemsChange.callCount).to.equal(1);
- expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['2', '1']);
- });
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ onSelectedItemsChange,
+ defaultSelectedItems: ['1'],
+ });
- it('should call the onSelectedItemsChange callback when the model is updated (multi selection and remove selected item)', () => {
- const onSelectedItemsChange = spy();
+ fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- onSelectedItemsChange,
- defaultSelectedItems: ['1'],
+ expect(onSelectedItemsChange.callCount).to.equal(1);
+ expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal(['2', '1']);
});
- fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
+ it('should call the onSelectedItemsChange callback when the model is updated (multi selection and remove selected item)', () => {
+ const onSelectedItemsChange = spy();
- expect(onSelectedItemsChange.callCount).to.equal(1);
- expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal([]);
- });
-
- it('should warn when switching from controlled to uncontrolled', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- selectedItems: [],
- });
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ onSelectedItemsChange,
+ defaultSelectedItems: ['1'],
+ });
- expect(() => {
- response.setProps({ selectedItems: undefined });
- }).toErrorDev(
- 'MUI X: A component is changing the controlled selectedItems state of TreeView to be uncontrolled.',
- );
- });
+ fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
- it('should warn and not react to update when updating the default state', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
+ expect(onSelectedItemsChange.callCount).to.equal(1);
+ expect(onSelectedItemsChange.lastCall.args[1]).to.deep.equal([]);
});
- expect(() => {
- response.setProps({ defaultSelectedItems: ['2'] });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- }).toErrorDev(
- 'MUI X: A component is changing the default selectedItems state of an uncontrolled TreeView after being initialized. To suppress this warning opt to use a controlled TreeView.',
- );
- });
- });
-
- describe('item click interaction', () => {
- describe('single selection', () => {
- it('should select un-selected item when clicking on an item content', () => {
+ it('should warn when switching from controlled to uncontrolled', () => {
const response = render({
items: [{ id: '1' }, { id: '2' }],
+ selectedItems: [],
});
- expect(response.isItemSelected('1')).to.equal(false);
-
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(true);
+ expect(() => {
+ response.setProps({ selectedItems: undefined });
+ }).toErrorDev(
+ 'MUI X: A component is changing the controlled selectedItems state of TreeView to be uncontrolled.',
+ );
});
- it('should not un-select selected item when clicking on an item content', () => {
+ it('should warn and not react to update when updating the default state', () => {
const response = render({
items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: '1',
+ defaultSelectedItems: ['1'],
});
- expect(response.isItemSelected('1')).to.equal(true);
-
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(true);
+ expect(() => {
+ response.setProps({ defaultSelectedItems: ['2'] });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ }).toErrorDev(
+ 'MUI X: A component is changing the default selectedItems state of an uncontrolled TreeView after being initialized. To suppress this warning opt to use a controlled TreeView.',
+ );
});
+ });
- it('should not select an item when click and disableSelection', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- disableSelection: true,
+ describe('item click interaction', () => {
+ describe('single selection', () => {
+ it('should select un-selected item when clicking on an item content', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
+
+ expect(response.isItemSelected('1')).to.equal(false);
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should not un-select selected item when clicking on an item content', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: '1',
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(true);
- it('should not select an item when clicking on a disabled item content', () => {
- const response = render({
- items: [{ id: '1', disabled: true }, { id: '2' }],
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.isItemSelected('1')).to.equal(false);
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
- });
+ it('should not select an item when click and disableSelection', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ disableSelection: true,
+ });
- describe('multi selection', () => {
- it('should select un-selected item and remove other selected items when clicking on an item content', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['2'],
+ expect(response.isItemSelected('1')).to.equal(false);
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should not select an item when clicking on a disabled item content', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }, { id: '2' }],
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ expect(response.isItemSelected('1')).to.equal(false);
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
+ });
});
- it('should not un-select selected item when clicking on an item content', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
+ describe('multi selection', () => {
+ it('should select un-selected item and remove other selected items when clicking on an item content', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['2'],
+ });
+
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
});
- expect(response.isItemSelected('1')).to.equal(true);
+ it('should not un-select selected item when clicking on an item content', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1'],
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(true);
- });
+ expect(response.isItemSelected('1')).to.equal(true);
- it('should un-select selected item when clicking on its content while holding Ctrl', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1', '2'],
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
- fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- });
+ it('should un-select selected item when clicking on its content while holding Ctrl', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1', '2'],
+ });
- it('should un-select selected item when clicking on its content while holding Meta', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1', '2'],
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
+ fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
});
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
+ it('should un-select selected item when clicking on its content while holding Meta', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1', '2'],
+ });
- fireEvent.click(response.getItemContent('1'), { metaKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
- it('should not select an item when click and disableSelection', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- disableSelection: true,
+ fireEvent.click(response.getItemContent('1'), { metaKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should not select an item when click and disableSelection', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ disableSelection: true,
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(false);
- it('should not select an item when clicking on a disabled item content', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1', disabled: true }, { id: '2' }],
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ it('should not select an item when clicking on a disabled item content', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1', disabled: true }, { id: '2' }],
+ });
- it('should select un-selected item when clicking on its content while holding Ctrl', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }, { id: '3' }],
- defaultSelectedItems: ['1'],
+ expect(response.isItemSelected('1')).to.equal(false);
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ it('should select un-selected item when clicking on its content while holding Ctrl', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '3' }],
+ defaultSelectedItems: ['1'],
+ });
- fireEvent.click(response.getItemContent('3'), { ctrlKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
- });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- it('should expand the selection range when clicking on an item content below the last selected item while holding Shift', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ fireEvent.click(response.getItemContent('3'), { ctrlKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
});
- fireEvent.click(response.getItemContent('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should do nothing when clicking on an item content on a fresh tree whil holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemContent('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal([]);
+ });
- it('should expand the selection range when clicking on an item content above the last selected item while holding Shift', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ it('should expand the selection range when clicking on an item content below the last selected item while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
+
+ fireEvent.click(response.getItemContent('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
});
- fireEvent.click(response.getItemContent('3'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['3']);
+ it('should expand the selection range when clicking on an item content above the last selected item while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemContent('2'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemContent('3'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['3']);
- it('should expand the selection range when clicking on an item content while holding Shift after un-selecting another item', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ fireEvent.click(response.getItemContent('2'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
});
- fireEvent.click(response.getItemContent('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ it('should expand the selection range when clicking on an item content while holding Shift after un-selecting another item', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
- fireEvent.click(response.getItemContent('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemContent('2'), { ctrlKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- it('should not expand the selection range when clicking on a disabled item content then clicking on an item content while holding Shift', () => {
- const response = render({
- multiSelect: true,
- items: [
- { id: '1' },
- { id: '2', disabled: true },
- { id: '2.1' },
- { id: '3' },
- { id: '4' },
- ],
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2', '2.1', '3']);
});
- fireEvent.click(response.getItemContent('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal([]);
+ it('should not expand the selection range when clicking on a disabled item content then clicking on an item content while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ items: [
+ { id: '1' },
+ { id: '2', disabled: true },
+ { id: '2.1' },
+ { id: '3' },
+ { id: '4' },
+ ],
+ });
- fireEvent.click(response.getItemContent('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal([]);
- });
+ fireEvent.click(response.getItemContent('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal([]);
- it('should not expand the selection range when clicking on an item content then clicking a disabled item content while holding Shift', () => {
- const response = render({
- multiSelect: true,
- items: [
- { id: '1' },
- { id: '2' },
- { id: '2.1' },
- { id: '3', disabled: true },
- { id: '4' },
- ],
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal([]);
});
- fireEvent.click(response.getItemContent('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should not expand the selection range when clicking on an item content then clicking a disabled item content while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ items: [
+ { id: '1' },
+ { id: '2' },
+ { id: '2.1' },
+ { id: '3', disabled: true },
+ { id: '4' },
+ ],
+ });
- fireEvent.click(response.getItemContent('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- });
+ fireEvent.click(response.getItemContent('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- it('should not select disabled items that are part of the selected range', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2', disabled: true }, { id: '3' }],
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
});
- fireEvent.click(response.getItemContent('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ it('should not select disabled items that are part of the selected range', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2', disabled: true }, { id: '3' }],
+ });
- fireEvent.click(response.getItemContent('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
- });
- });
- });
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- describe('checkbox interaction', () => {
- describe('render checkbox when needed', () => {
- it('should not render a checkbox when checkboxSelection is not defined', () => {
- const response = render({
- items: [{ id: '1' }],
+ fireEvent.click(response.getItemContent('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
});
- expect(response.getItemCheckbox('1')).to.equal(null);
- });
+ it('should not crash when selecting multiple items in a deeply nested tree', () => {
+ const response = render({
+ multiSelect: true,
+ items: [
+ { id: '1', children: [{ id: '1.1', children: [{ id: '1.1.1' }] }] },
+ { id: '2' },
+ ],
+ defaultExpandedItems: ['1', '1.1'],
+ });
- it('should not render a checkbox when checkboxSelection is false', () => {
- const response = render({
- checkboxSelection: false,
- items: [{ id: '1' }],
- });
+ fireEvent.click(response.getItemContent('1.1.1'));
+ fireEvent.click(response.getItemContent('2'), { shiftKey: true });
- expect(response.getItemCheckbox('1')).to.equal(null);
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1.1.1', '2']);
+ });
});
+ });
- it('should render a checkbox when checkboxSelection is true', () => {
- const response = render({
- checkboxSelection: true,
- items: [{ id: '1' }],
+ describe('checkbox interaction', () => {
+ describe('render checkbox when needed', () => {
+ it('should not render a checkbox when checkboxSelection is not defined', () => {
+ const response = render({
+ items: [{ id: '1' }],
+ });
+
+ expect(response.getItemCheckbox('1')).to.equal(null);
});
- expect(response.getItemCheckbox('1')).not.to.equal(null);
- });
- });
+ it('should not render a checkbox when checkboxSelection is false', () => {
+ const response = render({
+ checkboxSelection: false,
+ items: [{ id: '1' }],
+ });
- describe('single selection', () => {
- it('should not change selection when clicking on an item content', () => {
- const response = render({
- checkboxSelection: true,
- items: [{ id: '1' }],
+ expect(response.getItemCheckbox('1')).to.equal(null);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should render a checkbox when checkboxSelection is true', () => {
+ const response = render({
+ checkboxSelection: true,
+ items: [{ id: '1' }],
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
+ expect(response.getItemCheckbox('1')).not.to.equal(null);
+ });
});
- it('should select un-selected item when clicking on an item checkbox', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- checkboxSelection: true,
+ describe('single selection', () => {
+ it('should not change selection when clicking on an item content', () => {
+ const response = render({
+ checkboxSelection: true,
+ items: [{ id: '1' }],
+ });
+
+ expect(response.isItemSelected('1')).to.equal(false);
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should select un-selected item when clicking on an item checkbox', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ checkboxSelection: true,
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(true);
- });
+ expect(response.isItemSelected('1')).to.equal(false);
- it('should un-select selected item when clicking on an item checkbox', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: '1',
- checkboxSelection: true,
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(true);
});
- expect(response.isItemSelected('1')).to.equal(true);
+ it('should un-select selected item when clicking on an item checkbox', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: '1',
+ checkboxSelection: true,
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(true);
- it('should not select an item when click and disableSelection', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- disableSelection: true,
- checkboxSelection: true,
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should not select an item when click and disableSelection', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ disableSelection: true,
+ checkboxSelection: true,
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(false);
- it('should not select an item when clicking on a disabled item checkbox', () => {
- const response = render({
- items: [{ id: '1', disabled: true }, { id: '2' }],
- checkboxSelection: true,
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
- });
+ it('should not select an item when clicking on a disabled item checkbox', () => {
+ const response = render({
+ items: [{ id: '1', disabled: true }, { id: '2' }],
+ checkboxSelection: true,
+ });
- describe('multi selection', () => {
- it('should not change selection when clicking on an item content', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }],
+ expect(response.isItemSelected('1')).to.equal(false);
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
+ });
- expect(response.isItemSelected('1')).to.equal(false);
+ describe('multi selection', () => {
+ it('should not change selection when clicking on an item content', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }],
+ });
- fireEvent.click(response.getItemContent('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(false);
- it('should select un-selected item and keep other items selected when clicking on an item checkbox', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['2'],
+ fireEvent.click(response.getItemContent('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should select un-selected item and keep other items selected when clicking on an item checkbox', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['2'],
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
- });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- it('should un-select selected item when clicking on an item checkbox', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
});
- expect(response.isItemSelected('1')).to.equal(true);
+ it('should un-select selected item when clicking on an item checkbox', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1'],
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(true);
- it('should not select an item when click and disableSelection', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }],
- disableSelection: true,
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
+ it('should not select an item when click and disableSelection', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }],
+ disableSelection: true,
+ });
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ expect(response.isItemSelected('1')).to.equal(false);
- it('should not select an item when clicking on a disabled item content', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1', disabled: true }, { id: '2' }],
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- expect(response.isItemSelected('1')).to.equal(false);
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.isItemSelected('1')).to.equal(false);
- });
+ it('should not select an item when clicking on a disabled item content', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1', disabled: true }, { id: '2' }],
+ });
- it('should expand the selection range when clicking on an item checkbox below the last selected item while holding Shift', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ expect(response.isItemSelected('1')).to.equal(false);
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.isItemSelected('1')).to.equal(false);
});
- fireEvent.click(response.getItemCheckboxInput('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should expand the selection range when clicking on an item checkbox below the last selected item while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemCheckboxInput('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- it('should expand the selection range when clicking on an item checkbox above the last selected item while holding Shift', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
});
- fireEvent.click(response.getItemCheckboxInput('3'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['3']);
+ it('should expand the selection range when clicking on an item checkbox above the last selected item while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemCheckboxInput('2'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemCheckboxInput('3'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['3']);
- it('should expand the selection range when clicking on an item checkbox while holding Shift after un-selecting another item', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ fireEvent.click(response.getItemCheckboxInput('2'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2', '2.1', '3']);
});
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ it('should expand the selection range when clicking on an item checkbox while holding Shift after un-selecting another item', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2' }, { id: '2.1' }, { id: '3' }, { id: '4' }],
+ });
- fireEvent.click(response.getItemCheckboxInput('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- fireEvent.click(response.getItemCheckboxInput('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ fireEvent.click(response.getItemCheckboxInput('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2']);
- fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2', '2.1', '3']);
- });
+ fireEvent.click(response.getItemCheckboxInput('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- it('should not expand the selection range when clicking on a disabled item checkbox then clicking on an item checkbox while holding Shift', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [
- { id: '1' },
- { id: '2', disabled: true },
- { id: '2.1' },
- { id: '3' },
- { id: '4' },
- ],
+ fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '2', '2.1', '3']);
});
- fireEvent.click(response.getItemCheckboxInput('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal([]);
+ it('should not expand the selection range when clicking on a disabled item checkbox then clicking on an item checkbox while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [
+ { id: '1' },
+ { id: '2', disabled: true },
+ { id: '2.1' },
+ { id: '3' },
+ { id: '4' },
+ ],
+ });
- fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal([]);
- });
+ fireEvent.click(response.getItemCheckboxInput('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal([]);
- it('should not expand the selection range when clicking on an item checkbox then clicking a disabled item checkbox while holding Shift', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [
- { id: '1' },
- { id: '2' },
- { id: '2.1' },
- { id: '3', disabled: true },
- { id: '4' },
- ],
+ fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal([]);
});
- fireEvent.click(response.getItemCheckboxInput('2'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
+ it('should not expand the selection range when clicking on an item checkbox then clicking a disabled item checkbox while holding Shift', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [
+ { id: '1' },
+ { id: '2' },
+ { id: '2.1' },
+ { id: '3', disabled: true },
+ { id: '4' },
+ ],
+ });
- fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- });
+ fireEvent.click(response.getItemCheckboxInput('2'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
- it('should not select disabled items that are part of the selected range', () => {
- const response = render({
- multiSelect: true,
- checkboxSelection: true,
- items: [{ id: '1' }, { id: '2', disabled: true }, { id: '3' }],
+ fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['2']);
});
- fireEvent.click(response.getItemCheckboxInput('1'));
- expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
+ it('should not select disabled items that are part of the selected range', () => {
+ const response = render({
+ multiSelect: true,
+ checkboxSelection: true,
+ items: [{ id: '1' }, { id: '2', disabled: true }, { id: '3' }],
+ });
- fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
- expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
- });
- });
- });
+ fireEvent.click(response.getItemCheckboxInput('1'));
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1']);
- describe('aria-multiselectable tree attribute', () => {
- it('should have the attribute `aria-multiselectable=false if using single select`', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
+ fireEvent.click(response.getItemCheckboxInput('3'), { shiftKey: true });
+ expect(response.getSelectedTreeItems()).to.deep.equal(['1', '3']);
+ });
});
-
- expect(response.getRoot()).to.have.attribute('aria-multiselectable', 'false');
});
- it('should have the attribute `aria-multiselectable=true if using multi select`', () => {
- const response = render({ items: [{ id: '1' }, { id: '2' }], multiSelect: true });
-
- expect(response.getRoot()).to.have.attribute('aria-multiselectable', 'true');
- });
- });
-
- // The `aria-selected` attribute is used by the `response.isItemSelected` method.
- // This `describe` only tests basics scenarios, more complex scenarios are tested in this file's other `describe`.
- describe('aria-selected item attribute', () => {
- describe('single selection', () => {
- it('should not have the attribute `aria-selected=false` if not selected', () => {
+ describe('aria-multiselectable tree attribute', () => {
+ it('should have the attribute `aria-multiselectable=false if using single select`', () => {
const response = render({
items: [{ id: '1' }, { id: '2' }],
});
- expect(response.getItemRoot('1')).not.to.have.attribute('aria-selected');
+ expect(response.getRoot()).to.have.attribute('aria-multiselectable', 'false');
});
- it('should have the attribute `aria-selected=true` if selected', () => {
- const response = render({
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: '1',
- });
+ it('should have the attribute `aria-multiselectable=true if using multi select`', () => {
+ const response = render({ items: [{ id: '1' }, { id: '2' }], multiSelect: true });
- expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'true');
+ expect(response.getRoot()).to.have.attribute('aria-multiselectable', 'true');
});
});
- describe('multi selection', () => {
- it('should have the attribute `aria-selected=false` if not selected', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
+ // The `aria-selected` attribute is used by the `response.isItemSelected` method.
+ // This `describe` only tests basics scenarios, more complex scenarios are tested in this file's other `describe`.
+ describe('aria-selected item attribute', () => {
+ describe('single selection', () => {
+ it('should not have the attribute `aria-selected=false` if not selected', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ });
+
+ expect(response.getItemRoot('1')).not.to.have.attribute('aria-selected');
});
- expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
+ it('should have the attribute `aria-selected=true` if selected', () => {
+ const response = render({
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: '1',
+ });
+
+ expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'true');
+ });
});
- it('should have the attribute `aria-selected=true` if selected', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
+ describe('multi selection', () => {
+ it('should have the attribute `aria-selected=false` if not selected', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ });
+
+ expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
});
- expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'true');
- });
+ it('should have the attribute `aria-selected=true` if selected', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1'],
+ });
- it('should have the attribute `aria-selected=false` if disabledSelection is true', () => {
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- disableSelection: true,
+ expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'true');
});
- expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
+ it('should have the attribute `aria-selected=false` if disabledSelection is true', () => {
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ disableSelection: true,
+ });
+
+ expect(response.getItemRoot('1')).to.have.attribute('aria-selected', 'false');
+ });
});
});
- });
- describe('onItemSelectionToggle prop', () => {
- it('should call the onItemSelectionToggle callback when selecting an item', () => {
- const onItemSelectionToggle = spy();
+ describe('onItemSelectionToggle prop', () => {
+ it('should call the onItemSelectionToggle callback when selecting an item', () => {
+ const onItemSelectionToggle = spy();
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- onItemSelectionToggle,
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ onItemSelectionToggle,
+ });
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(onItemSelectionToggle.callCount).to.equal(1);
+ expect(onItemSelectionToggle.lastCall.args[1]).to.equal('1');
+ expect(onItemSelectionToggle.lastCall.args[2]).to.equal(true);
});
- fireEvent.click(response.getItemContent('1'));
- expect(onItemSelectionToggle.callCount).to.equal(1);
- expect(onItemSelectionToggle.lastCall.args[1]).to.equal('1');
- expect(onItemSelectionToggle.lastCall.args[2]).to.equal(true);
- });
+ it('should call the onItemSelectionToggle callback when un-selecting an item', () => {
+ const onItemSelectionToggle = spy();
- it('should call the onItemSelectionToggle callback when un-selecting an item', () => {
- const onItemSelectionToggle = spy();
+ const response = render({
+ multiSelect: true,
+ items: [{ id: '1' }, { id: '2' }],
+ defaultSelectedItems: ['1'],
+ onItemSelectionToggle,
+ });
- const response = render({
- multiSelect: true,
- items: [{ id: '1' }, { id: '2' }],
- defaultSelectedItems: ['1'],
- onItemSelectionToggle,
+ fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
+ expect(onItemSelectionToggle.callCount).to.equal(1);
+ expect(onItemSelectionToggle.lastCall.args[1]).to.equal('1');
+ expect(onItemSelectionToggle.lastCall.args[2]).to.equal(false);
});
-
- fireEvent.click(response.getItemContent('1'), { ctrlKey: true });
- expect(onItemSelectionToggle.callCount).to.equal(1);
- expect(onItemSelectionToggle.lastCall.args[1]).to.equal('1');
- expect(onItemSelectionToggle.lastCall.args[2]).to.equal(false);
});
- });
-});
+ },
+);
diff --git a/packages/x-tree-view/src/internals/useTreeView/useTreeView.test.tsx b/packages/x-tree-view/src/internals/useTreeView/useTreeView.test.tsx
new file mode 100644
index 0000000000000..2fba6b0a8ab97
--- /dev/null
+++ b/packages/x-tree-view/src/internals/useTreeView/useTreeView.test.tsx
@@ -0,0 +1,64 @@
+import * as React from 'react';
+import { expect } from 'chai';
+import { fireEvent, act } from '@mui/internal-test-utils';
+import {
+ describeTreeView,
+ DescribeTreeViewRendererUtils,
+} from 'test/utils/tree-view/describeTreeView';
+
+describeTreeView<[]>(
+ 'useTreeView hook',
+ ({ render, renderFromJSX, treeViewComponentName, TreeViewComponent, TreeItemComponent }) => {
+ it('should have the role="tree" on the root slot', () => {
+ const response = render({ items: [{ id: '1' }] });
+
+ expect(response.getRoot()).to.have.attribute('role', 'tree');
+ });
+
+ it('should work inside a Portal', () => {
+ let response: DescribeTreeViewRendererUtils;
+ if (treeViewComponentName === 'SimpleTreeView') {
+ response = renderFromJSX(
+
+
+
+
+
+
+
+
+ ,
+ );
+ } else {
+ response = renderFromJSX(
+
+
+ ({ 'data-testid': ownerState.itemId }) as any,
+ }}
+ getItemLabel={(item) => item.id}
+ />
+ ,
+ );
+ }
+
+ act(() => {
+ response.getItemRoot('1').focus();
+ });
+
+ fireEvent.keyDown(response.getItemRoot('1'), { key: 'ArrowDown' });
+ expect(response.getFocusedItemId()).to.equal('2');
+
+ fireEvent.keyDown(response.getItemRoot('2'), { key: 'ArrowDown' });
+ expect(response.getFocusedItemId()).to.equal('3');
+
+ fireEvent.keyDown(response.getItemRoot('3'), { key: 'ArrowDown' });
+ expect(response.getFocusedItemId()).to.equal('4');
+ });
+ },
+);
diff --git a/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx
new file mode 100644
index 0000000000000..946815953ccb7
--- /dev/null
+++ b/packages/x-tree-view/src/useTreeItem2/useTreeItem2.test.tsx
@@ -0,0 +1,177 @@
+import * as React from 'react';
+import { expect } from 'chai';
+import { spy } from 'sinon';
+import { act, createEvent, fireEvent, screen } from '@mui/monorepo/packages/test-utils';
+import {
+ describeTreeView,
+ DescribeTreeViewRendererUtils,
+} from 'test/utils/tree-view/describeTreeView';
+import {
+ UseTreeViewExpansionSignature,
+ UseTreeViewIconsSignature,
+} from '@mui/x-tree-view/internals';
+import { treeItemClasses } from '@mui/x-tree-view/TreeItem';
+
+describeTreeView<[UseTreeViewExpansionSignature, UseTreeViewIconsSignature]>(
+ 'useTreeItem2 hook',
+ ({
+ render,
+ renderFromJSX,
+ treeItemComponentName,
+ TreeItemComponent,
+ treeViewComponentName,
+ TreeViewComponent,
+ }) => {
+ describe('role prop', () => {
+ it('should have the role="treeitem" on the root slot', () => {
+ const response = render({ items: [{ id: '1' }] });
+
+ expect(response.getItemRoot('1')).to.have.attribute('role', 'treeitem');
+ });
+
+ it('should have the role "group" on the groupTransition slot if the item is expandable', () => {
+ const response = render({
+ items: [{ id: '1', children: [{ id: '1.1' }] }],
+ defaultExpandedItems: ['1'],
+ });
+
+ expect(
+ response.getItemRoot('1').querySelector(`.${treeItemClasses.groupTransition}`),
+ ).to.have.attribute('role', 'group');
+ });
+ });
+
+ describe('onClick prop', () => {
+ it('should call onClick when clicked, but not when children are clicked for TreeItem', () => {
+ const onClick = spy();
+
+ const response = render({
+ items: [{ id: '1', children: [{ id: '1.1' }] }],
+ defaultExpandedItems: ['1'],
+ slotProps: {
+ item: {
+ onClick,
+ },
+ },
+ });
+
+ fireEvent.click(response.getItemContent('1.1'));
+ expect(onClick.callCount).to.equal(treeItemComponentName === 'TreeItem' ? 1 : 2);
+ expect(onClick.lastCall.firstArg.target.parentElement.dataset.testid).to.equal('1.1');
+ });
+
+ it('should call onClick even when the element is disabled', () => {
+ const onClick = spy();
+
+ const response = render({
+ items: [{ id: '1', disabled: true }],
+ slotProps: {
+ item: {
+ onClick,
+ },
+ },
+ });
+
+ fireEvent.click(response.getItemContent('1'));
+ expect(onClick.callCount).to.equal(1);
+ });
+ });
+
+ it('should be able to type in a child input', () => {
+ const response = render({
+ items: [{ id: '1', children: [{ id: '1.1' }] }],
+ defaultExpandedItems: ['1'],
+ slotProps:
+ treeItemComponentName === 'TreeItem2'
+ ? {
+ item: {
+ slots: {
+ label: () => ,
+ },
+ },
+ }
+ : {
+ item: {
+ label: ,
+ },
+ },
+ });
+
+ const input = response.getItemRoot('1.1').querySelector('.icon-input')!;
+ const keydownEvent = createEvent.keyDown(input, {
+ key: 'a',
+ });
+
+ const handlePreventDefault = spy();
+ keydownEvent.preventDefault = handlePreventDefault;
+ fireEvent(input, keydownEvent);
+ expect(handlePreventDefault.callCount).to.equal(0);
+ });
+
+ it('should not focus steal', () => {
+ let setActiveItemMounted;
+ // a TreeItem whose mounted state we can control with `setActiveItemMounted`
+ function ConditionallyMountedItem(props) {
+ const [mounted, setMounted] = React.useState(true);
+ if (props.itemId === '2') {
+ setActiveItemMounted = setMounted;
+ }
+
+ if (!mounted) {
+ return null;
+ }
+ return ;
+ }
+
+ let response: DescribeTreeViewRendererUtils;
+ if (treeViewComponentName === 'SimpleTreeView') {
+ response = renderFromJSX(
+
+
+
+
+
+
+ ,
+ );
+ } else {
+ response = renderFromJSX(
+
+
+ ({ 'data-testid': ownerState.itemId }) as any,
+ }}
+ getItemLabel={(item) => item.id}
+ />
+ ,
+ );
+ }
+
+ act(() => {
+ response.getItemRoot('2').focus();
+ });
+
+ expect(response.getFocusedItemId()).to.equal('2');
+
+ act(() => {
+ screen.getByRole('button').focus();
+ });
+
+ expect(screen.getByRole('button')).toHaveFocus();
+
+ act(() => {
+ setActiveItemMounted(false);
+ });
+ act(() => {
+ setActiveItemMounted(true);
+ });
+
+ expect(screen.getByRole('button')).toHaveFocus();
+ });
+ },
+);
diff --git a/test/utils/tree-view/describeTreeView/index.ts b/test/utils/tree-view/describeTreeView/index.ts
index 4459a7a955838..d545fcba81745 100644
--- a/test/utils/tree-view/describeTreeView/index.ts
+++ b/test/utils/tree-view/describeTreeView/index.ts
@@ -1,2 +1,5 @@
export { describeTreeView } from './describeTreeView';
-export type { DescribeTreeViewRendererReturnValue } from './describeTreeView.types';
+export type {
+ DescribeTreeViewRendererReturnValue,
+ DescribeTreeViewRendererUtils,
+} from './describeTreeView.types';