Skip to content

Commit

Permalink
Convert StageProgessionButton to use modal confirmation
Browse files Browse the repository at this point in the history
  - For testing new modal confirmations, need to use a ref to access rendered modal content
    - per enzymejs/enzyme#252 (comment)
  • Loading branch information
crq authored and vanderhoop committed May 29, 2017
1 parent 54a2986 commit cbebd6a
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 54 deletions.
76 changes: 34 additions & 42 deletions test/components/stage_progression_button_test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from "react"
import { mount } from "enzyme"
import { mount, ReactWrapper } from "enzyme"
import sinon from "sinon"

import StageProgressionButton from "../../web/static/js/components/stage_progression_button"
Expand Down Expand Up @@ -51,7 +51,6 @@ describe("StageProgressionButton", () => {
context("when the stage progression config requires confirmation", () => {
let stageProgressionButton
let retroChannel
let confirmStub

beforeEach(() => {
retroChannel = { on: () => {}, push: sinon.spy() }
Expand All @@ -61,70 +60,63 @@ describe("StageProgressionButton", () => {
)
})

it("invokes a javascript confirmation", () => {
confirmStub = sinon.stub(global, "confirm")
stageProgressionButton.simulate("click")
expect(confirmStub.called).to.equal(true)
context("when the stage progression button is clicked", () => {
let modalActions

confirmStub.restore()
})

context("when the user confirms", () => {
beforeEach(() => {
confirmStub = sinon.stub(global, "confirm")
stageProgressionButton.find("button").simulate("click")
modalActions = new ReactWrapper(stageProgressionButton.instance().modalActionsRef, true)
})

afterEach(() => {
confirmStub.restore()
it("opens the modal", () => {
expect(stageProgressionButton.find("Modal").props().isOpen).to.equal(true)
})

it("pushes `proceed_to_next_stage` to the retroChannel, passing the next stage", () => {
confirmStub.returns(true)
stageProgressionButton.simulate("click")
context("when clicking yes in the open modal", () => {
beforeEach(() => {
modalActions.find("#yes").simulate("click")
})

expect(
retroChannel.push.calledWith("proceed_to_next_stage", { stage: "stageDos" })
).to.equal(true)
it("pushes `proceed_to_next_stage` to the retroChannel, passing the next stage", () => {
expect(
retroChannel.push.calledWith("proceed_to_next_stage", { stage: "stageDos" })
).to.equal(true)
})

it("closes the modal", () => {
expect(stageProgressionButton.find("Modal").props().isOpen).to.equal(false)
})
})
})

context("when the user does not confirm", () => {
it("does not push an event to the retro channel", () => {
confirmStub.returns(false)
stageProgressionButton.simulate("click")
context("when clicking no in the open modal", () => {
beforeEach(() => {
modalActions.find("#no").simulate("click")
})

expect(
retroChannel.push.called
).to.equal(false)
it("does not push an event to the retro channel", () => {
expect(
retroChannel.push.called
).to.equal(false)
})

it("closes the modal", () => {
expect(stageProgressionButton.find("Modal").props().isOpen).to.equal(false)
})
})
})
})

context("when the matching stage config lacks a `confirmationMessage`", () => {
beforeEach(() => {
stageProgressionButton = mount(
<StageProgressionButton {...defaultProps} stage="stageDos" />
)
})

context("onClick", () => {
let confirmSpy
let stageProgressionButton
let retroChannel

beforeEach(() => {
confirmSpy = sinon.spy(global, "confirm")
retroChannel = { on: () => {}, push: sinon.spy() }

const props = { ...defaultProps, retroChannel, stage: "stageDos" }
stageProgressionButton = mount(<StageProgressionButton {...props} />)

stageProgressionButton.simulate("click")
})

it("does not invoke a javascript confirmation", () => {
expect(confirmSpy.called).to.equal(false)
confirmSpy.restore()
stageProgressionButton.find("button").simulate("click")
})

it("pushes `proceed_to_next_stage` to the retroChannel, passing the next stage", () => {
Expand Down
65 changes: 53 additions & 12 deletions web/static/js/components/stage_progression_button.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,74 @@
import React, { Component } from "react"
import Modal from "react-modal"
import * as AppPropTypes from "../prop_types"

class StageProgressionButton extends Component {
constructor(props) {
super(props)
this.handleStageProgression = this.handleStageProgression.bind(this)
this.handleModalClose = this.handleModalClose.bind(this)
this.handleStageProgressionButtonClick = this.handleStageProgressionButtonClick.bind(this)
this.state = { modalOpen: false }
}

handleStageProgressionButtonClick() {
const { stage, stageProgressionConfigs } = this.props
const stageConfig = stageProgressionConfigs[stage]
const noConfirmationNecessary = !stageConfig.confirmationMessage
if (noConfirmationNecessary) {
this.handleStageProgression()
} else {
this.setState({ modalOpen: true })
}
}

handleStageProgression() {
const { stage, retroChannel, stageProgressionConfigs } = this.props
const stageConfig = stageProgressionConfigs[stage]
const noConfirmationNecessary = !stageConfig.confirmationMessage

if (noConfirmationNecessary || confirm(stageConfig.confirmationMessage)) {
retroChannel.push("proceed_to_next_stage", { stage: stageConfig.nextStage })
}
retroChannel.push("proceed_to_next_stage", { stage: stageConfig.nextStage })
this.setState({ modalOpen: false })
}

handleModalClose() {
this.setState({ modalOpen: false })
}

render() {
const { stage, stageProgressionConfigs } = this.props
const { buttonConfig } = stageProgressionConfigs[stage]
const { buttonConfig, confirmationMessage } = stageProgressionConfigs[stage]
const { modalOpen } = this.state
return (
<button
className="fluid ui right labeled teal icon button"
onClick={this.handleStageProgression}
>
{ buttonConfig.copy }
<i className={`${buttonConfig.iconClass} icon`} />
</button>
<div>
<Modal contentLabel="Modal" isOpen={modalOpen} className="ui small modal visible active">
<div className="content">
<p>{ confirmationMessage }</p>
</div>
<div className="actions" ref={ref => { this.modalActionsRef = ref }}>
<button
className="ui negative button"
id="no"
onClick={this.handleModalClose}
>
No
</button>
<button
className="ui positive button"
id="yes"
onClick={this.handleStageProgression}
>
Yes
</button>
</div>
</Modal>
<button
className="fluid ui right labeled teal icon button"
onClick={this.handleStageProgressionButtonClick}
>
{ buttonConfig.copy }
<i className={`${buttonConfig.iconClass} icon`} />
</button>
</div>
)
}
}
Expand Down

0 comments on commit cbebd6a

Please sign in to comment.