diff --git a/README.md b/README.md index 203bd1cf5e1..f62c421c0b8 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Package | Version | Dependencies [`@zendeskgarden/react-pagination`](packages/pagination) | [![npm version][pagination npm version]][pagination npm link] | [![Dependency Status][pagination dependency status]][pagination dependency link] [`@zendeskgarden/react-radios`](packages/radios) | [![npm version][radios npm version]][radios npm link] | [![Dependency Status][radios dependency status]][radios dependency link] [`@zendeskgarden/react-ranges`](packages/ranges) | [![npm version][ranges npm version]][ranges npm link] | [![Dependency Status][ranges dependency status]][ranges dependency link] +[`@zendeskgarden/react-scrollbars`](packages/scrollbars) | [![npm version][scrollbars npm version]][scrollbars npm link] | [![Dependency Status][scrollbars dependency status]][scrollbars dependency link] [`@zendeskgarden/react-select`](packages/select) | [![npm version][select npm version]][select npm link] | [![Dependency Status][select dependency status]][select dependency link] [`@zendeskgarden/react-selection`](packages/selection) | [![npm version][selection npm version]][selection npm link] | [![Dependency Status][selection dependency status]][selection dependency link] [`@zendeskgarden/react-tabs`](packages/tabs) | [![npm version][tabs npm version]][tabs npm link] | [![Dependency Status][tabs dependency status]][tabs dependency link] @@ -94,6 +95,10 @@ Package | Version | Dependencies [ranges npm link]: https://www.npmjs.com/package/@zendeskgarden/react-ranges [ranges dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/ranges&style=flat-square [ranges dependency link]: https://david-dm.org/zendeskgarden/react-components?path=packages/ranges +[scrollbars npm version]: https://img.shields.io/npm/v/@zendeskgarden/react-scrollbars.svg?style=flat-square +[scrollbars npm link]: https://www.npmjs.com/package/@zendeskgarden/react-scrollbars +[scrollbars dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/scrollbars&style=flat-square +[scrollbars dependency link]: https://david-dm.org/zendeskgarden/react-components?path=packages/scrollbars [select npm version]: https://img.shields.io/npm/v/@zendeskgarden/react-select.svg?style=flat-square [select npm link]: https://www.npmjs.com/package/@zendeskgarden/react-select [select dependency status]: https://img.shields.io/david/zendeskgarden/react-components.svg?path=packages/select&style=flat-square diff --git a/demo/index.html b/demo/index.html index 65e851ced3d..0064550f3b1 100644 --- a/demo/index.html +++ b/demo/index.html @@ -126,6 +126,9 @@

React Components

