Skip to content
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

fix(sidenav): sidenavmenu stays open when in rail #3626

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/react/src/components/UIShell/Link.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ const LinkPropTypes = {
* alternative tag names or custom components like `Link` from `react-router`.
*/
element: PropTypes.elementType,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

Link.displayName = 'Link';
Expand Down
7 changes: 6 additions & 1 deletion packages/react/src/components/UIShell/SideNav.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ const SideNav = React.forwardRef(function SideNav(props, ref) {
[`${prefix}--side-nav__overlay-active`]: expanded,
});

// for all children, add the expansion state as prop in case they need to show/hide themselves
const childrenWithExpandedState = React.Children.map(children, child => {
return React.cloneElement(child, { isSideNavExpanded: expanded });
});

return (
<>
{isFixedNav ? null : <div className={overlayClassName} />}
Expand All @@ -80,7 +85,7 @@ const SideNav = React.forwardRef(function SideNav(props, ref) {
onBlur={event => handleToggle(event, false)}
onMouseEnter={() => handleToggle(true)}
onMouseLeave={() => handleToggle(false)}>
{children}
{childrenWithExpandedState}
</nav>
</>
);
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/components/UIShell/SideNavFooter.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ SideNavFooter.propTypes = {
* with. Useful for controlling the expansion state of the side navigation.
*/
onToggle: PropTypes.func.isRequired,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

SideNavFooter.defaultProps = {
Expand Down
6 changes: 6 additions & 0 deletions packages/react/src/components/UIShell/SideNavHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ SideNavHeader.propTypes = {
*/
renderIcon: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
.isRequired,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

export default SideNavHeader;
17 changes: 15 additions & 2 deletions packages/react/src/components/UIShell/SideNavItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,16 @@ import React from 'react';

const { prefix } = settings;

const SideNavItems = ({ className: customClassName, children }) => {
const SideNavItems = ({
className: customClassName,
children,
isSideNavExpanded,
}) => {
const className = cx([`${prefix}--side-nav__items`], customClassName);
return <ul className={className}>{children}</ul>;
const childrenWithExpandedState = React.Children.map(children, child => {
return React.cloneElement(child, { isSideNavExpanded });
});
return <ul className={className}>{childrenWithExpandedState}</ul>;
};

SideNavItems.propTypes = {
Expand All @@ -28,6 +35,12 @@ SideNavItems.propTypes = {
* container
*/
children: PropTypes.node.isRequired,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

export default SideNavItems;
6 changes: 6 additions & 0 deletions packages/react/src/components/UIShell/SideNavLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ SideNavLink.propTypes = {
* Specify the text content for the link
*/
children: PropTypes.string.isRequired,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

SideNavLink.defaultProps = {
Expand Down
28 changes: 28 additions & 0 deletions packages/react/src/components/UIShell/SideNavMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,45 @@ export class SideNavMenu extends React.Component {
* be closed.
*/
defaultExpanded: PropTypes.bool,

/**
* Property to indicate if the side nav container is open (or not). Use to
* keep local state and styling in step with the SideNav expansion state.
*/
isSideNavExpanded: PropTypes.bool,
};

static defaultProps = {
defaultExpanded: false,
isActive: false,
};

static getDerivedStateFromProps = (props, state) => {
let derivedState = null;

if (props.isSideNavExpanded === false && state.isExpanded === true) {
derivedState = {
isExpanded: props.isSideNavExpanded,
wasPreviouslyExpanded: true,
};
} else if (
props.isSideNavExpanded === true &&
state.wasPreviouslyExpanded === true
) {
derivedState = {
isExpanded: props.isSideNavExpanded,
wasPreviouslyExpanded: false,
};
}

return derivedState;
};

constructor(props) {
super(props);
this.state = {
isExpanded: props.defaultExpanded || false,
wasPreviouslyExpanded: props.defaultExpanded || false,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,26 @@ describe('SideNavMenu', () => {
wrapper.find('button').simulate('click');
expect(wrapper.state('isExpanded')).toBe(true);
});

it('should reset expanded state if the isSideNavExpanded prop is false', () => {
const wrapper = mount(<SideNavMenu {...mockProps} />);
expect(wrapper.state('isExpanded')).toBe(false);
expect(wrapper.state('wasPreviouslyExpanded')).toBe(false);
wrapper.setState({ isExpanded: true });
expect(wrapper.state('isExpanded')).toBe(true);
expect(wrapper.state('wasPreviouslyExpanded')).toBe(false);
// set the prop to false. This should force isExpanded from true to false, and update wasPreviouslyExpanded to true
wrapper.setProps({ isSideNavExpanded: false });
expect(wrapper.state('isExpanded')).toBe(false);
expect(wrapper.state('wasPreviouslyExpanded')).toBe(true);
});

it('should reset expanded state if the SideNav was collapsed/expanded', () => {
const wrapper = mount(<SideNavMenu {...mockProps} />);
wrapper.setState({ isExpanded: false, wasPreviouslyExpanded: true });
// set the prop to false. This should force isExpanded from true to false, and update wasPreviouslyExpanded to true
wrapper.setProps({ isSideNavExpanded: true });
expect(wrapper.state('isExpanded')).toBe(true);
expect(wrapper.state('wasPreviouslyExpanded')).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ exports[`SideNav should render 1`] = `
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<h2>
<h2
isSideNavExpanded={false}
key=".0"
>
Navigation
</h2>
</nav>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ exports[`SideNavItems should render 1`] = `
<ul
className="bx--side-nav__items custom-classname"
>
<span>
<span
key=".0"
>
foo
</span>
</ul>
Expand Down