Skip to content

Commit eab4a9c

Browse files
committed
Feature: Capture notes/comment at the time of Form publish
1 parent 01fe14f commit eab4a9c

5 files changed

Lines changed: 68 additions & 48 deletions

File tree

apps/central/src/components/form-draft/publish.vue

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,37 +40,27 @@ except according to the terms contained in the LICENSE file.
4040

4141
<hr v-if="draftVersionStringIsDuplicate">
4242
<p v-if="draftVersionStringIsDuplicate">{{ $t('introduction[2]') }}</p>
43-
44-
<p v-if="!draftVersionStringIsDuplicate && !versionConflict">{{ $t('introduction[3]') }}</p>
4543
</div>
46-
<form v-if="draftVersionStringIsDuplicate || versionConflict" @submit.prevent="publish">
47-
<form-group ref="versionString" v-model.trim="versionString"
48-
:placeholder="$t('common.version')" required autocomplete="off"/>
44+
<hr>
45+
<form @submit.prevent="publish">
46+
<form-group v-if="draftVersionStringIsDuplicate || versionConflict" ref="versionString" v-model.trim="versionString" :placeholder="$t('common.version')"
47+
required autocomplete="off"/>
48+
<div class="form-group">
49+
<textarea id="form-draft-publish-note" v-model="notes" class="form-control"
50+
:placeholder="$t('placeholder.note')" rows="3"></textarea>
51+
<label for="form-draft-publish-note" class="form-label">{{ $t('field.note') }}</label>
52+
</div>
4953
<p>{{ $t('introduction[3]') }}</p>
50-
<!-- We specify two nearly identical .modal-actions, because here we
51-
want the Proceed button to be a submit button (which means that browsers
52-
will do some basic form validation when it is clicked). -->
5354
<div class="modal-actions">
5455
<button type="button" class="btn btn-link"
5556
:aria-disabled="awaitingResponse" @click="$emit('hide')">
5657
{{ $t('action.cancel') }}
5758
</button>
58-
<button type="submit" class="btn btn-primary"
59-
:aria-disabled="awaitingResponse">
59+
<button type="submit" class="btn btn-primary" :aria-disabled="awaitingResponse">
6060
{{ $t('action.proceed') }} <spinner :state="awaitingResponse"/>
6161
</button>
6262
</div>
6363
</form>
64-
<div v-else class="modal-actions">
65-
<button type="button" class="btn btn-link" :aria-disabled="awaitingResponse"
66-
@click="$emit('hide')">
67-
{{ $t('action.cancel') }}
68-
</button>
69-
<button type="button" class="btn btn-primary"
70-
:aria-disabled="awaitingResponse" @click="publish">
71-
{{ $t('action.proceed') }} <spinner :state="awaitingResponse"/>
72-
</button>
73-
</div>
7464
</template>
7565
</modal>
7666
</template>
@@ -111,6 +101,7 @@ export default {
111101
data() {
112102
return {
113103
versionString: '',
104+
notes: '',
114105
// versionConflict is used in a scenario where a user tries to
115106
// publish a form that conflicts with a form/version combo probably
116107
// found in the trash. This component doesn't have access to trashed
@@ -139,14 +130,20 @@ export default {
139130
},
140131
watch: {
141132
state(state) {
142-
if (state) this.versionString = this.formDraft.version;
133+
if (state) {
134+
this.versionString = this.formDraft.version;
135+
this.notes = '';
136+
}
143137
}
144138
},
145139
methods: {
146140
focusInput() {
147141
if (this.draftVersionStringIsDuplicate) this.$refs.versionString.focus();
148142
},
149143
publish() {
144+
const headers = {};
145+
if (this.notes !== '')
146+
headers['X-Action-Notes'] = encodeURIComponent(this.notes);
150147
this.request({
151148
method: 'POST',
152149
url: apiPaths.publishFormDraft(
@@ -156,6 +153,7 @@ export default {
156153
? { version: this.versionString }
157154
: null
158155
),
156+
headers,
159157
fulfillProblem: (problem) => problem.code === 409.6
160158
})
161159
.then(({ data }) => {
@@ -198,6 +196,12 @@ export default {
198196
"newProperties": "Publishing this draft will create {count} property. | Publishing this draft will create {count} properties.",
199197
"problem": {
200198
"409_6": "The version name of this Draft conflicts with a past version of this Form or a deleted Form. Please use the field below to change it to something new or upload a new Form definition."
199+
},
200+
"field": {
201+
"note": "Notes (optional)"
202+
},
203+
"placeholder": {
204+
"note": "Add optional publishing notes..."
201205
}
202206
}
203207
}

apps/central/test/components/form-attachment/list.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ describe('FormAttachmentList', () => {
11401140
.complete()
11411141
.request(async (app) => {
11421142
await app.get('#form-edit-publish-button').trigger('click');
1143-
return app.get('#form-draft-publish .btn-primary').trigger('click');
1143+
return app.get('#form-draft-publish form').trigger('submit');
11441144
})
11451145
.respondWithData(() => {
11461146
testData.extendedFormDrafts.publish(-1);
@@ -1184,7 +1184,7 @@ describe('FormAttachmentList', () => {
11841184
.complete()
11851185
.request(async (app) => {
11861186
await app.get('#form-edit-publish-button').trigger('click');
1187-
return app.get('#form-draft-publish .btn-primary').trigger('click');
1187+
return app.get('#form-draft-publish form').trigger('submit');
11881188
})
11891189
.respondWithData(() => {
11901190
testData.extendedFormDrafts.publish(-1);

apps/central/test/components/form-draft/publish.spec.js

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe('FormDraftPublish', () => {
109109
modal.findAll('form p').length.should.equal(1);
110110
});
111111

112-
it('does not show input if version string of draft is different', async () => {
112+
it('does not show version input if version string of draft is different', async () => {
113113
testData.extendedForms.createPast(1);
114114
testData.extendedFormVersions.createPast(1, {
115115
version: 'v2',
@@ -118,15 +118,15 @@ describe('FormDraftPublish', () => {
118118
const modal = mount(FormDraftPublish, mountOptions());
119119
await modal.setProps({ state: true });
120120
modal.find('input').exists().should.be.false;
121-
modal.findAll('.modal-introduction p').length.should.equal(3);
121+
modal.find('.modal-introduction').text().should.not.match(/your Draft Form has the same version name/);
122122
});
123123

124-
it('does not show the input for a form without a published version', async () => {
124+
it('does not show the version input for a form without a published version', async () => {
125125
testData.extendedForms.createPast(1, { draft: true });
126126
const modal = mount(FormDraftPublish, mountOptions());
127127
await modal.setProps({ state: true });
128128
modal.find('input').exists().should.be.false;
129-
modal.findAll('.modal-introduction p').length.should.equal(3);
129+
modal.find('.modal-introduction').text().should.not.match(/your Draft Form has the same version name/);
130130
});
131131

132132
it('focuses the input', async () => {
@@ -178,18 +178,6 @@ describe('FormDraftPublish', () => {
178178
modal: true
179179
});
180180
});
181-
182-
it('implements things if the version string input is not shown', () => {
183-
testData.extendedForms.createPast(1, { draft: true });
184-
return mockHttp()
185-
.mount(FormDraftPublish, mountOptions())
186-
.afterResponses(modal => modal.setProps({ state: true }))
187-
.testStandardButton({
188-
button: '.btn-primary',
189-
disabled: ['.btn-link'],
190-
modal: true
191-
});
192-
});
193181
});
194182

195183
describe('request', () => {
@@ -199,7 +187,7 @@ describe('FormDraftPublish', () => {
199187
.mount(FormDraftPublish, mountOptions())
200188
.request(async (modal) => {
201189
await modal.setProps({ state: true });
202-
return modal.get('.btn-primary').trigger('click');
190+
return modal.get('form').trigger('submit');
203191
})
204192
.beforeEachResponse((_, { method, url }) => {
205193
method.should.equal('POST');
@@ -249,7 +237,7 @@ describe('FormDraftPublish', () => {
249237
.mount(FormDraftPublish, mountOptions())
250238
.request(async (modal) => {
251239
await modal.setProps({ state: true });
252-
return modal.get('.btn-primary').trigger('click');
240+
return modal.get('form').trigger('submit');
253241
})
254242
.beforeEachResponse((_, { url }) => {
255243
url.should.equal('/v1/projects/1/forms/f/draft/publish');
@@ -283,7 +271,7 @@ describe('FormDraftPublish', () => {
283271
.mount(FormDraftPublish, mountOptions())
284272
.request(async (modal) => {
285273
await modal.setProps({ state: true });
286-
return modal.get('#form-draft-publish .btn-primary').trigger('click');
274+
return modal.get('#form-draft-publish form').trigger('submit');
287275
})
288276
.respondWithProblem({
289277
code: 409.17,
@@ -309,7 +297,7 @@ describe('FormDraftPublish', () => {
309297
.mount(FormDraftPublish, mountOptions())
310298
.request(async (modal) => {
311299
await modal.setProps({ state: true });
312-
return modal.get('#form-draft-publish .btn-primary').trigger('click');
300+
return modal.get('form').trigger('submit');
313301
})
314302
.respondWithProblem(409.6)
315303
.afterResponse(modal => {
@@ -329,13 +317,13 @@ describe('FormDraftPublish', () => {
329317
.mount(FormDraftPublish, mountOptions())
330318
.request(async (modal) => {
331319
await modal.setProps({ state: true });
332-
return modal.get('#form-draft-publish .btn-primary').trigger('click');
320+
return modal.get('form').trigger('submit');
333321
})
334322
.respondWithProblem(500.1)
335323
.afterResponse(modal => {
336324
modal.should.alert('danger');
337325
modal.find('input').exists().should.be.false;
338-
modal.findAll('.modal-introduction p').length.should.equal(3);
326+
modal.find('.modal-introduction').text().should.not.match(/your Draft Form has the same version name/);
339327
});
340328
});
341329

@@ -355,7 +343,7 @@ describe('FormDraftPublish', () => {
355343
.complete()
356344
.request(async (app) => {
357345
await app.get('#form-edit-publish-button').trigger('click');
358-
return app.get('#form-draft-publish .btn-primary').trigger('click');
346+
return app.get('#form-draft-publish form').trigger('submit');
359347
})
360348
.modify(respondToPublish);
361349
};
@@ -369,6 +357,23 @@ describe('FormDraftPublish', () => {
369357
{ url: '/v1/projects/1/forms/f/dataset-diff' }
370358
]));
371359

360+
it('sends X-Action-Notes header when notes are provided', () => {
361+
testData.extendedForms.createPast(1, { draft: true });
362+
return load('/projects/1/forms/f/draft')
363+
.complete()
364+
.request(async (app) => {
365+
await app.get('#form-edit-publish-button').trigger('click');
366+
await app.get('#form-draft-publish-note').setValue('Version 1 release notes');
367+
return app.get('#form-draft-publish form').trigger('submit');
368+
})
369+
.beforeEachResponse((_, { url, headers }) => {
370+
if (url === '/v1/projects/1/forms/f/draft/publish') {
371+
headers['X-Action-Notes'].should.equal(encodeURIComponent('Version 1 release notes'));
372+
}
373+
})
374+
.modify(respondToPublish);
375+
});
376+
372377
it('hides the modal', () =>
373378
publish()
374379
.afterResponses(app => {
@@ -422,7 +427,7 @@ describe('FormDraftPublish', () => {
422427
.complete()
423428
.request(async (app) => {
424429
await app.get('#form-edit-publish-button').trigger('click');
425-
return app.get('#form-draft-publish .btn-primary').trigger('click');
430+
return app.get('#form-draft-publish form').trigger('submit');
426431
})
427432
.modify(respondToPublish)
428433
.complete()

apps/central/test/components/form/edit.spec.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('FormEdit', () => {
5555
})
5656
.request(async (app) => {
5757
await app.get('#form-edit-publish-button').trigger('click');
58-
return app.get('#form-draft-publish .btn-primary').trigger('click');
58+
return app.get('#form-draft-publish form').trigger('submit');
5959
})
6060
.respondWithData(() => {
6161
testData.extendedFormDrafts.publish(-1);

apps/central/transifex/strings_en.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3324,6 +3324,17 @@
33243324
"409_6": {
33253325
"string": "The version name of this Draft conflicts with a past version of this Form or a deleted Form. Please use the field below to change it to something new or upload a new Form definition."
33263326
}
3327+
},
3328+
"field": {
3329+
"note": {
3330+
"string": "Notes (optional)",
3331+
"developer_comment": "This is the text of a form field."
3332+
}
3333+
},
3334+
"placeholder": {
3335+
"note": {
3336+
"string": "Add optional publishing notes..."
3337+
}
33273338
}
33283339
},
33293340
"FormDraftQrPanel": {

0 commit comments

Comments
 (0)