Skip to content

Commit 437669c

Browse files
chore: update react-popper to v2 (#3947)
Co-authored-by: Oleksandr Fediashov <[email protected]>
1 parent 8f92ac8 commit 437669c

File tree

8 files changed

+171
-111
lines changed

8 files changed

+171
-111
lines changed

docs/src/examples/modules/Popup/Usage/PopupExampleOffset.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ const PopupExampleOffset = () => (
66
<Popup
77
trigger={<Icon size='large' name='heart' circular />}
88
content='Way off to the left'
9-
offset='0, 50px'
9+
offset={[0, 50]}
1010
position='left center'
1111
/>
1212
<Popup
1313
trigger={<Icon size='large' name='heart' circular />}
1414
content='As expected this popup is way off to the right'
15-
offset='0, 50px'
15+
offset={[0, 50]}
1616
position='right center'
1717
/>
1818
<Popup
1919
trigger={<Icon size='large' name='heart' circular />}
2020
content='Way off to the top'
21-
offset='0, 50px'
21+
offset={[0, 50]}
2222
position='top center'
2323
/>
2424
<Popup
2525
trigger={<Icon size='large' name='heart' circular />}
2626
content='As expected this popup is way off to the bottom'
27-
offset='0, 50px'
27+
offset={[0, 50]}
2828
position='bottom center'
2929
/>
3030
</>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React from 'react'
2+
import { Icon, Popup } from 'semantic-ui-react'
3+
4+
function PopupExampleOffsetFunction() {
5+
const offset = ({ placement, popper }) => {
6+
if (placement === 'bottom') {
7+
return [0, popper.height / 2]
8+
}
9+
10+
return []
11+
}
12+
13+
return (
14+
<>
15+
<Popup
16+
trigger={<Icon size='large' name='heart' circular />}
17+
content='Has no offset'
18+
offset={offset}
19+
position='top center'
20+
/>
21+
<Popup
22+
trigger={<Icon size='large' name='heart' circular />}
23+
content='Has a half width offset'
24+
offset={offset}
25+
position='bottom center'
26+
/>
27+
</>
28+
)
29+
}
30+
31+
export default PopupExampleOffsetFunction

docs/src/examples/modules/Popup/Usage/index.js

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,33 +14,55 @@ const PopupUsageExamples = () => (
1414
<Message info>
1515
<p>
1616
We are using Popper.js for positioning, so you can use the{' '}
17-
<code>offset</code> prop as it described in their docs. Accepts the
18-
following units:
17+
<code>offset</code> prop as it described in{' '}
18+
<a
19+
href='https://popper.js.org/docs/v2/modifiers/offset/'
20+
rel='noreferrer'
21+
target='_blank'
22+
>
23+
their docs
24+
</a>
25+
. The basic offset accepts an array with two numbers in the form{' '}
26+
<code>[skidding, distance].</code>
1927
</p>
2028
<Message.List>
2129
<Message.Item>
22-
<code>px</code> or unit-less, interpreted as pixels
23-
</Message.Item>
24-
<Message.Item>
25-
<code>%</code>, percentage relative to the length of the trigger
26-
element
27-
</Message.Item>
28-
<Message.Item>
29-
<code>%p</code>, percentage relative to the length of the popup
30-
element
31-
</Message.Item>
32-
<Message.Item>
33-
<code>vw</code>, CSS viewport width unit
30+
<code>skidding</code> displaces the <code>Popup</code> along the
31+
reference element
3432
</Message.Item>
3533
<Message.Item>
36-
<code>vh</code>, CSS viewport height unit
34+
<code>distance</code> displaces the <code>Popup</code> away from, or
35+
toward, the reference element in the direction of its placement. A
36+
positive number displaces it further away, while a negative number
37+
lets it overlap the reference
3738
</Message.Item>
3839
</Message.List>
3940
</Message>
4041
<Button
4142
content='Popper.JS: offset'
42-
href='https://popper.js.org/popper-documentation.html#modifiers..offset'
43+
href='https://popper.js.org/docs/v2/modifiers/offset/'
44+
icon='book'
45+
rel='noreferrer'
46+
target='_blank'
47+
/>
48+
</ComponentExample>
49+
<ComponentExample
50+
title='Offset as a function'
51+
description='A popup position can accept computed offset via functions'
52+
examplePath='modules/Popup/Usage/PopupExampleOffsetFunction'
53+
>
54+
<Message>
55+
It's preferred to define <code>offset</code> as a tuple of values,
56+
however it's also possible to use functions for more complex scenarios.
57+
In this example, we are applying half the <code>Popup</code>'s height as
58+
margin between the two elements only when the popper is positioned below
59+
its reference element.
60+
</Message>
61+
<Button
62+
content='Popper.JS: offset'
63+
href='https://popper.js.org/docs/v2/modifiers/offset/'
4364
icon='book'
65+
rel='noreferrer'
4466
target='_blank'
4567
/>
4668
</ComponentExample>

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,15 @@
7070
"@babel/runtime": "^7.10.5",
7171
"@fluentui/react-component-event-listener": "~0.51.1",
7272
"@fluentui/react-component-ref": "~0.51.1",
73+
"@popperjs/core": "^2.5.2",
7374
"@semantic-ui-react/event-stack": "^3.1.0",
7475
"clsx": "^1.1.1",
7576
"keyboard-key": "^1.1.0",
7677
"lodash": "^4.17.19",
7778
"lodash-es": "^4.17.15",
7879
"prop-types": "^15.7.2",
7980
"react-is": "^16.8.6",
80-
"react-popper": "^1.3.7",
81+
"react-popper": "^2.2.3",
8182
"shallowequal": "^1.1.0"
8283
},
8384
"devDependencies": {

src/modules/Popup/Popup.d.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import * as React from 'react'
2+
import * as Popper from '@popperjs/core'
23

34
import { SemanticShorthandItem } from '../../generic'
45
import { StrictPortalProps } from '../../addons/Portal'
56
import PopupContent, { PopupContentProps } from './PopupContent'
67
import PopupHeader, { PopupHeaderProps } from './PopupHeader'
78

9+
type PopperOffsetsFunctionParams = {
10+
popper: Popper.Rect
11+
reference: Popper.Rect
12+
placement: Popper.Placement
13+
}
14+
type PopperOffsetsFunction = (params: PopperOffsetsFunctionParams) => [number?, number?]
15+
816
export interface PopupProps extends StrictPopupProps {
917
[key: string]: any
1018
}
@@ -49,14 +57,15 @@ export interface StrictPopupProps extends StrictPortalProps {
4957
/** Invert the colors of the popup */
5058
inverted?: boolean
5159

52-
/** Offset value to apply to rendered popup. Accepts the following units:
53-
* - px or unit-less, interpreted as pixels
54-
* - %, percentage relative to the length of the trigger element
55-
* - %p, percentage relative to the length of the popup element
56-
* - vw, CSS viewport width unit
57-
* - vh, CSS viewport height unit
60+
/**
61+
* Offset values in px unit to apply to rendered popup. The basic offset accepts an
62+
* array with two numbers in the form [skidding, distance]:
63+
* - `skidding` displaces the Popup along the reference element
64+
* - `distance` displaces the Popup away from, or toward, the reference element in the direction of its placement. A positive number displaces it further away, while a negative number lets it overlap the reference.
65+
*
66+
* @see https://popper.js.org/docs/v2/modifiers/offset/
5867
*/
59-
offset?: number | string
68+
offset?: [number, number?] | PopperOffsetsFunction
6069

6170
/** Events triggering the popup. */
6271
on?: 'hover' | 'click' | 'focus' | ('hover' | 'click' | 'focus')[]
@@ -110,8 +119,8 @@ export interface StrictPopupProps extends StrictPortalProps {
110119
/** Tells `Popper.js` to use the `position: fixed` strategy to position the popover. */
111120
positionFixed?: boolean
112121

113-
/** An object containing custom settings for the Popper.js modifiers. */
114-
popperModifiers?: Record<string, any>
122+
/** An array containing custom settings for the Popper.js modifiers. */
123+
popperModifiers?: any[]
115124

116125
/** A popup can have dependencies which update will schedule a position update. */
117126
popperDependencies?: any[]

src/modules/Popup/Popup.js

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import EventStack from '@semantic-ui-react/event-stack'
2-
import { Ref } from '@fluentui/react-component-ref'
32
import cx from 'clsx'
43
import _ from 'lodash'
54
import PropTypes from 'prop-types'
@@ -136,12 +135,7 @@ export default class Popup extends Component {
136135
if (this.positionUpdate) this.positionUpdate()
137136
}
138137

139-
renderContent = ({
140-
placement: popperPlacement,
141-
ref: popperRef,
142-
scheduleUpdate,
143-
style: popperStyle,
144-
}) => {
138+
renderContent = ({ placement: popperPlacement, ref: popperRef, update, style: popperStyle }) => {
145139
const {
146140
basic,
147141
children,
@@ -157,7 +151,7 @@ export default class Popup extends Component {
157151
} = this.props
158152
const { contentRestProps } = this.state
159153

160-
this.positionUpdate = scheduleUpdate
154+
this.positionUpdate = update
161155

162156
const classes = cx(
163157
'ui',
@@ -175,12 +169,17 @@ export default class Popup extends Component {
175169
// Heads up! We need default styles to get working correctly `flowing`
176170
left: 'auto',
177171
right: 'auto',
178-
...popperStyle,
172+
// This is required to be properly positioned inside wrapping `div`
173+
position: 'initial',
179174
...style,
180175
}
181176

182177
return (
183-
<Ref innerRef={popperRef}>
178+
// https://github.com/popperjs/popper-core/blob/f1f9d1ab75b6b0e962f90a5b2a50f6cfd307d794/src/createPopper.js#L136-L137
179+
// Heads up!
180+
// A wrapping `div` there is a pure magic, it's required as Popper warns on margins that are
181+
// defined by SUI CSS. It also means that this `div` will be positioned instead of `content`.
182+
<div ref={popperRef} style={popperStyle}>
184183
<ElementType {...contentRestProps} className={classes} style={styles}>
185184
{childrenUtils.isNil(children) ? (
186185
<>
@@ -192,7 +191,7 @@ export default class Popup extends Component {
192191
)}
193192
{hideOnScroll && <EventStack on={this.hideOnScroll} name='scroll' target='window' />}
194193
</ElementType>
195-
</Ref>
194+
</div>
196195
)
197196
}
198197

@@ -214,17 +213,16 @@ export default class Popup extends Component {
214213
return trigger
215214
}
216215

217-
const modifiers = _.merge(
218-
{
219-
arrow: { enabled: false },
220-
flip: { enabled: !pinned },
221-
// There are issues with `keepTogether` and `offset`
222-
// https://github.com/FezVrasta/popper.js/issues/557
223-
keepTogether: { enabled: !!offset },
224-
offset: { offset },
225-
},
226-
popperModifiers,
227-
)
216+
const modifiers = [
217+
{ name: 'arrow', enabled: false },
218+
{ name: 'eventListeners', options: { scroll: !!eventsEnabled, resize: !!eventsEnabled } },
219+
{ name: 'flip', enabled: !pinned },
220+
{ name: 'preventOverflow', enabled: !!offset },
221+
{ name: 'offset', enabled: !!offset, options: { offset } },
222+
...popperModifiers,
223+
]
224+
debug('popper modifiers:', modifiers)
225+
228226
const referenceElement = createReferenceProxy(_.isNil(context) ? this.triggerRef : context)
229227

230228
const mergedPortalProps = { ...this.getPortalProps(), ...portalRestProps }
@@ -241,10 +239,9 @@ export default class Popup extends Component {
241239
triggerRef={this.triggerRef}
242240
>
243241
<Popper
244-
eventsEnabled={eventsEnabled}
245242
modifiers={modifiers}
246243
placement={positionsMapping[position]}
247-
positionFixed={positionFixed}
244+
strategy={positionFixed ? 'fixed' : null}
248245
referenceElement={referenceElement}
249246
>
250247
{this.renderContent}
@@ -298,14 +295,15 @@ Popup.propTypes = {
298295
/** Invert the colors of the Popup. */
299296
inverted: PropTypes.bool,
300297

301-
/** Offset value to apply to rendered popup. Accepts the following units:
302-
* - px or unit-less, interpreted as pixels
303-
* - %, percentage relative to the length of the trigger element
304-
* - %p, percentage relative to the length of the popup element
305-
* - vw, CSS viewport width unit
306-
* - vh, CSS viewport height unit
298+
/**
299+
* Offset values in px unit to apply to rendered popup. The basic offset accepts an
300+
* array with two numbers in the form [skidding, distance]:
301+
* - `skidding` displaces the Popup along the reference element
302+
* - `distance` displaces the Popup away from, or toward, the reference element in the direction of its placement. A positive number displaces it further away, while a negative number lets it overlap the reference.
303+
*
304+
* @see https://popper.js.org/docs/v2/modifiers/offset/
307305
*/
308-
offset: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
306+
offset: PropTypes.oneOfType([PropTypes.func, PropTypes.arrayOf(PropTypes.number)]),
309307

310308
/** Events triggering the popup. */
311309
on: PropTypes.oneOfType([
@@ -354,8 +352,8 @@ Popup.propTypes = {
354352
/** Tells `Popper.js` to use the `position: fixed` strategy to position the popover. */
355353
positionFixed: PropTypes.bool,
356354

357-
/** An object containing custom settings for the Popper.js modifiers. */
358-
popperModifiers: PropTypes.object,
355+
/** An array containing custom settings for the Popper.js modifiers. */
356+
popperModifiers: PropTypes.array,
359357

360358
/** A popup can have dependencies which update will schedule a position update. */
361359
popperDependencies: PropTypes.array,
@@ -376,9 +374,9 @@ Popup.propTypes = {
376374
Popup.defaultProps = {
377375
disabled: false,
378376
eventsEnabled: true,
379-
offset: 0,
380377
on: ['click', 'hover'],
381378
pinned: false,
379+
popperModifiers: [],
382380
position: 'top left',
383381
}
384382

0 commit comments

Comments
 (0)