Skip to content

Commit

Permalink
Merge pull request #207 from LD4P/repeatable-mandatory-input
Browse files Browse the repository at this point in the history
Repeatable mandatory input for type Literal
  • Loading branch information
jermnelson committed Nov 29, 2018
2 parents 56e3787 + 683c7ef commit 0e0e870
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 5 deletions.
58 changes: 56 additions & 2 deletions __tests__/components/editor/InputLiteral.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const plProps = {
"propertyTemplate":
{
"propertyLabel": "Instance of",
"type": "literal"
"type": "literal",
"mandatory": "",
"repeatable": ""
}
}

Expand All @@ -21,5 +23,57 @@ describe('<InputLiteral />', () => {
it('<input> element should have a placeholder attribute with value propertyLabel', () => {
expect(wrapper.find('input').props().placeholder).toBe('Instance of')
})
it('contains required="true" attribute on input tag when mandatory is true', () => {
wrapper.instance().props.propertyTemplate.mandatory = "true"
wrapper.instance().forceUpdate() /** update plProps with mandatory: "true" **/
expect(wrapper.find('input').prop('required')).toEqual('required')
})
it('contains required="false" attribute on input tag when mandatory is false', () => {
wrapper.instance().props.propertyTemplate.mandatory = "false"
wrapper.instance().forceUpdate()
expect(wrapper.find('input').prop('required')).toBeFalsy()
})
it('property template contains repeatable "true", allowed to add more than one item into myItems array', () => {
wrapper.instance().props.propertyTemplate.repeatable = "true"
wrapper.instance().forceUpdate()

wrapper.find('input').simulate("change", { target: { value: "foo" }})
expect(wrapper.state('content_add')).toEqual('foo') /** expect state to have value onChange **/
wrapper.find('input').simulate('keypress', {key: 'Enter', preventDefault: () => {}})
wrapper.find('input').simulate("change", { target: { value: "bar" }})
expect(wrapper.state('content_add')).toEqual('bar')
wrapper.find('input').simulate('keypress', {key: 'Enter', preventDefault: () => {}})
expect(wrapper.state('myItems').length).toEqual(2)
wrapper.setState({content_add : '', myItems: []}) /** reset state **/
})
it('property template contains repeatable "false", only allowed to add one item into myItems array.', () => {
wrapper.instance().props.propertyTemplate.repeatable = "false"
wrapper.instance().forceUpdate()

})
wrapper.find('input').simulate("change", { target: { value: "foo" }})
expect(wrapper.state('content_add')).toEqual('foo')
wrapper.find('input').simulate('keypress', {key: 'Enter', preventDefault: () => {}})
wrapper.find('input').simulate("change", { target: { value: "bar" }})
expect(wrapper.state('content_add')).toEqual('bar')
wrapper.find('input').simulate('keypress', {key: 'Enter', preventDefault: () => {}})
expect(wrapper.state('myItems').length).toEqual(1)
wrapper.setState({content_add : '', myItems: []})
})
it('after adding one item into myItems array, required turns to false.', () => {
wrapper.instance().props.propertyTemplate.mandatory = "true"
wrapper.instance().props.propertyTemplate.repeatable = "true"
wrapper.instance().forceUpdate()

expect(wrapper.find('input').prop('required')).toEqual('required')
wrapper.find('input').simulate("change", { target: { value: "foo" }})
expect(wrapper.state('content_add')).toEqual('foo')
wrapper.find('input').simulate('keypress', {key: 'Enter', preventDefault: () => {}})
expect(wrapper.find('input').prop('required')).toBeFalsy()
})
it('click on user added list item to remove that item', () => {
wrapper.setState({content_add : '', myItems: [ {content: 'foo', id: 0}, {content: 'bar', id: 1}]})
expect(wrapper.find('button').length).toEqual(2)
wrapper.find('button').first().simulate('click', { target: { "dataset": {"item": 0 }}});
expect(wrapper.find('button').length).toEqual(1)
})
})
130 changes: 127 additions & 3 deletions src/components/editor/InputLiteral.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,140 @@
// Copyright 2018 Stanford University see Apache2.txt for license

