Skip to content

Commit

Permalink
feat: Control node width (#3)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: remove `nodeWidth` prop support

* renamed regression and fixed wrong Dom nesting

* do not prettify package.json

* added FPS measurement to example page

* slightly improved node width regression demo

* fixed the node width regression

* further improved regression demo

* fixed default position

* added same width regression

* fixed same width regression

* crucial perf optimization

* added inline examples

* flow type fixes & code cleanup

* fixed all flow type annotations and added prop types

* fix broken english

* added first test for node width

* improved node width calculation for StickPortal by adding an extra ref

problem before was: as we determined the anchor width by looking at
containerRef we relied on the portaled container node to have already a
calculated width set

* added more node width tests

* fix lint error

* trial to fix test on ci

* use scrollWidth instead of clientWidth

* fixed test for devices with scrollbars

* fixed linter errors

* added some more tests to increase coverage

* props docs

* fixed lint errors

* submit pr comment with release not preview

* circle syntax fix

* simplified ci github access

* test ci

* use correct gh token in publish deme script

* fix host authenticity for github.com

* fixed a bug when page is scrolled horizontally
  • Loading branch information
jfschwarz authored Feb 21, 2018
1 parent 53c0473 commit c78c48d
Show file tree
Hide file tree
Showing 24 changed files with 837 additions and 259 deletions.
22 changes: 18 additions & 4 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,29 @@ jobs:
steps:
- *attach_workspace
- *restore_cache
- add_ssh_keys:
fingerprints:
- "df:4b:a9:b9:c8:f3:65:7d:1a:64:0f:f8:a8:93:28:81"
- run:
name: Fix host authenticity for github.com
command: mkdir -p ~/.ssh/ && ssh-keyscan github.com >> ~/.ssh/known_hosts
- run:
name: Set git name & email
command: |
git config --global user.name "CircleCI" && git config --global user.email "circleci@signavio.com"
git config --global user.name "Workflow Bot" && git config --global user.email "dev@effektif.com"
- run:
name: Build demo
command: yarn build-demo
- run:
name: Publish demo
command: yarn publish-demo

release-preview:
<<: *defaults
steps:
- *attach_workspace
- *restore_cache
- run:
name: Post release preview
command: "[[ $CI_PULL_REQUEST != '' ]] && yarn semantic-release-preview || exit 0"

release:
<<: *defaults
steps:
Expand Down Expand Up @@ -130,6 +136,14 @@ workflows:
- publish-demo:
requires:
- install
- release-preview:
requires:
- lint
- flow
- test
filters:
branches:
ignore: master
- release:
requires:
- lint
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package.json
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,34 @@ _Stick_ is a component that allows to attach an absolutely positioned node to a
positioned anchor element. Per default, the node will be rendered in a portal as a direct
child of the `body` element.

```
```bash
npm install --save react-stick
```

```javascript
import Stick from 'react-stick'

<Stick node={<p>The sticked node</p>} position="bottom center" align="top center">
<Stick node={<p>The stick node</p>} position="bottom center" align="top center">
<p>The anchor node</p>
</Stick>
```

## Props

* `children` The anchor element
* `node` The node to stick to the anchor element
* `position` The reference point on the anchor element at which to position the sticked node
* `align` The alignment of the sticked node. You can also think of this as the reference point on the
sticked node which is placed on the `position` reference point of the anchor node. For example `position="top left" align="bottom right"` means "put the bottom right point of the sticked not onto the top left point of the anchor node".
* `inline` If set to `true`, the sticked node will not be rendered detached but inside the same container
as the anchor node.

For `position` and `align` props string values of the form `top|middle|bottom left|center|right` are supported.
| prop name | type | description |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | node | The anchor element |
| `node` | node | The node to stick to the anchor element |
| `position` | one of: `"bottom left"`, `"bottom left"`, `"bottom center"`,`"bottom center"`, `"bottom right"`, `"bottom right"`, `"middle left"`, `"middle left"`, `"middle center"`, `"middle center"`, `"middle right"`, `"middle right"`, `"top left"`, `"top left"`, `"top center"`, `"top center"`, `"top right"`, `"top right"` (default value: `"bottom left"`) | The reference point on the anchor element at which to position the stick node |
| `align` | one of: `"bottom left"`, `"bottom left"`, `"bottom center"`,`"bottom center"`, `"bottom right"`, `"bottom right"`, `"middle left"`, `"middle left"`, `"middle center"`, `"middle center"`, `"middle right"`, `"middle right"`, `"top left"`, `"top left"`, `"top center"`, `"top center"`, `"top right"`, `"top right"` (default value depends on the `position`) | The alignment of the stick node. You can also think of this as the reference point on the stick node which is placed on the `position` reference point of the anchor node. For example `position="top left" align="bottom right"` means "put the bottom right point of the stick not onto the top left point of the anchor node". |
| `sameWidth` | boolean | If set to `true`, the container of the stick node will have the same width as the anchor node. This enforces a maximum width on the content of the stick node. |
| `onClickOutside` | function: (event: Event) => void | A handler that is called on every click on any element outside of the anchor element and the stick node. |
| `inline` | boolean | If set to `true`, the stick node will not be rendered detached but inside the same container,as the anchor node. |
| `updateOnAnimationFrame` | boolean | If set to `true`, will update the stick node position on every animation frame. Per default, it will only update on idle callback. |

[build-badge]: https://circleci.com/gh/signavio/react-stick/tree/master.svg?style=shield&circle-token=:circle-token
[build]: https://circleci.com/gh/signavio/react-stick/tree/master
[npm-badge]: https://img.shields.io/npm/v/react-stick.svg
[npm]: https://www.npmjs.org/package/react-stick
[codecov-badge]: https://img.shields.io/codecov/c/github/signavio/react-stick.svg
[codecov]: https://codecov.io/gh/signavio/react-stick


49 changes: 43 additions & 6 deletions demo/src/PositionAlignOverview.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,44 @@ const Node = () => (
/>
)

class FramesPerSecond extends Component {
state = {
fps: 0,
}

lastUpdated = Date.now()
framesSinceLastUpdate = 0

startTracking() {
const requestCallback = this.props.updateOnAnimationFrame
? requestAnimationFrame
: requestIdleCallback
this.lastCallbackAsAnimationFrame = this.props.updateOnAnimationFrame

this.animationId = requestCallback(() => this.startTracking())
this.measure()
}

measure() {
this.framesSinceLastUpdate += 1
let duration = Date.now() - this.lastUpdated
if (duration >= 1000) {
this.setState({
fps: this.framesSinceLastUpdate,
})
this.framesSinceLastUpdate = 0
this.lastUpdated = Date.now()
}
}

componentDidMount() {
this.startTracking()
}

render() {
return <div>FPS: {this.state.fps}</div>
}
}
class PositionAlignOverview extends Component {
state = {
inline: false,
Expand Down Expand Up @@ -84,6 +122,7 @@ class PositionAlignOverview extends Component {
readOnly
defaultValue="resize me to check smoothness of Stick"
/>
<FramesPerSecond updateOnAnimationFrame={updateOnAnimationFrame} />
</div>
<table
style={{
Expand All @@ -109,9 +148,8 @@ class PositionAlignOverview extends Component {
fontSize: 11,
}}
>
<p>
<pre>position="{position}"</pre>
</p>
<pre>position="{position}"</pre>
<br />
<div style={{ display: 'inline-block' }}>
<Stick
inline={inline}
Expand Down Expand Up @@ -148,9 +186,8 @@ class PositionAlignOverview extends Component {
fontSize: 11,
}}
>
<p>
<pre>align="{alignment}"</pre>
</p>
<pre>align="{alignment}"</pre>
<br />
<div style={{ display: 'inline-block' }}>
<Stick
position={position}
Expand Down
2 changes: 1 addition & 1 deletion demo/src/regressions/ButtonOverlay.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export default function ButtonOverlay() {
title="Button overlay"
description="Stick created an element that prevented clicks to wrapped elements"
>
<Stick node="This is the sticked text">
<Stick node="This is the stick text">
<button>You should be able to click me</button>
</Stick>
</Regression>
Expand Down
42 changes: 0 additions & 42 deletions demo/src/regressions/OverlaySize.js

This file was deleted.

6 changes: 4 additions & 2 deletions demo/src/regressions/Regressions.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from 'react'

import ButtonOverlay from './ButtonOverlay'
import OverlaySize from './OverlaySize'
import SameWidth from './SameWidth'
import StickNodeWidth from './StickNodeWidth'

export default function Regressions() {
return (
<div>
<h1>Regressions</h1>

<ButtonOverlay />
<OverlaySize />
<SameWidth />
<StickNodeWidth />
</div>
)
}
64 changes: 64 additions & 0 deletions demo/src/regressions/SameWidth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react'

import Stick from '../../../src'

import Regression from './Regression'

const Anchor = ({ width, children }) => (
<div
style={{
height: 18,
color: 'white',
width,
backgroundColor: 'rgb(24, 170, 177)',
}}
>
{children}
</div>
)

const Node = ({ children }) => (
<div
style={{
backgroundColor: '#ae0d5c',
color: 'white',
minHeight: 18,
}}
>
{children}
</div>
)

export default function SameWidth() {
return (
<Regression
allBrowsers
fixed
title="Same width"
description="Stick node must have the same width as the anchor element, if `sameWidth` prop is set"
>
<div
style={{
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
height: 200,
marginRight: 250,
}}
>
<Stick sameWidth position="bottom center" node={<Node />}>
<Anchor>The stick node below should have the same width</Anchor>
</Stick>
<Stick
position="bottom center"
sameWidth
node={
<Node>This text should break to respect the anchor's width</Node>
}
>
<Anchor width={100} />
</Stick>
</div>
</Regression>
)
}
84 changes: 84 additions & 0 deletions demo/src/regressions/StickNodeWidth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react'

import Stick from '../../../src'

import Regression from './Regression'

const Anchor = ({ width }) => (
<div
style={{
height: 15,
width,
backgroundColor: 'rgb(24, 170, 177)',
}}
/>
)

const Node = ({ children }) => (
<div
style={{
backgroundColor: '#ae0d5c',
color: 'white',
}}
>
{children}
</div>
)

const Examples = ({ inline }) => (
<div
style={{
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
height: 100,
marginRight: 250,
}}
>
<Stick
inline={inline}
position="middle right"
node={<Node>This text should stay on one line</Node>}
>
<Anchor width={15} />
</Stick>

<Stick
inline={inline}
position="middle right"
node={<Node>This text should stay on one line</Node>}
>
<Anchor width={150} />
</Stick>

<div style={{ position: 'absolute', right: 150 }}>
<Stick
inline={inline}
position="middle right"
node={
<Node>
This text must line-break as it would reach off-screen otherwise
</Node>
}
>
<Anchor width={100} />
</Stick>
</div>
</div>
)

export default function StickNodeWidth() {
return (
<Regression
allBrowsers
fixed
version="1.0.0"
title="Stick node width"
description="The stick node should not line-break just because the anchor node is small. The stick node must only line-break if it would not fit onto the screen otherwise."
>
<Examples />
<p>with `inline` prop:</p>
<Examples inline />
</Regression>
)
}
Loading

0 comments on commit c78c48d

Please sign in to comment.