Commit 70c770a1 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'kp-add-issuable-show-app-edit-form' into 'master'

Issuable Show: Add IssuableEditForm component

See merge request gitlab-org/gitlab!44737
parents c5017785 dce812bd
<script>
import $ from 'jquery';
import { GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import Autosave from '~/autosave';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import eventHub from '../event_hub';
export default {
components: {
GlForm,
GlFormGroup,
GlFormInput,
MarkdownField,
},
props: {
issuable: {
type: Object,
required: true,
},
enableAutocomplete: {
type: Boolean,
required: true,
},
descriptionPreviewPath: {
type: String,
required: true,
},
descriptionHelpPath: {
type: String,
required: true,
},
},
data() {
const { title, description } = this.issuable;
return {
title,
description,
};
},
created() {
eventHub.$on('update.issuable', this.resetAutosave);
eventHub.$on('close.form', this.resetAutosave);
},
mounted() {
this.initAutosave();
},
beforeDestroy() {
eventHub.$off('update.issuable', this.resetAutosave);
eventHub.$off('close.form', this.resetAutosave);
},
methods: {
initAutosave() {
const { titleInput, descriptionInput } = this.$refs;
if (!titleInput || !descriptionInput) return;
this.autosaveTitle = new Autosave($(titleInput.$el), [
document.location.pathname,
document.location.search,
'title',
]);
this.autosaveDescription = new Autosave($(descriptionInput.$el), [
document.location.pathname,
document.location.search,
'description',
]);
},
resetAutosave() {
this.autosaveTitle.reset();
this.autosaveDescription.reset();
},
},
};
</script>
<template>
<gl-form>
<gl-form-group
data-testid="title"
:label="__('Title')"
:label-sr-only="true"
label-for="issuable-title"
class="col-12"
>
<gl-form-input
id="issuable-title"
ref="titleInput"
v-model.trim="title"
:placeholder="__('Title')"
:aria-label="__('Title')"
:autofocus="true"
class="qa-title-input"
/>
</gl-form-group>
<gl-form-group
data-testid="description"
:label="__('Description')"
:label-sr-only="true"
label-for="issuable-description"
class="col-12 common-note-form"
>
<markdown-field
:markdown-preview-path="descriptionPreviewPath"
:markdown-docs-path="descriptionHelpPath"
:enable-autocomplete="enableAutocomplete"
:textarea-value="description"
>
<template #textarea>
<textarea
id="issuable-description"
ref="descriptionInput"
v-model="description"
:data-supports-quick-actions="enableAutocomplete"
:aria-label="__('Description')"
:placeholder="__('Write a comment or drag your files here…')"
class="note-textarea js-gfm-input js-autosize markdown-area
qa-description-textarea"
dir="auto"
></textarea>
</template>
</markdown-field>
</gl-form-group>
<div data-testid="actions" class="col-12 gl-mt-3 gl-mb-3 clearfix">
<slot
name="edit-form-actions"
:issuable-title="title"
:issuable-description="description"
></slot>
</div>
</gl-form>
</template>
import createEventHub from '~/helpers/event_hub_factory';
export default createEventHub();
import { shallowMount } from '@vue/test-utils';
import { GlFormInput } from '@gitlab/ui';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import IssuableEditForm from '~/issuable_show/components/issuable_edit_form.vue';
import IssuableEventHub from '~/issuable_show/event_hub';
import { mockIssuableShowProps, mockIssuable } from '../mock_data';
const issuableEditFormProps = {
issuable: mockIssuable,
...mockIssuableShowProps,
};
const createComponent = ({ propsData = issuableEditFormProps } = {}) =>
shallowMount(IssuableEditForm, {
propsData,
stubs: {
MarkdownField,
},
slots: {
'edit-form-actions': `
<button class="js-save">Save changes</button>
<button class="js-cancel">Cancel</button>
`,
},
});
describe('IssuableEditForm', () => {
let wrapper;
const assertEvent = eventSpy => {
expect(eventSpy).toHaveBeenNthCalledWith(1, 'update.issuable', expect.any(Function));
expect(eventSpy).toHaveBeenNthCalledWith(2, 'close.form', expect.any(Function));
};
beforeEach(() => {
wrapper = createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('created', () => {
it('binds `update.issuable` and `close.form` event listeners', () => {
const eventOnSpy = jest.spyOn(IssuableEventHub, '$on');
const wrapperTemp = createComponent();
assertEvent(eventOnSpy);
wrapperTemp.destroy();
});
});
describe('beforeDestroy', () => {
it('unbinds `update.issuable` and `close.form` event listeners', () => {
const wrapperTemp = createComponent();
const eventOffSpy = jest.spyOn(IssuableEventHub, '$off');
wrapperTemp.destroy();
assertEvent(eventOffSpy);
});
});
describe('methods', () => {
describe('initAutosave', () => {
it('initializes `autosaveTitle` and `autosaveDescription` props', () => {
expect(wrapper.vm.autosaveTitle).toBeDefined();
expect(wrapper.vm.autosaveDescription).toBeDefined();
});
});
describe('resetAutosave', () => {
it('calls `reset` on `autosaveTitle` and `autosaveDescription` props', () => {
jest.spyOn(wrapper.vm.autosaveTitle, 'reset').mockImplementation(jest.fn);
jest.spyOn(wrapper.vm.autosaveDescription, 'reset').mockImplementation(jest.fn);
wrapper.vm.resetAutosave();
expect(wrapper.vm.autosaveTitle.reset).toHaveBeenCalled();
expect(wrapper.vm.autosaveDescription.reset).toHaveBeenCalled();
});
});
});
describe('template', () => {
it('renders title input field', () => {
const titleInputEl = wrapper.find('[data-testid="title"]');
expect(titleInputEl.exists()).toBe(true);
expect(titleInputEl.find(GlFormInput).attributes()).toMatchObject({
'aria-label': 'Title',
placeholder: 'Title',
});
});
it('renders description textarea field', () => {
const descriptionEl = wrapper.find('[data-testid="description"]');
expect(descriptionEl.exists()).toBe(true);
expect(descriptionEl.find(MarkdownField).props()).toMatchObject({
markdownPreviewPath: issuableEditFormProps.descriptionPreviewPath,
markdownDocsPath: issuableEditFormProps.descriptionHelpPath,
enableAutocomplete: issuableEditFormProps.enableAutocomplete,
textareaValue: mockIssuable.description,
});
expect(descriptionEl.find('textarea').attributes()).toMatchObject({
'data-supports-quick-actions': 'true',
'aria-label': 'Description',
placeholder: 'Write a comment or drag your files here…',
});
});
it('renders form actions', () => {
const actionsEl = wrapper.find('[data-testid="actions"]');
expect(actionsEl.find('button.js-save').exists()).toBe(true);
expect(actionsEl.find('button.js-cancel').exists()).toBe(true);
});
});
});
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