Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

Commit

Permalink
fix(Popup): call onOpenChange on all user actions
Browse files Browse the repository at this point in the history
  • Loading branch information
levithomason committed Dec 19, 2018
1 parent 81f97c4 commit 94dd3a7
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 37 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602))
- Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596))
- Fix property names used in shorthand factories @kuzhelov ([#591](https://github.com/stardust-ui/react/pull/591))
- Call `Popup` `onOpenChange` on all user initiated events @levithomason ([#619](https://github.com/stardust-ui/react/pull/619))

### Features
- `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
import React from 'react'
import { Button, Input, Popup } from '@stardust-ui/react'

class PopupControlledExample extends React.Component<any, any> {
state = { popupOpen: false }
class PopupControlledExample extends React.Component {
state = { open: false }

togglePopup() {
this.setState(prev => ({ popupOpen: !prev.popupOpen }))
}
handleOpenChange = (e, { open }) => this.setState({ open })

render() {
return (
<Popup
open={this.state.popupOpen}
onOpenChange={(e, newProps) => {
alert(`Popup is requested to change its open state to "${newProps.open}".`)
this.setState({ popupOpen: newProps.open })
}}
trigger={<Button icon="expand" onClick={() => this.togglePopup()} />}
open={this.state.open}
onOpenChange={this.handleOpenChange}
trigger={<Button icon="expand" />}
content={{ content: <Input icon="search" placeholder="Search..." /> }}
/>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import React from 'react'
import { Button, Input, Popup } from '@stardust-ui/react'

class PopupControlledExample extends React.Component<any, any> {
state = { popupOpen: false }
class PopupControlledExample extends React.Component {
state = { open: false }

togglePopup() {
this.setState(prev => ({ popupOpen: !prev.popupOpen }))
}
handleOpenChange = (e, { open }) => this.setState({ open })

render() {
return (
<Popup
open={this.state.popupOpen}
onOpenChange={(e, newProps) => {
alert(`Popup is requested to change its open state to "${newProps.open}".`)
this.setState({ popupOpen: newProps.open })
}}
open={this.state.open}
onOpenChange={this.handleOpenChange}
content={{ content: <Input icon="search" placeholder="Search..." /> }}
>
<Button icon="expand" onClick={() => this.togglePopup()} />
<Button icon="expand" />
</Popup>
)
}
Expand Down
13 changes: 6 additions & 7 deletions src/components/Popup/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default class Popup extends AutoControlledComponent<Extendable<PopupProps

protected actionHandlers: AccessibilityActionHandlers = {
toggle: e => {
this.trySetOpen(!this.state.open, e, true)
this.trySetOpen(!this.state.open, e)
},
closeAndFocusTrigger: e => {
this.closeAndFocusTrigger(e)
Expand All @@ -153,7 +153,7 @@ export default class Popup extends AutoControlledComponent<Extendable<PopupProps

private closeAndFocusTrigger = e => {
if (this.state.open) {
this.trySetOpen(false, e, true)
this.trySetOpen(false, e)
_.invoke(this.triggerDomElement, 'focus')
}
}
Expand All @@ -172,7 +172,7 @@ export default class Popup extends AutoControlledComponent<Extendable<PopupProps
this.triggerDomElement && !this.triggerDomElement.contains(e.target)

if (isOutsidePopupElement && isOutsideTriggerElement) {
this.state.open && this.trySetOpen(false, e, true)
this.state.open && this.trySetOpen(false, e)
}
},
{
Expand Down Expand Up @@ -326,10 +326,9 @@ export default class Popup extends AutoControlledComponent<Extendable<PopupProps
)
}

private trySetOpen(newValue: boolean, eventArgs: any, forceChangeEvent: boolean = false) {
if (this.trySetState({ open: newValue }) || forceChangeEvent) {
_.invoke(this.props, 'onOpenChange', eventArgs, { ...this.props, ...{ open: newValue } })
}
private trySetOpen(newValue: boolean, eventArgs: any) {
this.trySetState({ open: newValue })
_.invoke(this.props, 'onOpenChange', eventArgs, { ...this.props, ...{ open: newValue } })
}

private applyRtlToOffset(offset: string, rtl: boolean, position: Position): string {
Expand Down
9 changes: 2 additions & 7 deletions src/lib/AutoControlledComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export default class AutoControlledComponent<P = {}, S = {}> extends UIComponent
* @param {object} maybeState State that corresponds to controlled props.
* @param {object} [state] Actual state, useful when you also need to setState.
*/
trySetState = (maybeState, state?): boolean => {
trySetState = (maybeState, state?) => {
const { autoControlledProps } = this.constructor as any
if (process.env.NODE_ENV !== 'production') {
const { name } = this.constructor
Expand Down Expand Up @@ -218,11 +218,6 @@ export default class AutoControlledComponent<P = {}, S = {}> extends UIComponent

if (state) newState = { ...newState, ...state }

if (Object.keys(newState).length > 0) {
this.setState(newState)
return true
}

return false
if (Object.keys(newState).length > 0) this.setState(newState)
}
}
31 changes: 30 additions & 1 deletion test/specs/components/Popup/Popup-test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import computePopupPlacement, { Position, Alignment } from 'src/components/Popup/positioningHelper'
import * as React from 'react'
import { Placement } from 'popper.js'

import Popup from 'src/components/Popup/Popup'
import computePopupPlacement, { Position, Alignment } from 'src/components/Popup/positioningHelper'
import { mountWithProvider } from '../../../utils'

type PositionTestInput = {
align: Alignment
position: Position
Expand Down Expand Up @@ -56,4 +60,29 @@ describe('Popup', () => {
testPopupPositionInRtl({ position: 'after', align: 'center', expectedPlacement: 'left' })
testPopupPositionInRtl({ position: 'after', align: 'bottom', expectedPlacement: 'left-end' })
})

describe('onOpenChange', () => {
test('is called on click', () => {
const spy = jest.fn()

mountWithProvider(<Popup trigger={<button />} content="Hi" onOpenChange={spy} />)
.find('button')
.simulate('click')

expect(spy).toHaveBeenCalledTimes(1)
expect(spy.mock.calls[0][1]).toMatchObject({ open: true })
})

// https://github.com/stardust-ui/react/pull/619
test('is called on click when controlled', () => {
const spy = jest.fn()

mountWithProvider(<Popup open={false} trigger={<button />} content="Hi" onOpenChange={spy} />)
.find('button')
.simulate('click')

expect(spy).toHaveBeenCalledTimes(1)
expect(spy.mock.calls[0][1]).toMatchObject({ open: true })
})
})
})

0 comments on commit 94dd3a7

Please sign in to comment.