Skip to content

Commit

Permalink
Prevent user from submitting empty feedback
Browse files Browse the repository at this point in the history
Also, clear the component's state after a successful submission so the
UI is empty.

Closes 18F/crime-data-explorer#267
  • Loading branch information
Jeremia Kimelman committed Aug 2, 2017
1 parent 82948a6 commit 0101f80
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 11 deletions.
44 changes: 33 additions & 11 deletions src/components/Feedback.js
Expand Up @@ -9,10 +9,7 @@ class Feedback extends React.Component {
constructor(props) {
super(props)

this.state = {
data: Object.assign(...props.fields.map(f => ({ [f.id]: '' }))),
result: {},
}
this.state = this.createInitialState()
}

componentDidMount() {
Expand All @@ -35,6 +32,11 @@ class Feedback extends React.Component {
onClose()
}

createInitialState = () => ({
data: Object.assign(...this.props.fields.map(f => ({ [f.id]: '' }))),
result: {},
})

createIssueBody = () =>
this.props.fields
.map(f => ({
Expand All @@ -45,6 +47,14 @@ class Feedback extends React.Component {
.map(d => `## ${d.label}\n${d.data}\n\n`)
.join('\n')

validateSubmission = () => {
const { fields } = this.props
const { data } = this.state
const areEmpty = Object.keys(data).filter(key => data[key] === '')
if (areEmpty.length === fields.length) return false
return true
}

handleChange = e => {
const { name, value } = e.target
this.setState(prevState => ({
Expand All @@ -55,6 +65,10 @@ class Feedback extends React.Component {
handleSubmit = e => {
e.preventDefault()

if (!this.validateSubmission()) {
return this.handleValidationError()
}

http
.post('/feedback', {
body: this.createIssueBody(),
Expand All @@ -69,7 +83,7 @@ class Feedback extends React.Component {
this.setState({
result: {
type: 'error',
msg: response.statusText,
msg: "Please try again in a bit, we're having problems.",
},
})
throw new Error(
Expand All @@ -82,11 +96,20 @@ class Feedback extends React.Component {
const { html_url } = response.data
this.setState({ result: { type: 'success', url: html_url } })
setTimeout(() => {
this.setState({ result: {}, data: {} })
this.setState(this.createInitialState())
this.close()
}, 8000)
}

handleValidationError = () => {
this.setState({
result: {
type: 'error',
msg: 'Please provide feedback to at least one question above',
},
})
}

handleFirstFocus = () => {
if (this.props.isOpen) this.firstTextarea.focus()
}
Expand Down Expand Up @@ -145,10 +168,9 @@ class Feedback extends React.Component {
</div>,
)}
<p className="fs-14 sans-serif">
This information will be reported on Github, where it will
be publically visible. You can review all reported feedback
on our
{' '}
This information will be reported on Github, where it will be
publically visible. You can review all reported feedback on
our{' '}
<a
className="cursor-pointer underline white"
href="https://github.com/18f/crime-data-explorer/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aopen%20User%20feedback"
Expand All @@ -175,7 +197,7 @@ class Feedback extends React.Component {
</span>}
{result.type === 'error' &&
<span className="red-bright" role="alert">
There was an error: {result.msg}.
Error: {result.msg}.
</span>}
</div>
</div>
Expand Down
46 changes: 46 additions & 0 deletions test/components/Feedback.test.js
@@ -0,0 +1,46 @@
/* eslint no-undef: 0 */

import { shallow } from 'enzyme'
import React from 'react'

import Feedback from '../../src/components/Feedback'

describe('Feedback', () => {
describe('createInitialState()', () => {
it('returns an object with "result" set to {}', () => {
const wrapper = shallow(<Feedback onClose={() => {}} />)
const actual = wrapper.instance().createInitialState()

expect(actual.result).toEqual({})
})

it('returns an object with "data" based on props', () => {
const wrapper = shallow(<Feedback onClose={() => {}} />)
const instance = wrapper.instance()
const actualFields = Object.keys(instance.createInitialState().data)
const propFields = instance.props.fields

expect(actualFields.length).toEqual(propFields.length)
})
})

describe('validateSubmission()', () => {
it('returns false if all textareas are empty strings', () => {
const wrapper = shallow(<Feedback onClose={() => {}} />)
const actual = wrapper.instance().validateSubmission()

expect(actual).toEqual(false)
})

it('returns true if at least one textarea is not an empty string', () => {
const wrapper = shallow(
<Feedback fields={[{ id: 'foo', label: 'hey' }]} onClose={() => {}} />,
)
const instance = wrapper.instance()
const { state } = instance
instance.state = { ...state, data: { foo: 'yo' } }

expect(instance.validateSubmission()).toEqual(true)
})
})
})

0 comments on commit 0101f80

Please sign in to comment.