From 8652239087dee707a13ab9384a5b3da6824952a9 Mon Sep 17 00:00:00 2001 From: Oleksandr Fediashov Date: Fri, 24 Sep 2021 13:44:15 +0200 Subject: [PATCH] chore(Visibility): remove component (#4257) * chore(Visibility): remove component * remove behaviors section --- .../VisibilityExampleCallbackFrequency.js | 134 ---- .../Settings/VisibilityExampleFireOnMount.js | 126 ---- .../VisibilityExampleGroupedCallbacks.js | 130 ---- .../Settings/VisibilityExampleOffset.js | 102 --- .../Settings/VisibilityExampleUpdateOn.js | 114 ---- .../behaviors/Visibility/Settings/index.js | 52 -- .../Types/VisibilityExampleVisibility.js | 151 ----- .../behaviors/Visibility/Types/index.js | 16 - .../examples/behaviors/Visibility/index.js | 30 - docs/src/layouts/HomepageLayout.js | 13 +- docs/src/layouts/StickyLayout.js | 30 +- docs/src/utils/constants.js | 2 +- gulp/tasks/docs.js | 1 - index.d.ts | 10 - src/behaviors/Visibility/Visibility.d.ts | 176 ----- src/behaviors/Visibility/Visibility.js | 428 ------------ src/behaviors/Visibility/index.d.ts | 8 - src/behaviors/Visibility/index.js | 1 - src/collections/Form/Form.js | 1 - src/collections/Form/FormField.js | 1 - src/index.js | 3 - src/lib/index.js | 1 - src/lib/normalizeOffset.js | 7 - .../behaviors/Visibility/Visibility-test.js | 630 ------------------ 24 files changed, 14 insertions(+), 2153 deletions(-) delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleUpdateOn.js delete mode 100644 docs/src/examples/behaviors/Visibility/Settings/index.js delete mode 100644 docs/src/examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js delete mode 100644 docs/src/examples/behaviors/Visibility/Types/index.js delete mode 100644 docs/src/examples/behaviors/Visibility/index.js delete mode 100644 src/behaviors/Visibility/Visibility.d.ts delete mode 100644 src/behaviors/Visibility/Visibility.js delete mode 100644 src/behaviors/Visibility/index.d.ts delete mode 100644 src/behaviors/Visibility/index.js delete mode 100644 src/lib/normalizeOffset.js delete mode 100644 test/specs/behaviors/Visibility/Visibility-test.js diff --git a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js b/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js deleted file mode 100644 index e6bbf62c13..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleCallbackFrequency.js +++ /dev/null @@ -1,134 +0,0 @@ -import _ from 'lodash' -import React, { Component, createRef } from 'react' -import { - Button, - Checkbox, - Divider, - Grid, - Image, - Label, - Ref, - Segment, - Sticky, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleCallbackFrequency extends Component { - state = { - continuous: false, - log: [], - logCount: 0, - once: true, - } - contextRef = createRef() - - updateLog = (eventName) => () => - this.setState((prevState) => ({ - log: [ - `${new Date().toLocaleTimeString()}: ${eventName}`, - ...prevState.log, - ].slice(0, 20), - logCount: prevState.logCount + 1, - })) - - clearLog = () => this.setState({ log: [], logCount: 0 }) - - toggleOnce = () => this.setState((prevState) => ({ once: !prevState.once })) - - toggleContinuous = () => - this.setState((prevState) => ({ continuous: !prevState.continuous })) - - render() { - const { continuous, log, logCount, once } = this.state - - return ( - - - - - - {_.map( - [ - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - ], - (src, index, images) => ( - - - {index !== images.length - 1 && } - - ), - )} - - - - - - - - - - - - - - - Event Log - - -
-                    {log.map((e, i) => (
-                      
{e}
- ))} -
-
-
-
-
-
-
- ) - } -} diff --git a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js b/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js deleted file mode 100644 index 467116bf73..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleFireOnMount.js +++ /dev/null @@ -1,126 +0,0 @@ -import React, { Component } from 'react' -import { - Divider, - Grid, - Image, - Table, - Segment, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleFireOnMount extends Component { - state = { - calculations: { - direction: 'none', - 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 - - - - - direction - {calculations.direction} - - - 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/src/examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js b/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js deleted file mode 100644 index f671ba1ded..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleGroupedCallbacks.js +++ /dev/null @@ -1,130 +0,0 @@ -import _ from 'lodash' -import React, { Component, createRef } from 'react' -import { - Button, - Checkbox, - Divider, - Grid, - Image, - Label, - Ref, - Segment, - Sticky, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleGroupedCallbacks extends Component { - state = { - continuous: false, - log: [], - logCount: 0, - once: true, - } - contextRef = createRef() - - updateLog = (eventName) => () => - this.setState((prevState) => ({ - log: [ - `${new Date().toLocaleTimeString()}: ${eventName}`, - ...prevState.log, - ].slice(0, 20), - logCount: prevState.logCount + 1, - })) - - clearLog = () => this.setState({ log: [], logCount: 0 }) - - toggleOnce = () => this.setState((prevState) => ({ once: !prevState.once })) - - toggleContinuous = () => - this.setState((prevState) => ({ continuous: !prevState.continuous })) - - render() { - const { continuous, log, logCount, once } = this.state - - return ( - - - - - - {_.map( - [ - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - ], - (src, index, images) => ( - - - {index !== images.length - 1 && } - - ), - )} - - - - - - - - - - - - - - - Event Log - - -
-                    {log.map((e, i) => (
-                      
{e}
- ))} -
-
-
-
-
-
-
- ) - } -} diff --git a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js b/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js deleted file mode 100644 index 19b51b5e30..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleOffset.js +++ /dev/null @@ -1,102 +0,0 @@ -import _ from 'lodash' -import React, { Component, createRef } from 'react' -import { - Divider, - Grid, - Image, - Segment, - Sticky, - Table, - Ref, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleOffset extends Component { - state = { - calculations: { - topPassed: false, - bottomPassed: false, - topVisible: false, - bottomVisible: false, - }, - } - contextRef = createRef() - - handleUpdate = (e, { calculations }) => this.setState({ calculations }) - - render() { - const { calculations } = this.state - - return ( - - - - - - {_.map( - [ - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - ], - (src, index, images) => ( - - - {index !== images.length - 1 && } - - ), - )} - - - - - - - - - - Calculation - Value - - - - - topVisible - - {calculations.topVisible.toString()} - - - - bottomVisible - - {calculations.bottomVisible.toString()} - - - - topPassed - {calculations.topPassed.toString()} - - - bottomPassed - - {calculations.bottomPassed.toString()} - - - -
-
-
-
-
- ) - } -} diff --git a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleUpdateOn.js b/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleUpdateOn.js deleted file mode 100644 index 329680ddff..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/VisibilityExampleUpdateOn.js +++ /dev/null @@ -1,114 +0,0 @@ -import _ from 'lodash' -import React, { Component, createRef } from 'react' -import { - Checkbox, - Divider, - Grid, - Image, - Segment, - Sticky, - Table, - Ref, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleUpdateOn extends Component { - state = { - calculations: { - topVisible: false, - bottomVisible: false, - }, - showWireframe: true, - } - contextRef = createRef() - - handleUpdate = (e, { calculations }) => this.setState({ calculations }) - - handleWireframe = (e, { checked }) => - this.setState({ showWireframe: checked }) - - render() { - const { calculations, showWireframe } = this.state - - return ( - - - - {showWireframe ? ( - - {_.map( - [ - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - ], - (src, index, images) => ( - - - {index !== images.length - 1 && } - - ), - )} - - ) : null} - - - - It's a tricky Segment - - - - - - - - - - - - - Calculation - Value - - - - - topVisible - - {calculations.topVisible.toString()} - - - - bottomVisible - - {calculations.bottomVisible.toString()} - - - -
-
-
-
-
-
- ) - } -} diff --git a/docs/src/examples/behaviors/Visibility/Settings/index.js b/docs/src/examples/behaviors/Visibility/Settings/index.js deleted file mode 100644 index 2e79c9da4a..0000000000 --- a/docs/src/examples/behaviors/Visibility/Settings/index.js +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react' -import { Message } from 'semantic-ui-react' - -import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' -import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' - -const VisibilitySettingsExamples = () => ( - - - - - - - You can specify updateOn='repaint', it will allow to - update and fire callbacks on browser repaint (animation frames). - - } - examplePath='behaviors/Visibility/Settings/VisibilityExampleUpdateOn' - > - - By default Visibility handles events only on browser - events. It means that if you will hide a large block an event will not - be triggered and Visibility will not perform calculations. - This problem can be easily solved with updateOn='repaint'. - - - -) - -export default VisibilitySettingsExamples diff --git a/docs/src/examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js b/docs/src/examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js deleted file mode 100644 index f030c71811..0000000000 --- a/docs/src/examples/behaviors/Visibility/Types/VisibilityExampleVisibility.js +++ /dev/null @@ -1,151 +0,0 @@ -import _ from 'lodash' -import React, { Component, createRef } from 'react' -import { - Divider, - Grid, - Image, - Segment, - Sticky, - Table, - Ref, - Visibility, -} from 'semantic-ui-react' - -export default class VisibilityExampleVisibility extends Component { - state = { - calculations: { - direction: 'none', - height: 0, - width: 0, - topPassed: false, - bottomPassed: false, - pixelsPassed: 0, - percentagePassed: 0, - topVisible: false, - bottomVisible: false, - fits: false, - passing: false, - onScreen: false, - offScreen: false, - }, - } - contextRef = createRef() - - handleUpdate = (e, { calculations }) => this.setState({ calculations }) - - render() { - const { calculations } = this.state - - return ( - - - - - - {_.map( - [ - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - '/images/wireframe/centered-paragraph.png', - '/images/wireframe/short-paragraph.png', - '/images/wireframe/media-paragraph.png', - '/images/wireframe/paragraph.png', - ], - (src, index, images) => ( - - - {index !== images.length - 1 && } - - ), - )} - - - - - - - - - - Calculation - Value - - - - - direction - {calculations.direction} - - - 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/src/examples/behaviors/Visibility/Types/index.js b/docs/src/examples/behaviors/Visibility/Types/index.js deleted file mode 100644 index 9995e3e147..0000000000 --- a/docs/src/examples/behaviors/Visibility/Types/index.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from 'react' - -import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' -import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' - -const VisibilityTypesExamples = () => ( - - - -) - -export default VisibilityTypesExamples diff --git a/docs/src/examples/behaviors/Visibility/index.js b/docs/src/examples/behaviors/Visibility/index.js deleted file mode 100644 index 251c3879c1..0000000000 --- a/docs/src/examples/behaviors/Visibility/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' -import { Message, Icon } from 'semantic-ui-react' - -import Types from './Types' -import Settings from './Settings' - -const VisibilityExamples = () => ( - <> - - - - - Deprecation notice -

- Visibility component is deprecated and will be removed in - the next major release. Please follow our{' '} - - upgrade guide - {' '} - to avoid breaks during upgrade to v3. -

-
-
- - - - -) - -export default VisibilityExamples diff --git a/docs/src/layouts/HomepageLayout.js b/docs/src/layouts/HomepageLayout.js index 9d94fbc69c..26b3ee9bcc 100644 --- a/docs/src/layouts/HomepageLayout.js +++ b/docs/src/layouts/HomepageLayout.js @@ -4,6 +4,7 @@ import { createMedia } from '@artsy/fresnel' import PropTypes from 'prop-types' import React, { Component } from 'react' +import { InView } from 'react-intersection-observer' import { Button, Container, @@ -16,7 +17,6 @@ import { Menu, Segment, Sidebar, - Visibility, } from 'semantic-ui-react' const { MediaContextProvider, Media } = createMedia({ @@ -72,8 +72,7 @@ HomepageHeading.propTypes = { class DesktopContainer extends Component { state = {} - hideFixedMenu = () => this.setState({ fixed: false }) - showFixedMenu = () => this.setState({ fixed: true }) + toggleFixedMenu = (inView) => this.setState({ fixed: !inView }) render() { const { children } = this.props @@ -81,11 +80,7 @@ class DesktopContainer extends Component { return ( - + - + {children} diff --git a/docs/src/layouts/StickyLayout.js b/docs/src/layouts/StickyLayout.js index b3a0dfe8d6..f86a98b087 100644 --- a/docs/src/layouts/StickyLayout.js +++ b/docs/src/layouts/StickyLayout.js @@ -1,5 +1,6 @@ import _ from 'lodash' import React, { Component } from 'react' +import { InView } from 'react-intersection-observer' import { Container, Divider, @@ -11,7 +12,6 @@ import { List, Menu, Segment, - Visibility, } from 'semantic-ui-react' const menuStyle = { @@ -99,13 +99,8 @@ export default class StickyLayout extends Component { } } - stickOverlay = () => this.setState({ overlayFixed: true }) - - stickTopMenu = () => this.setState({ menuFixed: true }) - - unStickOverlay = () => this.setState({ overlayFixed: false }) - - unStickTopMenu = () => this.setState({ menuFixed: false }) + toggleOverlay = (inView) => this.setState({ overlayFixed: !inView }) + toggleTopMenu = (inView) => this.setState({ menuFixed: !inView }) render() { const { menuFixed, overlayFixed, overlayRect } = this.state @@ -134,11 +129,8 @@ export default class StickyLayout extends Component { {/* Attaching the top menu is a simple operation, we only switch `fixed` prop and add another style if it has gone beyond the scope of visibility */} - + + - + {_.times(3, (i) => ( @@ -186,13 +178,9 @@ export default class StickyLayout extends Component { An empty Visibility element controls the need to change the fixing of element below, it also uses height and width params received from its ref for correct display. */} - + +
+
{ const componentsSrc = [ toUniversalGlob(paths.src(), 'addons/*/*.js'), - toUniversalGlob(paths.src(), 'behaviors/*/*.js'), toUniversalGlob(paths.src(), 'elements/*/*.js'), toUniversalGlob(paths.src(), 'collections/*/*.js'), toUniversalGlob(paths.src(), 'modules/*/*.js'), diff --git a/index.d.ts b/index.d.ts index bc45d993a4..79ea92dff4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -39,16 +39,6 @@ export { StrictTransitionablePortalProps, } from './dist/commonjs/addons/TransitionablePortal' -// Behaviors -export { - default as Visibility, - VisibilityCalculations, - VisibilityEventData, - VisibilityOnPassed, - VisibilityProps, - StrictVisibilityProps, -} from './dist/commonjs/behaviors/Visibility' - // Collections export { default as Breadcrumb, diff --git a/src/behaviors/Visibility/Visibility.d.ts b/src/behaviors/Visibility/Visibility.d.ts deleted file mode 100644 index f9f168e62a..0000000000 --- a/src/behaviors/Visibility/Visibility.d.ts +++ /dev/null @@ -1,176 +0,0 @@ -import * as React from 'react' - -export interface VisibilityProps extends StrictVisibilityProps { - [key: string]: any -} - -export interface StrictVisibilityProps { - /** An element type to render as (string or function). */ - as?: any - - /** Primary content. */ - children?: React.ReactNode - - /** Context which sticky element should stick to. */ - context?: Document | Window | HTMLElement - - /** - * When set to true a callback will occur anytime an element passes a condition not just immediately after the - * threshold is met. - */ - continuous?: boolean - - /** Fires callbacks immediately after mount. */ - fireOnMount?: boolean - - /** - * Element's bottom edge has passed top of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onBottomPassed?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's bottom edge has not passed top of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onBottomPassedReverse?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's bottom edge has passed bottom of screen - * - * @param {null} - * @param {object} data - All props. - */ - onBottomVisible?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's bottom edge has not passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - 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 | (number | string)[] - - /** When set to false a callback will occur each time an element passes the threshold for a condition. */ - once?: boolean - - /** Element is not visible on the screen. */ - onPassed?: VisibilityOnPassed - - /** - * Any part of an element is visible on screen. - * - * @param {null} - * @param {object} data - All props. - */ - onPassing?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top has not passed top of screen but bottom has. - * - * @param {null} - * @param {object} data - All props. - */ - onPassingReverse?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element is not visible on the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onOffScreen?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element is visible on the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onOnScreen?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top edge has passed top of the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopPassed?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top edge has not passed top of the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopPassedReverse?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top edge has passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopVisible?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top edge has not passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopVisibleReverse?: (nothing: null, data: VisibilityEventData) => void - - /** - * Element's top edge has passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onUpdate?: (nothing: null, data: VisibilityEventData) => void - - /** - * Allows to choose the mode of the position calculations: - * - `events` - (default) update and fire callbacks only on scroll/resize events - * - `repaint` - update and fire callbacks on browser repaint (animation frames) - */ - updateOn?: 'events' | 'repaint' -} - -export interface VisibilityCalculations { - bottomPassed: boolean - bottomVisible: boolean - direction: 'down' | 'up' - fits: boolean - height: number - passing: boolean - percentagePassed: number - pixelsPassed: number - offScreen: boolean - onScreen: boolean - topPassed: boolean - topVisible: boolean - width: number -} - -export interface VisibilityEventData extends VisibilityProps { - calculations: VisibilityCalculations -} - -export interface VisibilityOnPassed { - [key: string]: (nothing: null, data: VisibilityEventData) => void -} - -declare const Visibility: React.ComponentClass - -export default Visibility diff --git a/src/behaviors/Visibility/Visibility.js b/src/behaviors/Visibility/Visibility.js deleted file mode 100644 index e0c93b141c..0000000000 --- a/src/behaviors/Visibility/Visibility.js +++ /dev/null @@ -1,428 +0,0 @@ -import { Ref } from '@fluentui/react-component-ref' -import _ from 'lodash' -import PropTypes from 'prop-types' -import React, { Component, createRef } from 'react' - -import { - eventStack, - getElementType, - getUnhandledProps, - normalizeOffset, - isBrowser, -} from '../../lib' - -/** - * Visibility provides a set of callbacks for when a content appears in the viewport. - * - * @deprecated This component is deprecated and will be removed in next major release. - */ -export default class Visibility extends Component { - calculations = { - bottomPassed: false, - bottomVisible: false, - fits: false, - passing: false, - offScreen: false, - onScreen: false, - topPassed: false, - topVisible: false, - } - firedCallbacks = [] - ref = createRef() - - // ---------------------------------------- - // Lifecycle - // ---------------------------------------- - - componentDidMount() { - this.mounted = true - - if (!isBrowser()) return - const { context, fireOnMount, updateOn } = this.props - - this.pageYOffset = this.getPageYOffset() - this.attachHandlers(context, updateOn) - - if (fireOnMount) this.update() - } - - componentDidUpdate(prevProps) { - const cleanHappened = - prevProps.continuous !== this.props.continuous || - prevProps.once !== this.props.once || - prevProps.updateOn !== this.props.updateOn - - // Heads up! We should clean up array of happened callbacks, if values of these props are changed - if (cleanHappened) this.firedCallbacks = [] - - if (prevProps.context !== this.props.context || prevProps.updateOn !== this.props.updateOn) { - this.unattachHandlers(prevProps.context) - this.attachHandlers(this.props.context, this.props.updateOn) - } - } - - componentWillUnmount() { - const { context } = this.props - - this.unattachHandlers(context) - this.mounted = false - } - - attachHandlers(context, updateOn) { - if (updateOn === 'events') { - if (context) { - eventStack.sub('resize', this.handleUpdate, { target: context }) - eventStack.sub('scroll', this.handleUpdate, { target: context }) - } - - return - } - - // Heads up! - // We will deal with `repaint` there - this.handleUpdate() - } - - unattachHandlers(context) { - if (context) { - eventStack.unsub('resize', this.handleUpdate, { target: context }) - eventStack.unsub('scroll', this.handleUpdate, { target: context }) - } - - if (this.frameId) cancelAnimationFrame(this.frameId) - } - - // ---------------------------------------- - // Callback handling - // ---------------------------------------- - - execute(callback, name) { - const { continuous } = this.props - if (!callback) return - - // Heads up! When `continuous` is true, callback will be fired always - if (!continuous && _.includes(this.firedCallbacks, name)) return - - callback(null, { ...this.props, calculations: this.calculations }) - this.firedCallbacks.push(name) - } - - fire = ({ callback, name }, value, reverse = false) => { - const { continuous, once } = this.props - - // Heads up! For the execution is required: - // - current value correspond to the fired direction - // - `continuous` is true or calculation values are different - const matchesDirection = this.calculations[value] !== reverse - const executionPossible = continuous || this.calculations[value] !== this.oldCalculations[value] - - if (matchesDirection && executionPossible) this.execute(callback, name) - - // Heads up! We should remove callback from the happened when it's not `once` - if (!once) this.firedCallbacks = _.without(this.firedCallbacks, name) - } - - fireOnPassed() { - const { percentagePassed, pixelsPassed } = this.calculations - const { onPassed } = this.props - - _.forEach(onPassed, (callback, passed) => { - const pixelsValue = Number(passed) - - if (pixelsValue && pixelsPassed >= pixelsValue) { - this.execute(callback, passed) - return - } - - const matchPercentage = `${passed}`.match(/^(\d+)%$/) - if (!matchPercentage) return - - const percentageValue = Number(matchPercentage[1]) / 100 - if (percentagePassed >= percentageValue) this.execute(callback, passed) - }) - } - - handleUpdate = () => { - if (this.ticking) return - - this.ticking = true - this.frameId = requestAnimationFrame(this.update) - } - - update = () => { - if (!this.mounted) return - - this.ticking = false - - this.oldCalculations = this.calculations - this.calculations = this.computeCalculations() - this.pageYOffset = this.getPageYOffset() - - const { - onBottomPassed, - onBottomPassedReverse, - onBottomVisible, - onBottomVisibleReverse, - onPassing, - onPassingReverse, - onTopPassed, - onTopPassedReverse, - onTopVisible, - onTopVisibleReverse, - onOffScreen, - onOnScreen, - updateOn, - } = this.props - const forward = { - bottomPassed: { callback: onBottomPassed, name: 'onBottomPassed' }, - bottomVisible: { callback: onBottomVisible, name: 'onBottomVisible' }, - passing: { callback: onPassing, name: 'onPassing' }, - offScreen: { callback: onOffScreen, name: 'onOffScreen' }, - onScreen: { callback: onOnScreen, name: 'onOnScreen' }, - topPassed: { callback: onTopPassed, name: 'onTopPassed' }, - topVisible: { callback: onTopVisible, name: 'onTopVisible' }, - } - - const reverse = { - bottomPassed: { callback: onBottomPassedReverse, name: 'onBottomPassedReverse' }, - bottomVisible: { callback: onBottomVisibleReverse, name: 'onBottomVisibleReverse' }, - passing: { callback: onPassingReverse, name: 'onPassingReverse' }, - topPassed: { callback: onTopPassedReverse, name: 'onTopPassedReverse' }, - topVisible: { callback: onTopVisibleReverse, name: 'onTopVisibleReverse' }, - } - - _.invoke(this.props, 'onUpdate', null, { ...this.props, calculations: this.calculations }) - this.fireOnPassed() - - // Heads up! Reverse callbacks should be fired first - _.forEach(reverse, (data, value) => this.fire(data, value, true)) - _.forEach(forward, (data, value) => this.fire(data, value)) - - if (updateOn === 'repaint') this.handleUpdate() - } - - // ---------------------------------------- - // Helpers - // ---------------------------------------- - - computeCalculations() { - const { offset } = this.props - const { bottom, height, top, width } = this.ref.current.getBoundingClientRect() - const [topOffset, bottomOffset] = normalizeOffset(offset) - - const newOffset = this.getPageYOffset() - const direction = newOffset > this.pageYOffset ? 'down' : 'up' - const topPassed = top < topOffset - const bottomPassed = bottom < bottomOffset - - const pixelsPassed = bottomPassed ? 0 : Math.max(top * -1, 0) - const percentagePassed = pixelsPassed / height - - const bottomVisible = bottom >= bottomOffset && bottom <= window.innerHeight - const topVisible = top >= topOffset && top <= window.innerHeight - - const fits = topVisible && bottomVisible - const passing = topPassed && !bottomPassed - - const onScreen = (topVisible || topPassed) && !bottomPassed - const offScreen = !onScreen - - return { - bottomPassed, - bottomVisible, - direction, - fits, - height, - passing, - percentagePassed, - pixelsPassed, - offScreen, - onScreen, - topPassed, - topVisible, - width, - } - } - - getPageYOffset() { - const { context } = this.props - - if (context) { - // Heads up! `window` doesn't have `pageYOffset` property - return context === window ? window.pageYOffset : context.scrollTop - } - - return 0 - } - - // ---------------------------------------- - // Render - // ---------------------------------------- - - render() { - const { children } = this.props - const ElementType = getElementType(Visibility, this.props) - const rest = getUnhandledProps(Visibility, this.props) - - return ( - - {children} - - ) - } -} - -Visibility.propTypes = { - /** An element type to render as (string or function). */ - as: PropTypes.elementType, - - /** Primary content. */ - children: PropTypes.node, - - /** Context which visibility should attach onscroll events. */ - context: PropTypes.object, - - /** - * When set to true a callback will occur anytime an element passes a condition not just immediately after the - * threshold is met. - */ - continuous: PropTypes.bool, - - /** Fires callbacks immediately after mount. */ - fireOnMount: PropTypes.bool, - - /** - * Element's bottom edge has passed top of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onBottomPassed: PropTypes.func, - - /** - * Element's bottom edge has not passed top of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onBottomPassedReverse: PropTypes.func, - - /** - * Element's bottom edge has passed bottom of screen - * - * @param {null} - * @param {object} data - All props. - */ - onBottomVisible: PropTypes.func, - - /** - * Element's bottom edge has not passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - 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.oneOfType([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, - - /** Element is not visible on the screen. */ - onPassed: PropTypes.object, - - /** - * Any part of an element is visible on screen. - * - * @param {null} - * @param {object} data - All props. - */ - onPassing: PropTypes.func, - - /** - * Element's top has not passed top of screen but bottom has. - * - * @param {null} - * @param {object} data - All props. - */ - onPassingReverse: PropTypes.func, - - /** - * Element is not visible on the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onOffScreen: PropTypes.func, - - /** - * Element is visible on the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onOnScreen: PropTypes.func, - - /** - * Element's top edge has passed top of the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopPassed: PropTypes.func, - - /** - * Element's top edge has not passed top of the screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopPassedReverse: PropTypes.func, - - /** - * Element's top edge has passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopVisible: PropTypes.func, - - /** - * Element's top edge has not passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onTopVisibleReverse: PropTypes.func, - - /** - * Element's top edge has passed bottom of screen. - * - * @param {null} - * @param {object} data - All props. - */ - onUpdate: PropTypes.func, - - /** - * Allows to choose the mode of the position calculations: - * - `events` - (default) update and fire callbacks only on scroll/resize events - * - `repaint` - update and fire callbacks on browser repaint (animation frames) - */ - updateOn: PropTypes.oneOf(['events', 'repaint']), -} - -Visibility.defaultProps = { - context: isBrowser() ? window : null, - continuous: false, - offset: [0, 0], - once: true, - updateOn: 'events', -} diff --git a/src/behaviors/Visibility/index.d.ts b/src/behaviors/Visibility/index.d.ts deleted file mode 100644 index 1026545769..0000000000 --- a/src/behaviors/Visibility/index.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export { - default, - VisibilityCalculations, - VisibilityEventData, - VisibilityOnPassed, - VisibilityProps, - StrictVisibilityProps, -} from './Visibility' diff --git a/src/behaviors/Visibility/index.js b/src/behaviors/Visibility/index.js deleted file mode 100644 index 0893be889f..0000000000 --- a/src/behaviors/Visibility/index.js +++ /dev/null @@ -1 +0,0 @@ -export default from './Visibility' diff --git a/src/collections/Form/Form.js b/src/collections/Form/Form.js index ef3c85718a..4f00d4d8e6 100644 --- a/src/collections/Form/Form.js +++ b/src/collections/Form/Form.js @@ -23,7 +23,6 @@ import FormTextArea from './FormTextArea' * @see Message * @see Radio * @see Select - * @see Visibility */ class Form extends Component { handleSubmit = (e, ...args) => { diff --git a/src/collections/Form/FormField.js b/src/collections/Form/FormField.js index 9180b2d153..8a656631f4 100644 --- a/src/collections/Form/FormField.js +++ b/src/collections/Form/FormField.js @@ -26,7 +26,6 @@ import Radio from '../../addons/Radio' * @see Input * @see Radio * @see Select - * @see Visibility */ function FormField(props) { const { diff --git a/src/index.js b/src/index.js index e7c6d2ebcc..881839bda1 100644 --- a/src/index.js +++ b/src/index.js @@ -12,9 +12,6 @@ export Select from './addons/Select' export TextArea from './addons/TextArea' export TransitionablePortal from './addons/TransitionablePortal' -// Behaviors -export Visibility from './behaviors/Visibility' - // Collections export Breadcrumb from './collections/Breadcrumb' export BreadcrumbDivider from './collections/Breadcrumb/BreadcrumbDivider' diff --git a/src/lib/index.js b/src/lib/index.js index cce0e9c54d..e6c0d60682 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -35,7 +35,6 @@ export createPaginationItems from './createPaginationItems' export * as SUI from './SUI' export { numberToWordMap, numberToWord } from './numberToWord' -export normalizeOffset from './normalizeOffset' export normalizeTransitionDuration from './normalizeTransitionDuration' export objectDiff from './objectDiff' diff --git a/src/lib/normalizeOffset.js b/src/lib/normalizeOffset.js deleted file mode 100644 index 97be40480f..0000000000 --- a/src/lib/normalizeOffset.js +++ /dev/null @@ -1,7 +0,0 @@ -/** - * 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 deleted file mode 100644 index 4d75f07fc2..0000000000 --- a/test/specs/behaviors/Visibility/Visibility-test.js +++ /dev/null @@ -1,630 +0,0 @@ -import _ from 'lodash' -import React from 'react' - -import Visibility from 'src/behaviors/Visibility' -import * as common from 'test/specs/commonTests' -import { domEvent, sandbox } from 'test/utils' - -let wrapper - -const wrapperMount = (node, opts) => { - wrapper = mount(node, opts) - return wrapper -} - -const mockScroll = (top, bottom) => { - if (wrapper) { - wrapper.instance().ref.current = { - getBoundingClientRect: () => ({ - bottom, - top, - height: bottom - top, - left: 0, - right: window.innerWidth, - width: window.innerWidth, - }), - } - } - - domEvent.scroll(window) -} - -const expectations = [ - { - name: 'topPassed', - callbackName: 'onTopPassed', - reversible: true, - truthy: [ - [-1, 100], - [-100, -1], - ], - falsy: [ - [0, 100], - [window.innerHeight + 100, window.innerHeight + 300], - ], - }, - { - name: 'bottomPassed', - callbackName: 'onBottomPassed', - reversible: true, - truthy: [ - [-100, -1], - [-100, -10], - ], - falsy: [ - [-10, 0], - [-100, window.innerHeight], - ], - }, - { - name: 'topVisible', - callbackName: 'onTopVisible', - reversible: true, - truthy: [ - [0, 100], - [window.innerHeight, window.innerHeight], - ], - falsy: [ - [-1, 100], - [window.innerHeight + 1, window.innerHeight + 2], - ], - }, - { - name: 'bottomVisible', - callbackName: 'onBottomVisible', - reversible: true, - truthy: [ - [-100, 0], - [-100, window.innerHeight], - ], - falsy: [ - [-100, -1], - [0, window.innerHeight + 1], - ], - }, - { - name: 'passing', - callbackName: 'onPassing', - reversible: true, - truthy: [ - [-1, window.innerHeight + 1], - [-1, window.innerHeight - 1], - [-1, 0], - ], - falsy: [ - [0, window.innerHeight], - [1, window.innerHeight + 1], - [1, window.innerHeight - 1], - [window.innerHeight, window.innerHeight + 1], - ], - }, - { - name: 'onScreen', - callbackName: 'onOnScreen', - truthy: [ - [0, window.innerHeight], - [-1, window.innerHeight + 1], - [-1, window.innerHeight], - [0, window.innerHeight + 1], - ], - falsy: [ - [-2, -1], - [window.innerHeight + 1, window.innerHeight + 2], - ], - }, - { - name: 'offScreen', - callbackName: 'onOffScreen', - truthy: [ - [-2, -1], - [window.innerHeight + 1, window.innerHeight + 2], - ], - falsy: [ - [0, window.innerHeight], - [-1, window.innerHeight + 1], - [-1, window.innerHeight], - [0, window.innerHeight + 1], - ], - }, - { - name: 'fits', - truthy: [[0, window.innerHeight]], - falsy: [ - [-1, window.innerHeight + 1], - [0, window.innerHeight + 1], - [-1, window.innerHeight], - ], - }, -] - -describe('Visibility', () => { - common.isConformant(Visibility) - - beforeEach(() => { - sandbox.stub(window, 'requestAnimationFrame').callsArg(0).returns(1) - wrapper = undefined - }) - - afterEach(() => { - if (wrapper && wrapper.unmount) { - try { - wrapper.unmount() - // eslint-disable-next-line no-empty - } catch (e) {} - } - }) - - describe('calculations', () => { - _.forEach(expectations, ({ falsy, name, truthy }) => { - it(`calculates ${name}`, () => { - const onUpdate = sandbox.spy() - wrapperMount() - - _.forEach(truthy, ([top, bottom]) => { - mockScroll(top, bottom) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - [name]: true, - }, - }) - }) - - _.forEach(falsy, ([top, bottom]) => { - mockScroll(top, bottom) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - [name]: false, - }, - }) - }) - }) - }) - }) - - describe('callbacks', () => { - _.forEach(_.filter(expectations, 'callbackName'), ({ callbackName, falsy, truthy }) => { - it(`fires ${callbackName}`, () => { - const callback = sandbox.spy() - const opts = { [callbackName]: callback } - wrapperMount() - - _.forEach(falsy, ([top, bottom]) => mockScroll(top, bottom)) - callback.should.not.have.been.called() - - _.forEach(truthy, ([top, bottom]) => mockScroll(top, bottom)) - callback.should.have.callCount(truthy.length) - }) - - it(`fires ${callbackName} once`, () => { - const callback = sandbox.spy() - const falsyCond = _.first(falsy) - const truthyCond = _.first(truthy) - const opts = { [callbackName]: callback } - - wrapperMount() - - mockScroll(...truthyCond) - mockScroll(...falsyCond) - mockScroll(...truthyCond) - mockScroll(...falsyCond) - mockScroll(...truthyCond) - - callback.should.have.been.calledOnce() - }) - - it(`fires ${callbackName} when condition changes`, () => { - const callback = sandbox.spy() - const falsyCond = _.first(falsy) - const truthyCond = _.first(truthy) - const opts = { [callbackName]: callback } - wrapperMount() - - mockScroll(...truthyCond) - mockScroll(...falsyCond) - mockScroll(...truthyCond) - mockScroll(...truthyCond) - - callback.should.have.been.calledTwice() - }) - }) - - describe('reverse', () => { - _.forEach(_.filter(expectations, 'reversible'), ({ callbackName, falsy, truthy }) => { - it(`fires ${callbackName}Reverse once`, () => { - const falsyCond = _.first(falsy) - const truthyCond = _.first(truthy) - - const forward = sandbox.spy() - const reverse = sandbox.spy() - const opts = { [callbackName]: forward, [`${callbackName}Reverse`]: reverse } - - wrapperMount() - - mockScroll(...truthyCond) - forward.should.have.been.calledOnce() - reverse.should.have.not.been.called() - - mockScroll(...falsyCond) - forward.should.have.been.calledOnce() - reverse.should.have.been.calledOnce() - }) - - it(`fires ${callbackName}Reverse when condition changes`, () => { - const falsyCond = _.first(falsy) - const truthyCond = _.first(truthy) - - const forward = sandbox.spy() - const reverse = sandbox.spy() - const opts = { [callbackName]: forward, [`${callbackName}Reverse`]: reverse } - - wrapperMount() - - mockScroll(...truthyCond) - forward.should.have.been.calledOnce() - reverse.should.have.not.been.called() - - mockScroll(...falsyCond) - forward.should.have.been.calledOnce() - reverse.should.have.been.calledOnce() - - mockScroll(...truthyCond) - forward.should.have.been.calledTwice() - reverse.should.have.been.calledOnce() - - mockScroll(...falsyCond) - forward.should.have.been.calledTwice() - reverse.should.have.been.calledTwice() - }) - }) - }) - - describe('direction', () => { - let pageYOffset - - beforeEach(() => { - pageYOffset = window.pageYOffset - }) - - afterEach(() => { - window.pageYOffset = pageYOffset - }) - - it('returns up when scrolling down', () => { - const onUpdate = sandbox.spy() - mount() - - window.pageYOffset = 5 - domEvent.scroll(window) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { direction: 'down' }, - }) - }) - - it('returns up when scrolling up', () => { - window.pageYOffset = 100 - const onUpdate = sandbox.spy() - mount() - - window.pageYOffset = 50 - domEvent.scroll(window) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { direction: 'up' }, - }) - }) - - it('gets direction from `context` element', () => { - const context = document.createElement('div') - const onUpdate = sandbox.spy() - - sandbox.stub(context, 'scrollTop').value(0) - mount() - - sandbox.stub(context, 'scrollTop').value(5) - domEvent.scroll(context) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { direction: 'down' }, - }) - }) - }) - }) - - describe('context', () => { - it('should use window as default scroll context', () => { - const onUpdate = sandbox.spy() - mount() - - domEvent.scroll(window) - onUpdate.should.have.been.called() - }) - - it('should set a scroll context', () => { - const div = document.createElement('div') - const onUpdate = sandbox.spy() - mount() - - domEvent.scroll(window) - onUpdate.should.not.have.been.called() - - domEvent.scroll(div) - onUpdate.should.have.been.called() - }) - - it('should not call onUpdate when context is null', () => { - const onUpdate = sandbox.spy() - mount() - - domEvent.scroll(document) - onUpdate.should.not.have.been.called() - }) - - it('should call onUpdate when context changes', () => { - const div = document.createElement('div') - const onUpdate = sandbox.spy() - const renderedComponent = mount() - renderedComponent.setProps({ context: div }) - - domEvent.scroll(div) - onUpdate.should.have.been.called() - }) - - it('should not call onUpdate when context changes and component is unmounted', () => { - const div = document.createElement('div') - const onUpdate = sandbox.spy() - const renderedComponent = mount() - renderedComponent.setProps({ context: div }) - renderedComponent.unmount() - - domEvent.scroll(div) - onUpdate.should.not.have.been.called() - - domEvent.scroll(document) - onUpdate.should.not.have.been.called() - }) - }) - - describe('componentWillUnmount', () => { - it('will cancel requestAnimationFrame', () => { - const cancelAnimationFrame = sandbox.spy(window, 'cancelAnimationFrame') - wrapperMount() - - mockScroll(0, 0) - wrapper.unmount() - - cancelAnimationFrame.should.have.been.calledOnce() - }) - }) - - 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('offset', () => { - _.forEach(_.filter(expectations, 'callbackName'), ({ callbackName, falsy, name, truthy }) => { - it(`fires ${name} when offset is number`, () => { - const callback = sandbox.spy() - const opts = { [callbackName]: callback } - - const offset = 10 - const falsyCond = _.map(_.first(falsy), (value) => value + offset) - const truthyCond = _.map(_.first(truthy), (value) => value + offset) - - wrapperMount() - mockScroll(...truthyCond) - mockScroll(...falsyCond) - - callback.should.have.been.calledOnce() - }) - - it(`fires ${name} when offset is array`, () => { - const callback = sandbox.spy() - const opts = { [callbackName]: callback } - - const bottomOffset = 20 - const topOffset = 10 - const falsyCond = [falsy[0][0] + topOffset, falsy[0][1] + bottomOffset] - const truthyCond = [truthy[0][0] + topOffset, truthy[0][1] + bottomOffset] - - wrapperMount() - mockScroll(...truthyCond) - mockScroll(...falsyCond) - - callback.should.have.been.calledOnce() - }) - }) - }) - - describe('onPassed', () => { - it('will flush firedCallbacks when value is changed', () => { - wrapperMount() - - wrapper.setProps({ once: false }) - wrapper.instance().firedCallbacks.should.be.empty() - }) - }) - - describe('onPassed', () => { - it('fires callback when pixels passed', () => { - const onPassed = { - 20: sandbox.spy(), - '20%': sandbox.spy(), - 50: sandbox.spy(), - '50%': sandbox.spy(), - 100: sandbox.spy(), - '100%': sandbox.spy(), - } - wrapperMount() - - mockScroll(100, 200) - onPassed[20].should.not.have.been.called('20px') - - mockScroll(-20, 180) - onPassed[20].should.have.been.called('20px') - onPassed['20%'].should.not.have.been.called('20%') - - mockScroll(-40, 160) - onPassed['20%'].should.have.been.called('20%') - onPassed[50].should.not.have.been.called('50px') - - mockScroll(-50, 150) - onPassed[50].should.have.been.called('50px') - onPassed['50%'].should.not.have.been.called('50%') - onPassed[100].should.not.have.been.called('100px') - - mockScroll(-100, 100) - onPassed['50%'].should.have.been.called('50%') - onPassed[100].should.have.been.called('100px') - onPassed['100%'].should.not.have.been.called('100%') - - mockScroll(-200, 0) - onPassed['100%'].should.have.been.called('100%') - }) - }) - - describe('onUpdate', () => { - it('fires when scrolling', () => { - const onUpdate = sandbox.spy() - wrapperMount() - - mockScroll(0, 0) - mockScroll(0, 0) - - onUpdate.should.have.been.calledTwice() - }) - - it('fires when window resized', () => { - const onUpdate = sandbox.spy() - wrapperMount() - - domEvent.resize(window) - onUpdate.should.have.been.calledOnce() - }) - - it('passes calculations to onUpdate', () => { - let calculations - const onUpdate = (e, props) => (calculations = props.calculations) - - wrapperMount() - mockScroll(0, 0) - - calculations.should.contain.all.keys([ - 'bottomPassed', - 'bottomVisible', - 'fits', - 'height', - 'offScreen', - 'onScreen', - 'passing', - 'percentagePassed', - 'pixelsPassed', - 'topPassed', - 'topVisible', - 'width', - ]) - }) - - it('updates width and height after scroll', () => { - const onUpdate = sandbox.spy() - wrapperMount() - - mockScroll(0, 100) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - height: 100, - width: window.innerWidth, - }, - }) - - mockScroll(50, 3000) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - height: 2950, - width: window.innerWidth, - }, - }) - }) - - it('shows passed pixels and percentage', () => { - const onUpdate = sandbox.spy() - wrapperMount() - - mockScroll(0, 100) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - percentagePassed: 0, - pixelsPassed: 0, - }, - }) - - mockScroll(-1, 99) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - percentagePassed: 0.01, - pixelsPassed: 1, - }, - }) - - mockScroll(-2, 198) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - percentagePassed: 0.01, - pixelsPassed: 2, - }, - }) - - mockScroll(-10, 0) - onUpdate.should.have.been.calledWithMatch(null, { - calculations: { - percentagePassed: 1, - pixelsPassed: 10, - }, - }) - }) - }) - - describe('updateOn', () => { - beforeEach(() => { - requestAnimationFrame.restore() - sandbox.stub(window, 'requestAnimationFrame').callsFake((fn) => setTimeout(() => fn(), 0)) - }) - - it('defaults to "events"', () => { - wrapperMount().should.have.prop('updateOn', 'events') - }) - - it('fires onUpdate after mount when updateOn="repaint"', (done) => { - const onUpdate = sandbox.spy() - wrapperMount() - - setTimeout(() => { - onUpdate.should.have.been.calledOnce() - wrapper.unmount() - - done() - }, 0) - }) - - it('fires onUpdate after change to updateOn="repaint"', (done) => { - const onUpdate = sandbox.spy() - wrapperMount() - - wrapper.setProps({ updateOn: 'repaint' }) - setTimeout(() => { - onUpdate.should.have.been.calledOnce() - wrapper.unmount() - - done() - }, 0) - }) - }) -})