diff --git a/.all-contributorsrc b/.all-contributorsrc
index 534f3a591b6c..45ab46897104 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -1277,6 +1277,15 @@
"avatar_url": "https://avatars.githubusercontent.com/u/56911544?v=4",
"profile": "https://github.com/Zoe-Gathercole",
"contributions": ["code"]
+ },
+ {
+ "login": "ashna000",
+ "name": "Ashna Thomas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12691034?s=96&v=4",
+ "profile": "https://github.com/ashna000",
+ "contributions": [
+ "code"
+ ]
}
],
"commitConvention": "none"
diff --git a/README.md b/README.md
index f1441bf2fc36..8e91d7e0a996 100644
--- a/README.md
+++ b/README.md
@@ -319,6 +319,7 @@ check out our [Contributing Guide](/.github/CONTRIBUTING.md) and our
Mariat 💻 |
Thamjith Thaha 💻 |
Zoë Gathercole 💻 |
+ Ashna Thomas 💻 |
diff --git a/packages/react/src/components/UIShell/__tests__/SideNav-test.js b/packages/react/src/components/UIShell/__tests__/SideNav-test.js
index 02d727ae319d..0a0d6d62cda4 100644
--- a/packages/react/src/components/UIShell/__tests__/SideNav-test.js
+++ b/packages/react/src/components/UIShell/__tests__/SideNav-test.js
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
-import { render, screen } from '@testing-library/react';
+import { render, screen, fireEvent } from '@testing-library/react';
import React from 'react';
import SideNav from '../SideNav';
@@ -15,8 +15,8 @@ Object.defineProperty(window, 'matchMedia', {
matches: false,
media: query,
onchange: null,
- addListener: jest.fn(), // deprecated
- removeListener: jest.fn(), // deprecated
+ addListener: jest.fn(),
+ removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
@@ -36,6 +36,11 @@ describe('SideNav', () => {
expect(container.childNodes.length).toBe(2);
});
+ it('should not render an overlay if `isFixedNav` is true', () => {
+ const { container } = render();
+ expect(container.childNodes.length).toBe(1);
+ });
+
it('should toggle the overlay-active class when `expanded` is true', () => {
const { container } = render();
expect(container.firstChild).toHaveClass('cds--side-nav__overlay-active');
@@ -64,4 +69,225 @@ describe('SideNav', () => {
render();
expect(ref).toHaveBeenCalledWith(screen.getByRole('navigation'));
});
+
+ it('should call onOverlayClick when overlay is clicked', () => {
+ const onOverlayClick = jest.fn();
+ const { container } = render(
+
+ );
+ const overlay = container.firstChild;
+ fireEvent.click(overlay);
+ expect(onOverlayClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('should not add focus or mouse listeners when disabled', () => {
+ const onToggle = jest.fn();
+ render(
+
+ );
+ const sideNav = screen.getByRole('navigation');
+ fireEvent.focus(sideNav);
+ fireEvent.mouseEnter(sideNav);
+ expect(onToggle).not.toHaveBeenCalled();
+ });
+
+ it('should handle keyboard events like Escape', () => {
+ const onToggle = jest.fn();
+ render();
+ const sideNav = screen.getByRole('navigation');
+ fireEvent.keyDown(sideNav, { key: 'Escape', keyCode: 27 });
+ expect(onToggle).toHaveBeenCalledWith(expect.anything(), false);
+ });
+
+ it('should correctly handle `isRail` when true', () => {
+ render();
+ const sideNav = screen.getByRole('navigation');
+ expect(sideNav).toHaveClass('cds--side-nav--rail');
+ });
+
+ it('should correctly handle `isRail` when false', () => {
+ render();
+ const sideNav = screen.getByRole('navigation');
+ expect(sideNav).not.toHaveClass('cds--side-nav--rail');
+ });
+
+ it('should toggle the expanded state when uncontrolled', () => {
+ const { container } = render();
+ const sideNav = screen.getByRole('navigation');
+ fireEvent.focus(sideNav);
+ expect(container.firstChild).toHaveClass('cds--side-nav__overlay');
+ });
+
+ it('should handle children without throwing error', () => {
+ const { container } = render(
+
+ No Errors Here!
+
+ );
+ expect(container).toBeInTheDocument();
+ });
+
+ it('should pass isSideNavExpanded to Carbon SideNav children', () => {
+ const TestChild = React.forwardRef(({ isSideNavExpanded }, ref) => (
+
+ {isSideNavExpanded ? 'Expanded' : 'Collapsed'}
+
+ ));
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
+ });
+
+ it('should not pass isSideNavExpanded to non-CarbsideNav children', () => {
+ const NonCarbonChild = () => ;
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('non-carbon-child')).toBeInTheDocument();
+ });
+
+ it('should pass isSideNavExpanded correctly based on controlled state', () => {
+ const TestChild = React.forwardRef(({ isSideNavExpanded }, ref) => (
+
+ {isSideNavExpanded ? 'Expanded' : 'Collapsed'}
+
+ ));
+ const { rerender } = render(
+
+
+
+ );
+ expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
+ rerender(
+
+
+
+ );
+ expect(screen.getByTestId('child')).toHaveTextContent('Collapsed');
+ });
+
+ it('should call handleToggle and onSideNavBlur when blurred', () => {
+ const onSideNavBlurMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ fireEvent.focus(sideNav);
+ fireEvent.blur(sideNav);
+ expect(onSideNavBlurMock).not.toHaveBeenCalled();
+ });
+
+ it('should not call onSideNavBlur if not expanded and isFixedNav is true', () => {
+ const onSideNavBlurMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ fireEvent.focus(sideNav);
+ fireEvent.blur(sideNav);
+ expect(onSideNavBlurMock).not.toHaveBeenCalled();
+ });
+
+ it('should call onSideNavBlur when blurred, is not fixed, and is expanded', () => {
+ const onSideNavBlurMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ fireEvent.focus(sideNav);
+ const unrelatedElement = document.createElement('div');
+ document.body.appendChild(unrelatedElement);
+ fireEvent.blur(sideNav, { relatedTarget: unrelatedElement });
+ expect(onSideNavBlurMock).toHaveBeenCalled();
+ document.body.removeChild(unrelatedElement);
+ });
+
+ it('should not call onSideNavBlur when isFixedNav is true', () => {
+ const onSideNavBlurMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ fireEvent.focus(sideNav);
+ const unrelatedElement = document.createElement('div');
+ document.body.appendChild(unrelatedElement);
+ fireEvent.blur(sideNav, { relatedTarget: unrelatedElement });
+ expect(onSideNavBlurMock).not.toHaveBeenCalled();
+ document.body.removeChild(unrelatedElement);
+ });
+
+ it('should set expanded state to false on mouse leave', () => {
+ const onToggleMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ expect(sideNav).toHaveClass('cds--side-nav__navigation');
+ fireEvent.mouseLeave(sideNav);
+ expect(sideNav).toHaveClass('cds--side-nav__navigation');
+ });
+
+ it('should not call handleToggle if isRail is false', () => {
+ const onToggleMock = jest.fn();
+ const TestChild = () => Child
;
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ fireEvent.mouseLeave(sideNav);
+ expect(onToggleMock).not.toHaveBeenCalled();
+ });
+
+ it('should expand the SideNav immediately on click', () => {
+ const TestChild = () => Child
;
+ const onToggleMock = jest.fn();
+ const { getByRole } = render(
+
+
+
+ );
+ const sideNav = getByRole('navigation');
+ expect(sideNav).not.toHaveClass('cds--side-nav--expanded');
+ fireEvent.click(sideNav);
+ expect(onToggleMock).toHaveBeenCalledWith(true, true);
+ expect(sideNav).toHaveClass('cds--side-nav--expanded');
+ });
});