diff --git a/src/components/sidebar-toggle-button/README.md b/src/components/sidebar-toggle-button/README.md new file mode 100644 index 0000000000..6fa7715fc0 --- /dev/null +++ b/src/components/sidebar-toggle-button/README.md @@ -0,0 +1,14 @@ +### Examples +**Open** +```js + +``` +**Closed** +```js + +``` +**When sidebar is on the left, change the direction** +```js + +``` + diff --git a/src/components/sidebar-toggle-button/SidebarToggleButton.js b/src/components/sidebar-toggle-button/SidebarToggleButton.js new file mode 100644 index 0000000000..426412d4b7 --- /dev/null +++ b/src/components/sidebar-toggle-button/SidebarToggleButton.js @@ -0,0 +1,57 @@ +// @flow +import * as React from 'react'; +import classNames from 'classnames'; +import { injectIntl } from 'react-intl'; + +import IconHide from '../../icons/general/IconHide'; +import IconShow from '../../icons/general/IconShow'; +import PlainButton from '../plain-button'; +import Tooltip from '../tooltip'; + +import messages from '../../elements/common/messages'; + +import './SidebarToggleButton.scss'; + +const DIRECTION_LEFT = 'left'; +const DIRECTION_RIGHT = 'right'; + +type Props = { + className?: string, + direction?: string, + isOpen: boolean, + onClick?: Function, +} & InjectIntlProvidedProps; + +const SidebarToggleButton = ({ + className = '', + direction = DIRECTION_RIGHT, + intl, + isOpen, + onClick, + ...rest +}: Props) => { + const isCollapsed = !isOpen ? 'collapsed' : ''; + const intlMessage = isOpen ? messages.sidebarHide : messages.sidebarShow; + const intlText = intl.formatMessage(intlMessage); + const classes = classNames(className, 'bdl-SidebarToggleButton', { + 'bdl-is-collapsed': isCollapsed, + }); + const tooltipPosition = direction === DIRECTION_LEFT ? 'middle-right' : 'middle-left'; + + const renderButton = () => { + if (direction === DIRECTION_LEFT) { + return isOpen ? : ; + } + return isOpen ? : ; + }; + + return ( + + + {renderButton()} + + + ); +}; + +export default injectIntl(SidebarToggleButton); diff --git a/src/components/sidebar-toggle-button/SidebarToggleButton.scss b/src/components/sidebar-toggle-button/SidebarToggleButton.scss new file mode 100644 index 0000000000..490a9ee003 --- /dev/null +++ b/src/components/sidebar-toggle-button/SidebarToggleButton.scss @@ -0,0 +1,29 @@ +@import '../../styles/variables'; + +.bdl-SidebarToggleButton { + border-radius: $bdl-border-radius-size; + margin: 0 3px; + padding: 4px; + + path { + fill: $bdl-gray-50; + } + + &:not(.is-disabled):hover { + background-color: $bdl-gray-05; + } + + &:not(.is-disabled):focus { + border-color: #96a0a6; + box-shadow: 0 1px 2px rgba(0, 0, 0, .1); + } + + &.bdl-is-collapsed, + &.bdl-is-collapsed:hover { + background-color: $bdl-box-blue; + + path { + fill: $white; + } + } +} diff --git a/src/components/sidebar-toggle-button/__tests__/SidebarToggleButton-test.js b/src/components/sidebar-toggle-button/__tests__/SidebarToggleButton-test.js new file mode 100644 index 0000000000..7e4843a9fa --- /dev/null +++ b/src/components/sidebar-toggle-button/__tests__/SidebarToggleButton-test.js @@ -0,0 +1,35 @@ +import React from 'react'; + +import SidebarToggleButton from '..'; + +describe('components/sidebar-toggle-button/SidebarToggleButton', () => { + test('should render correctly as open', () => { + const wrapper = mount(); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render correctly as closed', () => { + const wrapper = mount(); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should have the proper class when it is collapsed', () => { + const wrapper = mount(); + + expect(wrapper.find('PlainButton').hasClass('bdl-is-collapsed')).toBeTruthy(); + }); + + test('should render correctly as left oriented toggle when open', () => { + const wrapper = mount(); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should render correctly as left oriented toggle when closed', () => { + const wrapper = mount(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/src/components/sidebar-toggle-button/__tests__/__snapshots__/SidebarToggleButton-test.js.snap b/src/components/sidebar-toggle-button/__tests__/__snapshots__/SidebarToggleButton-test.js.snap new file mode 100644 index 0000000000..e779a1a9a0 --- /dev/null +++ b/src/components/sidebar-toggle-button/__tests__/__snapshots__/SidebarToggleButton-test.js.snap @@ -0,0 +1,385 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/sidebar-toggle-button/SidebarToggleButton should render correctly as closed 1`] = ` + + + + } + classPrefix="tooltip" + constraints={ + Array [ + Object { + "attachment": "together", + "to": "window", + }, + ] + } + enabled={false} + renderElementTag="div" + renderElementTo={null} + targetAttachment="middle left" + > + + + + + + + +`; + +exports[`components/sidebar-toggle-button/SidebarToggleButton should render correctly as left oriented toggle when closed 1`] = ` + + + + } + classPrefix="tooltip" + constraints={ + Array [ + Object { + "attachment": "together", + "to": "window", + }, + ] + } + enabled={false} + renderElementTag="div" + renderElementTo={null} + targetAttachment="middle right" + > + + + + + + + +`; + +exports[`components/sidebar-toggle-button/SidebarToggleButton should render correctly as left oriented toggle when open 1`] = ` + + + + } + classPrefix="tooltip" + constraints={ + Array [ + Object { + "attachment": "together", + "to": "window", + }, + ] + } + enabled={false} + renderElementTag="div" + renderElementTo={null} + targetAttachment="middle right" + > + + + + + + + +`; + +exports[`components/sidebar-toggle-button/SidebarToggleButton should render correctly as open 1`] = ` + + + + } + classPrefix="tooltip" + constraints={ + Array [ + Object { + "attachment": "together", + "to": "window", + }, + ] + } + enabled={false} + renderElementTag="div" + renderElementTo={null} + targetAttachment="middle left" + > + + + + + + + +`; diff --git a/src/components/sidebar-toggle-button/index.js b/src/components/sidebar-toggle-button/index.js new file mode 100644 index 0000000000..ebb60dbd59 --- /dev/null +++ b/src/components/sidebar-toggle-button/index.js @@ -0,0 +1,2 @@ +// @flow +export { default } from './SidebarToggleButton'; diff --git a/src/elements/common/interactionTargets.js b/src/elements/common/interactionTargets.js index 2fad7f7550..2d98892388 100644 --- a/src/elements/common/interactionTargets.js +++ b/src/elements/common/interactionTargets.js @@ -3,6 +3,7 @@ export const SIDEBAR_NAV_TARGETS = { DETAILS: 'sidebardetails', SKILLS: 'sidebarskills', METADATA: 'sidebarmetadata', + TOGGLE: 'sidebartoggle', }; export const SECTION_TARGETS = { diff --git a/src/elements/content-sidebar/SidebarNav.js b/src/elements/content-sidebar/SidebarNav.js index 6cb65c0591..fde70971c0 100644 --- a/src/elements/content-sidebar/SidebarNav.js +++ b/src/elements/content-sidebar/SidebarNav.js @@ -10,6 +10,7 @@ import IconMagicWand from '../../icons/general/IconMagicWand'; import IconMetadataThick from '../../icons/general/IconMetadataThick'; import IconDocInfo from '../../icons/general/IconDocInfo'; import IconChatRound from '../../icons/general/IconChatRound'; +import SidebarToggleButton from '../../components/sidebar-toggle-button'; import messages from '../common/messages'; import { SIDEBAR_NAV_TARGETS } from '../common/interactionTargets'; import SidebarNavButton from './SidebarNavButton'; @@ -46,52 +47,70 @@ const SidebarNav = ({ isOpen, onNavigate, }: Props) => ( -
- {hasActivity && ( - +
+ {hasActivity && ( + } + > + + + )} + {hasDetails && ( + } + > + + + )} + {hasSkills && ( + } + > + + + )} + {hasMetadata && ( + } + > + + + )} + {hasAdditionalTabs && } +
+
+ } - > - - - )} - {hasDetails && ( - } - > - - - )} - {hasSkills && ( - } - > - - - )} - {hasMetadata && ( - } - > - - - )} - {hasAdditionalTabs && } + onClick={event => { + if (onNavigate) { + onNavigate(event, { isToggle: true }); + } + }} + /> +
); diff --git a/src/elements/content-sidebar/SidebarNav.scss b/src/elements/content-sidebar/SidebarNav.scss index 649e7d238c..f4d5591f52 100644 --- a/src/elements/content-sidebar/SidebarNav.scss +++ b/src/elements/content-sidebar/SidebarNav.scss @@ -1,7 +1,24 @@ @import '../common/variables'; .bcs { - nav { + .bcs-SidebarNav { border-left: 1px solid $off-white; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + .bcs-SidebarNav-footer { + align-items: center; + display: flex; + height: 60px; + justify-content: center; + + // Need to add these overriding styles because there is a specificity issue with .btn-plain + .btn-plain.bdl-SidebarToggleButton { + height: 24px; + margin: 0 3px; + padding: 4px; + } } } diff --git a/src/elements/content-sidebar/SidebarNavButton.js b/src/elements/content-sidebar/SidebarNavButton.js index 4449e2bc17..9eb26ef8a9 100644 --- a/src/elements/content-sidebar/SidebarNavButton.js +++ b/src/elements/content-sidebar/SidebarNavButton.js @@ -11,15 +11,24 @@ import Tooltip from '../../components/tooltip/Tooltip'; import './SidebarNavButton.scss'; type Props = { + 'data-resin-target'?: string, + 'data-testid'?: string, children: React.Node, - interactionTarget: string, isOpen?: boolean, onNavigate?: (SyntheticEvent<>, NavigateOptions) => void, sidebarView: string, tooltip: React.Node, }; -const SidebarNavButton = ({ children, interactionTarget, isOpen, onNavigate, sidebarView, tooltip }: Props) => { +const SidebarNavButton = ({ + children, + 'data-resin-target': dataResinTarget, + 'data-testid': dataTestId, + isOpen, + onNavigate, + sidebarView, + tooltip, +}: Props) => { const sidebarPath = `/${sidebarView}`; return ( @@ -35,8 +44,8 @@ const SidebarNavButton = ({ children, interactionTarget, isOpen, onNavigate, sid aria-selected={isActive()} activeClassName="bcs-is-selected" className="bcs-NavButton" - data-resin-target={interactionTarget} - data-testid={interactionTarget} + data-resin-target={dataResinTarget} + data-testid={dataTestId} isActive={isActive} onClick={event => { if (onNavigate) { diff --git a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarNav-test.js.snap b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarNav-test.js.snap index be5f07593c..f24ec1c5fd 100644 --- a/src/elements/content-sidebar/__tests__/__snapshots__/SidebarNav-test.js.snap +++ b/src/elements/content-sidebar/__tests__/__snapshots__/SidebarNav-test.js.snap @@ -13,119 +13,230 @@ exports[`elements/content-sidebar/SidebarNav should render the additional tabs l >
- -
- - -
+ +
-
-
- -
+
+
+ +
-
-
- -
+
+
+ +
-
-
- -
+
+
+ +
-
-
- -
+
+
+ +
-
-
- -
+
+
+ + - - - - - - - -
-
- -
- + + + + + +
+ + +
+ +
+
+ + + + } + classPrefix="tooltip" + constraints={ + Array [ + Object { + "attachment": "together", + "to": "window", + }, + ] + } + enabled={false} + renderElementTag="div" + renderElementTo={null} + targetAttachment="middle left" + > + + + + + + + +
`; diff --git a/test/integration/content-sidebar/ContentSidebar.e2e.test.js b/test/integration/content-sidebar/ContentSidebar.e2e.test.js index 5114972c64..c5a56b881d 100644 --- a/test/integration/content-sidebar/ContentSidebar.e2e.test.js +++ b/test/integration/content-sidebar/ContentSidebar.e2e.test.js @@ -42,6 +42,32 @@ describe('ContentSidebar', () => { cy.getByTestId('sidebaractivity').should('have.class', 'bcs-is-selected'); cy.getByTestId('sidebarskills').should('not.have.class', 'bcs-is-selected'); }); + + it('should toggle sidebar content when a user clicks the toggle sidebar button', () => { + cy.getByTestId('bcs-content').should('exist'); + cy.getByTestId('sidebarskills').should('have.class', 'bcs-is-selected'); + + cy.getByTestId('sidebartoggle').click(); + cy.getByTestId('sidebarskills').should('not.have.class', 'bcs-is-selected'); + cy.getByTestId('bcs-content').should('not.exist'); + + cy.getByTestId('sidebartoggle').click(); + cy.getByTestId('sidebarskills').should('have.class', 'bcs-is-selected'); + cy.getByTestId('bcs-content').should('exist'); + }); + + it('should toggle sidebar content when using a combination of toggle sidebar button and sidebar tab', () => { + cy.getByTestId('bcs-content').should('exist'); + cy.getByTestId('sidebarskills').should('have.class', 'bcs-is-selected'); + + cy.getByTestId('sidebartoggle').click(); + cy.getByTestId('sidebarskills').should('not.have.class', 'bcs-is-selected'); + cy.getByTestId('bcs-content').should('not.exist'); + + cy.getByTestId('sidebaractivity').click(); + cy.getByTestId('sidebaractivity').should('have.class', 'bcs-is-selected'); + cy.getByTestId('sidebarskills').should('not.have.class', 'bcs-is-selected'); + }); }); describe('version history', () => {