Commit 2b98addb authored by Axel García's avatar Axel García

Update epic form to use stacked form groups

This also updates tests and strings accordingly
parent ec86096e
<script> <script>
import { GlButton, GlDatepicker, GlForm, GlFormCheckbox, GlFormInput } from '@gitlab/ui'; import {
GlButton,
GlDatepicker,
GlForm,
GlFormCheckbox,
GlFormGroup,
GlFormInput,
} from '@gitlab/ui';
import { visitUrl, joinPaths } from '~/lib/utils/url_utility'; import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -14,6 +21,7 @@ export default { ...@@ -14,6 +21,7 @@ export default {
GlForm, GlForm,
GlFormCheckbox, GlFormCheckbox,
GlFormInput, GlFormInput,
GlFormGroup,
MarkdownField, MarkdownField,
LabelsSelectVue, LabelsSelectVue,
}, },
...@@ -81,16 +89,15 @@ export default { ...@@ -81,16 +89,15 @@ export default {
const { errors, epic } = data.createEpic; const { errors, epic } = data.createEpic;
if (errors?.length > 0) { if (errors?.length > 0) {
createFlash(errors[0]); createFlash(errors[0]);
this.loading = false;
return; return;
} }
visitUrl(epic.webUrl); visitUrl(epic.webUrl);
}) })
.catch(() => { .catch(() => {
createFlash(__('Unable to save epic. Please try again'));
})
.finally(() => {
this.loading = false; this.loading = false;
createFlash(__('Unable to save epic. Please try again'));
}); });
}, },
updateDueDate(val) { updateDueDate(val) {
...@@ -118,26 +125,18 @@ export default { ...@@ -118,26 +125,18 @@ export default {
<div> <div>
<h3 class="page-title">{{ __('New Epic') }}</h3> <h3 class="page-title">{{ __('New Epic') }}</h3>
<hr /> <hr />
<gl-form class="common-note-form"> <gl-form class="common-note-form" @submit="save">
<div class="form-group row"> <gl-form-group :label="__('Title')" label-for="epic-title">
<div class="col-form-label col-sm-2">
<label for="epic-title">{{ __('Title') }}</label>
</div>
<div class="col-sm-10">
<gl-form-input <gl-form-input
id="epic-title" id="epic-title"
v-model="title" v-model="title"
:placeholder="__('Title')" :placeholder="__('Enter a title for your epic')"
autocomplete="off" autocomplete="off"
autofocus autofocus
/> />
</div> </gl-form-group>
</div>
<div class="form-group row"> <gl-form-group :label="__('Description')" label-for="epic-description">
<div class="col-form-label col-sm-2">
<label for="epic-description">{{ __('Description') }}</label>
</div>
<div class="col-sm-10">
<markdown-field <markdown-field
:markdown-preview-path="markdownPreviewPath" :markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath" :markdown-docs-path="markdownDocsPath"
...@@ -162,25 +161,18 @@ export default { ...@@ -162,25 +161,18 @@ export default {
</textarea> </textarea>
</template> </template>
</markdown-field> </markdown-field>
</div> </gl-form-group>
</div> <gl-form-group :label="__('Confidentiality')" label-for="epic-confidentiality">
<div class="form-group row gl-mt-7"> <gl-form-checkbox id="epic-confidentiality" v-model="confidential">{{
<div class="col-sm-10 offset-sm-2">
<gl-form-checkbox v-model="confidential" data-testid="epic-confidentiality">{{
__( __(
'This epic and any containing child epics are confidential and should only be visible to team members with at least Reporter access.', 'This epic and any containing child epics are confidential' +
' and should only be visible to team members' +
' with at least Reporter access.',
) )
}}</gl-form-checkbox> }}</gl-form-checkbox>
</div> </gl-form-group>
</div>
<hr /> <hr />
<div class="row"> <gl-form-group :label="__('Labels')">
<div class="col-lg-6">
<div class="form-group row">
<div class="col-form-label col-md-2 col-lg-4">
<label>{{ __('Labels') }}</label>
</div>
<div class="col-md-8 col-sm-10">
<div class="issuable-form-select-holder"> <div class="issuable-form-select-holder">
<labels-select-vue <labels-select-vue
:allow-label-edit="false" :allow-label-edit="false"
...@@ -194,76 +186,69 @@ export default { ...@@ -194,76 +186,69 @@ export default {
:labels-manage-path="groupUrl('labels')" :labels-manage-path="groupUrl('labels')"
:labels-filter-base-path="groupUrl('epics')" :labels-filter-base-path="groupUrl('epics')"
:labels-list-title="__('Select label')" :labels-list-title="__('Select label')"
:dropdown-button-text="__('Labels')" :dropdown-button-text="__('Choose labels')"
variant="embedded" variant="embedded"
class="block labels js-labels-block" class="block labels js-labels-block"
@updateSelectedLabels="handleUpdateSelectedLabels" @updateSelectedLabels="handleUpdateSelectedLabels"
>{{ __('None') }}</labels-select-vue
> >
{{ __('None') }}
</labels-select-vue>
</div> </div>
</div> </gl-form-group>
</div> <gl-form-group
</div> :label="__('Start date')"
<div class="col-lg-6"> :description="__('Leave empty to inherit from milestone dates')"
<div class="form-group row"> >
<div class="col-form-label col-md-2 col-lg-4"> <div class="gl-display-inline-block gl-mr-2">
<label>{{ __('Start date') }}</label>
</div>
<div class="col-md-8 col-sm-10">
<div class="issuable-form-select-holder gl-mr-2">
<gl-datepicker v-model="startDateFixed" data-testid="epic-start-date" /> <gl-datepicker v-model="startDateFixed" data-testid="epic-start-date" />
</div> </div>
<a <gl-button
v-show="startDateFixed" v-show="startDateFixed"
variant="link"
class="gl-white-space-nowrap" class="gl-white-space-nowrap"
data-testid="clear-start-date" data-testid="clear-start-date"
href="#"
@click="updateStartDate(null)" @click="updateStartDate(null)"
>{{ __('Clear start date') }}</a
> >
<span class="block gl-line-height-normal gl-mt-3 gl-text-gray-500">{{ {{ __('Clear start date') }}
__('Leave empty to inherit from milestone dates') </gl-button>
}}</span> </gl-form-group>
</div> <gl-form-group
</div> :label="__('Due date')"
<div class="form-group row"> :description="__('Leave empty to inherit from milestone dates')"
<div class="col-form-label col-md-2 col-lg-4"> >
<label>{{ __('Due Date') }}</label> <div class="gl-display-inline-block gl-mr-2">
</div>
<div class="col-md-8 col-sm-10">
<div class="issuable-form-select-holder gl-mr-2">
<gl-datepicker v-model="dueDateFixed" data-testid="epic-due-date" /> <gl-datepicker v-model="dueDateFixed" data-testid="epic-due-date" />
</div> </div>
<a <gl-button
v-show="dueDateFixed" v-show="dueDateFixed"
variant="link"
class="gl-white-space-nowrap" class="gl-white-space-nowrap"
data-testid="clear-due-date" data-testid="clear-due-date"
href="#"
@click="updateDueDate(null)" @click="updateDueDate(null)"
>{{ __('Clear due date') }}</a
> >
<span class="block gl-line-height-normal gl-mt-3 gl-text-gray-500">{{ {{ __('Clear due date') }}
__('Leave empty to inherit from milestone dates') </gl-button>
}}</span> </gl-form-group>
</div>
</div>
</div>
</div>
</gl-form>
<div class="footer-block row-content-block gl-display-flex"> <div class="footer-block row-content-block gl-display-flex">
<gl-button <gl-button
:loading="loading" type="submit"
data-testid="save-epic"
variant="success" variant="success"
:loading="loading"
:disabled="!title" :disabled="!title"
@click="save" data-testid="save-epic"
> >
{{ __('Create epic') }} {{ __('Create epic') }}
</gl-button> </gl-button>
<gl-button class="gl-ml-auto" data-testid="cancel-epic" :href="groupEpicsPath">{{ <gl-button
__('Cancel') type="button"
}}</gl-button> class="gl-ml-auto"
data-testid="cancel-epic"
:href="groupEpicsPath"
>
{{ __('Cancel') }}
</gl-button>
</div> </div>
</gl-form>
</div> </div>
</template> </template>
...@@ -97,6 +97,10 @@ ...@@ -97,6 +97,10 @@
} }
} }
.issuable-form-select-holder {
width: $gl-dropdown-width;
}
.labels-select-wrapper.is-embedded { .labels-select-wrapper.is-embedded {
.labels-select-dropdown-button { .labels-select-dropdown-button {
@include gl-bg-white; @include gl-bg-white;
...@@ -130,6 +134,7 @@ ...@@ -130,6 +134,7 @@
bottom: 100%; bottom: 100%;
width: 300px !important; width: 300px !important;
max-height: none;
margin-bottom: $gl-spacing-scale-6 !important; margin-bottom: $gl-spacing-scale-6 !important;
a:not(.btn) { a:not(.btn) {
...@@ -137,6 +142,11 @@ ...@@ -137,6 +142,11 @@
} }
} }
.dropdown-title {
padding-top: $gl-spacing-scale-2 !important;
padding-bottom: $gl-spacing-scale-4 !important;
}
.dropdown-footer .list-unstyled { .dropdown-footer .list-unstyled {
@include gl-m-0; @include gl-m-0;
} }
......
...@@ -41,7 +41,7 @@ describe('ee/epic/components/epic_form.vue', () => { ...@@ -41,7 +41,7 @@ describe('ee/epic/components/epic_form.vue', () => {
const findTitle = () => wrapper.find('#epic-title'); const findTitle = () => wrapper.find('#epic-title');
const findDescription = () => wrapper.find('#epic-description'); const findDescription = () => wrapper.find('#epic-description');
const findLabels = () => wrapper.find(LabelsSelectVue); const findLabels = () => wrapper.find(LabelsSelectVue);
const findConfidentialityCheck = () => wrapper.find('[data-testid="epic-confidentiality"]'); const findConfidentialityCheck = () => wrapper.find('#epic-confidentiality');
const findStartDate = () => wrapper.find('[data-testid="epic-start-date"]'); const findStartDate = () => wrapper.find('[data-testid="epic-start-date"]');
const findStartDateReset = () => wrapper.find('[data-testid="clear-start-date"]'); const findStartDateReset = () => wrapper.find('[data-testid="clear-start-date"]');
const findDueDate = () => wrapper.find('[data-testid="epic-due-date"]'); const findDueDate = () => wrapper.find('[data-testid="epic-due-date"]');
...@@ -75,14 +75,16 @@ describe('ee/epic/components/epic_form.vue', () => { ...@@ -75,14 +75,16 @@ describe('ee/epic/components/epic_form.vue', () => {
expect(wrapper.vm[field]).toBeTruthy(); expect(wrapper.vm[field]).toBeTruthy();
findResetter().trigger('click'); findResetter().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.vm[field]).toBe(null); expect(wrapper.vm[field]).toBe(null);
}); });
}); });
});
describe('save', () => { describe('save', () => {
it('submits successfully if form data is provided', () => { it('submits successfully if form data is provided', async () => {
createWrapper(); createWrapper();
const addLabelIds = [1]; const addLabelIds = [1];
...@@ -101,7 +103,7 @@ describe('ee/epic/components/epic_form.vue', () => { ...@@ -101,7 +103,7 @@ describe('ee/epic/components/epic_form.vue', () => {
findStartDate().vm.$emit('input', startDateFixed); findStartDate().vm.$emit('input', startDateFixed);
findDueDate().vm.$emit('input', dueDateFixed); findDueDate().vm.$emit('input', dueDateFixed);
findSaveButton().vm.$emit('click'); wrapper.vm.save();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({ expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createEpic, mutation: createEpic,
...@@ -122,18 +124,18 @@ describe('ee/epic/components/epic_form.vue', () => { ...@@ -122,18 +124,18 @@ describe('ee/epic/components/epic_form.vue', () => {
}); });
it.each` it.each`
status | result status | result | loading
${'succeeds'} | ${TEST_NEW_EPIC} ${'succeeds'} | ${TEST_NEW_EPIC} | ${true}
${'fails'} | ${TEST_FAILED} ${'fails'} | ${TEST_FAILED} | ${false}
`('resets loading indicator when $status', ({ result }) => { `('resets loading indicator when $status', ({ result, loading }) => {
createWrapper({ mutationResult: result }); createWrapper({ mutationResult: result });
const savePromise = wrapper.vm.save(); const savePromise = wrapper.vm.save();
expect(wrapper.vm.loading).toBeTruthy(); expect(wrapper.vm.loading).toBe(true);
return savePromise.then(() => { return savePromise.then(() => {
expect(findSaveButton().props('loading')).toBe(false); expect(findSaveButton().props('loading')).toBe(loading);
}); });
}); });
}); });
......
...@@ -4598,6 +4598,9 @@ msgstr "" ...@@ -4598,6 +4598,9 @@ msgstr ""
msgid "Choose file…" msgid "Choose file…"
msgstr "" msgstr ""
msgid "Choose labels"
msgstr ""
msgid "Choose the top-level group for your repository imports." msgid "Choose the top-level group for your repository imports."
msgstr "" msgstr ""
...@@ -8868,6 +8871,9 @@ msgstr "" ...@@ -8868,6 +8871,9 @@ msgstr ""
msgid "Enter a number" msgid "Enter a number"
msgstr "" msgstr ""
msgid "Enter a title for your epic"
msgstr ""
msgid "Enter a whole number between 0 and 100" msgid "Enter a whole number between 0 and 100"
msgstr "" msgstr ""
...@@ -20966,6 +20972,9 @@ msgstr "" ...@@ -20966,6 +20972,9 @@ msgstr ""
msgid "Select health status" msgid "Select health status"
msgstr "" msgstr ""
msgid "Select label"
msgstr ""
msgid "Select labels" msgid "Select labels"
msgstr "" msgstr ""
......
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