import React, {Component} from 'react';
import React, {Component} from 'react'
import PropTypes from 'prop-types'

class InputLiteral extends Component {

constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.handleKeypress = this.handleKeypress.bind(this)
this.handleClick = this.handleClick.bind(this)
this.checkMandatoryRepeatable = this.checkMandatoryRepeatable.bind(this)
this.nonRepeatableNonMandatory = this.nonRepeatableNonMandatory.bind(this)
this.nonRepeatableMandatory = this.nonRepeatableMandatory.bind(this)
this.addUserInput = this.addUserInput.bind(this)
this.state = {
content_add: "",
myItems: []
}
this.lastId = -1
}

handleChange(event) {
const usr_input = event.target.value
this.setState({ content_add: usr_input })
}

/** repeatable = false and mandatory = true **/
nonRepeatableMandatory(userInputArray, currentcontent) {
if (userInputArray.length == 0) {
this.addUserInput(userInputArray, currentcontent)
}
}

/** repeatable = false and mandatory = false **/
nonRepeatableNonMandatory(userInputArray, currentcontent) {
if (userInputArray.length < 1) {
this.addUserInput(userInputArray, currentcontent)
}
}

addUserInput(userInputArray, currentcontent) {
userInputArray.push({
content: currentcontent,
id: ++this.lastId
})
}

handleKeypress(event) {
if (event.key == "Enter") {
var userInputArray = this.state.myItems
var currentcontent = this.state.content_add.trim()
if (!currentcontent) {
return
}
/** Input field is repeatable, add user input to array.**/
if (this.props.propertyTemplate.repeatable == "true") {
this.addUserInput(userInputArray, currentcontent)
/** Input field is not repeatable **/
} else if (this.props.propertyTemplate.repeatable == "false") {
/** Mandatory true, means array must have only 1 item in the array **/
if (this.props.propertyTemplate.mandatory == "true") {
this.nonRepeatableMandatory(userInputArray, currentcontent)
/** Mandatory is false, or not defined. Array can have either 0 or 1 item in array. **/
} else {
this.nonRepeatableNonMandatory(userInputArray, currentcontent)
}
}
this.setState({
myItems: userInputArray,
content_add: "",
})
event.preventDefault()
}
}

handleClick(event) {
const idToRemove = Number(event.target.dataset["item"])
const userInputArray = this.state.myItems.filter((listitem) => {return listitem.id !== idToRemove})
this.setState({ myItems: userInputArray })
}

checkMandatoryRepeatable() {
if (this.props.propertyTemplate.mandatory == "true") {
if (this.makeAddedList().length > 0) {
return false
}
else {
return true
}
}
else if (this.props.propertyTemplate.mandatory == "false") {
return false
}
}

makeAddedList() {
const elements = this.state.myItems.map((listitem) => (
<button
key={listitem.id}
onClick={this.handleClick}
data-item={listitem.id}
>
{listitem.content}
</button>
))
return elements
}
render() {
return (
<div className="form-group">
<label>{this.props.propertyTemplate.propertyLabel}</label>
<input className="form-control" required = { this.props.propertyTemplate.mandatory? true : null} type="text" placeholder={this.props.propertyTemplate.propertyLabel}/>
<label htmlFor="typeLiteral">
{this.props.propertyTemplate.propertyLabel}
<input
{ ...(this.checkMandatoryRepeatable() ? {required: 'required'} : null) }
className="form-control"
placeholder={this.props.propertyTemplate.propertyLabel}
onChange={this.handleChange}
onKeyPress={this.handleKeypress}
value={this.state.content_add}
it="typeLiteral"
/>
{this.makeAddedList()}
</label>

</div>
)
}
}

InputLiteral.propTypes = {
propertyTemplate: PropTypes.shape({
propertyLabel: PropTypes.string.isRequired,
mandatory: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
repeatable: PropTypes.oneOfType([PropTypes.string, PropTypes.bool])
}).isRequired
}

export default InputLiteral;

0 comments on commit 0e0e870

Please sign in to comment.