From 8c56cb9e8873cee958cf657892ad78198b4a83ae Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Wed, 12 Jul 2017 18:34:30 +0300 Subject: [PATCH 1/8] feat(Breakpoint): add component --- src/addons/Breakpoint/Breakpoint.d.ts | 42 +++++ src/addons/Breakpoint/Breakpoint.js | 152 ++++++++++++++++++ src/addons/Breakpoint/index.d.ts | 1 + src/addons/Breakpoint/index.js | 1 + src/index.js | 1 + .../addons/Breakpoint/Breakpoint-test.js | 87 ++++++++++ test/specs/commonTests/isConformant.js | 14 +- 7 files changed, 291 insertions(+), 7 deletions(-) create mode 100644 src/addons/Breakpoint/Breakpoint.d.ts create mode 100644 src/addons/Breakpoint/Breakpoint.js create mode 100644 src/addons/Breakpoint/index.d.ts create mode 100644 src/addons/Breakpoint/index.js create mode 100644 test/specs/addons/Breakpoint/Breakpoint-test.js diff --git a/src/addons/Breakpoint/Breakpoint.d.ts b/src/addons/Breakpoint/Breakpoint.d.ts new file mode 100644 index 0000000000..5d0eed3f38 --- /dev/null +++ b/src/addons/Breakpoint/Breakpoint.d.ts @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { GridOnlyProp } from '../../collections/Grid/GridColumn'; + +export interface BreakpointProps { + [key: string]: any; + + /** An element type to render as (string or function). */ + as?: any; + + /** Primary content. */ + children?: React.ReactNode; + + /** A row can appear only for a specific device, or screen sizes. */ + only: GridOnlyProp; + + /** + * Called on update. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onUpdate?: BreakpointOnUpdateData; + + /** Breakpoints definition. */ + points?: BreakpointPoints; +} + +export interface BreakpointPoints { + computer: number; + largeScreen: number; + mobile: number; + tablet: number; + widescreen: number; +} + +export interface BreakpointOnUpdateData extends BreakpointProps { + number: string; +} + +declare const Confirm: React.ComponentClass; + +export default Confirm; diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Breakpoint/Breakpoint.js new file mode 100644 index 0000000000..5183c2b315 --- /dev/null +++ b/src/addons/Breakpoint/Breakpoint.js @@ -0,0 +1,152 @@ +import _ from 'lodash' +import PropTypes from 'prop-types' +import React, { Component } from 'react' + +import { + customPropTypes, + getElementType, + getUnhandledProps, + META, + SUI, +} from '../../lib' + +/** + * A Breakpoint can control visibility of content. + */ +export default class Breakpoint extends Component { + static propTypes = { + /** An element type to render as (string or function). */ + as: customPropTypes.as, + + /** Primary content. */ + children: PropTypes.node, + + /** A row can appear only for a specific device, or screen sizes. */ + only: customPropTypes.onlyProp(SUI.VISIBILITY).isRequired, + + /** + * Called on update. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onUpdate: PropTypes.func, + + /** Breakpoints definition. */ + points: PropTypes.shape({ + computer: PropTypes.number.isRequired, + largeScreen: PropTypes.number.isRequired, + mobile: PropTypes.number.isRequired, + tablet: PropTypes.number.isRequired, + widescreen: PropTypes.number.isRequired, + }), + } + + static defaultProps = { + points: { + computer: 992, + largeScreen: 1200, + mobile: 320, + tablet: 768, + widescreen: 1920, + }, + wait: 200, + } + + static _meta = { + name: 'Breakpoint', + type: META.TYPES.ADDON, + } + + constructor(...args) { + super(...args) + this.state = { width: window.innerWidth } + } + + componentDidMount() { + window.addEventListener('resize', this.handleUpdate) + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleUpdate) + } + + // ---------------------------------------- + // Breakpoint matchers + // ---------------------------------------- + + computer = () => { + const { points: { computer } } = this.props + const { width } = this.state + + return width >= computer + } + + largeScreen = () => { + const { points: { largeScreen, widescreen } } = this.props + const { width } = this.state + + return width >= largeScreen && width < widescreen + } + + mobile = () => { + const { points: { mobile, tablet } } = this.props + const { width } = this.state + + return width >= mobile && width < tablet + } + + tablet = () => { + const { points: { computer, tablet } } = this.props + const { width } = this.state + + return width >= tablet && width < computer + } + + widescreen = () => { + const { points: { widescreen } } = this.props + const { width } = this.state + + return width >= widescreen + } + + // ---------------------------------------- + // Helpers + // ---------------------------------------- + + visible = () => { + const { only } = this.props + const points = only.replace('large screen', 'largeScreen').split(' ') + + return _.some(points, point => _.invoke(this, point)) + } + + // ---------------------------------------- + // Event handlers + // ---------------------------------------- + + handleUpdate = e => { + requestAnimationFrame(() => { + const width = window.innerWidth + + this.setState({ width }) + _.invoke(this.props, 'onUpdate', e, { ...this.props, width }) + }) + } + + // ---------------------------------------- + // Render + // ---------------------------------------- + + render() { + const { children } = this.props + + const ElementType = getElementType(Breakpoint, this.props) + const rest = getUnhandledProps(Breakpoint, this.props) + + if (this.visible()) return {children} + return null + } +} + + diff --git a/src/addons/Breakpoint/index.d.ts b/src/addons/Breakpoint/index.d.ts new file mode 100644 index 0000000000..ee3195b503 --- /dev/null +++ b/src/addons/Breakpoint/index.d.ts @@ -0,0 +1 @@ +export { default, BreakpointPoints, BreakpointProps } from './Breakpoint'; diff --git a/src/addons/Breakpoint/index.js b/src/addons/Breakpoint/index.js new file mode 100644 index 0000000000..3d0e973b25 --- /dev/null +++ b/src/addons/Breakpoint/index.js @@ -0,0 +1 @@ +export default from './Breakpoint' diff --git a/src/index.js b/src/index.js index e6875c7bbe..999ca664ca 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ // Addons +export { default as Breakpoint } from './addons/Breakpoint' export { default as Confirm } from './addons/Confirm' export { default as Portal } from './addons/Portal' export { default as Radio } from './addons/Radio' diff --git a/test/specs/addons/Breakpoint/Breakpoint-test.js b/test/specs/addons/Breakpoint/Breakpoint-test.js new file mode 100644 index 0000000000..198bf8b551 --- /dev/null +++ b/test/specs/addons/Breakpoint/Breakpoint-test.js @@ -0,0 +1,87 @@ +import _ from 'lodash' +import React from 'react' + +import Breakpoint from 'src/addons/Breakpoint/Breakpoint' +import * as common from 'test/specs/commonTests' +import { domEvent, sandbox } from 'test/utils' + +const { points } = Breakpoint.defaultProps +const requiredProps = { only: 'mobile' } + +beforeEach(() => { + sandbox.stub(window, 'innerWidth').value(points.mobile) +}) + +describe('Breakpoint', () => { + common.isConformant(Breakpoint, { requiredProps }) + common.rendersChildren(Breakpoint, { requiredProps }) + + describe('children', () => { + _.each(points, (value, point) => { + it(`renders when point "${point}" fits`, () => { + sandbox.stub(window, 'innerWidth').value(value) + + shallow() + .should.have.tagName('div') + }) + }) + + _.each(points, (value, point) => { + it(`return null when point "${point}" doesn't fit`, () => { + sandbox.stub(window, 'innerWidth').value(value - 1) + + shallow() + .should.be.blank() + }) + }) + + it('renders when fits multiple points', () => { + sandbox.stub(window, 'innerWidth').value(points.largeScreen) + + shallow() + .should.have.tagName('div') + shallow() + .should.have.tagName('div') + + shallow() + .should.have.tagName('div') + }) + + it('renders when fits one of points', () => { + shallow() + .should.have.tagName('div') + shallow() + .should.be.blank() + + shallow() + .should.have.tagName('div') + }) + }) + + describe('onUpdate', () => { + it('listens for resize', () => { + const wrapper = mount() + wrapper.should.have.tagName('div') + + sandbox.stub(window, 'innerWidth').value(points.tablet) + domEvent.fire(window, 'resize') + wrapper.should.be.blank() + }) + + it('is called with (e, data) when window was resized', done => { + const onUpdate = sandbox.spy() + const width = points.tablet + + mount() + sandbox.stub(window, 'innerWidth').value(width) + domEvent.fire(window, 'resize') + + setTimeout(() => { + onUpdate.should.have.been.calledOnce() + onUpdate.should.have.been.calledWithMatch({}, { width, only: 'mobile' }) + + done() + }, 10) + }) + }) +}) diff --git a/test/specs/commonTests/isConformant.js b/test/specs/commonTests/isConformant.js index d97e5024e6..6bd17e0355 100644 --- a/test/specs/commonTests/isConformant.js +++ b/test/specs/commonTests/isConformant.js @@ -131,12 +131,12 @@ export default (Component, options = {}) => { const tags = ['a', 'em', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'p', 'span', 'strong'] try { tags.forEach((tag) => { - shallow() + shallow() .should.have.tagName(tag) }) } catch (err) { tags.forEach((tag) => { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', tag) }) @@ -147,11 +147,11 @@ export default (Component, options = {}) => { const MyComponent = () => null try { - shallow() + shallow() .type() .should.equal(MyComponent) } catch (err) { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', MyComponent) } @@ -165,11 +165,11 @@ export default (Component, options = {}) => { } try { - shallow() + shallow() .type() .should.equal(MyComponent) } catch (err) { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', MyComponent) } @@ -178,7 +178,7 @@ export default (Component, options = {}) => { it('passes extra props to the component it is renders as', () => { const MyComponent = () => null - shallow() + shallow() .should.have.descendants('[data-extra-prop="foo"]') }) }) From c419b0534b1ebaf84d3033ce79ada603dabe0f31 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Wed, 12 Jul 2017 18:34:30 +0300 Subject: [PATCH 2/8] feat(Breakpoint): add component --- .../Types/BreakpointExampleBreakpoint.js | 14 ++ .../Types/BreakpointExampleContent.js | 38 +++++ .../Types/BreakpointExampleMultiple.js | 11 ++ .../Types/BreakpointExampleNested.js | 27 ++++ .../Examples/addons/Breakpoint/Types/index.js | 36 +++++ .../Usage/BreakpointExamplePoints.js | 22 +++ .../Examples/addons/Breakpoint/Usage/index.js | 16 ++ docs/app/Examples/addons/Breakpoint/index.js | 13 ++ src/addons/Breakpoint/Breakpoint.d.ts | 42 +++++ src/addons/Breakpoint/Breakpoint.js | 151 ++++++++++++++++++ src/addons/Breakpoint/index.d.ts | 1 + src/addons/Breakpoint/index.js | 1 + src/index.js | 1 + .../addons/Breakpoint/Breakpoint-test.js | 91 +++++++++++ test/specs/commonTests/isConformant.js | 14 +- 15 files changed, 471 insertions(+), 7 deletions(-) create mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js create mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js create mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js create mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js create mode 100644 docs/app/Examples/addons/Breakpoint/Types/index.js create mode 100644 docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js create mode 100644 docs/app/Examples/addons/Breakpoint/Usage/index.js create mode 100644 docs/app/Examples/addons/Breakpoint/index.js create mode 100644 src/addons/Breakpoint/Breakpoint.d.ts create mode 100644 src/addons/Breakpoint/Breakpoint.js create mode 100644 src/addons/Breakpoint/index.d.ts create mode 100644 src/addons/Breakpoint/index.js create mode 100644 test/specs/addons/Breakpoint/Breakpoint-test.js diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js new file mode 100644 index 0000000000..6f98e8b9e1 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js @@ -0,0 +1,14 @@ +import React from 'react' +import { Breakpoint, Segment } from 'semantic-ui-react' + +const BreakpointExampleBreakpoint = () => ( + + Mobile + Tablet + Computer + Large Screen + Widescreen + +) + +export default BreakpointExampleBreakpoint diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js new file mode 100644 index 0000000000..0cd80a74b1 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js @@ -0,0 +1,38 @@ +import React, { Component } from 'react' +import { Breakpoint, Button, Menu } from 'semantic-ui-react' + +export default class BreakpointExampleContent extends Component { + state = { active: 'home' } + + handleItemClick = (e, { name }) => this.setState({ active: name }) + + render() { + const { active } = this.state + + return ( + + + + + + + + + + + + ) + } +} diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js new file mode 100644 index 0000000000..31019d6924 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js @@ -0,0 +1,11 @@ +import React from 'react' +import { Breakpoint, Segment } from 'semantic-ui-react' + +const BreakpointExampleMultiple = () => ( + + Mobile & Tablet + Tablet & Computer + +) + +export default BreakpointExampleMultiple diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js new file mode 100644 index 0000000000..dc60518a2e --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js @@ -0,0 +1,27 @@ +import React from 'react' +import { Breakpoint, Segment } from 'semantic-ui-react' + +const BreakpointExampleNested = () => ( + + + +

