diff --git a/.github/DEVELOPMENT.md b/.github/DEVELOPMENT.md index 4c325e6c950..bf120692d0d 100644 --- a/.github/DEVELOPMENT.md +++ b/.github/DEVELOPMENT.md @@ -46,7 +46,7 @@ yarn start --scope @zendeskgarden/react-buttons All elements must -* Be implemented with associated `Conatiner` and `View` components +* Be implemented with associated `Container` and `View` components * Provide `uncontrolled` and `controlled` state management if necessary * Be implemented with the `ControlledComponent` state abstractions if necessary * Create an abstraction to benefit a majority of use cases diff --git a/.travis.yml b/.travis.yml index f05662cd518..4775e47048b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ install: yarn --frozen-lockfile script: - yarn lint + - yarn format - yarn test:all --coverage --runInBand after_success: yarn coveralls < demo/coverage/lcov.info diff --git a/README.md b/README.md index b52d75a4efc..aa4e728e0d8 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,6 @@ components are packaged and published individually, but combined under this single repository. Components rely on [Garden CSS](https://github.com/zendeskgarden/css-components) for styling. -Try out the Garden React Components in a mock product environment with CodeSandbox - -[![Edit Garden Create-React-App](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/43nwpkn717) - ## Installation See the individual package README for the React component you would like @@ -22,6 +18,7 @@ Package | Version | Dependencies ------- | ------- | ------------ [`@zendeskgarden/react-autocomplete`](packages/autocomplete) | [![npm version][autocomplete npm version]][autocomplete npm link] | [![Dependency Status][autocomplete dependency status]][autocomplete dependency link] [`@zendeskgarden/react-avatars`](packages/avatars) | [![npm version][avatars npm version]][avatars npm link] | [![Dependency Status][avatars dependency status]][avatars dependency link] +[`@zendeskgarden/react-breadcrumbs`](packages/breadcrumbs) | [![npm version][breadcrumbs npm version]][breadcrumbs npm link] | [![Dependency Status][breadcrumbs dependency status]][breadcrumbs dependency link] [`@zendeskgarden/react-buttons`](packages/buttons) | [![npm version][buttons npm version]][buttons npm link] | [![Dependency Status][buttons dependency status]][buttons dependency link] [`@zendeskgarden/react-checkboxes`](packages/checkboxes) | [![npm version][checkboxes npm version]][checkboxes npm link] | [![Dependency Status][checkboxes dependency status]][checkboxes dependency link] [`@zendeskgarden/react-chrome`](packages/chrome) | [![npm version][chrome npm version]][chrome npm link] | [![Dependency Status][chrome dependency status]][chrome dependency link] @@ -54,6 +51,10 @@ Package | Version | Dependencies [avatars npm link]: https://www.npmjs.com/package/@zendeskgarden/react-avatars [avatars dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/avatars&style=flat-square [avatars dependency link]: https://david-dm.org/zendeskgarden/react-components?path=packages/avatars +[breadcrumbs npm version]: https://img.shields.io/npm/v/@zendeskgarden/react-breadcrumbs.svg?style=flat-square +[breadcrumbs npm link]: https://www.npmjs.com/package/@zendeskgarden/react-breadcrumbs +[breadcrumbs dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/breadcrumbs&style=flat-square +[breadcrumbs dependency link]: https://david-dm.org/zendeskgarden/react-components?path=packages/breadcrumbs [buttons npm version]: https://img.shields.io/npm/v/@zendeskgarden/react-buttons.svg?style=flat-square [buttons npm link]: https://www.npmjs.com/package/@zendeskgarden/react-buttons [buttons dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/buttons&style=flat-square @@ -149,11 +150,11 @@ Package | Version | Dependencies ## Usage -Our packages are easily consumable with [create-react-app](https://github.com/facebook/create-react-app) -and standard webpack configs. - -All packages follow a similar installation process. Below is an example of -consuming our [react-buttons](https://www.npmjs.com/package/@zendeskgarden/react-buttons) +Our packages are easily consumable with +[create-react-app](https://github.com/facebook/create-react-app) +and standard webpack configs. All packages follow a similar installation process. +Below is an example of consuming our +[react-buttons](https://www.npmjs.com/package/@zendeskgarden/react-buttons) package. ### Install dependencies @@ -194,6 +195,10 @@ class App extends Component { render(, document.getElementById('root')); ``` +Try out Garden React components in a mock product environment. + +[![Edit Garden Create-React-App](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/43nwpkn717) + ## Contribution Thanks for your interest in Garden! Community involvement helps make our diff --git a/demo/index.html b/demo/index.html index 65e851ced3d..6040c846c56 100644 --- a/demo/index.html +++ b/demo/index.html @@ -91,6 +91,9 @@

React Components

Avatars
+
+ Breadcrumbs +
Buttons
@@ -109,11 +112,11 @@

React Components

Menus
+ +
-
-
@@ -133,10 +136,10 @@

React Components

Selection
- Tabs + Tables
- Tables + Tabs
diff --git a/packages/autocomplete/src/examples/multiselect.md b/packages/autocomplete/src/examples/multiselect.md index 4e5e88b9796..decc4e4b62c 100644 --- a/packages/autocomplete/src/examples/multiselect.md +++ b/packages/autocomplete/src/examples/multiselect.md @@ -283,8 +283,8 @@ const MoreAnchor = styled(Anchor)` style: Object.assign( { margin: '0 2px', flexGrow: 1, width: 60 }, Object.keys(state.selectedKeys).length !== 0 && - (!state.isFocused || tagFocusedKey !== undefined) && - !isOpen + (!state.isFocused || tagFocusedKey !== undefined) && + !isOpen ? { opacity: 0, height: 0, minHeight: 0 } : {} ) diff --git a/packages/breadcrumbs/CHANGELOG.md b/packages/breadcrumbs/CHANGELOG.md new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/packages/breadcrumbs/CHANGELOG.md @@ -0,0 +1 @@ + diff --git a/packages/breadcrumbs/README.md b/packages/breadcrumbs/README.md new file mode 100644 index 00000000000..9492a7531f3 --- /dev/null +++ b/packages/breadcrumbs/README.md @@ -0,0 +1,37 @@ +# @zendeskgarden/react-breadcrumbs [![npm version](https://img.shields.io/npm/v/@zendeskgarden/react-breadcrumbs.svg?style=flat-square)](https://www.npmjs.com/package/@zendeskgarden/react-breadcrumbs) + +This package includes components relating to breadcrumbs in the +[Garden Design System](https://zendeskgarden.github.io/). + +## Installation + +```sh +npm install @zendeskgarden/react-breadcrumbs + +# Peer Dependencies - Also Required +npm install react react-dom prop-types styled-components @zendeskgarden/react-theming +``` + +## Usage + +```jsx static +/** + * Include breadcrumbs styling at the root of your application + */ +import '@zendeskgarden/react-breadcrumbs/dist/styles.css'; + +import { ThemeProvider } from '@zendeskgarden/react-theming'; +import { Breadcrumb, Item } from '@zendeskgarden/react-breadcrumbs'; +import { Anchor } from '@zendeskgarden/react-buttons'; + +/** + * Place a `ThemeProvider` at the root of your React application + */ + + + Root + Parent + Self + +; +``` diff --git a/packages/breadcrumbs/package.json b/packages/breadcrumbs/package.json new file mode 100644 index 00000000000..882120c4202 --- /dev/null +++ b/packages/breadcrumbs/package.json @@ -0,0 +1,47 @@ +{ + "name": "@zendeskgarden/react-breadcrumbs", + "description": "Components relating to breadcrumbs in the Garden Design System", + "license": "Apache-2.0", + "author": "Zendesk Garden ", + "homepage": "https://garden.zendesk.com/", + "repository": "https://github.com/zendeskgarden/react-components", + "bugs": { + "url": "https://github.com/zendeskgarden/react-components/issues" + }, + "version": "0.0.0", + "main": "./dist/index.js", + "files": [ + "dist" + ], + "scripts": { + "build": "../../utils/scripts/build.sh", + "build:demo": "../../utils/scripts/build-demo.sh", + "start": "../../utils/scripts/start.sh" + }, + "dependencies": { + "classnames": "^2.2.5" + }, + "peerDependencies": { + "@zendeskgarden/react-theming": "^1.0.0 || ^2.0.0 || ^3.0.0", + "prop-types": "^15.6.1", + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0", + "styled-components": "^3.2.6" + }, + "devDependencies": { + "@zendeskgarden/css-breadcrumbs": "0.1.2", + "@zendeskgarden/react-theming": "^3.1.3", + "@zendeskgarden/react-utilities": "^0.2.2" + }, + "keywords": [ + "components", + "garden", + "react", + "zendesk" + ], + "publishConfig": { + "access": "public" + }, + "zendeskgarden:library": "GardenBreadcrumbs", + "zendeskgarden:src": "src/index.js" +} diff --git a/packages/breadcrumbs/src/containers/BreadcrumbContainer.example.md b/packages/breadcrumbs/src/containers/BreadcrumbContainer.example.md new file mode 100644 index 00000000000..50928b9969b --- /dev/null +++ b/packages/breadcrumbs/src/containers/BreadcrumbContainer.example.md @@ -0,0 +1,20 @@ +```jsx +const { Anchor } = require('@zendeskgarden/react-buttons/src'); + + + {({ getContainerProps }) => ( + /* role not needed as `BreadcrumbView` is a navigation landmark. */ + + + + One + + + Two + + Three + + + )} +; +``` diff --git a/packages/breadcrumbs/src/containers/BreadcrumbContainer.js b/packages/breadcrumbs/src/containers/BreadcrumbContainer.js new file mode 100644 index 00000000000..4210ab58ee7 --- /dev/null +++ b/packages/breadcrumbs/src/containers/BreadcrumbContainer.js @@ -0,0 +1,39 @@ +/** + * 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 { Component } from 'react'; +import PropTypes from 'prop-types'; + +export default class BreadcrumbContainer extends Component { + static propTypes = { + /** + * @param {Object} renderProps + * @param {Function} renderProps.getContainerProps - Props to be spread onto containing element + */ + children: PropTypes.func, + /** + * Identical to children + */ + render: PropTypes.func + }; + + getContainerProps = ({ role = 'navigation', ...other } = {}) => { + return { + role, + 'aria-label': 'Breadcrumb navigation', + ...other + }; + }; + + render() { + const { children, render = children } = this.props; + + return render({ + getContainerProps: this.getContainerProps + }); + } +} diff --git a/packages/breadcrumbs/src/containers/BreadcrumbContainer.spec.js b/packages/breadcrumbs/src/containers/BreadcrumbContainer.spec.js new file mode 100644 index 00000000000..8b5bd5a45e8 --- /dev/null +++ b/packages/breadcrumbs/src/containers/BreadcrumbContainer.spec.js @@ -0,0 +1,34 @@ +/** + * 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 { mountWithTheme } from '@zendeskgarden/react-testing'; + +import BreadcrumbContainer from './BreadcrumbContainer'; + +describe('BreadcrumbContainer', () => { + let wrapper; + + beforeEach(() => { + wrapper = mountWithTheme( + + {({ getContainerProps }) =>
} + + ); + }); + + const findContainer = enzymeWrapper => enzymeWrapper.find('[data-test-id="container"]'); + + describe('getContainerProps()', () => { + it('applies correct accessibility attributes', () => { + const container = findContainer(wrapper); + + expect(container).toHaveProp('role', 'navigation'); + expect(container).toHaveProp('aria-label', 'Breadcrumb navigation'); + }); + }); +}); diff --git a/packages/breadcrumbs/src/elements/Breadcrumb.example.md b/packages/breadcrumbs/src/elements/Breadcrumb.example.md new file mode 100644 index 00000000000..5788bf1e49c --- /dev/null +++ b/packages/breadcrumbs/src/elements/Breadcrumb.example.md @@ -0,0 +1,14 @@ +The `Breadcrumb` component follows the +[W3C breadcrumb accessibility design pattern](https://www.w3.org/TR/wai-aria-practices/#breadcrumb) +and applies the correct accessibility attributes to the `BreadcrumbView` listed below. +Implementations are expected to override `aria-label` with a translated value describing usage. + +```jsx +const { Anchor } = require('@zendeskgarden/react-buttons/src'); + + + Home + React Components + Breadcrumbs +; +``` diff --git a/packages/breadcrumbs/src/elements/Breadcrumb.js b/packages/breadcrumbs/src/elements/Breadcrumb.js new file mode 100644 index 00000000000..211258ac13e --- /dev/null +++ b/packages/breadcrumbs/src/elements/Breadcrumb.js @@ -0,0 +1,57 @@ +/** + * 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, { Component, Children, cloneElement } from 'react'; +import PropTypes from 'prop-types'; +import { hasType } from '@zendeskgarden/react-utilities'; + +import BreadcrumbContainer from '../containers/BreadcrumbContainer'; +import BreadcrumbView from '../views/BreadcrumbView'; +import List from '../views/List'; +import Item from '../views/Item'; + +/** + * High-level abstraction for basic Breadcrumb implementations. Accepts all + * `
    ` props. + */ +export default class Breadcrumb extends Component { + static propTypes = { + children: PropTypes.any + }; + + renderItems = items => { + const total = Children.count(items); + + return Children.map(items, (item, index) => { + const itemProps = { + current: index === total - 1, + key: index + }; + + return hasType(item, Item) ? ( + cloneElement(item, itemProps) + ) : ( + {item} + ); + }); + }; + + render() { + const { children, ...breadcrumbProps } = this.props; + + return ( + + {({ getContainerProps }) => ( + /* role not needed as `BreadcrumbView` is a navigation landmark. */ + + {this.renderItems(children)} + + )} + + ); + } +} diff --git a/packages/breadcrumbs/src/elements/Breadcrumb.spec.js b/packages/breadcrumbs/src/elements/Breadcrumb.spec.js new file mode 100644 index 00000000000..4d490a0bba1 --- /dev/null +++ b/packages/breadcrumbs/src/elements/Breadcrumb.spec.js @@ -0,0 +1,64 @@ +/** + * 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 { mountWithTheme } from '@zendeskgarden/react-testing'; + +import Breadcrumb from './Breadcrumb'; +import BreadcrumbView from '../views/BreadcrumbView'; +import List from '../views/List'; +import Item from '../views/Item'; + +describe('Breadcrumb', () => { + let wrapper; + + beforeEach(() => { + wrapper = mountWithTheme( + + One + + Two + + Three + + ); + }); + + describe('BreadcrumbView', () => { + it('receives BreadcrumbContainer props', () => { + expect(wrapper.find(BreadcrumbView)).toHaveProp('aria-label', 'Breadcrumb navigation'); + }); + + it('does not receive BreadcrumbContainer `role` prop', () => { + expect(wrapper.find(BreadcrumbView)).not.toHaveProp('role', 'navigation'); + }); + }); + + describe('List', () => { + it('receives Breadcrumb props', () => { + expect(wrapper.find(List)).toHaveProp('data-test-breadcrumb', true); + }); + }); + + describe('Item', () => { + it('receives Item props', () => { + expect(wrapper.find(Item).last()).toHaveProp('data-test-item', true); + }); + + it('does not receive non-Item props', () => { + expect(wrapper.find(Item).at(1)).not.toHaveProp('data-test-anchor', true); + }); + + it('receives current styling if last', () => { + expect(wrapper.find(Item).last()).toHaveProp('current', true); + }); + + it('does not receive current styling if not last', () => { + expect(wrapper.find(Item).first()).not.toHaveProp('current', true); + }); + }); +}); diff --git a/packages/breadcrumbs/src/index.js b/packages/breadcrumbs/src/index.js new file mode 100644 index 00000000000..45e1b1902ce --- /dev/null +++ b/packages/breadcrumbs/src/index.js @@ -0,0 +1,12 @@ +/** + * 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. + */ + +export { default as Breadcrumb } from './elements/Breadcrumb'; +export { default as BreadcrumbContainer } from './containers/BreadcrumbContainer'; +export { default as BreadcrumbView } from './views/BreadcrumbView'; +export { default as List } from './views/List'; +export { default as Item } from './views/Item'; diff --git a/packages/breadcrumbs/src/views/BreadcrumbView.js b/packages/breadcrumbs/src/views/BreadcrumbView.js new file mode 100644 index 00000000000..7781b34c3e9 --- /dev/null +++ b/packages/breadcrumbs/src/views/BreadcrumbView.js @@ -0,0 +1,24 @@ +/** + * 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 styled from 'styled-components'; +import { retrieveTheme } from '@zendeskgarden/react-theming'; + +const COMPONENT_ID = 'breadcrumbs.breadcrumb_view'; + +/** + * Accepts all `