Merge pull request #516 from dod-ccpo/multicheckbox_with_other
Multicheckbox with 'other' option
This commit is contained in:
commit
7a7f8914c8
@ -7,6 +7,7 @@ from wtforms.fields import (
|
|||||||
TextAreaField,
|
TextAreaField,
|
||||||
)
|
)
|
||||||
from wtforms.fields.html5 import DateField
|
from wtforms.fields.html5 import DateField
|
||||||
|
from wtforms.widgets import ListWidget, CheckboxInput
|
||||||
|
|
||||||
from .forms import CacheableForm
|
from .forms import CacheableForm
|
||||||
from .data import (
|
from .data import (
|
||||||
@ -46,6 +47,8 @@ class AppInfoForm(CacheableForm):
|
|||||||
description="Which of these describes how complex your team's use of the cloud will be? (Select all that apply.)",
|
description="Which of these describes how complex your team's use of the cloud will be? (Select all that apply.)",
|
||||||
choices=PROJECT_COMPLEXITY,
|
choices=PROJECT_COMPLEXITY,
|
||||||
default="",
|
default="",
|
||||||
|
widget=ListWidget(prefix_label=False),
|
||||||
|
option_widget=CheckboxInput(),
|
||||||
)
|
)
|
||||||
complexity_other = StringField("Project Complexity Other")
|
complexity_other = StringField("Project Complexity Other")
|
||||||
dev_team = SelectMultipleField(
|
dev_team = SelectMultipleField(
|
||||||
@ -53,6 +56,8 @@ class AppInfoForm(CacheableForm):
|
|||||||
description="Which people or teams will be completing the development work for your cloud applications?",
|
description="Which people or teams will be completing the development work for your cloud applications?",
|
||||||
choices=DEV_TEAM,
|
choices=DEV_TEAM,
|
||||||
default="",
|
default="",
|
||||||
|
widget=ListWidget(prefix_label=False),
|
||||||
|
option_widget=CheckboxInput(),
|
||||||
)
|
)
|
||||||
dev_team_other = StringField("Development Team Other")
|
dev_team_other = StringField("Development Team Other")
|
||||||
team_experience = RadioField(
|
team_experience = RadioField(
|
||||||
|
@ -52,11 +52,6 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
|||||||
filter(None, [self.clin_01, self.clin_02, self.clin_03, self.clin_04])
|
filter(None, [self.clin_01, self.clin_02, self.clin_03, self.clin_04])
|
||||||
)
|
)
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<TaskOrder(number='{}', budget='{}', end_date='{}', id='{}')>".format(
|
|
||||||
self.number, self.budget, self.end_date, self.id
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def portfolio_name(self):
|
def portfolio_name(self):
|
||||||
return self.workspace.name
|
return self.workspace.name
|
||||||
@ -70,3 +65,8 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
|||||||
if c.name not in ["id"]
|
if c.name not in ["id"]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<TaskOrder(number='{}', budget='{}', end_date='{}', id='{}')>".format(
|
||||||
|
self.number, self.budget, self.end_date, self.id
|
||||||
|
)
|
||||||
|
@ -49,12 +49,20 @@ class ShowTaskOrderWorkflow:
|
|||||||
|
|
||||||
return self._task_order
|
return self._task_order
|
||||||
|
|
||||||
|
@property
|
||||||
|
def task_order_formdata(self):
|
||||||
|
task_order_dict = self.task_order.to_dictionary()
|
||||||
|
for field in task_order_dict:
|
||||||
|
if task_order_dict[field] is None:
|
||||||
|
task_order_dict[field] = ""
|
||||||
|
return task_order_dict
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def form(self):
|
def form(self):
|
||||||
if self._form:
|
if self._form:
|
||||||
pass
|
pass
|
||||||
elif self.task_order:
|
elif self.task_order:
|
||||||
self._form = self._section["form"](data=self.task_order.to_dictionary())
|
self._form = self._section["form"](formdata=self.task_order_formdata)
|
||||||
else:
|
else:
|
||||||
self._form = self._section["form"]()
|
self._form = self._section["form"]()
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import FormMixin from '../../mixins/form'
|
import FormMixin from '../../mixins/form'
|
||||||
import optionsinput from '../options_input'
|
|
||||||
import textinput from '../text_input'
|
import textinput from '../text_input'
|
||||||
import checkboxinput from '../checkbox_input'
|
import checkboxinput from '../checkbox_input'
|
||||||
|
|
||||||
@ -9,7 +8,6 @@ export default {
|
|||||||
mixins: [FormMixin],
|
mixins: [FormMixin],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
optionsinput,
|
|
||||||
textinput,
|
textinput,
|
||||||
checkboxinput,
|
checkboxinput,
|
||||||
},
|
},
|
||||||
|
60
js/components/multi_checkbox_input.js
Normal file
60
js/components/multi_checkbox_input.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import otherinput from '../components/other_input'
|
||||||
|
import optionsinput from '../components/options_input'
|
||||||
|
import textinput from '../components/text_input'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'multicheckboxinput',
|
||||||
|
|
||||||
|
components: {
|
||||||
|
otherinput,
|
||||||
|
optionsinput,
|
||||||
|
textinput,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
initialErrors: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
initialValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
initialOtherValue: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
const showError = (this.initialErrors && this.initialErrors.length) || false
|
||||||
|
return {
|
||||||
|
showError: showError,
|
||||||
|
showValid: !showError && !!this.initialValue,
|
||||||
|
validationError: this.initialErrors.join(' '),
|
||||||
|
otherChecked: this.initialValue.includes("other") ? true : this.otherChecked,
|
||||||
|
otherText: this.initialOtherValue,
|
||||||
|
selections: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted: function () {
|
||||||
|
for (let choice of this.initialValue) {
|
||||||
|
this.selections.push(choice)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onInput: function (e) {
|
||||||
|
this.$root.$emit('field-change', {
|
||||||
|
value: e.target.value,
|
||||||
|
name: this.name
|
||||||
|
})
|
||||||
|
this.showError = false
|
||||||
|
this.showValid = true
|
||||||
|
},
|
||||||
|
otherToggle: function() {
|
||||||
|
this.otherChecked = !this.otherChecked
|
||||||
|
this.otherText = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
js/components/other_input.js
Normal file
29
js/components/other_input.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import FormMixin from '../mixins/form'
|
||||||
|
import textinput from '../components/text_input'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'otherinput',
|
||||||
|
|
||||||
|
mixins: [FormMixin],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
textinput,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
initialData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data: function () {
|
||||||
|
const {
|
||||||
|
other = true
|
||||||
|
} = this.initialData
|
||||||
|
|
||||||
|
return {
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,8 @@ import Vue from 'vue/dist/vue'
|
|||||||
import VTooltip from 'v-tooltip'
|
import VTooltip from 'v-tooltip'
|
||||||
|
|
||||||
import optionsinput from './components/options_input'
|
import optionsinput from './components/options_input'
|
||||||
|
import multicheckboxinput from './components/multi_checkbox_input'
|
||||||
|
import otherinput from './components/other_input'
|
||||||
import textinput from './components/text_input'
|
import textinput from './components/text_input'
|
||||||
import checkboxinput from './components/checkbox_input'
|
import checkboxinput from './components/checkbox_input'
|
||||||
import DetailsOfUse from './components/forms/details_of_use'
|
import DetailsOfUse from './components/forms/details_of_use'
|
||||||
@ -37,6 +39,8 @@ const app = new Vue({
|
|||||||
components: {
|
components: {
|
||||||
toggler,
|
toggler,
|
||||||
optionsinput,
|
optionsinput,
|
||||||
|
multicheckboxinput,
|
||||||
|
otherinput,
|
||||||
textinput,
|
textinput,
|
||||||
checkboxinput,
|
checkboxinput,
|
||||||
DetailsOfUse,
|
DetailsOfUse,
|
||||||
|
60
templates/components/multi_checkbox_input.html
Normal file
60
templates/components/multi_checkbox_input.html
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{% from "components/icon.html" import Icon %}
|
||||||
|
{% from "components/tooltip.html" import Tooltip %}
|
||||||
|
|
||||||
|
{% macro MultiCheckboxInput(field, other_input_field, tooltip, inline=False) -%}
|
||||||
|
<multicheckboxinput
|
||||||
|
v-cloak
|
||||||
|
name='{{ field.name }}'
|
||||||
|
inline-template
|
||||||
|
{% if field.errors %}v-bind:initial-errors='{{ field.errors | list }}'{% endif %}
|
||||||
|
{% if field.data and field.data != "None" %}v-bind:initial-value="{{ field.data }}"{% endif %}
|
||||||
|
{% if other_input_field.data and other_input_field.data != "None" %}initial-other-value="{{ other_input_field.data }}"{% endif %}
|
||||||
|
key='{{ field.name }}'>
|
||||||
|
<div
|
||||||
|
v-bind:class="['usa-input', { 'usa-input--error': showError, 'usa-input--success': showValid }]">
|
||||||
|
|
||||||
|
<fieldset v-on:change="onInput" class="usa-input__choices {% if inline %}usa-input__choices--inline{% endif %}">
|
||||||
|
<legend>
|
||||||
|
<div class="usa-input__title">
|
||||||
|
{{ field.label | striptags}}
|
||||||
|
{% if tooltip %}{{ Tooltip(tooltip) }}{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if field.description %}
|
||||||
|
<span class='usa-input__help'>{{ field.description | safe }}</span>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<span v-show='showError'>{{ Icon('alert',classes="icon-validation") }}</span>
|
||||||
|
<span v-show='showValid'>{{ Icon('ok',classes="icon-validation") }}</span>
|
||||||
|
</legend>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for choice in field.choices %}
|
||||||
|
<li>
|
||||||
|
{% if choice[0] != 'other' %}
|
||||||
|
<input type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ field.choices.index(choice) }}' value='{{ choice[0] }}' v-model="selections"/>
|
||||||
|
<label for='{{ field.name }}-{{ field.choices.index(choice) }}'>{{ choice[1] }}</label>
|
||||||
|
{% else %}
|
||||||
|
<input @click="otherToggle" type='checkbox' name='{{ field.name }}' id='{{ field.name }}-{{ field.choices.index(choice) }}' value='other' v-model="selections"/>
|
||||||
|
<label for='{{ field.name }}-{{ field.choices.index(choice) }}'>{{ choice[1] }}</label>
|
||||||
|
|
||||||
|
<div v-show="otherChecked">
|
||||||
|
<input type='text' name='{{ other_input_field.name}}' id='{{ field.name }}-other' v-bind:value="otherText" aria-expanded='false' />
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template v-if='showError'>
|
||||||
|
<span class='usa-input__message' v-html='validationError'></span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</multicheckboxinput>
|
||||||
|
|
||||||
|
{%- endmacro %}
|
@ -3,6 +3,7 @@
|
|||||||
{% from "components/text_input.html" import TextInput %}
|
{% from "components/text_input.html" import TextInput %}
|
||||||
{% from "components/options_input.html" import OptionsInput %}
|
{% from "components/options_input.html" import OptionsInput %}
|
||||||
{% from "components/date_input.html" import DateInput %}
|
{% from "components/date_input.html" import DateInput %}
|
||||||
|
{% from "components/multi_checkbox_input.html" import MultiCheckboxInput %}
|
||||||
|
|
||||||
{% block heading %}
|
{% block heading %}
|
||||||
What You're Building
|
What You're Building
|
||||||
@ -28,14 +29,12 @@
|
|||||||
<h3>About Your Project</h3>
|
<h3>About Your Project</h3>
|
||||||
{{ OptionsInput(form.app_migration) }}
|
{{ OptionsInput(form.app_migration) }}
|
||||||
{{ OptionsInput(form.native_apps) }}
|
{{ OptionsInput(form.native_apps) }}
|
||||||
{{ OptionsInput(form.complexity) }}
|
{{ MultiCheckboxInput(form.complexity, form.complexity_other) }}
|
||||||
{{ TextInput(form.complexity_other) }}
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<h3>About Your Team</h3>
|
<h3>About Your Team</h3>
|
||||||
{{ OptionsInput(form.dev_team) }}
|
{{ MultiCheckboxInput(form.dev_team, form.dev_team_other) }}
|
||||||
{{ TextInput(form.dev_team_other) }}
|
|
||||||
{{ OptionsInput(form.team_experience) }}
|
{{ OptionsInput(form.team_experience) }}
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
@ -78,6 +78,16 @@ def test_show_task_order():
|
|||||||
assert another_workflow.task_order == task_order
|
assert another_workflow.task_order == task_order
|
||||||
|
|
||||||
|
|
||||||
|
def test_show_task_order_formdata():
|
||||||
|
task_order = TaskOrderFactory.create()
|
||||||
|
workflow = ShowTaskOrderWorkflow(task_order_id=task_order.id)
|
||||||
|
|
||||||
|
assert workflow.task_order.to_dictionary()["user_id"] is None
|
||||||
|
assert workflow.task_order_formdata["user_id"] is ""
|
||||||
|
for field in workflow.task_order_formdata:
|
||||||
|
assert not field is None
|
||||||
|
|
||||||
|
|
||||||
def test_show_task_order_form():
|
def test_show_task_order_form():
|
||||||
workflow = ShowTaskOrderWorkflow()
|
workflow = ShowTaskOrderWorkflow()
|
||||||
assert not workflow.form.data["app_migration"]
|
assert not workflow.form.data["app_migration"]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user