diff --git a/CHANGELOG.md b/CHANGELOG.md index c52426ad960..610df1454db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Added `tableLayout` prop to `EuiTable`, `EuiBasicTable` and `EuiInMemoryTable` to provide the option of auto layout ([#2697](https://github.com/elastic/eui/pull/2697)) - Converted `EuiSuggest` to Typescript ([#2692](https://github.com/elastic/eui/pull/2692)) - Converted `EuiErrorBoundary` to Typescript ([#2690](https://github.com/elastic/eui/pull/2690)) +- Updated `EuiNavDrawer` to accept React fragments ([#2710](https://github.com/elastic/eui/pull/2710)) **Bug fixes** diff --git a/src/components/nav_drawer/__snapshots__/nav_drawer.test.js.snap b/src/components/nav_drawer/__snapshots__/nav_drawer.test.js.snap new file mode 100644 index 00000000000..62d0b4ff117 --- /dev/null +++ b/src/components/nav_drawer/__snapshots__/nav_drawer.test.js.snap @@ -0,0 +1,845 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EuiNavDrawer is rendered 1`] = ` + +`; + +exports[`EuiNavDrawer renders with falsy children 1`] = ` + +`; + +exports[`EuiNavDrawer renders with fragments 1`] = ` + +`; diff --git a/src/components/nav_drawer/nav_drawer.js b/src/components/nav_drawer/nav_drawer.js index c8da6de6dfd..c9ccf97474d 100644 --- a/src/components/nav_drawer/nav_drawer.js +++ b/src/components/nav_drawer/nav_drawer.js @@ -212,6 +212,26 @@ export class EuiNavDrawer extends Component { } }; + modifyChildren = children => { + // Loop through the EuiNavDrawer children (EuiListGroup, EuiHorizontalRules, etc) + // Filter out falsy items + const filteredChildren = React.Children.toArray(children); + return React.Children.map(filteredChildren, child => { + // Allow for Fragments by recursive modification + if (child.type === React.Fragment) { + return this.modifyChildren(child.props.children); + } else if (child.type === EuiNavDrawerGroup) { + // Check if child is an EuiNavDrawerGroup and if it does have a flyout, add the expand function + return React.cloneElement(child, { + flyoutMenuButtonClick: this.expandFlyout, + showToolTips: this.state.toolTipsEnabled && this.props.showToolTips, + }); + } else { + return child; + } + }); + }; + render() { const { children, @@ -312,19 +332,7 @@ export class EuiNavDrawer extends Component { // that have a flyoutMenu prop (sub links) let modifiedChildren = children; - // 1. Loop through the EuiNavDrawer children (EuiListGroup, EuiHorizontalRules, etc) - modifiedChildren = React.Children.map(this.props.children, child => { - // 2. Check if child is an EuiNavDrawerGroup and if it does have a flyout, add the expand function - if (child.type === EuiNavDrawerGroup) { - const item = React.cloneElement(child, { - flyoutMenuButtonClick: this.expandFlyout, - showToolTips: this.state.toolTipsEnabled && showToolTips, - }); - return item; - } else { - return child; - } - }); + modifiedChildren = this.modifyChildren(this.props.children); const menuClasses = classNames('euiNavDrawerMenu', { 'euiNavDrawerMenu-hasFooter': footerContent, diff --git a/src/components/nav_drawer/nav_drawer.test.js b/src/components/nav_drawer/nav_drawer.test.js new file mode 100644 index 00000000000..afa761a6511 --- /dev/null +++ b/src/components/nav_drawer/nav_drawer.test.js @@ -0,0 +1,154 @@ +import React from 'react'; +import { render } from 'enzyme'; + +import { EuiNavDrawer } from './nav_drawer'; +import { EuiNavDrawerGroup } from './nav_drawer_group'; + +const extraAction = { + color: 'subdued', + iconType: 'pin', + iconSize: 's', +}; + +const topLinks = [ + { + label: 'Recently viewed', + iconType: 'clock', + flyoutMenu: { + title: 'Recent items', + listItems: [ + { + label: 'My dashboard', + href: '#', + iconType: 'dashboardApp', + extraAction, + }, + { + label: 'Workpad with title that wraps', + href: '#', + iconType: 'canvasApp', + extraAction, + }, + { + label: 'My logs', + href: '#', + iconType: 'logsApp', + 'aria-label': 'This is an alternate aria-label', + extraAction, + }, + ], + }, + }, + { + label: 'Favorites', + iconType: 'starEmpty', + flyoutMenu: { + title: 'Favorite items', + listItems: [ + { + label: 'My workpad', + href: '#', + iconType: 'canvasApp', + extraAction: { + color: 'subdued', + iconType: 'starFilled', + iconSize: 's', + 'aria-label': 'Remove from favorites', + alwaysShow: true, + }, + }, + { + label: 'My logs', + href: '#', + iconType: 'logsApp', + extraAction: { + color: 'subdued', + iconType: 'starFilled', + iconSize: 's', + 'aria-label': 'Remove from favorites', + alwaysShow: true, + }, + }, + ], + }, + }, +]; + +const exploreLinks = [ + { + label: 'Canvas', + href: '#', + iconType: 'canvasApp', + isActive: true, + extraAction, + }, + { + label: 'Discover', + href: '#', + iconType: 'discoverApp', + extraAction, + }, + { + label: 'Visualize', + href: '#', + iconType: 'visualizeApp', + extraAction, + }, + { + label: 'Dashboard', + href: '#', + iconType: 'dashboardApp', + extraAction, + }, + { + label: 'Machine learning', + href: '#', + iconType: 'machineLearningApp', + extraAction, + }, + { + label: 'Custom Plugin (no icon)', + href: '#', + extraAction, + }, +]; + +describe('EuiNavDrawer', () => { + test('is rendered', () => { + const component = render( + + + + + ); + + expect(component).toMatchSnapshot(); + }); + + describe('renders', () => { + test('with fragments', () => { + const component = render( + + <> + + + + + ); + + expect(component).toMatchSnapshot(); + }); + + test('with falsy children', () => { + const component = render( + + {false && } + {true ? undefined : } + + + ); + + expect(component).toMatchSnapshot(); + }); + }); +});