From c0a70e69fb7caeb4e1c1fb0179dcdcc28fcec804 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Mon, 15 Oct 2018 15:10:15 -0700 Subject: [PATCH 01/14] feat(chrome): add new CollapsibleSubNavItem component --- packages/chrome/package.json | 2 +- packages/chrome/src/views/Chrome.example.md | 30 +- .../src/views/subnav/CollapsibleSubNavItem.js | 75 +++++ .../subnav/CollapsibleSubNavItem.spec.js | 57 ++++ .../CollapsibleSubNavItem.spec.js.snap | 309 ++++++++++++++++++ 5 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 packages/chrome/src/views/subnav/CollapsibleSubNavItem.js create mode 100644 packages/chrome/src/views/subnav/CollapsibleSubNavItem.spec.js create mode 100644 packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap diff --git a/packages/chrome/package.json b/packages/chrome/package.json index 55f0579f724..aab5dd99042 100644 --- a/packages/chrome/package.json +++ b/packages/chrome/package.json @@ -31,7 +31,7 @@ "styled-components": "^3.2.6" }, "devDependencies": { - "@zendeskgarden/css-chrome": "3.3.2", + "@zendeskgarden/css-chrome": "3.4.0", "@zendeskgarden/css-variables": "5.1.1", "@zendeskgarden/react-theming": "^3.1.3" }, diff --git a/packages/chrome/src/views/Chrome.example.md b/packages/chrome/src/views/Chrome.example.md index 2f38e2053b2..537c605bac2 100644 --- a/packages/chrome/src/views/Chrome.example.md +++ b/packages/chrome/src/views/Chrome.example.md @@ -22,7 +22,8 @@ const PersonIcon = require('svg-react-loader?name=Settings!@zendeskgarden/svg-ic initialState = { currentNavItem: 'home', currentSubnavItem: 'item-1', - expanded: true + expanded: true, + showCollapsed: false };
@@ -99,6 +100,33 @@ initialState = { > Subnav 2 + setState({ showCollapsed: !state.showCollapsed })} + > + setState({ currentSubnavItem: 'collapsed-item-1' })} + href="#/" + > + Item 1 + + setState({ currentSubnavItem: 'collapsed-item-2' })} + href="#/" + > + Item 2 + + setState({ currentSubnavItem: 'collapsed-item-3' })} + href="#/" + > + Item 3 + + setState({ currentSubnavItem: 'item-3' })} diff --git a/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js b/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js new file mode 100644 index 00000000000..5a4a93c821e --- /dev/null +++ b/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js @@ -0,0 +1,75 @@ +/** + * Copyright Zendesk, Inc. + * + * Use of this source code is governed under the Apache License, Version 2.0 + * found at http://www.apache.org/licenses/LICENSE-2.0. + */ + +import React from 'react'; +import PropTypes from 'prop-types'; +import styled from 'styled-components'; +import classNames from 'classnames'; +import ChromeStyles from '@zendeskgarden/css-chrome'; +import { retrieveTheme } from '@zendeskgarden/react-theming'; + +const COMPONENT_ID = 'chrome.collapsable_sub_nav_item'; +const PANEL_COMPONENT_ID = 'chrome.collapsable_sub_nav_item_panel'; + +import SubNavItem from './SubNavItem'; + +/** Accepts all `
` props */ +const StyledSubNavItemHeader = styled(SubNavItem).attrs({ + 'data-garden-id': COMPONENT_ID, + 'data-garden-version': PACKAGE_VERSION, + 'aria-expanded': props => props.expanded, + className: props => + classNames(ChromeStyles['c-chrome__subnav__item--header'], { + [ChromeStyles['is-expanded']]: props.expanded + }) +})` + ${props => retrieveTheme(COMPONENT_ID, props)}; +`; + +StyledSubNavItemHeader.propTypes = { + expanded: PropTypes.bool +}; + +/** Accepts all `
` props */ +const StyledSubNavPanel = styled.div.attrs({ + 'data-garden-id': PANEL_COMPONENT_ID, + 'data-garden-version': PACKAGE_VERSION, + className: props => + classNames(ChromeStyles['c-chrome__subnav__panel'], { + [ChromeStyles['is-hidden']]: props.hidden + }) +})` + ${props => retrieveTheme(PANEL_COMPONENT_ID, props)}; +`; + +StyledSubNavPanel.propTypes = { + hidden: PropTypes.bool +}; + +/** + * Accepts all `
+ +`; + +exports[`CollapsibleSubNavItem States renders focused styling if provided 1`] = ` + +
+ + + + +
+
+`; + +exports[`CollapsibleSubNavItem States renders hovered styling if provided 1`] = ` + +
+ + + + +
+
+`; + +exports[`CollapsibleSubNavItem States renders hovered styling if provided 2`] = ` + +
+ + + + +
+
+`; + +exports[`CollapsibleSubNavItem renders default styling 1`] = ` + +
+ + + + +
+
+`; From 75f465d3ad063f3d97d7ac6f347bf2d6d2477369 Mon Sep 17 00:00:00 2001 From: Austin Green Date: Tue, 16 Oct 2018 10:59:48 -0700 Subject: [PATCH 02/14] Added panel maxHeight calculation --- .../src/views/subnav/CollapsibleSubNavItem.js | 10 +++- .../CollapsibleSubNavItem.spec.js.snap | 55 +++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js b/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js index 5a4a93c821e..95a644ea8aa 100644 --- a/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js +++ b/packages/chrome/src/views/subnav/CollapsibleSubNavItem.js @@ -5,7 +5,7 @@ * found at http://www.apache.org/licenses/LICENSE-2.0. */ -import React from 'react'; +import React, { Children } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import classNames from 'classnames'; @@ -14,6 +14,7 @@ import { retrieveTheme } from '@zendeskgarden/react-theming'; const COMPONENT_ID = 'chrome.collapsable_sub_nav_item'; const PANEL_COMPONENT_ID = 'chrome.collapsable_sub_nav_item_panel'; +const SUB_NAV_ITEM_HEIGHT_PX = 38; import SubNavItem from './SubNavItem'; @@ -38,6 +39,7 @@ StyledSubNavItemHeader.propTypes = { const StyledSubNavPanel = styled.div.attrs({ 'data-garden-id': PANEL_COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION, + 'aria-hidden': props => props.hidden, className: props => classNames(ChromeStyles['c-chrome__subnav__panel'], { [ChromeStyles['is-hidden']]: props.hidden @@ -54,12 +56,16 @@ StyledSubNavPanel.propTypes = { * Accepts all ` +
+
Panel Content
+
+ )} + + ); + + beforeEach(() => { + onChangeSpy = jest.fn(); + wrapper = mountWithTheme(); + }); + + const findHeading = enzymeWrapper => enzymeWrapper.find('[data-test-id="heading"]'); + const findHeadingButton = enzymeWrapper => enzymeWrapper.find('[data-test-id="heading-button"]'); + const findPanel = enzymeWrapper => enzymeWrapper.find('[data-test-id="panel"]'); + + describe('getHeadingProps()', () => { + it('applies correct role attribute', () => { + expect(findHeading(wrapper)).toHaveProp('role', 'heading'); + }); + }); + + describe('getHeadingButtonProps()', () => { + it('applies correct id attribute', () => { + expect(findHeadingButton(wrapper)).toHaveProp('id', 'test-header'); + }); + + it('applies correct aria-controls attribute', () => { + expect(findHeadingButton(wrapper)).toHaveProp('aria-controls', 'test-panel'); + }); + + describe('aria-expanded', () => { + it('applies correct attribute when collapsed', () => { + expect(findHeadingButton(wrapper)).toHaveProp('aria-expanded', false); + }); + + it('applies correct attribute when expanded', () => { + wrapper = mountWithTheme(); + + expect(findHeadingButton(wrapper)).toHaveProp('aria-expanded', true); + }); + }); + + describe('onClick()', () => { + it('calls onStateChange with correct value when collapsed', () => { + findHeadingButton(wrapper).simulate('click'); + expect(onChangeSpy).toHaveBeenCalledWith({ expanded: true }); + }); + + it('calls onStateChange with correct value when expanded', () => { + wrapper = mountWithTheme(); + + findHeadingButton(wrapper).simulate('click'); + expect(onChangeSpy).toHaveBeenCalledWith({ expanded: false }); + }); + }); + }); + + describe('getPanelProps()', () => { + it('applies correct role attribute', () => { + expect(findPanel(wrapper)).toHaveProp('role', 'region'); + }); + + it('applies correct aria-labelledby attribute', () => { + expect(findPanel(wrapper)).toHaveProp('aria-labelledby', 'test-header'); + }); + + it('applies correct id attribute', () => { + expect(findPanel(wrapper)).toHaveProp('id', 'test-panel'); + }); + + describe('aria-hidden', () => { + it('applies correct attribute when collapsed', () => { + expect(findPanel(wrapper)).toHaveProp('aria-hidden', true); + }); + + it('applies correct attribute when expanded', () => { + wrapper = mountWithTheme(); + + expect(findPanel(wrapper)).toHaveProp('aria-hidden', false); + }); + }); + }); +}); diff --git a/packages/chrome/src/views/Chrome.example.md b/packages/chrome/src/views/Chrome.example.md index 60ac4364f88..0302aec142e 100644 --- a/packages/chrome/src/views/Chrome.example.md +++ b/packages/chrome/src/views/Chrome.example.md @@ -103,7 +103,7 @@ initialState = { setState({ showCollapsed: !state.showCollapsed })} + onChange={showCollapsed => setState({ showCollapsed })} > ` props */ const StyledSubNavItemHeader = styled(SubNavItem).attrs({ 'data-garden-id': COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION, - 'aria-expanded': props => props.expanded, className: props => classNames(ChromeStyles['c-chrome__subnav__item--header'], { [ChromeStyles['is-expanded']]: props.expanded @@ -39,7 +38,6 @@ StyledSubNavItemHeader.propTypes = { const StyledSubNavPanel = styled.div.attrs({ 'data-garden-id': PANEL_COMPONENT_ID, 'data-garden-version': PACKAGE_VERSION, - 'aria-hidden': props => props.hidden, className: props => classNames(ChromeStyles['c-chrome__subnav__panel'], { [ChromeStyles['is-hidden']]: props.hidden @@ -55,27 +53,56 @@ StyledSubNavPanel.propTypes = { /** * Accepts all ` @@ -38,6 +38,27 @@ describe('AccordionContainer', () => { const findPanel = enzymeWrapper => enzymeWrapper.find('[data-test-id="panel"]'); describe('getHeadingProps()', () => { + it('throws accessibility error if no headingLevel is provided', () => { + console.error = jest.fn(); // eslint-disable-line no-console + + expect(() => { + wrapper = mountWithTheme( + + {({ getHeadingProps, getHeadingButtonProps, getPanelProps }) => ( +
+
+ +
+
Panel Content
+
+ )} +
+ ); + }).toThrow( + 'Accessibility Error: You must apply the `headingLevel` prop to the element that contains your heading. Equivalent to `aria-level`.' + ); + }); + it('applies correct role attribute', () => { expect(findHeading(wrapper)).toHaveProp('role', 'heading'); }); diff --git a/packages/chrome/src/views/Chrome.example.md b/packages/chrome/src/views/Chrome.example.md index 0302aec142e..df2e8fb1105 100644 --- a/packages/chrome/src/views/Chrome.example.md +++ b/packages/chrome/src/views/Chrome.example.md @@ -103,6 +103,7 @@ initialState = { setState({ showCollapsed })} > {({ getHeadingProps, getHeadingButtonProps, getPanelProps }) => (
-
+
{ it('renders default styling', () => { const wrapper = mount( - +

Content

); @@ -24,7 +24,7 @@ describe('CollapsibleSubNavItem', () => { describe('States', () => { it('renders expanded styling if provided', () => { const wrapper = mount( - +

Content

); @@ -34,7 +34,7 @@ describe('CollapsibleSubNavItem', () => { it('renders focused styling if provided', () => { const wrapper = mount( - +

Content

); @@ -44,14 +44,12 @@ describe('CollapsibleSubNavItem', () => { it('renders hovered styling if provided', () => { const wrapper = mount( - +

Content

); expect(wrapper).toMatchSnapshot(); - - expect(wrapper).toMatchSnapshot(); }); }); }); diff --git a/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap b/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap index 7c050b41844..fc826b17556 100644 --- a/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap +++ b/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap @@ -3,6 +3,7 @@ exports[`CollapsibleSubNavItem States renders expanded styling if provided 1`] = `
@@ -194,96 +199,7 @@ exports[`CollapsibleSubNavItem States renders hovered styling if provided 1`] = >
- - - - -
- -
-

- Content -

-
-
-
- - -`; - -exports[`CollapsibleSubNavItem States renders hovered styling if provided 2`] = ` - - -
-
Date: Thu, 18 Oct 2018 10:56:12 -0700 Subject: [PATCH 13/14] Remove svg-icons as a dependency --- packages/chrome/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/chrome/package.json b/packages/chrome/package.json index 84c20006bc3..7dfeaef79bb 100644 --- a/packages/chrome/package.json +++ b/packages/chrome/package.json @@ -20,7 +20,6 @@ }, "dependencies": { "@zendeskgarden/react-selection": "^4.6.0", - "@zendeskgarden/svg-icons": "^4.0.1", "classnames": "^2.2.5" }, "peerDependencies": { From 3369d15ad468b8e8e4220f3f8b8a2e6be98f341f Mon Sep 17 00:00:00 2001 From: Austin Green Date: Thu, 18 Oct 2018 11:10:12 -0700 Subject: [PATCH 14/14] Default aria-level to 2 for all CollapsibleSubNavItem's --- packages/chrome/src/views/Chrome.example.md | 1 - .../chrome/src/views/subnav/CollapsibleSubNavItem.js | 8 ++------ .../src/views/subnav/CollapsibleSubNavItem.spec.js | 8 ++++---- .../__snapshots__/CollapsibleSubNavItem.spec.js.snap | 12 ++++-------- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/packages/chrome/src/views/Chrome.example.md b/packages/chrome/src/views/Chrome.example.md index df2e8fb1105..0302aec142e 100644 --- a/packages/chrome/src/views/Chrome.example.md +++ b/packages/chrome/src/views/Chrome.example.md @@ -103,7 +103,6 @@ initialState = { setState({ showCollapsed })} > {({ getHeadingProps, getHeadingButtonProps, getPanelProps }) => (
-
+
{ it('renders default styling', () => { const wrapper = mount( - +

Content

); @@ -24,7 +24,7 @@ describe('CollapsibleSubNavItem', () => { describe('States', () => { it('renders expanded styling if provided', () => { const wrapper = mount( - +

Content

); @@ -34,7 +34,7 @@ describe('CollapsibleSubNavItem', () => { it('renders focused styling if provided', () => { const wrapper = mount( - +

Content

); @@ -44,7 +44,7 @@ describe('CollapsibleSubNavItem', () => { it('renders hovered styling if provided', () => { const wrapper = mount( - +

Content

); diff --git a/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap b/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap index fc826b17556..208f76dffc9 100644 --- a/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap +++ b/packages/chrome/src/views/subnav/__snapshots__/CollapsibleSubNavItem.spec.js.snap @@ -3,7 +3,6 @@ exports[`CollapsibleSubNavItem States renders expanded styling if provided 1`] = `
@@ -199,7 +196,7 @@ exports[`CollapsibleSubNavItem States renders hovered styling if provided 1`] = >