diff --git a/atst/forms/fields.py b/atst/forms/fields.py index c69c8ed6..584aeca8 100644 --- a/atst/forms/fields.py +++ b/atst/forms/fields.py @@ -1,4 +1,4 @@ -from wtforms.fields import Field, StringField, SelectField as SelectField_ +from wtforms.fields import Field, FormField, StringField, SelectField as SelectField_ from wtforms.widgets import TextArea @@ -39,3 +39,14 @@ class NumberStringField(StringField): self.data = str(value) else: self.data = value + + +class FormFieldWrapper(FormField): + def has_changes(self): + if not self.object_data: + return False + + for (attr, field) in self._fields.items(): + if attr in self.object_data and self.object_data[attr] != field.data: + return True + return False diff --git a/atst/forms/officers.py b/atst/forms/officers.py index 84e2283a..4df1f5a7 100644 --- a/atst/forms/officers.py +++ b/atst/forms/officers.py @@ -1,11 +1,12 @@ from flask_wtf import FlaskForm -from wtforms.fields import FormField, StringField +from wtforms.fields import StringField from wtforms.fields.html5 import TelField from wtforms.validators import Length, Optional from atst.forms.validators import IsNumber, PhoneNumber from .forms import CacheableForm +from .fields import FormFieldWrapper class OfficerForm(FlaskForm): @@ -18,9 +19,9 @@ class OfficerForm(FlaskForm): class EditTaskOrderOfficersForm(CacheableForm): - contracting_officer = FormField(OfficerForm) - contracting_officer_representative = FormField(OfficerForm) - security_officer = FormField(OfficerForm) + contracting_officer = FormFieldWrapper(OfficerForm) + contracting_officer_representative = FormFieldWrapper(OfficerForm) + security_officer = FormFieldWrapper(OfficerForm) OFFICER_PREFIXES = { "contracting_officer": "ko", diff --git a/js/components/forms/__tests__/edit_officer_form.test.js b/js/components/forms/__tests__/edit_officer_form.test.js index b5b02aa2..18f36ffc 100644 --- a/js/components/forms/__tests__/edit_officer_form.test.js +++ b/js/components/forms/__tests__/edit_officer_form.test.js @@ -22,6 +22,13 @@ describe('EditOfficerForm', () => { expect(wrapper.vm.$data.editing).toEqual(true) }) + it('does start in editing mode when the form has changes', () => { + const wrapper = shallowMount(EditOfficerForm, { + propsData: { hasChanges: true }, + }) + expect(wrapper.vm.$data.editing).toEqual(true) + }) + it('starts editing when edit method called', () => { const wrapper = shallowMount(EditOfficerForm) wrapper.vm.edit({ preventDefault: () => null }) diff --git a/js/components/forms/edit_officer_form.js b/js/components/forms/edit_officer_form.js index a8e4311b..016187bc 100644 --- a/js/components/forms/edit_officer_form.js +++ b/js/components/forms/edit_officer_form.js @@ -13,6 +13,10 @@ export default { }, props: { + hasChanges: { + type: Boolean, + default: () => false, + }, hasErrors: { type: Boolean, default: () => false, @@ -21,7 +25,7 @@ export default { data: function() { return { - editing: this.hasErrors, + editing: this.hasErrors || this.hasChanges, } }, diff --git a/templates/portfolios/task_orders/invitations.html b/templates/portfolios/task_orders/invitations.html index 7fda4f2d..3abc03e7 100644 --- a/templates/portfolios/task_orders/invitations.html +++ b/templates/portfolios/task_orders/invitations.html @@ -56,7 +56,7 @@

{{ ("task_orders.invitations." + officer_type + ".title") | translate }}

{{ ("task_orders.invitations." + officer_type + ".description") | translate }}

- +
{% set prefix = { "contracting_officer": "ko", "contracting_officer_representative": "cor", "security_officer": "so" }[officer_type] %} diff --git a/tests/forms/test_fields.py b/tests/forms/test_fields.py index e3cfa3ba..8e6f4002 100644 --- a/tests/forms/test_fields.py +++ b/tests/forms/test_fields.py @@ -1,9 +1,10 @@ import pytest from wtforms import Form +from wtforms.fields import StringField import pendulum from werkzeug.datastructures import ImmutableMultiDict -from atst.forms.fields import NewlineListField +from atst.forms.fields import NewlineListField, FormFieldWrapper class NewlineListForm(Form): @@ -38,3 +39,37 @@ def test_newline_list_value(input_, expected): assert form.validate() assert form.newline_list._value() == expected + + +class PersonForm(Form): + first_name = StringField("first_name") + + +class FormWithFormField(Form): + person = FormFieldWrapper(PersonForm) + + +class TestFormFieldWrapper: + class Foo: + person = {"first_name": "Luke"} + + obj = Foo() + + def test_form_data_does_not_match_object_data(self): + form_data = ImmutableMultiDict({"person-first_name": "Han"}) + form = FormWithFormField(form_data, obj=self.obj) + assert form.person.has_changes() + + def test_when_no_form_data(self): + form = FormWithFormField(None, obj=self.obj) + assert not form.person.has_changes() + + def test_when_no_obj_data(self): + form_data = ImmutableMultiDict({"person-first_name": "Han"}) + form = FormWithFormField(form_data) + assert not form.person.has_changes() + + def test_when_form_data_matches_obj_dta(self): + form_data = ImmutableMultiDict({"person-first_name": "Luke"}) + form = FormWithFormField(form_data, obj=self.obj) + assert not form.person.has_changes()