diff --git a/docs/pages/x/api/tree-view/tree-item-2.json b/docs/pages/x/api/tree-view/tree-item-2.json index 00c9508458ca7..bac50963652a1 100644 --- a/docs/pages/x/api/tree-view/tree-item-2.json +++ b/docs/pages/x/api/tree-view/tree-item-2.json @@ -93,7 +93,10 @@ "isGlobal": true } ], + "spread": true, + "themeDefaultProps": true, "muiName": "MuiTreeItem2", + "forwardsRefTo": "HTMLLIElement", "filename": "/packages/x-tree-view/src/TreeItem2/TreeItem2.tsx", "inheritance": null, "demos": "", diff --git a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx index 49a9702b6f238..426275dd9f682 100644 --- a/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx +++ b/packages/x-tree-view/src/TreeItem/TreeItem.test.tsx @@ -2,47 +2,11 @@ import * as React from 'react'; import { expect } from 'chai'; import PropTypes from 'prop-types'; import { createRenderer } from '@mui/internal-test-utils'; -import { SimpleTreeViewPluginSignatures } from '@mui/x-tree-view/SimpleTreeView/SimpleTreeView.plugins'; import { TreeItem, treeItemClasses as classes } from '@mui/x-tree-view/TreeItem'; -import { TreeViewContextValue } from '@mui/x-tree-view/internals/TreeViewProvider'; import { TreeViewContext } from '@mui/x-tree-view/internals/TreeViewProvider/TreeViewContext'; import { describeConformance } from 'test/utils/describeConformance'; import { describeTreeView } from 'test/utils/tree-view/describeTreeView'; - -const TEST_TREE_VIEW_CONTEXT_VALUE: TreeViewContextValue = { - instance: { - isItemExpandable: () => false, - isItemExpanded: () => false, - isItemFocused: () => false, - isItemSelected: () => false, - isItemDisabled: (itemId: string | null): itemId is string => !!itemId, - getTreeItemIdAttribute: () => '', - mapFirstCharFromJSX: () => () => {}, - canItemBeTabbed: () => false, - } as any, - publicAPI: { - focusItem: () => {}, - getItem: () => ({}), - setItemExpansion: () => {}, - }, - runItemPlugins: () => ({ rootRef: null, contentRef: null }), - wrapItem: ({ children }) => children, - wrapRoot: ({ children }) => children, - disabledItemsFocusable: false, - indentationAtItemLevel: false, - icons: { - slots: {}, - slotProps: {}, - }, - selection: { - multiSelect: false, - checkboxSelection: false, - disableSelection: false, - }, - rootRef: { - current: null, - }, -}; +import { getFakeContextValue } from 'test/utils/tree-view/fakeContextValue'; describeTreeView<[]>('TreeItem component', ({ render, treeItemComponentName }) => { describe('ContentComponent / ContentProps props (TreeItem only)', () => { @@ -94,17 +58,13 @@ describe('', () => { inheritComponent: 'li', wrapMount: (mount) => (item: React.ReactNode) => { const wrapper = mount( - - {item} - , + {item}, ); return wrapper.childAt(0); }, render: (item) => { return render( - - {item} - , + {item}, ); }, muiName: 'MuiTreeItem', diff --git a/packages/x-tree-view/src/TreeItem2/TreeItem2.test.tsx b/packages/x-tree-view/src/TreeItem2/TreeItem2.test.tsx new file mode 100644 index 0000000000000..6476ec7eb8a94 --- /dev/null +++ b/packages/x-tree-view/src/TreeItem2/TreeItem2.test.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import { createRenderer } from '@mui/internal-test-utils'; +import { TreeItem2 } from '@mui/x-tree-view/TreeItem2'; +import { treeItemClasses as classes } from '@mui/x-tree-view/TreeItem'; +import { TreeViewContext } from '@mui/x-tree-view/internals/TreeViewProvider/TreeViewContext'; +import { describeConformance } from 'test/utils/describeConformance'; +import { getFakeContextValue } from 'test/utils/tree-view/fakeContextValue'; +import { describeSlotsConformance } from 'test/utils/describeSlotsConformance'; + +describe('', () => { + const { render } = createRenderer(); + + describeConformance(, () => ({ + classes, + inheritComponent: 'li', + wrapMount: (mount) => (item: React.ReactNode) => { + const wrapper = mount( + {item}, + ); + return wrapper.childAt(0); + }, + render: (item) => { + return render( + {item}, + ); + }, + muiName: 'MuiTreeItem2', + refInstanceof: window.HTMLLIElement, + skip: ['reactTestRenderer', 'componentProp', 'componentsProp', 'themeVariants'], + })); + + describeSlotsConformance({ + render, + getElement: ({ props, slotName }) => ( + + + + ), + slots: { + label: { className: classes.label }, + iconContainer: { className: classes.iconContainer }, + content: { className: classes.content }, + checkbox: { className: classes.checkbox }, + }, + }); +}); diff --git a/test/utils/describeSlotsConformance.tsx b/test/utils/describeSlotsConformance.tsx new file mode 100644 index 0000000000000..ffb2e2da4eea8 --- /dev/null +++ b/test/utils/describeSlotsConformance.tsx @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { expect } from 'chai'; +import createDescribe from '@mui/internal-test-utils/createDescribe'; +import { MuiRenderResult } from '@mui/internal-test-utils/createRenderer'; + +interface DescribeSlotsConformanceParams { + getElement: (params: { slotName: string; props: any }) => React.ReactElement; + render: (node: React.ReactElement) => MuiRenderResult; + slots: { [slotName: string]: { className: string } }; +} + +export function innerDescribeSlotsConformance(params: DescribeSlotsConformanceParams) { + const { getElement, render, slots } = params; + + Object.keys(slots).forEach((slotName) => { + describe(`Slot: ${slotName}`, () => { + it('should replace the default slot when defined', () => { + const slotConfig = slots[slotName]; + const response = render( + getElement({ + slotName, + props: { + slots: { + [slotName]: React.forwardRef((props, ref: React.Ref) => ( +
+ )), + }, + }, + }), + ); + + // Check if the default slot is being rendered + expect(response.container.querySelector(`.${slotConfig.className}`)).to.equal(null); + + // Check if the custom slot is being rendered + expect(response.getByTestId('custom-slot')).to.not.equal(null); + }); + + it('should pass props to the default slot', () => { + const slotConfig = slots[slotName]; + const response = render( + getElement({ + slotName, + props: { + slotProps: { + [slotName]: { 'data-testid': 'default-slot', className: 'default-slot' }, + }, + }, + }), + ); + + const slotElement = response.container.querySelector(`.${slotConfig.className}`); + + // Check if the default slot is being rendered + expect(slotElement).not.to.equal(null); + + // Check if the default slot receives the `data-testid` + expect(slotElement).to.have.attribute('data-testid', 'default-slot'); + + // Check if the custom class is being applied + expect(slotElement).to.have.class('default-slot'); + + // Make sure that the default class has not been removed + expect(slotElement).to.have.class(slotConfig.className); + }); + }); + }); +} + +/** + * Test the slots of the component. + */ +export const describeSlotsConformance = createDescribe( + 'Slots conformance', + innerDescribeSlotsConformance, +); diff --git a/test/utils/tree-view/fakeContextValue.ts b/test/utils/tree-view/fakeContextValue.ts new file mode 100644 index 0000000000000..5f4e44545102c --- /dev/null +++ b/test/utils/tree-view/fakeContextValue.ts @@ -0,0 +1,42 @@ +import { TreeViewContextValue } from '@mui/x-tree-view/internals/TreeViewProvider'; +import { SimpleTreeViewPluginSignatures } from '@mui/x-tree-view/SimpleTreeView/SimpleTreeView.plugins'; + +export const getFakeContextValue = ( + features: { checkboxSelection?: boolean } = {}, +): TreeViewContextValue => ({ + instance: { + isItemExpandable: () => false, + isItemExpanded: () => false, + isItemFocused: () => false, + isItemSelected: () => false, + isItemDisabled: (itemId: string | null): itemId is string => !!itemId, + getTreeItemIdAttribute: () => '', + mapFirstCharFromJSX: () => () => {}, + canItemBeTabbed: () => false, + } as any, + publicAPI: { + focusItem: () => {}, + getItem: () => ({}), + setItemExpansion: () => {}, + }, + runItemPlugins: () => ({ + rootRef: null, + contentRef: null, + }), + wrapItem: ({ children }) => children, + wrapRoot: ({ children }) => children, + disabledItemsFocusable: false, + indentationAtItemLevel: false, + icons: { + slots: {}, + slotProps: {}, + }, + selection: { + multiSelect: false, + checkboxSelection: features.checkboxSelection ?? false, + disableSelection: false, + }, + rootRef: { + current: null, + }, +});