Ranges
+
+ Scrollbars +
Select
diff --git a/packages/scrollbars/CHANGELOG.md b/packages/scrollbars/CHANGELOG.md new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/packages/scrollbars/CHANGELOG.md @@ -0,0 +1 @@ + diff --git a/packages/scrollbars/README.md b/packages/scrollbars/README.md new file mode 100644 index 00000000000..27b0ae31889 --- /dev/null +++ b/packages/scrollbars/README.md @@ -0,0 +1,34 @@ +# @zendeskgarden/react-scrollbars [![npm version](https://img.shields.io/npm/v/@zendeskgarden/react-scrollbars.svg?style=flat-square)](https://www.npmjs.com/package/@zendeskgarden/react-scrollbars) + +This package includes components relating to scrollbars in the +[Garden Design System](https://zendeskgarden.github.io/). + +## Installation + +```sh +npm install @zendeskgarden/react-scrollbars + +# Peer Dependencies - Also Required +npm install react react-dom prop-types styled-components @zendeskgarden/react-theming +``` + +## Usage + +```jsx static +/** + * Include scrollbars styling at the root of your application + */ +import '@zendeskgarden/react-scrollbars/dist/styles.css'; + +import { ThemeProvider } from '@zendeskgarden/react-theming'; +import { Scrollbar } from '@zendeskgarden/react-scrollbars'; + +/** + * Place a `ThemeProvider` at the root of your React application + */ + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit... + +; +``` diff --git a/packages/scrollbars/package.json b/packages/scrollbars/package.json new file mode 100644 index 00000000000..6106dfa885b --- /dev/null +++ b/packages/scrollbars/package.json @@ -0,0 +1,47 @@ +{ + "name": "@zendeskgarden/react-scrollbars", + "description": "Components relating to scrollbars 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", + "perfect-scrollbar": "^1.4.0" + }, + "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-scrollbars": "0.1.0", + "@zendeskgarden/css-variables": "5.1.0" + }, + "keywords": [ + "components", + "garden", + "react", + "zendesk" + ], + "publishConfig": { + "access": "public" + }, + "zendeskgarden:library": "GardenScrollbars", + "zendeskgarden:src": "src/index.js" +} diff --git a/packages/scrollbars/src/Scrollbar.example.md b/packages/scrollbars/src/Scrollbar.example.md new file mode 100644 index 00000000000..a0f92ba5c11 --- /dev/null +++ b/packages/scrollbars/src/Scrollbar.example.md @@ -0,0 +1,115 @@ +### Basic Scrollable Example + +Our custom scrollbar implementation is uses the +[perfect-scrollbars](https://github.com/utatti/perfect-scrollbar) +library internally. + +Take a look at their [API documentation](https://github.com/utatti/perfect-scrollbar#options) +to view all of the available customization options and events. + +```jsx +const { MD } = require('@zendeskgarden/react-typography'); +const { zdSpacing } = require('@zendeskgarden/css-variables'); + +const StyledSection = styled(MD)` + margin-bottom: ${zdSpacing}; +`; + +const ExampleText = () => ( + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque + laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto + beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut + odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. + Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, + sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat + voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit + laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui + in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat + quo voluptas nulla pariatur? + +); + + + + + + + + +; +``` + +### Dark Mode + +```jsx + + Dark Beach Example + +``` + +## Scroll Jumping + +```jsx +const { Button } = require('@zendeskgarden/react-buttons/src'); + +const StyledSection = styled.div` + margin-bottom: 16px; +`; + +const ExampleText = () => ( + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque + laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto + beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut + odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. + Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, + sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat + voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit + laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui + in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat + quo voluptas nulla pariatur? + +); + + + + + + + + + + + + + (this.scrollbarRef = ref)}> + + + + + + + + + +; +``` diff --git a/packages/scrollbars/src/Scrollbar.js b/packages/scrollbars/src/Scrollbar.js new file mode 100644 index 00000000000..500cd4c7a99 --- /dev/null +++ b/packages/scrollbars/src/Scrollbar.js @@ -0,0 +1,165 @@ +/** + * 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 } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; +import styled from 'styled-components'; +import PerfectScrollbar from 'perfect-scrollbar'; +import { isRtl, retrieveTheme } from '@zendeskgarden/react-theming'; + +/** + * These styles are NOT consumed through CSS Modules to allow + * interaction with with perfect-scrollbars. + */ +import '@zendeskgarden/css-scrollbars'; + +const EVENT_KEYS = { + SCROLL_Y: 'ps-scroll-y', + SCROLL_X: 'ps-scroll-x', + SCROLL_UP: 'ps-scroll-up', + SCROLL_DOWN: 'ps-scroll-down', + SCROLL_LEFT: 'ps-scroll-left', + SCROLL_RIGHT: 'ps-scroll-right', + REACH_START_Y: 'ps-y-reach-start', + REACH_END_Y: 'ps-y-reach-end', + REACH_START_X: 'ps-x-reach-start', + REACH_END_X: 'ps-x-reach-end' +}; + +const COMPONENT_ID = 'scrollbars.scrollbar'; + +/** + * Accepts all `
` props + */ +const ScrollbarContainer = styled.div.attrs({ + 'data-garden-id': COMPONENT_ID, + 'data-garden-version': PACKAGE_VERSION, + className: props => + classNames('c-scrollbar', { + 'c-scrollbar--dark': props.dark, + 'is-rtl': isRtl(props) + }) +})` + ${props => retrieveTheme(COMPONENT_ID, props)}; +`; + +export default class Scrollbar extends Component { + static propTypes = { + /** Apply dark mode styling */ + dark: PropTypes.bool, + /** Customizations provided to [perfect-scrollbars](https://github.com/utatti/perfect-scrollbar) */ + perfectScrollbarOptions: PropTypes.object, + /** Container ref */ + innerRef: PropTypes.func, + /** Custom tag for the containing element */ + tagName: PropTypes.any, + onScrollY: PropTypes.func, + onScrollX: PropTypes.func, + onScrollUp: PropTypes.func, + onScrollDown: PropTypes.func, + onScrollLeft: PropTypes.func, + onScrollRight: PropTypes.func, + onReachStartY: PropTypes.func, + onReachEndY: PropTypes.func, + onReachStartX: PropTypes.func, + onReachEndX: PropTypes.func + }; + + static defaultProps = { + tagName: 'div', + dark: false + }; + + componentDidMount() { + const { perfectScrollbarOptions } = this.props; + + if (this.scrollbarContainerRef) { + this.perfectScrollbar = new PerfectScrollbar( + this.scrollbarContainerRef, + perfectScrollbarOptions + ); + + for (const eventKey in EVENT_KEYS) { + if (Object.prototype.hasOwnProperty.call(EVENT_KEYS, eventKey)) { + this.scrollbarContainerRef.addEventListener( + EVENT_KEYS[eventKey], + this.onCustomEvent(EVENT_KEYS[eventKey]) + ); + } + } + } + } + + componentDidUpdate() { + if (this.perfectScrollbar) { + this.perfectScrollbar.update(); + } + } + + componentWillUnmount() { + this.perfectScrollbar.destroy(); + this.perfectScrollbar = null; + + for (const eventKey in EVENT_KEYS) { + if (Object.prototype.hasOwnProperty.call(EVENT_KEYS, eventKey)) { + this.scrollbarContainerRef.removeEventListener( + EVENT_KEYS[eventKey], + this.onCustomEvent(EVENT_KEYS[eventKey]) + ); + } + } + } + + onCustomEvent = eventKey => { + const { + onScrollY, + onScrollX, + onScrollUp, + onScrollDown, + onScrollLeft, + onScrollRight, + onReachStartY, + onReachEndY, + onReachStartX, + onReachEndX + } = this.props; + + const EVENTS = { + [EVENT_KEYS.SCROLL_Y]: onScrollY, + [EVENT_KEYS.SCROLL_X]: onScrollX, + [EVENT_KEYS.SCROLL_UP]: onScrollUp, + [EVENT_KEYS.SCROLL_DOWN]: onScrollDown, + [EVENT_KEYS.SCROLL_LEFT]: onScrollLeft, + [EVENT_KEYS.SCROLL_RIGHT]: onScrollRight, + [EVENT_KEYS.REACH_START_Y]: onReachStartY, + [EVENT_KEYS.REACH_END_Y]: onReachEndY, + [EVENT_KEYS.REACH_START_X]: onReachStartX, + [EVENT_KEYS.REACH_END_X]: onReachEndX + }; + + return (...args) => EVENTS[eventKey] && EVENTS[eventKey](...args); + }; + + render() { + const { children, innerRef, tagName, ...other } = this.props; + + const CustomScrollbarContainer = ScrollbarContainer.withComponent(tagName); + + return ( + { + this.scrollbarContainerRef = ref; + innerRef && innerRef(ref); + }} + > + {children} + + ); + } +} diff --git a/packages/scrollbars/src/index.js b/packages/scrollbars/src/index.js new file mode 100644 index 00000000000..9dc64607337 --- /dev/null +++ b/packages/scrollbars/src/index.js @@ -0,0 +1,8 @@ +/** + * 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 Scrollbar } from './Scrollbar'; diff --git a/packages/scrollbars/styleguide.config.js b/packages/scrollbars/styleguide.config.js new file mode 100644 index 00000000000..54e214cd90e --- /dev/null +++ b/packages/scrollbars/styleguide.config.js @@ -0,0 +1,23 @@ +/** + * 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. + */ + +/** + * Package specific styleguide configuration + * https://github.com/styleguidist/react-styleguidist/blob/master/docs/Configuration.md + */ +module.exports = { + sections: [ + { + name: '', + content: '../../packages/scrollbars/README.md' + }, + { + name: 'Components', + components: '../../packages/scrollbars/src/[A-Z]*.js' + } + ] +}; diff --git a/utils/build/webpack.base.js b/utils/build/webpack.base.js index 12adcf726d4..3d6ec559d1d 100644 --- a/utils/build/webpack.base.js +++ b/utils/build/webpack.base.js @@ -73,12 +73,12 @@ found at http://www.apache.org/licenses/LICENSE-2.0 }, { test: /\.css?$/u, - exclude: /@zendeskgarden\/css/u, + exclude: /@zendeskgarden\/css-(?!scrollbars)/u, use: [MiniCssExtractPlugin.loader, 'css-loader'] }, { test: /\.css?$/u, - include: /@zendeskgarden\/css/u, + include: /@zendeskgarden\/css-(?!scrollbars)/u, use: [ MiniCssExtractPlugin.loader, { diff --git a/utils/styleguide/public/images/beach-dark.jpg b/utils/styleguide/public/images/beach-dark.jpg new file mode 100644 index 00000000000..0feeffa5967 Binary files /dev/null and b/utils/styleguide/public/images/beach-dark.jpg differ diff --git a/utils/styleguide/styleguide.base.config.js b/utils/styleguide/styleguide.base.config.js index 06c167728bf..e49c62f921e 100644 --- a/utils/styleguide/styleguide.base.config.js +++ b/utils/styleguide/styleguide.base.config.js @@ -138,12 +138,12 @@ const defaultStyleguideConfig = { }, { test: /\.css$/u, - exclude: /@zendeskgarden\/css-/u, + exclude: /@zendeskgarden\/css-(?!scrollbars)/u, loader: 'style-loader!css-loader' }, { test: /\.css$/u, - include: /@zendeskgarden\/css-/u, + include: /@zendeskgarden\/css-(?!scrollbars)/u, loader: 'style-loader!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' }, @@ -188,6 +188,7 @@ const defaultStyleguideConfig = { '@zendeskgarden/react-pagination': path.resolve('..', 'pagination'), '@zendeskgarden/react-radios': path.resolve('..', 'radios'), '@zendeskgarden/react-ranges': path.resolve('..', 'ranges'), + '@zendeskgarden/react-scrollbars': path.resolve('..', 'scrollbars'), '@zendeskgarden/react-select': path.resolve('..', 'select'), '@zendeskgarden/react-selection': path.resolve('..', 'selection'), '@zendeskgarden/react-tables': path.resolve('..', 'tables'),