Mobile

+
+ +

Mobile

+
+
+ + +

Computer

+ +

Large Screen

+
+ +

Widescreen

+
+
+
+) + +export default BreakpointExampleNested diff --git a/docs/app/Examples/addons/Breakpoint/Types/index.js b/docs/app/Examples/addons/Breakpoint/Types/index.js new file mode 100644 index 0000000000..dacdb654d7 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Types/index.js @@ -0,0 +1,36 @@ +import React from 'react' +import { Message } from 'semantic-ui-react' + +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const BreakpointTypesExamples = () => ( + + + + Instead of Grid visibility breakpoints, Breakpoint doesn't render invisible content. + + + + + + +) + +export default BreakpointTypesExamples diff --git a/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js b/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js new file mode 100644 index 0000000000..35864a6126 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js @@ -0,0 +1,22 @@ +import React from 'react' +import { Breakpoint, Segment } from 'semantic-ui-react' + +const points = { + computer: 990, + largeScreen: 1300, + mobile: 300, + tablet: 800, + widescreen: 1900, +} + +const BreakpointExamplePoints = () => ( + + Mobile + Tablet + Computer + Large Screen + Widescreen + +) + +export default BreakpointExamplePoints diff --git a/docs/app/Examples/addons/Breakpoint/Usage/index.js b/docs/app/Examples/addons/Breakpoint/Usage/index.js new file mode 100644 index 0000000000..5945c12712 --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/Usage/index.js @@ -0,0 +1,16 @@ +import React from 'react' + +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const BreakpointUsageExamples = () => ( + + + +) + +export default BreakpointUsageExamples diff --git a/docs/app/Examples/addons/Breakpoint/index.js b/docs/app/Examples/addons/Breakpoint/index.js new file mode 100644 index 0000000000..0274a6635b --- /dev/null +++ b/docs/app/Examples/addons/Breakpoint/index.js @@ -0,0 +1,13 @@ +import React from 'react' + +import Types from './Types' +import Usage from './Usage' + +const BreakpointExamples = () => ( +
+ + +
+) + +export default BreakpointExamples diff --git a/src/addons/Breakpoint/Breakpoint.d.ts b/src/addons/Breakpoint/Breakpoint.d.ts new file mode 100644 index 0000000000..5d0eed3f38 --- /dev/null +++ b/src/addons/Breakpoint/Breakpoint.d.ts @@ -0,0 +1,42 @@ +import * as React from 'react'; +import { GridOnlyProp } from '../../collections/Grid/GridColumn'; + +export interface BreakpointProps { + [key: string]: any; + + /** An element type to render as (string or function). */ + as?: any; + + /** Primary content. */ + children?: React.ReactNode; + + /** A row can appear only for a specific device, or screen sizes. */ + only: GridOnlyProp; + + /** + * Called on update. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onUpdate?: BreakpointOnUpdateData; + + /** Breakpoints definition. */ + points?: BreakpointPoints; +} + +export interface BreakpointPoints { + computer: number; + largeScreen: number; + mobile: number; + tablet: number; + widescreen: number; +} + +export interface BreakpointOnUpdateData extends BreakpointProps { + number: string; +} + +declare const Confirm: React.ComponentClass; + +export default Confirm; diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Breakpoint/Breakpoint.js new file mode 100644 index 0000000000..cf2481da55 --- /dev/null +++ b/src/addons/Breakpoint/Breakpoint.js @@ -0,0 +1,151 @@ +import _ from 'lodash' +import PropTypes from 'prop-types' +import React, { Component } from 'react' + +import { + customPropTypes, + getElementType, + getUnhandledProps, + META, + SUI, +} from '../../lib' + +/** + * A Breakpoint can control visibility of content. + */ +export default class Breakpoint extends Component { + static propTypes = { + /** An element type to render as (string or function). */ + as: customPropTypes.as, + + /** Primary content. */ + children: PropTypes.node, + + /** A row can appear only for a specific device, or screen sizes. */ + only: customPropTypes.onlyProp(SUI.VISIBILITY).isRequired, + + /** + * Called on update. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onUpdate: PropTypes.func, + + /** Breakpoints definition. */ + points: PropTypes.shape({ + computer: PropTypes.number.isRequired, + largeScreen: PropTypes.number.isRequired, + mobile: PropTypes.number.isRequired, + tablet: PropTypes.number.isRequired, + widescreen: PropTypes.number.isRequired, + }), + } + + static defaultProps = { + points: { + computer: 992, + largeScreen: 1200, + mobile: 320, + tablet: 768, + widescreen: 1920, + }, + } + + static _meta = { + name: 'Breakpoint', + type: META.TYPES.ADDON, + } + + constructor(...args) { + super(...args) + this.state = { width: window.innerWidth } + } + + componentDidMount() { + window.addEventListener('resize', this.handleUpdate) + } + + componentWillUnmount() { + window.removeEventListener('resize', this.handleUpdate) + } + + // ---------------------------------------- + // Breakpoint matchers + // ---------------------------------------- + + computer = () => { + const { points: { computer } } = this.props + const { width } = this.state + + return width >= computer + } + + largeScreen = () => { + const { points: { largeScreen, widescreen } } = this.props + const { width } = this.state + + return width >= largeScreen && width < widescreen + } + + mobile = () => { + const { points: { mobile, tablet } } = this.props + const { width } = this.state + + return width >= mobile && width < tablet + } + + tablet = () => { + const { points: { computer, tablet } } = this.props + const { width } = this.state + + return width >= tablet && width < computer + } + + widescreen = () => { + const { points: { widescreen } } = this.props + const { width } = this.state + + return width >= widescreen + } + + // ---------------------------------------- + // Helpers + // ---------------------------------------- + + visible = () => { + const { only } = this.props + const points = only.replace('large screen', 'largeScreen').split(' ') + + return _.some(points, point => _.invoke(this, point)) + } + + // ---------------------------------------- + // Event handlers + // ---------------------------------------- + + handleUpdate = e => { + requestAnimationFrame(() => { + const width = window.innerWidth + + this.setState({ width }) + _.invoke(this.props, 'onUpdate', e, { ...this.props, width }) + }) + } + + // ---------------------------------------- + // Render + // ---------------------------------------- + + render() { + const { children } = this.props + + const ElementType = getElementType(Breakpoint, this.props) + const rest = getUnhandledProps(Breakpoint, this.props) + + if (this.visible()) return {children} + return null + } +} + + diff --git a/src/addons/Breakpoint/index.d.ts b/src/addons/Breakpoint/index.d.ts new file mode 100644 index 0000000000..ee3195b503 --- /dev/null +++ b/src/addons/Breakpoint/index.d.ts @@ -0,0 +1 @@ +export { default, BreakpointPoints, BreakpointProps } from './Breakpoint'; diff --git a/src/addons/Breakpoint/index.js b/src/addons/Breakpoint/index.js new file mode 100644 index 0000000000..3d0e973b25 --- /dev/null +++ b/src/addons/Breakpoint/index.js @@ -0,0 +1 @@ +export default from './Breakpoint' diff --git a/src/index.js b/src/index.js index e6875c7bbe..999ca664ca 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ // Addons +export { default as Breakpoint } from './addons/Breakpoint' export { default as Confirm } from './addons/Confirm' export { default as Portal } from './addons/Portal' export { default as Radio } from './addons/Radio' diff --git a/test/specs/addons/Breakpoint/Breakpoint-test.js b/test/specs/addons/Breakpoint/Breakpoint-test.js new file mode 100644 index 0000000000..ad9e40c4d7 --- /dev/null +++ b/test/specs/addons/Breakpoint/Breakpoint-test.js @@ -0,0 +1,91 @@ +import _ from 'lodash' +import React from 'react' + +import Breakpoint from 'src/addons/Breakpoint/Breakpoint' +import * as common from 'test/specs/commonTests' +import { domEvent, sandbox } from 'test/utils' + +const { points } = Breakpoint.defaultProps +const requiredProps = { only: 'mobile' } + +beforeEach(() => { + sandbox.stub(window, 'innerWidth').value(points.mobile) +}) + +describe('Breakpoint', () => { + common.isConformant(Breakpoint, { requiredProps }) + common.rendersChildren(Breakpoint, { requiredProps }) + + describe('children', () => { + _.each(points, (value, point) => { + it(`renders when point "${point}" fits`, () => { + sandbox.stub(window, 'innerWidth').value(value) + + shallow() + .should.have.tagName('div') + }) + }) + + _.each(points, (value, point) => { + it(`return null when point "${point}" doesn't fit`, () => { + sandbox.stub(window, 'innerWidth').value(value - 1) + + shallow() + .should.be.blank() + }) + }) + + it('renders when fits multiple points', () => { + sandbox.stub(window, 'innerWidth').value(points.largeScreen) + + shallow() + .should.have.tagName('div') + shallow() + .should.have.tagName('div') + + shallow() + .should.have.tagName('div') + }) + + it('renders when fits one of points', () => { + shallow() + .should.have.tagName('div') + shallow() + .should.be.blank() + + shallow() + .should.have.tagName('div') + }) + }) + + describe('onUpdate', () => { + it('listens for resize', done => { + const wrapper = mount() + wrapper.should.have.tagName('div') + + sandbox.stub(window, 'innerWidth').value(points.tablet) + domEvent.fire(window, 'resize') + + setTimeout(() => { + wrapper.should.be.blank() + done() + }, 25) + }) + + it('is called with (e, data) when window was resized', done => { + const onUpdate = sandbox.spy() + const width = points.tablet + + mount() + sandbox.stub(window, 'innerWidth').value(width) + domEvent.fire(window, 'resize') + + setTimeout(() => { + onUpdate.should.have.been.calledOnce() + onUpdate.should.have.been.calledWithMatch({}, { width, only: 'mobile' }) + + done() + }, 25) + }) + }) +}) diff --git a/test/specs/commonTests/isConformant.js b/test/specs/commonTests/isConformant.js index d97e5024e6..6bd17e0355 100644 --- a/test/specs/commonTests/isConformant.js +++ b/test/specs/commonTests/isConformant.js @@ -131,12 +131,12 @@ export default (Component, options = {}) => { const tags = ['a', 'em', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'i', 'p', 'span', 'strong'] try { tags.forEach((tag) => { - shallow() + shallow() .should.have.tagName(tag) }) } catch (err) { tags.forEach((tag) => { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', tag) }) @@ -147,11 +147,11 @@ export default (Component, options = {}) => { const MyComponent = () => null try { - shallow() + shallow() .type() .should.equal(MyComponent) } catch (err) { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', MyComponent) } @@ -165,11 +165,11 @@ export default (Component, options = {}) => { } try { - shallow() + shallow() .type() .should.equal(MyComponent) } catch (err) { - const wrapper = shallow() + const wrapper = shallow() wrapper.type().should.not.equal(Component) wrapper.should.have.prop('as', MyComponent) } @@ -178,7 +178,7 @@ export default (Component, options = {}) => { it('passes extra props to the component it is renders as', () => { const MyComponent = () => null - shallow() + shallow() .should.have.descendants('[data-extra-prop="foo"]') }) }) From 8e3209c10eeb95de8f161ecd533f00d723968210 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Tue, 18 Jul 2017 11:20:04 +0300 Subject: [PATCH 3/8] feat(Breakpoint): add component --- index.d.ts | 6 ++++++ package.json | 2 +- src/addons/Breakpoint/Breakpoint.d.ts | 4 ++-- src/addons/Breakpoint/Breakpoint.js | 4 +--- src/addons/Breakpoint/index.d.ts | 2 +- src/lib/customPropTypes.js | 12 ++++++++++-- test/specs/addons/Breakpoint/Breakpoint-test.js | 8 ++++---- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/index.d.ts b/index.d.ts index 348ff8b064..85bb7be7c1 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,10 @@ // Addons +export { + default as Breakpoint, + BreakpointPoints, + BreakpointProps, + BreakpointOnUpdateData +} from './dist/commonjs/addons/Breakpoint'; export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm'; export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal'; export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio'; diff --git a/package.json b/package.json index d7c7c6d741..a14b6164e2 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "satisfied": "^1.1.0", "semantic-ui-css": "^2.2.11", "simulant": "^0.2.2", - "sinon": "^2.1.0", + "sinon": "^2.3.8", "sinon-chai": "^2.11.0", "ta-scripts": "^2.5.2", "through2": "^2.0.2", diff --git a/src/addons/Breakpoint/Breakpoint.d.ts b/src/addons/Breakpoint/Breakpoint.d.ts index 5d0eed3f38..accfd623f9 100644 --- a/src/addons/Breakpoint/Breakpoint.d.ts +++ b/src/addons/Breakpoint/Breakpoint.d.ts @@ -37,6 +37,6 @@ export interface BreakpointOnUpdateData extends BreakpointProps { number: string; } -declare const Confirm: React.ComponentClass; +declare const Breakpoint: React.ComponentClass; -export default Confirm; +export default Breakpoint; diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Breakpoint/Breakpoint.js index cf2481da55..4f31b6b955 100644 --- a/src/addons/Breakpoint/Breakpoint.js +++ b/src/addons/Breakpoint/Breakpoint.js @@ -114,7 +114,7 @@ export default class Breakpoint extends Component { // ---------------------------------------- visible = () => { - const { only } = this.props + const { only = '' } = this.props const points = only.replace('large screen', 'largeScreen').split(' ') return _.some(points, point => _.invoke(this, point)) @@ -147,5 +147,3 @@ export default class Breakpoint extends Component { return null } } - - diff --git a/src/addons/Breakpoint/index.d.ts b/src/addons/Breakpoint/index.d.ts index ee3195b503..0c42ac3578 100644 --- a/src/addons/Breakpoint/index.d.ts +++ b/src/addons/Breakpoint/index.d.ts @@ -1 +1 @@ -export { default, BreakpointPoints, BreakpointProps } from './Breakpoint'; +export { default, BreakpointPoints, BreakpointProps, BreakpointOnUpdateData } from './Breakpoint'; diff --git a/src/lib/customPropTypes.js b/src/lib/customPropTypes.js index a4736b1cec..0993526eef 100644 --- a/src/lib/customPropTypes.js +++ b/src/lib/customPropTypes.js @@ -241,7 +241,7 @@ export const demand = (requiredProps) => { * @param {string[]} possible An array of possible values to prop. */ export const onlyProp = possible => { - return (props, propName, componentName) => { + const typeChecker = (required, props, propName, componentName) => { if (!Array.isArray(possible)) { throw new Error([ 'Invalid argument supplied to some, expected an instance of array.', @@ -252,7 +252,10 @@ export const onlyProp = possible => { const propValue = props[propName] // skip if prop is undefined - if (_.isNil(propValue) || propValue === false) return + if (_.isNil(propValue) || propValue === false) { + if (required) throw new Error(`Required \`${propName}\` prop was not specified in \`${componentName}\`.`) + return + } const values = propValue .replace('large screen', 'large-screen') @@ -265,6 +268,11 @@ export const onlyProp = possible => { return new Error(`\`${propName}\` prop in \`${componentName}\` has invalid values: \`${invalid.join('`, `')}\`.`) } } + + const chainedCheckType = typeChecker.bind(null, false) + chainedCheckType.isRequired = typeChecker.bind(null, true) + + return chainedCheckType } /** diff --git a/test/specs/addons/Breakpoint/Breakpoint-test.js b/test/specs/addons/Breakpoint/Breakpoint-test.js index ad9e40c4d7..a759de8960 100644 --- a/test/specs/addons/Breakpoint/Breakpoint-test.js +++ b/test/specs/addons/Breakpoint/Breakpoint-test.js @@ -8,11 +8,11 @@ import { domEvent, sandbox } from 'test/utils' const { points } = Breakpoint.defaultProps const requiredProps = { only: 'mobile' } -beforeEach(() => { - sandbox.stub(window, 'innerWidth').value(points.mobile) -}) - describe('Breakpoint', () => { + beforeEach(() => { + sandbox.stub(window, 'innerWidth').value(points.mobile) + }) + common.isConformant(Breakpoint, { requiredProps }) common.rendersChildren(Breakpoint, { requiredProps }) From 3e9073098235f63a678f5f7d4e40a6835d9ef839 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Mon, 14 Aug 2017 11:08:47 +0300 Subject: [PATCH 4/8] style(Breakpoint): prefix bools with "is" --- src/addons/Breakpoint/Breakpoint.js | 16 +- src/lib/customPropTypes.js | 370 +++++++++++++--------------- 2 files changed, 186 insertions(+), 200 deletions(-) diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Breakpoint/Breakpoint.js index 4f31b6b955..0fa6edbc0b 100644 --- a/src/addons/Breakpoint/Breakpoint.js +++ b/src/addons/Breakpoint/Breakpoint.js @@ -74,35 +74,35 @@ export default class Breakpoint extends Component { // Breakpoint matchers // ---------------------------------------- - computer = () => { + isComputer = () => { const { points: { computer } } = this.props const { width } = this.state return width >= computer } - largeScreen = () => { + isLargeScreen = () => { const { points: { largeScreen, widescreen } } = this.props const { width } = this.state return width >= largeScreen && width < widescreen } - mobile = () => { + isMobile = () => { const { points: { mobile, tablet } } = this.props const { width } = this.state return width >= mobile && width < tablet } - tablet = () => { + isTablet = () => { const { points: { computer, tablet } } = this.props const { width } = this.state return width >= tablet && width < computer } - widescreen = () => { + isWidescreen = () => { const { points: { widescreen } } = this.props const { width } = this.state @@ -113,11 +113,11 @@ export default class Breakpoint extends Component { // Helpers // ---------------------------------------- - visible = () => { + isVisible = () => { const { only = '' } = this.props const points = only.replace('large screen', 'largeScreen').split(' ') - return _.some(points, point => _.invoke(this, point)) + return _.some(points, point => _.invoke(this, `is${_.upperFirst(point)}`)) } // ---------------------------------------- @@ -143,7 +143,7 @@ export default class Breakpoint extends Component { const ElementType = getElementType(Breakpoint, this.props) const rest = getUnhandledProps(Breakpoint, this.props) - if (this.visible()) return {children} + if (this.isVisible()) return {children} return null } } diff --git a/src/lib/customPropTypes.js b/src/lib/customPropTypes.js index 0993526eef..88b8318c0f 100644 --- a/src/lib/customPropTypes.js +++ b/src/lib/customPropTypes.js @@ -18,90 +18,86 @@ export const as = (...args) => PropTypes.oneOfType([ * Useful for very large lists of options (e.g. Icon name, Flag name, etc.) * @param {string[]} suggestions An array of allowed values. */ -export const suggest = suggestions => { - return (props, propName, componentName) => { - if (!Array.isArray(suggestions)) { - throw new Error([ - 'Invalid argument supplied to suggest, expected an instance of array.', - ` See \`${propName}\` prop in \`${componentName}\`.`, - ].join('')) - } - const propValue = props[propName] - - // skip if prop is undefined or is included in the suggestions - if (_.isNil(propValue) || propValue === false || _.includes(propValue, suggestions)) return - - // find best suggestions - const propValueWords = propValue.split(' ') - - /* eslint-disable max-nested-callbacks */ - const bestMatches = _.flow( - _.map(suggestion => { - const suggestionWords = suggestion.split(' ') - - const propValueScore = _.flow( - _.map(x => _.map(y => leven(x, y), suggestionWords)), - _.map(_.min), - _.sum - )(propValueWords) - - const suggestionScore = _.flow( - _.map(x => _.map(y => leven(x, y), propValueWords)), - _.map(_.min), - _.sum - )(suggestionWords) - - return { suggestion, score: propValueScore + suggestionScore } - }), - _.sortBy(['score', 'suggestion']), - _.take(3) - )(suggestions) - /* eslint-enable max-nested-callbacks */ - - // skip if a match scored 0 - // since we're matching on words (classNames) this allows any word order to pass validation - // e.g. `left chevron` vs `chevron left` - if (bestMatches.some(x => x.score === 0)) return - - return new Error([ - `Invalid prop \`${propName}\` of value \`${propValue}\` supplied to \`${componentName}\`.`, - `\n\nInstead of \`${propValue}\`, did you mean:`, - bestMatches.map(x => `\n - ${x.suggestion}`).join(''), - '\n', +export const suggest = suggestions => (props, propName, componentName) => { + if (!Array.isArray(suggestions)) { + throw new Error([ + 'Invalid argument supplied to suggest, expected an instance of array.', + ` See \`${propName}\` prop in \`${componentName}\`.`, ].join('')) } + const propValue = props[propName] + + // skip if prop is undefined or is included in the suggestions + if (_.isNil(propValue) || propValue === false || _.includes(propValue, suggestions)) return + + // find best suggestions + const propValueWords = propValue.split(' ') + + /* eslint-disable max-nested-callbacks */ + const bestMatches = _.flow( + _.map((suggestion) => { + const suggestionWords = suggestion.split(' ') + + const propValueScore = _.flow( + _.map(x => _.map(y => leven(x, y), suggestionWords)), + _.map(_.min), + _.sum, + )(propValueWords) + + const suggestionScore = _.flow( + _.map(x => _.map(y => leven(x, y), propValueWords)), + _.map(_.min), + _.sum, + )(suggestionWords) + + return { suggestion, score: propValueScore + suggestionScore } + }), + _.sortBy(['score', 'suggestion']), + _.take(3), + )(suggestions) + /* eslint-enable max-nested-callbacks */ + + // skip if a match scored 0 + // since we're matching on words (classNames) this allows any word order to pass validation + // e.g. `left chevron` vs `chevron left` + if (bestMatches.some(x => x.score === 0)) return + + return new Error([ + `Invalid prop \`${propName}\` of value \`${propValue}\` supplied to \`${componentName}\`.`, + `\n\nInstead of \`${propValue}\`, did you mean:`, + bestMatches.map(x => `\n - ${x.suggestion}`).join(''), + '\n', + ].join('')) } /** * Disallow other props form being defined with this prop. * @param {string[]} disallowedProps An array of props that cannot be used with this prop. */ -export const disallow = disallowedProps => { - return (props, propName, componentName) => { - if (!Array.isArray(disallowedProps)) { - throw new Error([ - 'Invalid argument supplied to disallow, expected an instance of array.', - ` See \`${propName}\` prop in \`${componentName}\`.`, - ].join('')) - } - - // skip if prop is undefined - if (_.isNil(props[propName]) || props[propName] === false) return +export const disallow = disallowedProps => (props, propName, componentName) => { + if (!Array.isArray(disallowedProps)) { + throw new Error([ + 'Invalid argument supplied to disallow, expected an instance of array.', + ` See \`${propName}\` prop in \`${componentName}\`.`, + ].join('')) + } - // find disallowed props with values - const disallowed = disallowedProps.reduce((acc, disallowedProp) => { - if (!_.isNil(props[disallowedProp]) && props[disallowedProp] !== false) { - return [...acc, disallowedProp] - } - return acc - }, []) + // skip if prop is undefined + if (_.isNil(props[propName]) || props[propName] === false) return - if (disallowed.length > 0) { - return new Error([ - `Prop \`${propName}\` in \`${componentName}\` conflicts with props: \`${disallowed.join('`, `')}\`.`, - 'They cannot be defined together, choose one or the other.', - ].join(' ')) + // find disallowed props with values + const disallowed = disallowedProps.reduce((acc, disallowedProp) => { + if (!_.isNil(props[disallowedProp]) && props[disallowedProp] !== false) { + return [...acc, disallowedProp] } + return acc + }, []) + + if (disallowed.length > 0) { + return new Error([ + `Prop \`${propName}\` in \`${componentName}\` conflicts with props: \`${disallowed.join('`, `')}\`.`, + 'They cannot be defined together, choose one or the other.', + ].join(' ')) } } @@ -109,56 +105,52 @@ export const disallow = disallowedProps => { * Ensure a prop adherers to multiple prop type validators. * @param {function[]} validators An array of propType functions. */ -export const every = (validators) => { - return (props, propName, componentName, ...rest) => { - if (!Array.isArray(validators)) { - throw new Error([ - 'Invalid argument supplied to every, expected an instance of array.', - `See \`${propName}\` prop in \`${componentName}\`.`, - ].join(' ')) - } - - const errors = _.flow( - _.map(validator => { - if (typeof validator !== 'function') { - throw new Error(`every() argument "validators" should contain functions, found: ${typeOf(validator)}.`) - } - return validator(props, propName, componentName, ...rest) - }), - _.compact - )(validators) - - // we can only return one error at a time - return errors[0] +export const every = validators => (props, propName, componentName, ...rest) => { + if (!Array.isArray(validators)) { + throw new Error([ + 'Invalid argument supplied to every, expected an instance of array.', + `See \`${propName}\` prop in \`${componentName}\`.`, + ].join(' ')) } + + const errors = _.flow( + _.map((validator) => { + if (typeof validator !== 'function') { + throw new Error(`every() argument "validators" should contain functions, found: ${typeOf(validator)}.`) + } + return validator(props, propName, componentName, ...rest) + }), + _.compact, + )(validators) + + // we can only return one error at a time + return errors[0] } /** * Ensure a prop adherers to at least one of the given prop type validators. * @param {function[]} validators An array of propType functions. */ -export const some = (validators) => { - return (props, propName, componentName, ...rest) => { - if (!Array.isArray(validators)) { - throw new Error([ - 'Invalid argument supplied to some, expected an instance of array.', - `See \`${propName}\` prop in \`${componentName}\`.`, - ].join(' ')) - } - - const errors = _.compact(_.map(validators, validator => { - if (!_.isFunction(validator)) { - throw new Error(`some() argument "validators" should contain functions, found: ${typeOf(validator)}.`) - } - return validator(props, propName, componentName, ...rest) - })) +export const some = validators => (props, propName, componentName, ...rest) => { + if (!Array.isArray(validators)) { + throw new Error([ + 'Invalid argument supplied to some, expected an instance of array.', + `See \`${propName}\` prop in \`${componentName}\`.`, + ].join(' ')) + } - // fail only if all validators failed - if (errors.length === validators.length) { - const error = new Error('One of these validators must pass:') - error.message += '\n' + _.map(errors, (err, i) => (`[${i + 1}]: ${err.message}`)).join('\n') - return error + const errors = _.compact(_.map(validators, (validator) => { + if (!_.isFunction(validator)) { + throw new Error(`some() argument "validators" should contain functions, found: ${typeOf(validator)}.`) } + return validator(props, propName, componentName, ...rest) + })) + + // fail only if all validators failed + if (errors.length === validators.length) { + const error = new Error('One of these validators must pass:') + error.message += `\n${_.map(errors, (err, i) => (`[${i + 1}]: ${err.message}`)).join('\n')}` + return error } } @@ -167,47 +159,45 @@ export const some = (validators) => { * @param {object} propsShape An object describing the prop shape. * @param {function} validator A propType function. */ -export const givenProps = (propsShape, validator) => { - return (props, propName, componentName, ...rest) => { - if (!_.isPlainObject(propsShape)) { - throw new Error([ - 'Invalid argument supplied to givenProps, expected an object.', - `See \`${propName}\` prop in \`${componentName}\`.`, - ].join(' ')) - } +export const givenProps = (propsShape, validator) => (props, propName, componentName, ...rest) => { + if (!_.isPlainObject(propsShape)) { + throw new Error([ + 'Invalid argument supplied to givenProps, expected an object.', + `See \`${propName}\` prop in \`${componentName}\`.`, + ].join(' ')) + } - if (typeof validator !== 'function') { - throw new Error([ - 'Invalid argument supplied to givenProps, expected a function.', - `See \`${propName}\` prop in \`${componentName}\`.`, - ].join(' ')) - } + if (typeof validator !== 'function') { + throw new Error([ + 'Invalid argument supplied to givenProps, expected a function.', + `See \`${propName}\` prop in \`${componentName}\`.`, + ].join(' ')) + } - const shouldValidate = _.keys(propsShape).every(key => { - const val = propsShape[key] - // require propShape validators to pass or prop values to match - return typeof val === 'function' ? !val(props, key, componentName, ...rest) : val === props[propName] - }) + const shouldValidate = _.keys(propsShape).every((key) => { + const val = propsShape[key] + // require propShape validators to pass or prop values to match + return typeof val === 'function' ? !val(props, key, componentName, ...rest) : val === props[propName] + }) - if (!shouldValidate) return + if (!shouldValidate) return - const error = validator(props, propName, componentName, ...rest) + const error = validator(props, propName, componentName, ...rest) - if (error) { - // poor mans shallow pretty print, prevents JSON circular reference errors - const prettyProps = `{ ${_.keys(_.pick(_.keys(propsShape), props)).map(key => { - const val = props[key] - let renderedValue = val - if (typeof val === 'string') renderedValue = `"${val}"` - else if (Array.isArray(val)) renderedValue = `[${val.join(', ')}]` - else if (_.isObject(val)) renderedValue = '{...}' + if (error) { + // poor mans shallow pretty print, prevents JSON circular reference errors + const prettyProps = `{ ${_.keys(_.pick(_.keys(propsShape), props)).map((key) => { + const val = props[key] + let renderedValue = val + if (typeof val === 'string') renderedValue = `"${val}"` + else if (Array.isArray(val)) renderedValue = `[${val.join(', ')}]` + else if (_.isObject(val)) renderedValue = '{...}' - return `${key}: ${renderedValue}` - }).join(', ')} }` + return `${key}: ${renderedValue}` + }).join(', ')} }` - error.message = `Given props ${prettyProps}: ${error.message}` - return error - } + error.message = `Given props ${prettyProps}: ${error.message}` + return error } } @@ -215,24 +205,22 @@ export const givenProps = (propsShape, validator) => { * Define prop dependencies by requiring other props. * @param {string[]} requiredProps An array of required prop names. */ -export const demand = (requiredProps) => { - return (props, propName, componentName) => { - if (!Array.isArray(requiredProps)) { - throw new Error([ - 'Invalid `requiredProps` argument supplied to require, expected an instance of array.', - ` See \`${propName}\` prop in \`${componentName}\`.`, - ].join('')) - } +export const demand = requiredProps => (props, propName, componentName) => { + if (!Array.isArray(requiredProps)) { + throw new Error([ + 'Invalid `requiredProps` argument supplied to require, expected an instance of array.', + ` See \`${propName}\` prop in \`${componentName}\`.`, + ].join('')) + } - // skip if prop is undefined - if (props[propName] === undefined) return + // skip if prop is undefined + if (props[propName] === undefined) return - const missingRequired = requiredProps.filter(requiredProp => props[requiredProp] === undefined) - if (missingRequired.length > 0) { - return new Error( - `\`${propName}\` prop in \`${componentName}\` requires props: \`${missingRequired.join('`, `')}\`.` - ) - } + const missingRequired = requiredProps.filter(requiredProp => props[requiredProp] === undefined) + if (missingRequired.length > 0) { + return new Error( + `\`${propName}\` prop in \`${componentName}\` requires props: \`${missingRequired.join('`, `')}\`.`, + ) } } @@ -308,37 +296,35 @@ export const collectionShorthand = (...args) => every([ * @param {string} help A help message to display with the deprecation warning. * @param {function} [validator] A propType function. */ -export const deprecate = (help, validator) => { - return (props, propName, componentName, ...args) => { - if (typeof help !== 'string') { +export const deprecate = (help, validator) => (props, propName, componentName, ...args) => { + if (typeof help !== 'string') { + throw new Error([ + 'Invalid `help` argument supplied to deprecate, expected a string.', + `See \`${propName}\` prop in \`${componentName}\`.`, + ].join(' ')) + } + + // skip if prop is undefined + if (props[propName] === undefined) return + + // deprecation error and help + const error = new Error(`The \`${propName}\` prop in \`${componentName}\` is deprecated.`) + if (help) error.message += ` ${help}` + + // add optional validation error message + if (validator) { + if (typeof validator === 'function') { + const validationError = validator(props, propName, componentName, ...args) + if (validationError) { + error.message = `${error.message} ${validationError.message}` + } + } else { throw new Error([ - 'Invalid `help` argument supplied to deprecate, expected a string.', + 'Invalid argument supplied to deprecate, expected a function.', `See \`${propName}\` prop in \`${componentName}\`.`, ].join(' ')) } - - // skip if prop is undefined - if (props[propName] === undefined) return - - // deprecation error and help - const error = new Error(`The \`${propName}\` prop in \`${componentName}\` is deprecated.`) - if (help) error.message += ` ${help}` - - // add optional validation error message - if (validator) { - if (typeof validator === 'function') { - const validationError = validator(props, propName, componentName, ...args) - if (validationError) { - error.message = `${error.message} ${validationError.message}` - } - } else { - throw new Error([ - 'Invalid argument supplied to deprecate, expected a function.', - `See \`${propName}\` prop in \`${componentName}\`.`, - ].join(' ')) - } - } - - return error } + + return error } From 3247632810c1d9d278f7ac40ee2a5c8acc90e71e Mon Sep 17 00:00:00 2001 From: Levi Thomason Date: Sun, 20 Aug 2017 09:51:00 -0700 Subject: [PATCH 5/8] refactor(Breakpoint): rename Responsive --- .../Types/BreakpointExampleBreakpoint.js | 14 ------ .../Types/BreakpointExampleMultiple.js | 11 ----- .../Types/BreakpointExampleNested.js | 27 ----------- .../Examples/addons/Breakpoint/Types/index.js | 36 --------------- .../Usage/BreakpointExamplePoints.js | 22 --------- .../Types/ResponsiveExampleContent.js} | 8 ++-- .../Types/ResponsiveExampleMultiple.js | 11 +++++ .../Types/ResponsiveExampleNested.js | 27 +++++++++++ .../Types/ResponsiveExampleResponsive.js | 14 ++++++ .../Examples/addons/Responsive/Types/index.js | 36 +++++++++++++++ .../Usage/ResponsiveExamplePoints.js | 22 +++++++++ .../{Breakpoint => Responsive}/Usage/index.js | 8 ++-- .../{Breakpoint => Responsive}/index.js | 4 +- index.d.ts | 10 ++-- src/addons/Breakpoint/index.d.ts | 1 - src/addons/Breakpoint/index.js | 1 - .../Responsive.d.ts} | 16 +++---- .../Responsive.js} | 32 ++++++------- src/addons/Responsive/index.d.ts | 1 + src/addons/Responsive/index.js | 1 + src/index.js | 2 +- .../addons/Breakpoint/Breakpoint-test.js | 46 +++++++++---------- 22 files changed, 175 insertions(+), 175 deletions(-) delete mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js delete mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js delete mode 100644 docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js delete mode 100644 docs/app/Examples/addons/Breakpoint/Types/index.js delete mode 100644 docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js rename docs/app/Examples/addons/{Breakpoint/Types/BreakpointExampleContent.js => Responsive/Types/ResponsiveExampleContent.js} (84%) create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMultiple.js create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js create mode 100644 docs/app/Examples/addons/Responsive/Types/index.js create mode 100644 docs/app/Examples/addons/Responsive/Usage/ResponsiveExamplePoints.js rename docs/app/Examples/addons/{Breakpoint => Responsive}/Usage/index.js (59%) rename docs/app/Examples/addons/{Breakpoint => Responsive}/index.js (65%) delete mode 100644 src/addons/Breakpoint/index.d.ts delete mode 100644 src/addons/Breakpoint/index.js rename src/addons/{Breakpoint/Breakpoint.d.ts => Responsive/Responsive.d.ts} (66%) rename src/addons/{Breakpoint/Breakpoint.js => Responsive/Responsive.js} (77%) create mode 100644 src/addons/Responsive/index.d.ts create mode 100644 src/addons/Responsive/index.js diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js deleted file mode 100644 index 6f98e8b9e1..0000000000 --- a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleBreakpoint.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { Breakpoint, Segment } from 'semantic-ui-react' - -const BreakpointExampleBreakpoint = () => ( - - Mobile - Tablet - Computer - Large Screen - Widescreen - -) - -export default BreakpointExampleBreakpoint diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js deleted file mode 100644 index 31019d6924..0000000000 --- a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleMultiple.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { Breakpoint, Segment } from 'semantic-ui-react' - -const BreakpointExampleMultiple = () => ( - - Mobile & Tablet - Tablet & Computer - -) - -export default BreakpointExampleMultiple diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js b/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js deleted file mode 100644 index dc60518a2e..0000000000 --- a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleNested.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import { Breakpoint, Segment } from 'semantic-ui-react' - -const BreakpointExampleNested = () => ( - - - -

Mobile

-
- -

Mobile

-
-
- - -

Computer

- -

Large Screen

-
- -

Widescreen

-
-
-
-) - -export default BreakpointExampleNested diff --git a/docs/app/Examples/addons/Breakpoint/Types/index.js b/docs/app/Examples/addons/Breakpoint/Types/index.js deleted file mode 100644 index dacdb654d7..0000000000 --- a/docs/app/Examples/addons/Breakpoint/Types/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { Message } from 'semantic-ui-react' - -import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' -import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' - -const BreakpointTypesExamples = () => ( - - - - Instead of Grid visibility breakpoints, Breakpoint doesn't render invisible content. - - - - - - -) - -export default BreakpointTypesExamples diff --git a/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js b/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js deleted file mode 100644 index 35864a6126..0000000000 --- a/docs/app/Examples/addons/Breakpoint/Usage/BreakpointExamplePoints.js +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' -import { Breakpoint, Segment } from 'semantic-ui-react' - -const points = { - computer: 990, - largeScreen: 1300, - mobile: 300, - tablet: 800, - widescreen: 1900, -} - -const BreakpointExamplePoints = () => ( - - Mobile - Tablet - Computer - Large Screen - Widescreen - -) - -export default BreakpointExamplePoints diff --git a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js similarity index 84% rename from docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js rename to docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js index 0cd80a74b1..992e29e97b 100644 --- a/docs/app/Examples/addons/Breakpoint/Types/BreakpointExampleContent.js +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js @@ -1,7 +1,7 @@ import React, { Component } from 'react' -import { Breakpoint, Button, Menu } from 'semantic-ui-react' +import { Responsive, Button, Menu } from 'semantic-ui-react' -export default class BreakpointExampleContent extends Component { +export default class ResponsiveExampleContent extends Component { state = { active: 'home' } handleItemClick = (e, { name }) => this.setState({ active: name }) @@ -16,14 +16,14 @@ export default class BreakpointExampleContent extends Component { - - ( + + Mobile & Tablet + Tablet & Computer + +) + +export default ResponsiveExampleMultiple diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js new file mode 100644 index 0000000000..923ebaabbf --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js @@ -0,0 +1,27 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleNested = () => ( + + + +

Mobile

+
+ +

Mobile

+
+
+ + +

Computer

+ +

Large Screen

+
+ +

Widescreen

+
+
+
+) + +export default ResponsiveExampleNested diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js new file mode 100644 index 0000000000..b4265faf95 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js @@ -0,0 +1,14 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleResponsive = () => ( + + Mobile + Tablet + Computer + Large Screen + Widescreen + +) + +export default ResponsiveExampleResponsive diff --git a/docs/app/Examples/addons/Responsive/Types/index.js b/docs/app/Examples/addons/Responsive/Types/index.js new file mode 100644 index 0000000000..01557c0d2e --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/index.js @@ -0,0 +1,36 @@ +import React from 'react' +import { Message } from 'semantic-ui-react' + +import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' +import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' + +const ResponsiveTypesExamples = () => ( + + + + Instead of Grid visibility breakpoints, Responsive doesn't render invisible content. + + + + + + +) + +export default ResponsiveTypesExamples diff --git a/docs/app/Examples/addons/Responsive/Usage/ResponsiveExamplePoints.js b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExamplePoints.js new file mode 100644 index 0000000000..06ec17c540 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExamplePoints.js @@ -0,0 +1,22 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const breakpoints = { + computer: 990, + largeScreen: 1300, + mobile: 300, + tablet: 800, + widescreen: 1900, +} + +const ResponsiveExamplePoints = () => ( + + Mobile + Tablet + Computer + Large Screen + Widescreen + +) + +export default ResponsiveExamplePoints diff --git a/docs/app/Examples/addons/Breakpoint/Usage/index.js b/docs/app/Examples/addons/Responsive/Usage/index.js similarity index 59% rename from docs/app/Examples/addons/Breakpoint/Usage/index.js rename to docs/app/Examples/addons/Responsive/Usage/index.js index 5945c12712..a886ff2bb6 100644 --- a/docs/app/Examples/addons/Breakpoint/Usage/index.js +++ b/docs/app/Examples/addons/Responsive/Usage/index.js @@ -3,14 +3,14 @@ import React from 'react' import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' -const BreakpointUsageExamples = () => ( +const ResponsiveUsageExamples = () => ( ) -export default BreakpointUsageExamples +export default ResponsiveUsageExamples diff --git a/docs/app/Examples/addons/Breakpoint/index.js b/docs/app/Examples/addons/Responsive/index.js similarity index 65% rename from docs/app/Examples/addons/Breakpoint/index.js rename to docs/app/Examples/addons/Responsive/index.js index 0274a6635b..e976ba8196 100644 --- a/docs/app/Examples/addons/Breakpoint/index.js +++ b/docs/app/Examples/addons/Responsive/index.js @@ -3,11 +3,11 @@ import React from 'react' import Types from './Types' import Usage from './Usage' -const BreakpointExamples = () => ( +const ResponsiveExamples = () => (
) -export default BreakpointExamples +export default ResponsiveExamples diff --git a/index.d.ts b/index.d.ts index a3ff8255f6..79f2ac3c40 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,10 +1,10 @@ // Addons export { - default as Breakpoint, - BreakpointPoints, - BreakpointProps, - BreakpointOnUpdateData -} from './dist/commonjs/addons/Breakpoint'; + default as Responsive, + ResponsivePoints, + ResponsiveProps, + ResponsiveOnUpdateData +} from './dist/commonjs/addons/Responsive'; export { default as Confirm, ConfirmProps } from './dist/commonjs/addons/Confirm'; export { default as Portal, PortalProps } from './dist/commonjs/addons/Portal'; export { default as Radio, RadioProps } from './dist/commonjs/addons/Radio'; diff --git a/src/addons/Breakpoint/index.d.ts b/src/addons/Breakpoint/index.d.ts deleted file mode 100644 index 0c42ac3578..0000000000 --- a/src/addons/Breakpoint/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export { default, BreakpointPoints, BreakpointProps, BreakpointOnUpdateData } from './Breakpoint'; diff --git a/src/addons/Breakpoint/index.js b/src/addons/Breakpoint/index.js deleted file mode 100644 index 3d0e973b25..0000000000 --- a/src/addons/Breakpoint/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Breakpoint' diff --git a/src/addons/Breakpoint/Breakpoint.d.ts b/src/addons/Responsive/Responsive.d.ts similarity index 66% rename from src/addons/Breakpoint/Breakpoint.d.ts rename to src/addons/Responsive/Responsive.d.ts index accfd623f9..d10825f18f 100644 --- a/src/addons/Breakpoint/Breakpoint.d.ts +++ b/src/addons/Responsive/Responsive.d.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { GridOnlyProp } from '../../collections/Grid/GridColumn'; -export interface BreakpointProps { +export interface ResponsiveProps { [key: string]: any; /** An element type to render as (string or function). */ @@ -19,13 +19,13 @@ export interface BreakpointProps { * @param {SyntheticEvent} event - The React SyntheticEvent object * @param {object} data - All props and the event value. */ - onUpdate?: BreakpointOnUpdateData; + onUpdate?: ResponsiveOnUpdateData; - /** Breakpoints definition. */ - points?: BreakpointPoints; + /** Responsives definition. */ + breakpoints?: ResponsivePoints; } -export interface BreakpointPoints { +export interface ResponsivePoints { computer: number; largeScreen: number; mobile: number; @@ -33,10 +33,10 @@ export interface BreakpointPoints { widescreen: number; } -export interface BreakpointOnUpdateData extends BreakpointProps { +export interface ResponsiveOnUpdateData extends ResponsiveProps { number: string; } -declare const Breakpoint: React.ComponentClass; +declare const Responsive: React.ComponentClass; -export default Breakpoint; +export default Responsive; diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Responsive/Responsive.js similarity index 77% rename from src/addons/Breakpoint/Breakpoint.js rename to src/addons/Responsive/Responsive.js index 0fa6edbc0b..92402a62f5 100644 --- a/src/addons/Breakpoint/Breakpoint.js +++ b/src/addons/Responsive/Responsive.js @@ -11,9 +11,9 @@ import { } from '../../lib' /** - * A Breakpoint can control visibility of content. + * Responsive can control visibility of content. */ -export default class Breakpoint extends Component { +export default class Responsive extends Component { static propTypes = { /** An element type to render as (string or function). */ as: customPropTypes.as, @@ -32,8 +32,8 @@ export default class Breakpoint extends Component { */ onUpdate: PropTypes.func, - /** Breakpoints definition. */ - points: PropTypes.shape({ + /** Responsives definition. */ + breakpoints: PropTypes.shape({ computer: PropTypes.number.isRequired, largeScreen: PropTypes.number.isRequired, mobile: PropTypes.number.isRequired, @@ -43,7 +43,7 @@ export default class Breakpoint extends Component { } static defaultProps = { - points: { + breakpoints: { computer: 992, largeScreen: 1200, mobile: 320, @@ -53,7 +53,7 @@ export default class Breakpoint extends Component { } static _meta = { - name: 'Breakpoint', + name: 'Responsive', type: META.TYPES.ADDON, } @@ -71,39 +71,39 @@ export default class Breakpoint extends Component { } // ---------------------------------------- - // Breakpoint matchers + // Responsive matchers // ---------------------------------------- isComputer = () => { - const { points: { computer } } = this.props + const { breakpoints: { computer } } = this.props const { width } = this.state return width >= computer } isLargeScreen = () => { - const { points: { largeScreen, widescreen } } = this.props + const { breakpoints: { largeScreen, widescreen } } = this.props const { width } = this.state return width >= largeScreen && width < widescreen } isMobile = () => { - const { points: { mobile, tablet } } = this.props + const { breakpoints: { mobile, tablet } } = this.props const { width } = this.state return width >= mobile && width < tablet } isTablet = () => { - const { points: { computer, tablet } } = this.props + const { breakpoints: { computer, tablet } } = this.props const { width } = this.state return width >= tablet && width < computer } isWidescreen = () => { - const { points: { widescreen } } = this.props + const { breakpoints: { widescreen } } = this.props const { width } = this.state return width >= widescreen @@ -115,9 +115,9 @@ export default class Breakpoint extends Component { isVisible = () => { const { only = '' } = this.props - const points = only.replace('large screen', 'largeScreen').split(' ') + const breakpoints = only.replace('large screen', 'largeScreen').split(' ') - return _.some(points, point => _.invoke(this, `is${_.upperFirst(point)}`)) + return _.some(breakpoints, point => _.invoke(this, `is${_.upperFirst(point)}`)) } // ---------------------------------------- @@ -140,8 +140,8 @@ export default class Breakpoint extends Component { render() { const { children } = this.props - const ElementType = getElementType(Breakpoint, this.props) - const rest = getUnhandledProps(Breakpoint, this.props) + const ElementType = getElementType(Responsive, this.props) + const rest = getUnhandledProps(Responsive, this.props) if (this.isVisible()) return {children} return null diff --git a/src/addons/Responsive/index.d.ts b/src/addons/Responsive/index.d.ts new file mode 100644 index 0000000000..1c9fd328ac --- /dev/null +++ b/src/addons/Responsive/index.d.ts @@ -0,0 +1 @@ +export { default, ResponsivePoints, ResponsiveProps, ResponsiveOnUpdateData } from './Responsive'; diff --git a/src/addons/Responsive/index.js b/src/addons/Responsive/index.js new file mode 100644 index 0000000000..fd5e7015aa --- /dev/null +++ b/src/addons/Responsive/index.js @@ -0,0 +1 @@ +export default from './Responsive' diff --git a/src/index.js b/src/index.js index 505152b969..e42c3cd3e8 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ // Addons -export { default as Breakpoint } from './addons/Breakpoint' +export { default as Responsive } from './addons/Responsive' export { default as Confirm } from './addons/Confirm' export { default as Portal } from './addons/Portal' export { default as Radio } from './addons/Radio' diff --git a/test/specs/addons/Breakpoint/Breakpoint-test.js b/test/specs/addons/Breakpoint/Breakpoint-test.js index a759de8960..d5e1a26bd9 100644 --- a/test/specs/addons/Breakpoint/Breakpoint-test.js +++ b/test/specs/addons/Breakpoint/Breakpoint-test.js @@ -1,69 +1,69 @@ import _ from 'lodash' import React from 'react' -import Breakpoint from 'src/addons/Breakpoint/Breakpoint' +import Responsive from 'src/addons/Responsive/Responsive' import * as common from 'test/specs/commonTests' import { domEvent, sandbox } from 'test/utils' -const { points } = Breakpoint.defaultProps +const { breakpoints } = Responsive.defaultProps const requiredProps = { only: 'mobile' } -describe('Breakpoint', () => { +describe('Responsive', () => { beforeEach(() => { - sandbox.stub(window, 'innerWidth').value(points.mobile) + sandbox.stub(window, 'innerWidth').value(breakpoints.mobile) }) - common.isConformant(Breakpoint, { requiredProps }) - common.rendersChildren(Breakpoint, { requiredProps }) + common.isConformant(Responsive, { requiredProps }) + common.rendersChildren(Responsive, { requiredProps }) describe('children', () => { - _.each(points, (value, point) => { + _.each(breakpoints, (value, point) => { it(`renders when point "${point}" fits`, () => { sandbox.stub(window, 'innerWidth').value(value) - shallow() + shallow() .should.have.tagName('div') }) }) - _.each(points, (value, point) => { + _.each(breakpoints, (value, point) => { it(`return null when point "${point}" doesn't fit`, () => { sandbox.stub(window, 'innerWidth').value(value - 1) - shallow() + shallow() .should.be.blank() }) }) - it('renders when fits multiple points', () => { - sandbox.stub(window, 'innerWidth').value(points.largeScreen) + it('renders when fits multiple breakpoints', () => { + sandbox.stub(window, 'innerWidth').value(breakpoints.largeScreen) - shallow() + shallow() .should.have.tagName('div') - shallow() + shallow() .should.have.tagName('div') - shallow() + shallow() .should.have.tagName('div') }) - it('renders when fits one of points', () => { - shallow() + it('renders when fits one of breakpoints', () => { + shallow() .should.have.tagName('div') - shallow() + shallow() .should.be.blank() - shallow() + shallow() .should.have.tagName('div') }) }) describe('onUpdate', () => { it('listens for resize', done => { - const wrapper = mount() + const wrapper = mount() wrapper.should.have.tagName('div') - sandbox.stub(window, 'innerWidth').value(points.tablet) + sandbox.stub(window, 'innerWidth').value(breakpoints.tablet) domEvent.fire(window, 'resize') setTimeout(() => { @@ -74,9 +74,9 @@ describe('Breakpoint', () => { it('is called with (e, data) when window was resized', done => { const onUpdate = sandbox.spy() - const width = points.tablet + const width = breakpoints.tablet - mount() + mount() sandbox.stub(window, 'innerWidth').value(width) domEvent.fire(window, 'resize') From 6d63ded2546fb2817a890a4c67434e99adc2e0ac Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Mon, 21 Aug 2017 11:46:34 +0300 Subject: [PATCH 6/8] style(mixed): fix lint issues --- src/addons/Breakpoint/Breakpoint.js | 2 +- src/lib/customPropTypes.js | 2 +- test/specs/addons/Breakpoint/Breakpoint-test.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/addons/Breakpoint/Breakpoint.js b/src/addons/Breakpoint/Breakpoint.js index 0fa6edbc0b..faab7c538d 100644 --- a/src/addons/Breakpoint/Breakpoint.js +++ b/src/addons/Breakpoint/Breakpoint.js @@ -124,7 +124,7 @@ export default class Breakpoint extends Component { // Event handlers // ---------------------------------------- - handleUpdate = e => { + handleUpdate = (e) => { requestAnimationFrame(() => { const width = window.innerWidth diff --git a/src/lib/customPropTypes.js b/src/lib/customPropTypes.js index 88b8318c0f..2f623f15e7 100644 --- a/src/lib/customPropTypes.js +++ b/src/lib/customPropTypes.js @@ -228,7 +228,7 @@ export const demand = requiredProps => (props, propName, componentName) => { * Ensure an only prop contains a string with only possible values. * @param {string[]} possible An array of possible values to prop. */ -export const onlyProp = possible => { +export const onlyProp = (possible) => { const typeChecker = (required, props, propName, componentName) => { if (!Array.isArray(possible)) { throw new Error([ diff --git a/test/specs/addons/Breakpoint/Breakpoint-test.js b/test/specs/addons/Breakpoint/Breakpoint-test.js index a759de8960..55747eac6e 100644 --- a/test/specs/addons/Breakpoint/Breakpoint-test.js +++ b/test/specs/addons/Breakpoint/Breakpoint-test.js @@ -59,7 +59,7 @@ describe('Breakpoint', () => { }) describe('onUpdate', () => { - it('listens for resize', done => { + it('listens for resize', (done) => { const wrapper = mount() wrapper.should.have.tagName('div') @@ -72,7 +72,7 @@ describe('Breakpoint', () => { }, 25) }) - it('is called with (e, data) when window was resized', done => { + it('is called with (e, data) when window was resized', (done) => { const onUpdate = sandbox.spy() const width = points.tablet From 81872354aa13ec8f2e6bd09321a0299c2b3c0710 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Sun, 3 Sep 2017 18:59:18 +0300 Subject: [PATCH 7/8] feat(Responsive): refactor component --- .../Types/ResponsiveExampleContent.js | 4 +- .../Types/ResponsiveExampleMaxWidth.js | 15 +++ .../Types/ResponsiveExampleMinWidth.js | 15 +++ .../Types/ResponsiveExampleMixed.js | 10 ++ .../Types/ResponsiveExampleMultiple.js | 11 -- .../Types/ResponsiveExampleNested.js | 27 ----- .../Types/ResponsiveExampleResponsive.js | 6 +- .../Examples/addons/Responsive/Types/index.js | 25 +++-- .../Usage/ResponsiveExampleBreakpoints.js | 14 +++ .../Usage/ResponsiveExampleOnUpdate.js | 53 +++++++++ .../Usage/ResponsiveExamplePoints.js | 22 ---- .../Examples/addons/Responsive/Usage/index.js | 11 +- index.d.ts | 3 +- src/addons/Responsive/Responsive.d.ts | 34 +++--- src/addons/Responsive/Responsive.js | 102 +++++++----------- src/addons/Responsive/index.d.ts | 2 +- src/lib/eventStack.js | 8 +- .../addons/Breakpoint/Breakpoint-test.js | 91 ---------------- .../addons/Responsive/Responsive-test.js | 77 +++++++++++++ 19 files changed, 273 insertions(+), 257 deletions(-) create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMaxWidth.js create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMinWidth.js create mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMixed.js delete mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMultiple.js delete mode 100644 docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js create mode 100644 docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleBreakpoints.js create mode 100644 docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleOnUpdate.js delete mode 100644 docs/app/Examples/addons/Responsive/Usage/ResponsiveExamplePoints.js delete mode 100644 test/specs/addons/Breakpoint/Breakpoint-test.js create mode 100644 test/specs/addons/Responsive/Responsive-test.js diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js index 992e29e97b..ad951be010 100644 --- a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleContent.js @@ -17,18 +17,18 @@ export default class ResponsiveExampleContent extends Component { diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMaxWidth.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMaxWidth.js new file mode 100644 index 0000000000..08bd71af08 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMaxWidth.js @@ -0,0 +1,15 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleMaxWidth = () => ( + + + Visible only if display has 767px width and lower + + + Visible only if display has 2569px width + + +) + +export default ResponsiveExampleMaxWidth diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMinWidth.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMinWidth.js new file mode 100644 index 0000000000..5875fa8e57 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMinWidth.js @@ -0,0 +1,15 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleMinWidth = () => ( + + + Visible only if display has 768px width and higher + + + Visible only if display has 992px width and higher + + +) + +export default ResponsiveExampleMinWidth diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMixed.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMixed.js new file mode 100644 index 0000000000..4584563b49 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMixed.js @@ -0,0 +1,10 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleMixed = () => ( + + Visible only if display has width between 320px and 2559px + +) + +export default ResponsiveExampleMixed diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMultiple.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMultiple.js deleted file mode 100644 index 04cd0fe7fe..0000000000 --- a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleMultiple.js +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import { Responsive, Segment } from 'semantic-ui-react' - -const ResponsiveExampleMultiple = () => ( - - Mobile & Tablet - Tablet & Computer - -) - -export default ResponsiveExampleMultiple diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js deleted file mode 100644 index 923ebaabbf..0000000000 --- a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleNested.js +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react' -import { Responsive, Segment } from 'semantic-ui-react' - -const ResponsiveExampleNested = () => ( - - - -

Mobile

-
- -

Mobile

-
-
- - -

Computer

- -

Large Screen

-
- -

Widescreen

-
-
-
-) - -export default ResponsiveExampleNested diff --git a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js index b4265faf95..78c1883cad 100644 --- a/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js +++ b/docs/app/Examples/addons/Responsive/Types/ResponsiveExampleResponsive.js @@ -3,11 +3,7 @@ import { Responsive, Segment } from 'semantic-ui-react' const ResponsiveExampleResponsive = () => ( - Mobile - Tablet - Computer - Large Screen - Widescreen + I'm always visible by default ) diff --git a/docs/app/Examples/addons/Responsive/Types/index.js b/docs/app/Examples/addons/Responsive/Types/index.js index 01557c0d2e..94a474bc1a 100644 --- a/docs/app/Examples/addons/Responsive/Types/index.js +++ b/docs/app/Examples/addons/Responsive/Types/index.js @@ -8,7 +8,7 @@ const ResponsiveTypesExamples = () => ( @@ -16,19 +16,24 @@ const ResponsiveTypesExamples = () => ( + ) diff --git a/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleBreakpoints.js b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleBreakpoints.js new file mode 100644 index 0000000000..f2b7d56fd4 --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleBreakpoints.js @@ -0,0 +1,14 @@ +import React from 'react' +import { Responsive, Segment } from 'semantic-ui-react' + +const ResponsiveExampleBreakpoints = () => ( + + Mobile + Tablet + Computer + Large Screen + Widescreen + +) + +export default ResponsiveExampleBreakpoints diff --git a/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleOnUpdate.js b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleOnUpdate.js new file mode 100644 index 0000000000..02bf1d303e --- /dev/null +++ b/docs/app/Examples/addons/Responsive/Usage/ResponsiveExampleOnUpdate.js @@ -0,0 +1,53 @@ +import React, { Component } from 'react' +import { Button, Grid, Label, Responsive, Segment } from 'semantic-ui-react' + +export default class ResponsiveExampleOnUpdate extends Component { + state = { + log: [], + logCount: 0, + } + + handleOnUpdate = () => this.setState({ + log: [ + `${new Date().toLocaleTimeString()}: onUpdate()`, + ...this.state.log, + ].slice(0, 20), + logCount: this.state.logCount + 1, + }) + + clearLog = () => this.setState({ log: [], logCount: 0 }) + + render() { + const { log, logCount } = this.state + + return ( + + + + Responsive Segment + + + + + + +