Commit ba8f1edd authored by minahilnichols's avatar minahilnichols Committed by Ezekiel Kigbo

Update new iterations form to match pajamas

parent a6f4e0bb
<script>
import { GlButton, GlForm, GlFormInput } from '@gitlab/ui';
import initDatePicker from '~/behaviors/date_picker';
import { GlButton, GlForm, GlFormGroup, GlFormInput, GlDatepicker } from '@gitlab/ui';
import createFlash from '~/flash';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { formatDate } from '~/lib/utils/datetime_utility';
import createIteration from '../queries/create_iteration.mutation.graphql';
import updateIteration from '../queries/update_iteration.mutation.graphql';
......@@ -12,8 +12,10 @@ export default {
components: {
GlButton,
GlForm,
GlFormGroup,
GlFormInput,
MarkdownField,
GlDatepicker,
},
props: {
groupPath: {
......@@ -49,6 +51,8 @@ export default {
description: this.iteration.description ?? '',
startDate: this.iteration.startDate,
dueDate: this.iteration.dueDate,
isValidTitle: true,
isValidStartDate: true,
};
},
computed: {
......@@ -58,18 +62,39 @@ export default {
groupPath: this.groupPath,
title: this.title,
description: this.description,
startDate: this.startDate,
dueDate: this.dueDate,
startDate: this.startDate ? formatDate(this.startDate, 'yyyy-mm-dd') : null,
dueDate: this.dueDate ? formatDate(this.dueDate, 'yyyy-mm-dd') : null,
},
};
},
},
mounted() {
// TODO: utilize GlDatepicker instead of relying on this jQuery behavior
initDatePicker();
invalidFeedback() {
return __('This field is required.');
},
},
methods: {
checkValidations() {
let isValid = true;
if (!this.title) {
this.isValidTitle = false;
isValid = false;
} else {
this.isValidTitle = true;
}
if (!this.startDate) {
this.isValidStartDate = false;
isValid = false;
} else {
this.isValidStartDate = true;
}
return isValid;
},
save() {
if (!this.checkValidations()) {
return {};
}
this.loading = true;
return this.isEditing ? this.updateIteration() : this.createIteration();
},
......@@ -154,93 +179,89 @@ export default {
</h3>
</div>
<hr class="gl-mt-0" />
<gl-form class="row common-note-form">
<gl-form class="row common-note-form" novalidate>
<div class="col-md-6">
<div class="form-group row">
<div class="col-form-label col-sm-2">
<label for="iteration-title">{{ __('Title') }}</label>
</div>
<div class="col-sm-10">
<gl-form-input
id="iteration-title"
v-model="title"
autocomplete="off"
data-qa-selector="iteration_title_field"
/>
</div>
</div>
<gl-form-group
:label="__('Title')"
class="gl-flex-grow-1"
label-for="iteration-title"
:state="isValidTitle"
:invalid-feedback="invalidFeedback"
>
<gl-form-input
id="iteration-title"
v-model="title"
autocomplete="off"
data-qa-selector="iteration_title_field"
:state="isValidTitle"
required
/>
</gl-form-group>
<div class="form-group row">
<div class="col-form-label col-sm-2">
<label for="iteration-description">{{ __('Description') }}</label>
</div>
<div class="col-sm-10">
<markdown-field
:markdown-preview-path="previewMarkdownPath"
:can-attach-file="false"
:enable-autocomplete="true"
label="Description"
:textarea-value="description"
markdown-docs-path="/help/user/markdown"
:add-spacing-classes="false"
class="md-area"
>
<template #textarea>
<textarea
id="iteration-description"
v-model="description"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
:aria-label="__('Description')"
data-qa-selector="iteration_description_field"
>
</textarea>
</template>
</markdown-field>
</div>
</div>
<gl-form-group :label="__('Description')" label-for="iteration-description">
<markdown-field
:markdown-preview-path="previewMarkdownPath"
:can-attach-file="false"
:enable-autocomplete="true"
label="__('Description')"
:textarea-value="description"
markdown-docs-path="/help/user/markdown"
:add-spacing-classes="false"
class="md-area"
>
<template #textarea>
<textarea
id="iteration-description"
v-model="description"
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
:aria-label="__('Description')"
data-qa-selector="iteration_description_field"
>
</textarea>
</template>
</markdown-field>
</gl-form-group>
</div>
<div class="col-md-6">
<div class="form-group row">
<div class="col-form-label col-sm-2">
<label for="iteration-start-date">{{ __('Start date') }}</label>
</div>
<div class="col-sm-10">
<gl-form-input
<gl-form-group
:label="__('Start date')"
:state="isValidStartDate"
:invalid-feedback="invalidFeedback"
>
<div class="gl-display-inline-block gl-mr-2">
<gl-datepicker
id="iteration-start-date"
v-model="startDate"
class="datepicker form-control"
:placeholder="__('Select start date')"
autocomplete="off"
data-qa-selector="iteration_start_date_field"
@change="updateStartDate"
:state="isValidStartDate"
required
/>
<a class="inline float-right gl-mt-2 js-clear-start-date" href="#">{{
__('Clear start date')
}}</a>
</div>
</div>
<div class="form-group row">
<div class="col-form-label col-sm-2">
<label for="iteration-due-date">{{ __('Due date') }}</label>
</div>
<div class="col-sm-10">
<gl-form-input
id="iteration-due-date"
v-model="dueDate"
class="datepicker form-control"
:placeholder="__('Select due date')"
autocomplete="off"
data-qa-selector="iteration_due_date_field"
@change="updateDueDate"
/>
<a class="inline float-right gl-mt-2 js-clear-due-date" href="#">{{
__('Clear due date')
}}</a>
<gl-button
v-show="startDate"
variant="link"
class="gl-white-space-nowrap"
@click="updateStartDate(null)"
>
{{ __('Clear start date') }}
</gl-button>
</gl-form-group>
<gl-form-group :label="__('Due date')">
<div class="gl-display-inline-block gl-mr-2">
<gl-datepicker id="iteration-due-date" v-model="dueDate" />
</div>
</div>
<gl-button
v-show="dueDate"
variant="link"
class="gl-white-space-nowrap"
@click="updateDueDate(null)"
>
{{ __('Clear due date') }}
</gl-button>
</gl-form-group>
</div>
</gl-form>
......
......@@ -19,8 +19,8 @@ describe('Iteration Form', () => {
id: `gid://gitlab/Iteration/${id}`,
title: 'An iteration',
description: 'The words',
startDate: '2020-06-28',
dueDate: '2020-07-05',
startDate: new Date('2020-06-28'),
dueDate: new Date('2020-07-05'),
};
const createMutationSuccess = { data: { createIteration: { iteration, errors: [] } } };
......@@ -88,8 +88,11 @@ describe('Iteration Form', () => {
findTitle().vm.$emit('input', title);
findDescription().setValue(description);
findStartDate().vm.$emit('input', startDate);
findDueDate().vm.$emit('input', dueDate);
findStartDate().vm.$emit('input', startDate ? new Date(startDate) : null);
findDueDate().vm.$emit('input', dueDate ? new Date(dueDate) : null);
findTitle().trigger('change');
findStartDate().trigger('change');
clickSave();
......@@ -110,6 +113,19 @@ describe('Iteration Form', () => {
it('redirects to Iteration page on success', async () => {
createComponent();
const title = 'Iteration 5';
const description = 'The fifth iteration';
const startDate = '2020-05-05';
const dueDate = '2020-05-25';
findTitle().vm.$emit('input', title);
findDescription().setValue(description);
findStartDate().vm.$emit('input', startDate ? new Date(startDate) : null);
findDueDate().vm.$emit('input', dueDate ? new Date(dueDate) : null);
findTitle().trigger('change');
findStartDate().trigger('change');
clickSave();
await nextTick();
......@@ -117,6 +133,20 @@ describe('Iteration Form', () => {
expect(visitUrl).toHaveBeenCalled();
});
it('validates required fields and sets isValid state to false', async () => {
createComponent();
clickSave();
await nextTick();
expect(findSaveButton().props('loading')).toBe(false);
expect(wrapper.vm.checkValidations()).toBe(false);
expect(wrapper.vm.isValidTitle).toBe(false);
expect(wrapper.vm.isValidStartDate).toBe(false);
expect(visitUrl).not.toHaveBeenCalled();
});
it('loading=false on error', () => {
createComponent({ mutationResult: createMutationFailure });
......@@ -149,10 +179,13 @@ describe('Iteration Form', () => {
props: propsWithIteration,
});
const startDate = new Date(findStartDate().attributes('value'));
const dueDate = new Date(findDueDate().attributes('value'));
expect(findTitle().attributes('value')).toBe(iteration.title);
expect(findDescription().element.value).toBe(iteration.description);
expect(findStartDate().attributes('value')).toBe(iteration.startDate);
expect(findDueDate().attributes('value')).toBe(iteration.dueDate);
expect(startDate).toEqual(iteration.startDate);
expect(dueDate).toEqual(iteration.dueDate);
});
it('shows update text on submit button', () => {
......@@ -175,8 +208,8 @@ describe('Iteration Form', () => {
findTitle().vm.$emit('input', title);
findDescription().setValue(description);
findStartDate().vm.$emit('input', startDate);
findDueDate().vm.$emit('input', dueDate);
findStartDate().vm.$emit('input', startDate ? new Date(startDate) : null);
findDueDate().vm.$emit('input', dueDate ? new Date(dueDate) : null);
clickSave();
......@@ -220,6 +253,27 @@ describe('Iteration Form', () => {
expect(wrapper.emitted('updated')).toBeUndefined();
});
it('validates required fields and sets isValid state to false', async () => {
createComponent({
props: propsWithIteration,
});
findTitle().vm.$emit('input', '');
findStartDate().vm.$emit('input', null);
findTitle().trigger('change');
findStartDate().trigger('change');
clickSave();
await nextTick();
expect(findSaveButton().props('loading')).toBe(false);
expect(wrapper.vm.checkValidations()).toBe(false);
expect(wrapper.vm.isValidTitle).toBe(false);
expect(wrapper.vm.isValidStartDate).toBe(false);
expect(visitUrl).not.toHaveBeenCalled();
});
it('emits cancel when cancel clicked', async () => {
createComponent({
props: propsWithIteration,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment