From ba2f092dc312bee43468d67c70b0e18a18ad9e09 Mon Sep 17 00:00:00 2001 From: Shawn Erquhart Date: Wed, 19 Apr 2017 17:57:09 -0400 Subject: [PATCH] document Sticky microlib --- src/components/UI/Sticky/Sticky.js | 59 +++++++++++++++++++ .../RawEditor/index.js | 2 +- .../VisualEditor/index.js | 2 +- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/components/UI/Sticky/Sticky.js b/src/components/UI/Sticky/Sticky.js index 910617e5..7b7f97a0 100644 --- a/src/components/UI/Sticky/Sticky.js +++ b/src/components/UI/Sticky/Sticky.js @@ -3,6 +3,33 @@ import classnames from 'classnames'; import { partial, without } from 'lodash'; import styles from './Sticky.css'; +/** + * Sticky is a collection of three components meant to facilitate "sticky" UI + * behavior for nested components. It uses React Context to provide an isolated, + * children-accessible state machine. It was specifically built for the rich + * text editor toolbar to achieve the following: + * + * - work within a scrollable section as if it were the window + * - remain at the top of the scrollable section if the rich text field begins + * to scroll up and out + * - scroll away with the rich text field when it is almost out of view + * - work when multiple rich text fields are present + * + * No available solution was near facilitating this for a React app. Eventually, + * if use continues, it should be improved to be more abstract and potentially + * split off to a separate library unto itself, covering more use cases than + * just the rich text toolbar. + * + * Sticky consists of three components, which are documented right here to + * facilitate a concise, high level overview: + * + * - StickyContext: the scrollable area that essentially serves as the window + * should be wrapped in StickyContext + * - StickyContainer: wraps the secondary container that the sticky element is + * bound within, eg. the rich text field + * - Sticky: wraps the sticky element itself + */ + export class StickyContext extends Component { static childContextTypes = { subscribeToStickyContext: PropTypes.func, @@ -10,6 +37,22 @@ export class StickyContext extends Component { requestUpdate: PropTypes.func, }; + static propTypes = { + className: PropTypes.string, + + /** + * registerListener: accepts a function that is called with the `updateStickies` method as an + * arg, so it can be accessed from the component implementation site similar to how refs are + * accessed: + * + * this.updateStickyContext = fn}> + * + * This function can then be used from the component implementation site to force update the + * entire Sticky instance, which is sometimes necessary. + */ + registerListener: PropTypes.func, + } + constructor(props) { super(props); this.subscriptions = []; @@ -76,6 +119,12 @@ export class StickyContainer extends Component { }; } + /** + * getPosition: used for updating the sticky element to stick or not stick, and also provides + * width info. Because a sticky element uses fixed positioning, it may not be able to be sized + * relative to a parent, so the StickyContainer width is provided to allow the Sticky to be sized + * accordingly. + */ getPosition = (contextTop) => { const rect = this.ref.getBoundingClientRect(); const shouldStick = rect.top < contextTop; @@ -111,6 +160,16 @@ export class Sticky extends Component { requestUpdate: PropTypes.func, }; + static propTypes = { + className: PropTypes.string, + + /** + * fillContainerWidth: allows the sticky width to be dynamically set to the width of it's + * StickyContainer when sticky (fixed positioning). + */ + fillContainerWidth: PropTypes.bool, + } + constructor(props, context) { super(props, context); this.state = {}; diff --git a/src/components/Widgets/MarkdownControlElements/RawEditor/index.js b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js index 5533e96c..55725f71 100644 --- a/src/components/Widgets/MarkdownControlElements/RawEditor/index.js +++ b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js @@ -319,7 +319,7 @@ export default class RawEditor extends React.Component { onDragOver={this.handleDragOver} onDrop={this.handleDrop} > - + - +