From be89956682d86e35b3857bf3d76e4180ab101c0f Mon Sep 17 00:00:00 2001 From: Vandish Gandhi Date: Fri, 5 Apr 2019 14:57:52 +1100 Subject: [PATCH] feat: overlay loader --- docs/components/Layout/index.jsx | 4 +- docs/examples/OverlayLoaderExample.jsx | 104 ++++++++++++++++++ docs/examples/styles.scss | 20 ++++ .../adslot-ui/OverlayLoader/index.jsx | 33 ++++++ .../adslot-ui/OverlayLoader/index.spec.jsx | 26 +++++ .../adslot-ui/OverlayLoader/styles.scss | 54 +++++++++ src/components/adslot-ui/index.js | 2 + src/index.js | 2 + src/styles/variable.scss | 2 + 9 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 docs/examples/OverlayLoaderExample.jsx create mode 100644 src/components/adslot-ui/OverlayLoader/index.jsx create mode 100644 src/components/adslot-ui/OverlayLoader/index.spec.jsx create mode 100644 src/components/adslot-ui/OverlayLoader/styles.scss diff --git a/docs/components/Layout/index.jsx b/docs/components/Layout/index.jsx index 1290bff8f..9577d60dc 100644 --- a/docs/components/Layout/index.jsx +++ b/docs/components/Layout/index.jsx @@ -60,6 +60,7 @@ import InformationBoxExample from '../../examples/InformationBoxExample'; import SplitPaneExample from '../../examples/SplitPaneExample'; import HoverDropdownMenuExample from '../../examples/HoverDropdownMenuExample'; import NavigationExample from '../../examples/NavigationExample'; +import OverlayLoaderExample from '../../examples/OverlayLoaderExample'; import './styles.scss'; import '../../examples/styles.scss'; @@ -93,7 +94,7 @@ const componentsBySection = { 'stats-and-data': ['count-badge', 'statistic', 'totals', 'slicey'], 'icons-and-graphics': ['svg-symbol', 'svg-symbol-circle'], navigation: ['breadcrumb', 'tab', 'hover-dropdown-menu', 'navigation-tabs'], - 'feedback-and-states': ['alert', 'empty', 'spinner', 'pretty-diff', 'status-pill'], + 'feedback-and-states': ['alert', 'empty', 'spinner', 'overlay-loader', 'pretty-diff', 'status-pill'], dialogue: ['popover', 'help-icon-popover', 'avatar'], modals: ['confirm-modal'], search: ['search', 'search-bar', 'tag'], @@ -213,6 +214,7 @@ class PageLayout extends React.Component { + diff --git a/docs/examples/OverlayLoaderExample.jsx b/docs/examples/OverlayLoaderExample.jsx new file mode 100644 index 000000000..63a27eb18 --- /dev/null +++ b/docs/examples/OverlayLoaderExample.jsx @@ -0,0 +1,104 @@ +import React from 'react'; +import Example from '../components/Example'; +import { OverlayLoader, Button } from '../../src'; + +class OverlayLoaderExample extends React.Component { + constructor(props) { + super(props); + this.state = { + showDisabledLoader: false, + showLoader: false, + }; + this.toggleLoader = this.toggleLoader.bind(this); + this.toggleDisabledLoader = this.toggleDisabledLoader.bind(this); + this.handleKeyPress = this.handleKeyPress.bind(this); + } + + handleKeyPress(event) { + if (event.keyCode === 27) { + this.toggleDisabledLoader(); + } + } + + toggleLoader() { + this.setState(prevState => ({ showLoader: !prevState.showLoader })); + } + + toggleDisabledLoader() { + this.setState( + prevState => ({ showDisabledLoader: !prevState.showDisabledLoader, showLoader: false }), + () => { + const eventTrigger = this.state.showDisabledLoader ? window.addEventListener : window.removeEventListener; + eventTrigger('keydown', this.handleKeyPress); + } + ); + } + + render() { + return ( + +

Static markup

+
+ + +
+
+

Demo

+ + {this.state.showLoader && } + + {this.state.showDisabledLoader && } +
+
+ ); + } +} + +const exampleProps = { + componentName: 'Overlay Loader', + notes: `Fixed position loader which provides user experience for page loading or interim loading states`, + exampleCodeSnippet: ` + // Static Markup Loaders + + + + // Demo Loaders + + + `, + propTypeSectionArray: [ + { + propTypes: [ + { + defaultValue: 'Loading', + propType: 'heading', + type: 'string', + }, + { + propType: 'top', + type: 'number', + note: 'Position from top of DOM', + defaultValue: '320', + }, + { + propType: 'text', + type: 'string', + }, + { + propType: 'disableBackground', + defaultValue: 'false', + type: 'bool', + note: 'prevents event propogation', + }, + ], + }, + ], +}; + +export default () => ( + + + +); diff --git a/docs/examples/styles.scss b/docs/examples/styles.scss index aca7bfb67..990932840 100644 --- a/docs/examples/styles.scss +++ b/docs/examples/styles.scss @@ -107,6 +107,26 @@ } } } + + &.overlay-loader-example { + .static-markup { + display: flex; + margin-bottom: 50px; + width: 300px; + + .aui--overlay-loader { + position: inherit; + + .loader { + position: inherit; + } + + + .aui--overlay-loader { + margin-right: 10px; + } + } + } + } } .full-width { diff --git a/src/components/adslot-ui/OverlayLoader/index.jsx b/src/components/adslot-ui/OverlayLoader/index.jsx new file mode 100644 index 000000000..690e9d4d8 --- /dev/null +++ b/src/components/adslot-ui/OverlayLoader/index.jsx @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +// import ReactDOM from 'react-dom'; +import { Spinner } from 'alexandria'; +import './styles.scss'; + +const OverlayLoader = ({ text, top, heading, disableBackground }) => ( +
event.stopPropagation() } : {})} + > +
+ + {heading} + {text && {text}} +
+
+); + +OverlayLoader.defaultProps = { + heading: 'Loading', + top: 320, + disableBackground: false, +}; + +OverlayLoader.propTypes = { + heading: PropTypes.string, + text: PropTypes.string, + top: PropTypes.number, + disableBackground: PropTypes.bool, +}; + +export default OverlayLoader; diff --git a/src/components/adslot-ui/OverlayLoader/index.spec.jsx b/src/components/adslot-ui/OverlayLoader/index.spec.jsx new file mode 100644 index 000000000..2f5ed67c4 --- /dev/null +++ b/src/components/adslot-ui/OverlayLoader/index.spec.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import sinon from 'sinon'; +import { mount, shallow } from 'enzyme'; +import OverlayLoader from 'adslot-ui/OverlayLoader'; + +describe('Overlay Loader Component', () => { + it('should render Overlay Loader', () => { + const wrapper = shallow(); + expect(wrapper.find('.aui--overlay-loader')).to.have.length(1); + expect(wrapper.find('.loader-heading').text()).to.equal('Loading'); + }); + + it('should stop event propogation when disabled background', () => { + const eventStub = sinon.stub(); + const wrapper = mount( +
eventStub} className="my-div"> + +
+ ); + expect(wrapper.find(OverlayLoader)).to.have.length(1); + expect(wrapper.find('.loader-heading').text()).to.equal('Loading'); + wrapper.find('.my-div').simulate('click'); + wrapper.find(OverlayLoader).simulate('click'); + expect(eventStub.called).to.equal(false); + }); +}); diff --git a/src/components/adslot-ui/OverlayLoader/styles.scss b/src/components/adslot-ui/OverlayLoader/styles.scss new file mode 100644 index 000000000..ba5ee9d34 --- /dev/null +++ b/src/components/adslot-ui/OverlayLoader/styles.scss @@ -0,0 +1,54 @@ +@import '~styles/variable'; + +.aui--overlay-loader { + width: 100%; + height: 100%; + position: absolute; + top: 10px; + z-index: $zindex-popover; + + &-disabled { + position: fixed; + } + + .loader { + position: fixed; + left: 50%; + width: 96px; + min-height: 132px; + background-color: $color-gray-white; + display: flex; + align-items: center; + flex-direction: column; + padding: 20px; + border-radius: 5px; + border: 1px solid $color-gray-light; + font-weight: $font-weight-bold; + opacity: .9; + line-height: 14px; + + .loader-heading { + color: $color-gray-darker; + } + + .loader-text { + margin-top: 5px; + color: $color-gray; + width: 65px; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-word; + } + + .spinner-medium { + height: 40px; + width: 40px; + margin-bottom: 20px; + border-bottom-color: $color-gray-darker; + border-left-color: $color-gray-darker; + border-right-color: $color-gray-darker; + border-width: 5px; + } + } +} diff --git a/src/components/adslot-ui/index.js b/src/components/adslot-ui/index.js index c54f7af24..27604c532 100644 --- a/src/components/adslot-ui/index.js +++ b/src/components/adslot-ui/index.js @@ -32,6 +32,7 @@ import HoverDropdownMenu from 'adslot-ui/HoverDropdownMenu'; import fastStatelessWrapper from 'adslot-ui/fastStatelessWrapper'; import InformationBox from 'adslot-ui/InformationBox'; import Nav from 'adslot-ui/Navigation'; +import OverlayLoader from 'adslot-ui/OverlayLoader'; export { Accordion, @@ -68,4 +69,5 @@ export { UserListPicker, InformationBox, HoverDropdownMenu, + OverlayLoader, }; diff --git a/src/index.js b/src/index.js index d4e6ac5d8..8032f76cf 100644 --- a/src/index.js +++ b/src/index.js @@ -77,6 +77,7 @@ import { UserListPicker, InformationBox, HoverDropdownMenu, + OverlayLoader, } from 'adslot-ui'; export { @@ -145,4 +146,5 @@ export { UserListPicker, InformationBox, HoverDropdownMenu, + OverlayLoader, }; diff --git a/src/styles/variable.scss b/src/styles/variable.scss index 755f40154..bcbb05f0f 100644 --- a/src/styles/variable.scss +++ b/src/styles/variable.scss @@ -127,6 +127,8 @@ $popover-border-color: $color-border-lighter; $popover-arrow-width: 5px; $popover-arrow-outer-color: $popover-border-color; $popover-title-bg: $color-background; +$zindex-popover: 1060; + // == TreePicker $treepicker-height: 500px;