From 09c5867c851950fd43fcf1356d973611910d6b0f Mon Sep 17 00:00:00 2001 From: Ben Biggs Date: Mon, 23 Sep 2019 17:11:21 -0500 Subject: [PATCH] fix(Topbar): remove previous context api, add examples --- react/src/lib/Topbar/examples/Blue.js | 58 ++++++ react/src/lib/Topbar/examples/Dark.js | 58 ++++++ react/src/lib/Topbar/examples/KitchenSink.js | 22 +++ react/src/lib/Topbar/examples/Light.js | 58 ++++++ react/src/lib/Topbar/examples/NoMiddle.js | 46 +++++ react/src/lib/Topbar/examples/index.js | 6 + react/src/lib/Topbar/index.js | 74 +++----- .../tests/__snapshots__/index.spec.js.snap | 11 +- react/src/lib/Topbar/tests/index.spec.js | 170 +++++++----------- react/src/lib/TopbarMobile/index.js | 25 ++- .../tests/__snapshots__/index.spec.js.snap | 5 + .../src/lib/TopbarMobile/tests/index.spec.js | 74 +++++++- react/src/lib/TopbarNav/index.js | 16 +- .../tests/__snapshots__/index.spec.js.snap | 2 + react/src/lib/TopbarNav/tests/index.spec.js | 9 +- react/src/lib/TopbarRight/index.js | 17 +- .../tests/__snapshots__/index.spec.js.snap | 2 + react/src/lib/TopbarRight/tests/index.spec.js | 9 +- react/src/lib/utils/index.js | 3 +- react/src/lib/utils/prefixer/index.js | 1 + yarn.lock | 30 +--- 21 files changed, 477 insertions(+), 219 deletions(-) create mode 100644 react/src/lib/Topbar/examples/Blue.js create mode 100644 react/src/lib/Topbar/examples/Dark.js create mode 100644 react/src/lib/Topbar/examples/KitchenSink.js create mode 100644 react/src/lib/Topbar/examples/Light.js create mode 100644 react/src/lib/Topbar/examples/NoMiddle.js create mode 100644 react/src/lib/Topbar/examples/index.js create mode 100644 react/src/lib/utils/prefixer/index.js diff --git a/react/src/lib/Topbar/examples/Blue.js b/react/src/lib/Topbar/examples/Blue.js new file mode 100644 index 0000000000..c470ee6dee --- /dev/null +++ b/react/src/lib/Topbar/examples/Blue.js @@ -0,0 +1,58 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { + Button, + Icon, + ListItem, + Topbar, + TopbarMobile, + TopbarNav, + TopbarRight, +} from '@momentum-ui/react'; + +export default class TopbarBlue extends React.Component { + render() { + return ( + + + + } + > + + Develop + Styles + Layout + Navigation + + +
+
+ Log In + +
+ + + + + + Develop + Styles + Layout + Navigation + + + ); + } +} +/* eslint-enable jsx-a11y/anchor-is-valid */ diff --git a/react/src/lib/Topbar/examples/Dark.js b/react/src/lib/Topbar/examples/Dark.js new file mode 100644 index 0000000000..c325527578 --- /dev/null +++ b/react/src/lib/Topbar/examples/Dark.js @@ -0,0 +1,58 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { + Button, + Icon, + ListItem, + Topbar, + TopbarMobile, + TopbarNav, + TopbarRight, +} from '@momentum-ui/react'; + +export default class TopbarDark extends React.Component { + render() { + return ( + + + + } + > + + Develop + Styles + Layout + Navigation + + +
+
+ Log In + +
+ + + + + + Develop + Styles + Layout + Navigation + + + ); + } +} +/* eslint-enable jsx-a11y/anchor-is-valid */ diff --git a/react/src/lib/Topbar/examples/KitchenSink.js b/react/src/lib/Topbar/examples/KitchenSink.js new file mode 100644 index 0000000000..0de9ec29aa --- /dev/null +++ b/react/src/lib/Topbar/examples/KitchenSink.js @@ -0,0 +1,22 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { + TopbarBlue, + TopbarDark, + TopbarLight, + TopbarNoMiddle +} from './index'; + +export default class TopbarKitchenSink extends React.Component { + render() { + return ( + + + + + + + ); + } +} +/* eslint-enable jsx-a11y/anchor-is-valid */ diff --git a/react/src/lib/Topbar/examples/Light.js b/react/src/lib/Topbar/examples/Light.js new file mode 100644 index 0000000000..f3b8b70cdb --- /dev/null +++ b/react/src/lib/Topbar/examples/Light.js @@ -0,0 +1,58 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { + Button, + Icon, + ListItem, + Topbar, + TopbarMobile, + TopbarNav, + TopbarRight, +} from '@momentum-ui/react'; + +export default class TopbarLight extends React.Component { + render() { + return ( + + + + } + > + + Develop + Styles + Layout + Navigation + + +
+
+ Log In + +
+ + + + + + Develop + Styles + Layout + Navigation + + + ); + } +} +/* eslint-enable jsx-a11y/anchor-is-valid */ diff --git a/react/src/lib/Topbar/examples/NoMiddle.js b/react/src/lib/Topbar/examples/NoMiddle.js new file mode 100644 index 0000000000..256caf9176 --- /dev/null +++ b/react/src/lib/Topbar/examples/NoMiddle.js @@ -0,0 +1,46 @@ +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React from 'react'; +import { + Button, + Icon, + Topbar, + TopbarMobile, + TopbarRight, +} from '@momentum-ui/react'; + +export default class TopbarNoMiddle extends React.Component { + render() { + return ( + + + + } + > + +
+
+ Log In + +
+ + + + + + + + ); + } +} +/* eslint-enable jsx-a11y/anchor-is-valid */ diff --git a/react/src/lib/Topbar/examples/index.js b/react/src/lib/Topbar/examples/index.js new file mode 100644 index 0000000000..23cbbb7331 --- /dev/null +++ b/react/src/lib/Topbar/examples/index.js @@ -0,0 +1,6 @@ +export { default as TopbarLight } from './Light'; +export { default as TopbarDark } from './Dark'; +export { default as TopbarBlue } from './Blue'; +export { default as TopbarNoMiddle } from './NoMiddle'; +export { default as TopbarKitchenSink } from './KitchenSink'; + diff --git a/react/src/lib/Topbar/index.js b/react/src/lib/Topbar/index.js index f59214b017..d1b34f6321 100644 --- a/react/src/lib/Topbar/index.js +++ b/react/src/lib/Topbar/index.js @@ -2,54 +2,35 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { prefix } from '../utils/index'; class Topbar extends React.Component { - static displayName = 'Topbar'; - - state = { - isMobileOpen: false, - activeIndex: null, - focus: 0, - }; - - getChildContext = () => { - return { - activeIndex: this.state.activeIndex, - onActivate: index => this.setSelected(index), - onFocus: index => this.setState(() => ({ focus: index })), - focus: this.state.focus, - }; - }; - - setSelected = index => { - if (index === this.state.activeIndex) return; - this.setState(() => ({ activeIndex: index })); - }; - render() { const { anchor, brandAnchorElement, - color, + children, className, + color, fixed, icon, image, - title + title, + ...otherProps } = this.props; - const mdTopBarClass = 'md-top-bar'; - const mdBrandClass = 'md-brand'; + const topBarClass = `${prefix}-top-bar`; + const brandClass = `${prefix}-brand`; const brandNodeChildren = ([ -
+
{ image ? image : }
, -
+
{title}
]); @@ -59,24 +40,24 @@ class Topbar extends React.Component { ? React.cloneElement( brandAnchorElement, { - className: `${mdBrandClass}` + + className: + `${brandClass}` + `${(brandAnchorElement.props.className && ` ${brandAnchorElement.props.className}`) || ''}`, }, brandNodeChildren ) - : + : {brandNodeChildren} ); const brandNode = ( -
+
{getBrandAnchor()}
); - const injectChildren = React.Children.map(this.props.children, child => { - if (!child) return; + const injectChildren = React.Children.map(children, child => { if ((child.type.displayName === 'TopbarMobile') && (!child.props.brandNode)) { return React.cloneElement(child, { brandNode @@ -88,16 +69,16 @@ class Topbar extends React.Component { return (
{ - this.parentContainer = ref; - }}> -
+ role='navigation' + {...otherProps} + > +
{brandNode} {injectChildren}
@@ -121,7 +102,7 @@ Topbar.propTypes = { fixed: PropTypes.bool, /** @prop Icon class name | 'icon-cisco-logo' */ icon: PropTypes.string, - /** @prop Image source URL | null */ + /** @prop Image node | null */ image: PropTypes.node, /** @prop Topbar title text | '' */ title: PropTypes.string, @@ -139,13 +120,6 @@ Topbar.defaultProps = { title: '', }; -Topbar.childContextTypes = { - focus: PropTypes.number, - activeIndex: PropTypes.number, - onActivate: PropTypes.func, - onFocus: PropTypes.func, -}; - Topbar.displayName = 'Topbar'; export default Topbar; diff --git a/react/src/lib/Topbar/tests/__snapshots__/index.spec.js.snap b/react/src/lib/Topbar/tests/__snapshots__/index.spec.js.snap index 4aed6b8032..4ccc5655bc 100644 --- a/react/src/lib/Topbar/tests/__snapshots__/index.spec.js.snap +++ b/react/src/lib/Topbar/tests/__snapshots__/index.spec.js.snap @@ -55,7 +55,7 @@ ShallowWrapper { "className": "md-top-bar md-top-bar--dark", "role": "navigation", }, - "ref": [Function], + "ref": null, "rendered": Object { "instance": null, "key": undefined, @@ -219,7 +219,7 @@ ShallowWrapper { "className": "md-top-bar md-top-bar--dark", "role": "navigation", }, - "ref": [Function], + "ref": null, "rendered": Object { "instance": null, "key": undefined, @@ -376,11 +376,6 @@ ShallowWrapper { Symbol(enzyme.__providerValues__): undefined, }, Symbol(enzyme.__providerValues__): Map {}, - Symbol(enzyme.__childContext__): Object { - "activeIndex": null, - "focus": 0, - "onActivate": [Function], - "onFocus": [Function], - }, + Symbol(enzyme.__childContext__): null, } `; diff --git a/react/src/lib/Topbar/tests/index.spec.js b/react/src/lib/Topbar/tests/index.spec.js index 8a5916a003..6af6b68422 100644 --- a/react/src/lib/Topbar/tests/index.spec.js +++ b/react/src/lib/Topbar/tests/index.spec.js @@ -1,6 +1,7 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { Topbar } from '@momentum-ui/react'; +import { Topbar, TopbarMobile } from '@momentum-ui/react'; +import { prefix } from '../../utils/index'; describe('', () => { it('should render a Topbar', () => { @@ -9,118 +10,67 @@ describe('', () => { expect(wrapper).toMatchSnapshot(); }); - it('should handle brandAnchorElement prop', () => { + it('should apply fixed prop', () => { + const container = mount(); + + expect(container.find(`.${prefix}-top-bar--fixed`).exists()).toBeTruthy(); + }); + + it('should apply color prop', () => { + const container = mount(); + + expect(container.find(`.${prefix}-top-bar--light`).exists()).toBeTruthy(); + }); + + it('should not apply null color prop', () => { + const container = mount(); + + expect(container.find(`.${prefix}-top-bar`).props().className).toEqual(`${prefix}-top-bar`); + }); + + it('should apply brand brandAnchorElement class', () => { + const container = mount(}/>); + + expect(container.find(`.${prefix}-brand`).length).toEqual(1); + }); + + it('should handle brandAnchorElement prop with class', () => { const container = mount(}/>); + expect(container.find(`.${prefix}-brand`).length).toEqual(1); expect(container.find('.testAnchor').length).toEqual(1); }); - // it('should render a Topbar with AppTitle' , () => { - // const wrapper = mount(); - // - // expect(wrapper.props().appTitle).toEqual('Test'); - // }); + it('should handle image prop', () => { + const container = mount(}/>); + + expect(container.find('.testimg').length).toEqual(1); + }); + + it('should handle cloning onto TopbarMobile brandNode when TMbn not present', () => { + const container = mount( + }> + + + ); + + expect(container.find('.testAnchor').length).toEqual(2); + }); + + it('should handle cloning onto TopbarMobile brandNode when TMbn is present', () => { + const container = mount( + }> + }/> + + ); + + expect(container.find('.testAnchor1').length).toEqual(1); + expect(container.find('.testAnchor2').length).toEqual(1); + }); + + it('should add customized class name if className prop is set', () => { + const wrapper = shallow(); + + expect(wrapper.find('.testClassName').exists()).toBeTruthy(); + }); }); -// import renderer from 'react-test-renderer'; -// import {shallow, mount} from 'enzyme'; -// import Checkbox from '../Checkbox'; -// -// describe('', () => { -// /** -// * Snapshot based testing for look and feel. -// */ -// it('should render regular default checkbox', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should render regular checked checkbox', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should render disabled checkbox', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should render reversed checkbox', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should render an indeterminate state checkbox', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should have a `disabled` attribute when `disabled` is `true`', () => { -// const wrapper = mount( -// -// ); -// expect(wrapper.find('Checkbox').find('input').html().includes('disabled')).toBe(true); -// }); -// -// it('should render with additional classes applied', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should render the element with any extra attributes that are passed', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// it('should apply custom id if passed', () => { -// const rendered = renderer.create( -// -// ); -// expect(rendered.toJSON()).toMatchSnapshot(); -// }); -// -// /** -// * Functionality testing. This single test case covers the sanity for checkbox behavior. -// * jest.fn() mocks a function. -// * Refer: https://facebook.github.io/jest/docs/mock-functions.html -// */ -// it('should be clickable ', () => { -// const onCheckboxClick = jest.fn(); -// const wrapper = shallow(); -// wrapper.first().simulate('click'); -// expect(onCheckboxClick).toHaveBeenCalled(); -// }); -// -// it('should get checked with a single click', () => { -// const wrapper = mount(); -// wrapper.find('input').simulate('change', { target: { value: 'on' } }); -// expect(wrapper.find('svg').hasClass('opacity0')).toBe(false); -// }); -// -// it('should get unchecked when clicked twice', () => { -// const wrapper = mount(); -// wrapper.find('input').simulate('change', { target: { value: 'on' } }); -// wrapper.find('input').simulate('change', { target: { value: 'off' } }); -// expect(wrapper.find('svg').hasClass('opacity0')).toBe(true); -// }); -// -// it('should call the user defined onChange when passed', () => { -// const onCheckChange = jest.fn(); -// const wrapper = mount(); -// wrapper.find('Checkbox').first().find('input').simulate('change'); -// expect(onCheckChange).toHaveBeenCalled(); -// }); -// }); diff --git a/react/src/lib/TopbarMobile/index.js b/react/src/lib/TopbarMobile/index.js index c41810887d..869bb3e9b9 100644 --- a/react/src/lib/TopbarMobile/index.js +++ b/react/src/lib/TopbarMobile/index.js @@ -3,10 +3,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Icon, ListSeparator } from '@momentum-ui/react'; +import { prefix } from '../utils/index'; class TopbarMobile extends React.Component { - static displayName = 'TopbarMobile'; - state = { isMobileOpen: false, }; @@ -39,16 +38,18 @@ class TopbarMobile extends React.Component { const { brandNode, children, + className, closeMenuAriaLabel, shouldCloseOnClick, openMenuAriaLabel, + ...otherProps } = this.props; const { isMobileOpen } = this.state; const mobileButton = ( { - if (!child) return; return React.cloneElement(child, { onClick: this.handleClose }); @@ -67,17 +67,19 @@ class TopbarMobile extends React.Component { {!isMobileOpen && mobileButton}
shouldCloseOnClick ? this.handleClose : null} onKeyDown={this.handleKeyDown} role='menu' tabIndex={0} + {...otherProps} > {brandNode} {/* eslint-enable jsx-a11y/no-static-element-interactions */} - +
@@ -103,6 +107,8 @@ TopbarMobile.propTypes = { brandNode: PropTypes.node, /** @prop Children node to render inside of TopbarMobile | null */ children: PropTypes.node, + /** @prop Optional CSS class string | '' */ + className: PropTypes.string, /** @prop Aria Label for close Button | 'Close Menu' */ closeMenuAriaLabel: PropTypes.string, /** @prop Set mobile menu to close on any click | true */ @@ -114,6 +120,7 @@ TopbarMobile.propTypes = { TopbarMobile.defaultProps = { brandNode: null, children: null, + className: '', closeMenuAriaLabel: 'Close Menu', shouldCloseOnClick: true, openMenuAriaLabel: 'Open Menu', diff --git a/react/src/lib/TopbarMobile/tests/__snapshots__/index.spec.js.snap b/react/src/lib/TopbarMobile/tests/__snapshots__/index.spec.js.snap index 7df27a04ac..8c44eb540a 100644 --- a/react/src/lib/TopbarMobile/tests/__snapshots__/index.spec.js.snap +++ b/react/src/lib/TopbarMobile/tests/__snapshots__/index.spec.js.snap @@ -5,6 +5,7 @@ ShallowWrapper { Symbol(enzyme.__root__): [Circular], Symbol(enzyme.__unrendered__): ,