From f85b4449ef1af14698665ab979305d7fd51b8fb2 Mon Sep 17 00:00:00 2001 From: AgentChris Date: Wed, 30 Aug 2017 17:45:44 +0300 Subject: [PATCH 1/9] feat(Dropdown): remove diacritics on filter (#2021) * remove diacritics on filter * change to arrow function * use lodash _.deburr instead * added a test for filter after diacritics * remove unnecessary ternary check * remove unnecessary tests check on filter diacritics --- src/modules/Dropdown/Dropdown.js | 3 ++- test/specs/modules/Dropdown/Dropdown-test.js | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/modules/Dropdown/Dropdown.js b/src/modules/Dropdown/Dropdown.js index 8b0ea2a615..0b53ce8e4b 100644 --- a/src/modules/Dropdown/Dropdown.js +++ b/src/modules/Dropdown/Dropdown.js @@ -795,7 +795,8 @@ export default class Dropdown extends Component { filteredOptions = search(filteredOptions, searchQuery) } else { const re = new RegExp(_.escapeRegExp(searchQuery), 'i') - filteredOptions = _.filter(filteredOptions, opt => re.test(opt.text)) + // remove diacritics on search + filteredOptions = _.filter(filteredOptions, opt => re.test(_.deburr(opt.text))) } } diff --git a/test/specs/modules/Dropdown/Dropdown-test.js b/test/specs/modules/Dropdown/Dropdown-test.js index 456aae0ca4..9954bdc559 100644 --- a/test/specs/modules/Dropdown/Dropdown-test.js +++ b/test/specs/modules/Dropdown/Dropdown-test.js @@ -610,6 +610,23 @@ describe('Dropdown', () => { .find('.selected') .should.contain.text('a2') }) + it('filter after diacritics', () => { + const opts = [ + { text: 'FLOREŞTI', value: '1' }, + { text: 'ŞANŢU FLOREŞTI', value: '2' }, + { text: 'FLOREŞTI Alba', value: '3' }, + ] + + // search for 'floresti' + wrapperMount() + .simulate('click') + .find('input.search') + .simulate('change', { target: { value: 'floresti' } }) + + wrapper + .find('.selected') + .should.contain.text('FLOREŞTI') + }) it('still works after encountering "no results"', () => { const opts = [ { text: 'a1', value: 'a1' }, From 61c0b21be7c51d1ac83ac7fc3bf41bba996ac323 Mon Sep 17 00:00:00 2001 From: Alec Zvoncov Date: Thu, 31 Aug 2017 17:37:38 +0300 Subject: [PATCH 2/9] docs(readme): add appfollow to built with (#2030) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bb4083024c..17ebfddf6c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ These great products are built on Semantic UI React. Add yours [here][22]. - https://www.stackforge.co - https://sublimefund.org - https://thefaithcircle.com +- https://appfollow.io ## Voice Your Opinion From ef9faee4d1e20c5e30125cfc2ff8a9dfaa0822e5 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Fri, 1 Sep 2017 06:40:01 +0300 Subject: [PATCH 3/9] chore(tests): replace phantomjs with chromium (#2024) --- karma.conf.babel.js | 12 +++++++++--- package.json | 6 ++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/karma.conf.babel.js b/karma.conf.babel.js index f1ea507c82..4ee0d06c5f 100644 --- a/karma.conf.babel.js +++ b/karma.conf.babel.js @@ -1,6 +1,13 @@ +import puppeteerPkg from 'puppeteer/package.json' +import Downloader from 'puppeteer/utils/ChromiumDownloader' import config from './config' import webpackConfig from './webpack.config.babel' +const revision = puppeteerPkg.puppeteer.chromium_revision +const revisionInfo = Downloader.revisionInfo(Downloader.currentPlatform(), revision) + +process.env.CHROME_BIN = revisionInfo.executablePath + const formatError = (msg) => { // filter out empty lines and node_modules if (!msg.trim() || /~/.test(msg)) return '' @@ -22,7 +29,7 @@ const formatError = (msg) => { export default (karmaConfig) => { karmaConfig.set({ basePath: process.cwd(), - browsers: ['PhantomJS'], + browsers: ['ChromeHeadless'], client: { mocha: { reporter: 'html', // change Karma's debug.html to mocha web reporter @@ -37,11 +44,10 @@ export default (karmaConfig) => { includeAllSources: true, }, files: [ - 'node_modules/es6-shim/es6-shim.js', './test/tests.bundle.js', ], formatError, - frameworks: ['phantomjs-shim', 'mocha'], + frameworks: ['mocha'], reporters: ['mocha', 'coverage'], singleRun: true, preprocessors: { diff --git a/package.json b/package.json index 369292f8a7..91a31044fa 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,6 @@ "doctoc": "^1.3.0", "doctrine": "^2.0.0", "enzyme": "^2.9.1", - "es6-shim": "^0.35.3", "eslint": "^4.4.1", "eslint-config-airbnb": "^15.1.0", "eslint-plugin-import": "^2.7.0", @@ -105,16 +104,15 @@ "js-beautify": "^1.6.14", "json-loader": "^0.5.7", "karma": "^1.7.0", + "karma-chrome-launcher": "^2.2.0", "karma-cli": "^1.0.1", "karma-coverage": "^1.1.1", "karma-mocha": "^1.3.0", "karma-mocha-reporter": "^2.2.3", - "karma-phantomjs-launcher": "^1.0.4", - "karma-phantomjs-shim": "^1.4.0", "karma-webpack-with-fast-source-maps": "^1.10.2", "mocha": "^3.5.0", "node-sass": "^4.5.3", - "phantomjs-prebuilt": "^2.1.15", + "puppeteer": "^0.10.1", "raw-loader": "^0.5.1", "react": "^15.6.1", "react-ace": "^5.1.2", From 578b957773b9ea8e800061664026ec083b46533e Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Fri, 1 Sep 2017 08:53:19 +0300 Subject: [PATCH 4/9] feat(Visibility): add fireOnMount (#2018) * feat(Visibility): add fireOnMount * docs(Visibility): remove "once" from fireOnMount --- .../Settings/VisibilityExampleFireOnMount.js | 112 ++++++++++++++++++ .../behaviors/Visibility/Settings/index.js | 9 +- .../behaviors/Visibility/Types/index.js | 2 +- src/behaviors/Visibility/Visibility.d.ts | 3 + src/behaviors/Visibility/Visibility.js | 13 +- .../behaviors/Visibility/Visibility-test.js | 15 +++ 6 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js diff --git a/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js new file mode 100644 index 0000000000..1193681e9c --- /dev/null +++ b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js @@ -0,0 +1,112 @@ +import React, { Component } from 'react' +import { Divider, Grid, Image, Table, Segment, Visibility } from 'semantic-ui-react' + +export default class VisibilityExampleFireOnMount extends Component { + state = { + calculations: { + height: 0, + width: 0, + topPassed: false, + bottomPassed: false, + pixelsPassed: 0, + percentagePassed: 0, + topVisible: false, + bottomVisible: false, + fits: false, + passing: false, + onScreen: false, + offScreen: false, + }, + } + + handleOnScreen = (e, { calculations }) => this.setState({ calculations }) + + handleOffScreen = (e, { calculations }) => this.setState({ calculations }) + + render() { + const { calculations } = this.state + + return ( + + + + + + + + + + + + + + + + + + + + Calculation + Value + + + + + pixelsPassed + {calculations.pixelsPassed.toFixed()}px + + + percentagePassed + {(calculations.percentagePassed * 100).toFixed()}% + + + fits + {calculations.fits.toString()} + + + width + {calculations.width.toFixed()}px + + + height + {calculations.height.toFixed()}px + + + onScreen + {calculations.onScreen.toString()} + + + offScreen + {calculations.offScreen.toString()} + + + passing + {calculations.passing.toString()} + + + topVisible + {calculations.topVisible.toString()} + + + bottomVisible + {calculations.bottomVisible.toString()} + + + topPassed + {calculations.topPassed.toString()} + + + bottomPassed + {calculations.bottomPassed.toString()} + + +
+
+
+ ) + } +} diff --git a/docs/app/Examples/behaviors/Visibility/Settings/index.js b/docs/app/Examples/behaviors/Visibility/Settings/index.js index e2f372d761..c68dbd1dfc 100644 --- a/docs/app/Examples/behaviors/Visibility/Settings/index.js +++ b/docs/app/Examples/behaviors/Visibility/Settings/index.js @@ -5,14 +5,19 @@ import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' const VisibilityExample = () => ( + diff --git a/docs/app/Examples/behaviors/Visibility/Types/index.js b/docs/app/Examples/behaviors/Visibility/Types/index.js index 08f39c1ec4..ceed463a2b 100644 --- a/docs/app/Examples/behaviors/Visibility/Types/index.js +++ b/docs/app/Examples/behaviors/Visibility/Types/index.js @@ -7,7 +7,7 @@ const VisibilityExample = () => ( diff --git a/src/behaviors/Visibility/Visibility.d.ts b/src/behaviors/Visibility/Visibility.d.ts index dc7ad7fdb8..1ce1d60b90 100644 --- a/src/behaviors/Visibility/Visibility.d.ts +++ b/src/behaviors/Visibility/Visibility.d.ts @@ -18,6 +18,9 @@ export interface VisibilityProps { */ continuous?: boolean; + /** Fires callbacks immediately after mount. */ + fireOnMount?: boolean; + /** * Element's bottom edge has passed top of screen. * diff --git a/src/behaviors/Visibility/Visibility.js b/src/behaviors/Visibility/Visibility.js index 547cda644b..ef41889e02 100644 --- a/src/behaviors/Visibility/Visibility.js +++ b/src/behaviors/Visibility/Visibility.js @@ -30,6 +30,9 @@ export default class Visibility extends Component { */ continuous: PropTypes.bool, + /** Fires callbacks immediately after mount. */ + fireOnMount: PropTypes.bool, + /** * Element's bottom edge has passed top of screen. * @@ -173,15 +176,17 @@ export default class Visibility extends Component { componentDidMount() { if (!isBrowser) return - const { context } = this.props - context.addEventListener('scroll', this.handleScroll) + const { context, fireOnMount } = this.props + + context.addEventListener('scroll', this.handleUpdate) + if (fireOnMount) this.handleUpdate() } componentWillUnmount() { if (!isBrowser) return const { context } = this.props - context.removeEventListener('scroll', this.handleScroll) + context.removeEventListener('scroll', this.handleUpdate) } execute = (callback, name) => { @@ -274,7 +279,7 @@ export default class Visibility extends Component { handleRef = c => (this.ref = c) - handleScroll = () => { + handleUpdate = () => { const { bottom, height, top, width } = this.ref.getBoundingClientRect() const topPassed = top < 0 diff --git a/test/specs/behaviors/Visibility/Visibility-test.js b/test/specs/behaviors/Visibility/Visibility-test.js index 703a37a335..a9da20a139 100644 --- a/test/specs/behaviors/Visibility/Visibility-test.js +++ b/test/specs/behaviors/Visibility/Visibility-test.js @@ -185,6 +185,21 @@ describe('Visibility', () => { }) }) + describe('fireOnMount', () => { + it('fires callbacks after mount', () => { + const onUpdate = sandbox.spy() + + mockScroll(0, 0) + wrapperMount() + + onUpdate.should.have.been.calledOnce() + onUpdate.should.have.been.calledWithMatch(null, { + calculations: { height: 0, width: 0 }, + fireOnMount: true, + }) + }) + }) + describe('onPassed', () => { it('fires callback when pixels passed', () => { const onPassed = { From f1f7ceb1dec7f8c45489ca0744b77c4aa3096219 Mon Sep 17 00:00:00 2001 From: Allen Reinmeyer Date: Fri, 1 Sep 2017 02:08:55 -0400 Subject: [PATCH 5/9] breaking(Checkbox): callback with new checked value in onClick (#2014) breaking(Checkbox): callback with new checked value in onClick --- src/modules/Checkbox/Checkbox.js | 2 +- test/specs/modules/Checkbox/Checkbox-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/Checkbox/Checkbox.js b/src/modules/Checkbox/Checkbox.js index d8ec83c1b9..963db30392 100644 --- a/src/modules/Checkbox/Checkbox.js +++ b/src/modules/Checkbox/Checkbox.js @@ -159,7 +159,7 @@ export default class Checkbox extends Component { if (!this.canToggle()) return - _.invoke(this.props, 'onClick', e, { ...this.props, checked: !!checked, indeterminate: !!indeterminate }) + _.invoke(this.props, 'onClick', e, { ...this.props, checked: !checked, indeterminate: !!indeterminate }) _.invoke(this.props, 'onChange', e, { ...this.props, checked: !checked, indeterminate: false }) this.trySetState({ checked: !checked, indeterminate: false }) diff --git a/test/specs/modules/Checkbox/Checkbox-test.js b/test/specs/modules/Checkbox/Checkbox-test.js index 2a42a62f1f..8fbc4c4c34 100644 --- a/test/specs/modules/Checkbox/Checkbox-test.js +++ b/test/specs/modules/Checkbox/Checkbox-test.js @@ -155,7 +155,7 @@ describe('Checkbox', () => { spy.should.have.been.calledOnce() spy.should.have.been.calledWithMatch({}, { ...expectProps, - checked: expectProps.checked, + checked: !expectProps.checked, indeterminate: expectProps.indeterminate, }) }) From 12107dbcdc34c428a2d89a53712066f0eef8518e Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Fri, 1 Sep 2017 17:58:52 +0300 Subject: [PATCH 6/9] feat(Visibility): add offset (#2016) * feat(Visibility): add offset * fix(ComponentExample): add Wireframe, fix editor padding * docs(Visibility): better event log ux --- .../ComponentExample/ComponentExample.js | 9 +- .../Settings/CallbackFrequencyExample.js | 66 ----------- .../Settings/GroupedCallbacksExample.js | 63 ----------- .../VisibilityExampleCallbackFrequency.js | 79 +++++++++++++ .../VisibilityExampleGroupedCallbacks.js | 75 +++++++++++++ .../Settings/VisibilityExampleOffset.js | 66 +++++++++++ .../behaviors/Visibility/Settings/index.js | 16 ++- .../Visibility/Types/VisibilityExample.js | 101 ----------------- .../Types/VisibilityExampleVisibility.js | 106 ++++++++++++++++++ .../behaviors/Visibility/Types/index.js | 6 +- src/behaviors/Visibility/Visibility.d.ts | 6 + src/behaviors/Visibility/Visibility.js | 25 ++++- src/lib/index.js | 1 + src/lib/normalizeOffset.js | 6 + .../behaviors/Visibility/Visibility-test.js | 36 ++++++ 15 files changed, 418 insertions(+), 243 deletions(-) delete mode 100644 docs/app/Examples/behaviors/Visibility/Settings/CallbackFrequencyExample.js delete mode 100644 docs/app/Examples/behaviors/Visibility/Settings/GroupedCallbacksExample.js create mode 100644 docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js create mode 100644 docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js create mode 100644 docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js delete mode 100644 docs/app/Examples/behaviors/Visibility/Types/VisibilityExample.js create mode 100644 docs/app/Examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js create mode 100644 src/lib/normalizeOffset.js diff --git a/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js b/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js index a2529856b7..70bdb3e4f8 100644 --- a/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js +++ b/docs/app/Components/ComponentDoc/ComponentExample/ComponentExample.js @@ -166,6 +166,7 @@ class ComponentExample extends Component { const LODASH = require('lodash') const REACT = require('react') const SEMANTIC_UI_REACT = require('semantic-ui-react') + let WIREFRAME let COMMON /* eslint-enable no-unused-vars */ @@ -191,6 +192,8 @@ class ComponentExample extends Component { if (module === 'COMMON') { const componentPath = examplePath.split(__PATH_SEP__).splice(0, 2).join(__PATH_SEP__) COMMON = require(`docs/app/Examples/${componentPath}/common`) + } else if (module === 'WIREFRAME') { + WIREFRAME = require('docs/app/Examples/behaviors/Visibility/Wireframe').default } const constStatements = [] @@ -420,8 +423,10 @@ class ComponentExample extends Component { {exampleElement} - {this.renderJSX()} - {this.renderHTML()} + + {this.renderJSX()} + {this.renderHTML()} + ) diff --git a/docs/app/Examples/behaviors/Visibility/Settings/CallbackFrequencyExample.js b/docs/app/Examples/behaviors/Visibility/Settings/CallbackFrequencyExample.js deleted file mode 100644 index 94bc3a1848..0000000000 --- a/docs/app/Examples/behaviors/Visibility/Settings/CallbackFrequencyExample.js +++ /dev/null @@ -1,66 +0,0 @@ -import React, { Component } from 'react' -import { Button, Checkbox, Divider, Grid, Segment, Visibility } from 'semantic-ui-react' - -import Wireframe from '../Wireframe' - -export default class VisibilityExample extends Component { - state = { - continuous: false, - log: [], - once: true, - } - - updateLog = eventName => () => this.setState({ log: [eventName, ...this.state.log] }) - - clearLog = () => this.setState({ log: [] }) - - toggleOnce = () => this.setState({ once: !this.state.once }) - - toggleContinuous = () => this.setState({ continuous: !this.state.continuous }) - - render() { - const { continuous, log, once } = this.state - - return ( - - - - - - - - - - - - - - - - Event Log - -
-                {log.map((e, i) => 

{e} fired

)} -
-
-
-
-
- ) - } -} diff --git a/docs/app/Examples/behaviors/Visibility/Settings/GroupedCallbacksExample.js b/docs/app/Examples/behaviors/Visibility/Settings/GroupedCallbacksExample.js deleted file mode 100644 index 7e78225358..0000000000 --- a/docs/app/Examples/behaviors/Visibility/Settings/GroupedCallbacksExample.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, { Component } from 'react' -import { Button, Checkbox, Divider, Grid, Segment, Visibility } from 'semantic-ui-react' -import Wireframe from '../Wireframe' - -class VisibilityExample extends Component { - state = { - continuous: false, - log: [], - once: true, - } - - updateLog = eventName => () => this.setState({ log: [eventName, ...this.state.log] }) - - clearLog = () => this.setState({ log: [] }) - - toggleOnce = () => this.setState({ once: !this.state.once }) - - toggleContinuous = () => this.setState({ continuous: !this.state.continuous }) - - render() { - const { continuous, log, once } = this.state - - return ( - - - - - - - - - - - - - - - - Event Log - -
-                {log.map((e, i) => 

{e} fired

)} -
-
-
-
-
- ) - } -} - -export default VisibilityExample diff --git a/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js new file mode 100644 index 0000000000..f2aaa0e5b3 --- /dev/null +++ b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js @@ -0,0 +1,79 @@ +import React, { Component } from 'react' +import { Button, Checkbox, Divider, Grid, Label, Segment, Sticky, Visibility } from 'semantic-ui-react' + +import Wireframe from '../Wireframe' + +export default class VisibilityExampleCallbackFrequency extends Component { + state = { + continuous: false, + log: [], + logCount: 0, + once: true, + } + + handleContextRef = contextRef => this.setState({ contextRef }) + + updateLog = eventName => () => this.setState(({ + log: [ + `${new Date().toLocaleTimeString()}: ${eventName}`, + ...this.state.log, + ].slice(0, 20), + logCount: this.state.logCount + 1, + })) + + clearLog = () => this.setState({ log: [], logCount: 0 }) + + toggleOnce = () => this.setState({ once: !this.state.once }) + + toggleContinuous = () => this.setState({ continuous: !this.state.continuous }) + + render() { + const { continuous, contextRef, log, logCount, once } = this.state + + return ( +
+ + + + + + + + + + + + + + + + + + Event Log + + +
{log.map((e, i) => 
{e}
)}
+
+
+
+
+
+
+ ) + } +} diff --git a/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js new file mode 100644 index 0000000000..9652bfb6a6 --- /dev/null +++ b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js @@ -0,0 +1,75 @@ +import React, { Component } from 'react' +import { Button, Checkbox, Divider, Grid, Label, Segment, Sticky, Visibility } from 'semantic-ui-react' + +import Wireframe from '../Wireframe' + +export default class VisibilityExampleGroupedCallbacks extends Component { + state = { + continuous: false, + log: [], + logCount: 0, + once: true, + } + + handleContextRef = contextRef => this.setState({ contextRef }) + + updateLog = eventName => () => this.setState(({ + log: [ + `${new Date().toLocaleTimeString()}: ${eventName}`, + ...this.state.log, + ].slice(0, 20), + logCount: this.state.logCount + 1, + })) + + clearLog = () => this.setState({ log: [], logCount: 0 }) + + toggleOnce = () => this.setState({ once: !this.state.once }) + + toggleContinuous = () => this.setState({ continuous: !this.state.continuous }) + + render() { + const { continuous, contextRef, log, logCount, once } = this.state + + return ( +
+ + + + + + + + + + + + + + + + + + Event Log + + +
{log.map((e, i) => 
{e}
)}
+
+
+
+
+
+
+ ) + } +} diff --git a/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js new file mode 100644 index 0000000000..6b7a9c823e --- /dev/null +++ b/docs/app/Examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js @@ -0,0 +1,66 @@ +import React, { Component } from 'react' +import { Grid, Sticky, Table, Visibility } from 'semantic-ui-react' + +import Wireframe from '../Wireframe' + +export default class VisibilityExampleOffset extends Component { + state = { + calculations: { + topPassed: false, + bottomPassed: false, + topVisible: false, + bottomVisible: false, + }, + } + + handleContextRef = contextRef => this.setState({ contextRef }) + + handleUpdate = (e, { calculations }) => this.setState({ calculations }) + + render() { + const { calculations, contextRef } = this.state + + return ( +
+ + + + + + + + + + + + + Calculation + Value + + + + + topVisible + {calculations.topVisible.toString()} + + + bottomVisible + {calculations.bottomVisible.toString()} + + + topPassed + {calculations.topPassed.toString()} + + + bottomPassed + {calculations.bottomPassed.toString()} + + +
+
+
+
+
+ ) + } +} diff --git a/docs/app/Examples/behaviors/Visibility/Settings/index.js b/docs/app/Examples/behaviors/Visibility/Settings/index.js index c68dbd1dfc..7c21456f3a 100644 --- a/docs/app/Examples/behaviors/Visibility/Settings/index.js +++ b/docs/app/Examples/behaviors/Visibility/Settings/index.js @@ -3,24 +3,32 @@ import React from 'react' import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection' -const VisibilityExample = () => ( +const VisibilitySettingsExamples = () => ( + ) -export default VisibilityExample +export default VisibilitySettingsExamples diff --git a/docs/app/Examples/behaviors/Visibility/Types/VisibilityExample.js b/docs/app/Examples/behaviors/Visibility/Types/VisibilityExample.js deleted file mode 100644 index 78f66398b7..0000000000 --- a/docs/app/Examples/behaviors/Visibility/Types/VisibilityExample.js +++ /dev/null @@ -1,101 +0,0 @@ -import React, { Component } from 'react' -import { Grid, Table, Visibility } from 'semantic-ui-react' -import Wireframe from '../Wireframe' - -class VisibilityExample extends Component { - state = { - calculations: { - height: 0, - width: 0, - topPassed: false, - bottomPassed: false, - pixelsPassed: 0, - percentagePassed: 0, - topVisible: false, - bottomVisible: false, - fits: false, - passing: false, - onScreen: false, - offScreen: false, - }, - } - - handleUpdate = (e, { calculations }) => this.setState({ calculations }) - - render() { - const { calculations } = this.state - - return ( - - - - - - - - - - - - Calculation - Value - - - - - pixelsPassed - {calculations.pixelsPassed.toFixed()}px - - - percentagePassed - {(calculations.percentagePassed * 100).toFixed()}% - - - fits - {calculations.fits.toString()} - - - width - {calculations.width.toFixed()}px - - - height - {calculations.height.toFixed()}px - - - onScreen - {calculations.onScreen.toString()} - - - offScreen - {calculations.offScreen.toString()} - - - passing - {calculations.passing.toString()} - - - topVisible - {calculations.topVisible.toString()} - - - bottomVisible - {calculations.bottomVisible.toString()} - - - topPassed - {calculations.topPassed.toString()} - - - bottomPassed - {calculations.bottomPassed.toString()} - - -
-
-
- ) - } -} - -export default VisibilityExample diff --git a/docs/app/Examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js b/docs/app/Examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js new file mode 100644 index 0000000000..418c85f676 --- /dev/null +++ b/docs/app/Examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js @@ -0,0 +1,106 @@ +import React, { Component } from 'react' +import { Grid, Sticky, Table, Visibility } from 'semantic-ui-react' + +import Wireframe from '../Wireframe' + +export default class VisibilityExampleVisibility extends Component { + state = { + calculations: { + height: 0, + width: 0, + topPassed: false, + bottomPassed: false, + pixelsPassed: 0, + percentagePassed: 0, + topVisible: false, + bottomVisible: false, + fits: false, + passing: false, + onScreen: false, + offScreen: false, + }, + } + + handleContextRef = contextRef => this.setState({ contextRef }) + + handleUpdate = (e, { calculations }) => this.setState({ calculations }) + + render() { + const { calculations, contextRef } = this.state + + return ( +
+ + + + + + + + + + + + + Calculation + Value + + + + + pixelsPassed + {calculations.pixelsPassed.toFixed()}px + + + percentagePassed + {(calculations.percentagePassed * 100).toFixed()}% + + + fits + {calculations.fits.toString()} + + + width + {calculations.width.toFixed()}px + + + height + {calculations.height.toFixed()}px + + + onScreen + {calculations.onScreen.toString()} + + + offScreen + {calculations.offScreen.toString()} + + + passing + {calculations.passing.toString()} + + + topVisible + {calculations.topVisible.toString()} + + + bottomVisible + {calculations.bottomVisible.toString()} + + + topPassed + {calculations.topPassed.toString()} + + + bottomPassed + {calculations.bottomPassed.toString()} + + +
+
+
+
+
+ ) + } +} diff --git a/docs/app/Examples/behaviors/Visibility/Types/index.js b/docs/app/Examples/behaviors/Visibility/Types/index.js index ceed463a2b..52ff021199 100644 --- a/docs/app/Examples/behaviors/Visibility/Types/index.js +++ b/docs/app/Examples/behaviors/Visibility/Types/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 VisibilityExample = () => ( +const VisibilityTypesExamples = () => ( ) -export default VisibilityExample +export default VisibilityTypesExamples diff --git a/src/behaviors/Visibility/Visibility.d.ts b/src/behaviors/Visibility/Visibility.d.ts index 1ce1d60b90..9d6b24abf8 100644 --- a/src/behaviors/Visibility/Visibility.d.ts +++ b/src/behaviors/Visibility/Visibility.d.ts @@ -53,6 +53,12 @@ export interface VisibilityProps { */ onBottomVisibleReverse?: (nothing: null, data: VisibilityEventData) => void; + /** + * Value that context should be adjusted in pixels. Useful for making content appear below content fixed to the + * page. + */ + offset?: number | string | Array; + /** When set to false a callback will occur each time an element passes the threshold for a condition. */ once?: boolean; diff --git a/src/behaviors/Visibility/Visibility.js b/src/behaviors/Visibility/Visibility.js index ef41889e02..3c4d9c5882 100644 --- a/src/behaviors/Visibility/Visibility.js +++ b/src/behaviors/Visibility/Visibility.js @@ -7,6 +7,7 @@ import { getElementType, getUnhandledProps, META, + normalizeOffset, isBrowser, } from '../../lib' @@ -65,6 +66,19 @@ export default class Visibility extends Component { */ onBottomVisibleReverse: PropTypes.func, + /** + * Value that context should be adjusted in pixels. Useful for making content appear below content fixed to the + * page. + */ + offset: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + PropTypes.arrayOf([ + PropTypes.number, + PropTypes.string, + ]), + ]), + /** When set to false a callback will occur each time an element passes the threshold for a condition. */ once: PropTypes.bool, @@ -147,6 +161,7 @@ export default class Visibility extends Component { static defaultProps = { context: isBrowser ? window : null, continuous: false, + offset: [0, 0], once: true, } @@ -280,16 +295,18 @@ export default class Visibility extends Component { handleRef = c => (this.ref = c) handleUpdate = () => { + const { offset } = this.props const { bottom, height, top, width } = this.ref.getBoundingClientRect() + const [topOffset, bottomOffset] = normalizeOffset(offset) - const topPassed = top < 0 - const bottomPassed = bottom < 0 + const topPassed = top < topOffset + const bottomPassed = bottom < bottomOffset const pixelsPassed = bottomPassed ? 0 : Math.max(top * -1, 0) const percentagePassed = pixelsPassed / height - const bottomVisible = bottom >= 0 && bottom <= window.innerHeight - const topVisible = top >= 0 && top <= window.innerHeight + const bottomVisible = bottom >= bottomOffset && bottom <= window.innerHeight + const topVisible = top >= topOffset && top <= window.innerHeight const fits = topVisible && bottomVisible const passing = topPassed && !bottomPassed diff --git a/src/lib/index.js b/src/lib/index.js index eea44556e1..4778c777c9 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -39,6 +39,7 @@ export * as SUI from './SUI' export { default as keyboardKey } from './keyboardKey' export { numberToWordMap, numberToWord } from './numberToWord' +export normalizeOffset from './normalizeOffset' export normalizeTransitionDuration from './normalizeTransitionDuration' export { default as objectDiff } from './objectDiff' export shallowEqual from './shallowEqual' diff --git a/src/lib/normalizeOffset.js b/src/lib/normalizeOffset.js new file mode 100644 index 0000000000..861d02f90b --- /dev/null +++ b/src/lib/normalizeOffset.js @@ -0,0 +1,6 @@ +/** + * Normalizes the offset value. + * @param {number|array} value The value to normalize. + * @returns {number} + */ +export default value => ((typeof value === 'number' || typeof value === 'string') ? [value, value] : value) diff --git a/test/specs/behaviors/Visibility/Visibility-test.js b/test/specs/behaviors/Visibility/Visibility-test.js index a9da20a139..244d372f4d 100644 --- a/test/specs/behaviors/Visibility/Visibility-test.js +++ b/test/specs/behaviors/Visibility/Visibility-test.js @@ -1,3 +1,4 @@ +import _ from 'lodash' import React from 'react' import Visibility from 'src/behaviors/Visibility' @@ -200,6 +201,41 @@ describe('Visibility', () => { }) }) + describe('offset', () => { + _.each(_.filter(expectations, 'callback'), (expectation) => { + it(`fires ${expectation.name} when offset is number`, () => { + const callback = sandbox.spy() + const opts = { [expectation.callback]: callback } + + const offset = 10 + const falseCond = _.map(expectation.false[0], value => value + offset) + const trueCond = _.map(expectation.true[0], value => value + offset) + + wrapperMount() + mockScroll(...trueCond) + mockScroll(...falseCond) + + callback.should.have.been.calledOnce() + }) + + it(`fires ${expectation.name} when offset is array`, () => { + const callback = sandbox.spy() + const opts = { [expectation.callback]: callback } + + const bottomOffset = 20 + const topOffset = 10 + const falseCond = [expectation.false[0][0] + topOffset, expectation.false[0][1] + bottomOffset] + const trueCond = [expectation.true[0][0] + topOffset, expectation.true[0][1] + bottomOffset] + + wrapperMount() + mockScroll(...trueCond) + mockScroll(...falseCond) + + callback.should.have.been.calledOnce() + }) + }) + }) + describe('onPassed', () => { it('fires callback when pixels passed', () => { const onPassed = { From 1596486874851c349cf201009317dea58baa9c63 Mon Sep 17 00:00:00 2001 From: Alexander Fedyashov Date: Fri, 1 Sep 2017 18:24:47 +0300 Subject: [PATCH 7/9] fix(Modal): fix typo in className (#2004) * fix(Modal): fix typo in className * test(Modal): add missing body class coverage * test(Modal): restore window.innerHeight after tests --- src/modules/Modal/Modal.js | 2 +- test/specs/modules/Modal/Modal-test.js | 50 +++++++++++++++++++++----- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src/modules/Modal/Modal.js b/src/modules/Modal/Modal.js index 19745eabed..d8cf253360 100644 --- a/src/modules/Modal/Modal.js +++ b/src/modules/Modal/Modal.js @@ -227,7 +227,7 @@ class Modal extends Component { mountNode.classList.remove('blurring') mountNode.classList.remove('dimmable') mountNode.classList.remove('dimmed') - mountNode.classList.remove('scrollable') + mountNode.classList.remove('scrolling') cancelAnimationFrame(this.animationRequestId) diff --git a/test/specs/modules/Modal/Modal-test.js b/test/specs/modules/Modal/Modal-test.js index f16db4fa1c..7304b33653 100644 --- a/test/specs/modules/Modal/Modal-test.js +++ b/test/specs/modules/Modal/Modal-test.js @@ -240,9 +240,14 @@ describe('Modal', () => { }) describe('true', () => { - it('adds classes "dimmable dimmed" to the body', () => { + it('adds/removes body classes "dimmable dimmed" on mount/unmount', () => { + assertBodyClasses('dimmable', 'dimmed', false) + wrapperMount() assertBodyClasses('dimmable', 'dimmed') + + wrapper.unmount() + assertBodyClasses('dimmable', 'dimmed', false) }) it('adds a dimmer to the body', () => { @@ -264,9 +269,14 @@ describe('Modal', () => { }) describe('blurring', () => { - it('adds class "dimmable dimmed blurring" to the body', () => { + it('adds/removes body classes "dimmable dimmed blurring" on mount/unmount', () => { + assertBodyClasses('dimmable', 'dimmed', 'blurring', false) + wrapperMount() assertBodyClasses('dimmable', 'dimmed', 'blurring') + + wrapper.unmount() + assertBodyClasses('dimmable', 'dimmed', 'blurring', false) }) it('adds a dimmer to the body', () => { @@ -276,10 +286,14 @@ describe('Modal', () => { }) describe('inverted', () => { - it('adds class "dimmable dimmed" to the body', () => { - wrapperMount() + it('adds/removes body classes "dimmable dimmed" on mount/unmount', () => { + assertBodyClasses('dimmable', 'dimmed', false) + + wrapperMount() assertBodyClasses('dimmable', 'dimmed') - assertBodyClasses('inverted', false) + + wrapper.unmount() + assertBodyClasses('dimmable', 'dimmed', false) }) it('adds an inverted dimmer to the body', () => { @@ -460,19 +474,24 @@ describe('Modal', () => { }) describe('scrolling', () => { + const innerHeight = window.innerHeight + afterEach(() => { document.body.classList.remove('scrolling') }) + after(() => { + window.innerHeight = innerHeight + }) + it('does not add the scrolling class to the body by default', () => { wrapperMount() assertBodyClasses('scrolling', false) }) it('adds the scrolling class to the body when taller than the window', (done) => { - wrapperMount(foo) - window.innerHeight = 10 + wrapperMount(foo) requestAnimationFrame(() => { assertBodyClasses('scrolling') @@ -480,7 +499,7 @@ describe('Modal', () => { }) }) - it('removes the scrolling class from the body when the window grows taller', (done) => { + it('adds/removes the scrolling class to the body when the window grows/shrinks', (done) => { assertBodyClasses('scrolling', false) wrapperMount(foo) @@ -496,5 +515,20 @@ describe('Modal', () => { }) }) }) + + it('removes the scrolling class from the body on unmount', (done) => { + assertBodyClasses('scrolling', false) + + window.innerHeight = 10 + wrapperMount(foo) + + requestAnimationFrame(() => { + assertBodyClasses('scrolling') + wrapper.unmount() + + assertBodyClasses('scrolling', false) + done() + }) + }) }) }) From 85a2b991ceb22f600398597149d18b5271b435c5 Mon Sep 17 00:00:00 2001 From: nickpedersen Date: Sat, 2 Sep 2017 01:41:20 +1000 Subject: [PATCH 8/9] fix(TextArea): use onInput for updating height for IE compatibility (#1982) * fix(TextArea): use onInput for updating height for IE compatibility * fix(TextArea): cleanup onChange and onInput typings * fix(typings): remove useless export --- index.d.ts | 2 +- src/addons/TextArea/TextArea.d.ts | 14 +++++++++----- src/addons/TextArea/TextArea.js | 14 ++++++++++++++ src/addons/TextArea/index.d.ts | 2 +- test/specs/addons/TextArea/TextArea-test.js | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 7 deletions(-) diff --git a/index.d.ts b/index.d.ts index 167d3b1842..df2212038c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,7 +3,7 @@ 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'; export { default as Select, SelectProps } from './dist/commonjs/addons/Select'; -export { default as TextArea, TextAreaProps, TextAreaOnChangeData } from './dist/commonjs/addons/TextArea'; +export { default as TextArea, TextAreaProps } from './dist/commonjs/addons/TextArea'; // Behaviors export { diff --git a/src/addons/TextArea/TextArea.d.ts b/src/addons/TextArea/TextArea.d.ts index 9e3e919359..32f8ffff62 100644 --- a/src/addons/TextArea/TextArea.d.ts +++ b/src/addons/TextArea/TextArea.d.ts @@ -15,7 +15,15 @@ export interface TextAreaProps { * @param {SyntheticEvent} event - The React SyntheticEvent object * @param {object} data - All props and the event value. */ - onChange?: (event: React.FormEvent, data: TextAreaOnChangeData) => void; + onChange?: (event: React.FormEvent, data: TextAreaProps) => void; + + /** + * Called on input. + * + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onInput?: (event: React.FormEvent, data: TextAreaProps) => void; /** Indicates row count for a TextArea. */ rows?: number | string; @@ -27,10 +35,6 @@ export interface TextAreaProps { value?: number | string; } -export interface TextAreaOnChangeData extends TextAreaProps { - value?: string; -} - declare class TextArea extends React.Component { focus: () => void; } diff --git a/src/addons/TextArea/TextArea.js b/src/addons/TextArea/TextArea.js index 4f41421ae2..be60e645e6 100644 --- a/src/addons/TextArea/TextArea.js +++ b/src/addons/TextArea/TextArea.js @@ -33,6 +33,13 @@ class TextArea extends Component { */ onChange: PropTypes.func, + /** + * Called on input. + * @param {SyntheticEvent} event - The React SyntheticEvent object + * @param {object} data - All props and the event value. + */ + onInput: PropTypes.func, + /** Indicates row count for a TextArea. */ rows: PropTypes.oneOfType([ PropTypes.number, @@ -75,6 +82,12 @@ class TextArea extends Component { const value = _.get(e, 'target.value') _.invoke(this.props, 'onChange', e, { ...this.props, value }) + } + + handleInput = (e) => { + const value = _.get(e, 'target.value') + + _.invoke(this.props, 'onInput', e, { ...this.props, value }) this.updateHeight() } @@ -116,6 +129,7 @@ class TextArea extends Component { { }) }) + describe('onInput', () => { + it('is called with (e, data) on input', () => { + const spy = sandbox.spy() + const e = { target: { value: 'name' } } + const props = { 'data-foo': 'bar', onInput: spy } + + wrapperShallow(