Commit f9700381 authored by Eric Eastwood's avatar Eric Eastwood
parent c5999f7c
<script>
import eventHub from '../event_hub';
import IssueToken from './issue_token.vue';
export default {
name: 'AddIssuableForm',
props: {
inputValue: {
type: String,
required: true,
},
addButtonLabel: {
type: String,
required: true,
},
pendingIssuables: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
isInputFocused: false,
};
},
components: {
issueToken: IssueToken,
},
methods: {
onInput() {
const value = this.$refs.input.value;
eventHub.$emit('addIssuableFormInput', value, $(this.$refs.input).caret('pos'));
},
onFocus() {
this.isInputFocused = true;
},
onBlur() {
this.isInputFocused = false;
const value = this.$refs.input.value;
eventHub.$emit('addIssuableFormBlur', value);
},
onInputWrapperClick() {
this.$refs.input.focus();
},
onFormSubmit() {
eventHub.$emit('addIssuableFormSubmit');
},
onFormCancel() {
eventHub.$emit('addIssuableFormCancel');
},
},
mounted() {
const $input = $(this.$refs.input);
gl.GfmAutoComplete.setup($input, {
issues: true,
});
$input.on('inserted-issues.atwho', this.onInput);
},
beforeDestroy() {
const $input = $(this.$refs.input);
$input.off('inserted-issues.atwho', this.onInput);
},
};
</script>
<template>
<div>
<div
ref="issuableFormWrapper"
class="add-issuable-form-input-wrapper form-control"
:class="{ focus: isInputFocused }"
role="button"
@click="onInputWrapperClick">
<ul class="add-issuable-form-input-token-list">
<li
:key="issuable.reference"
v-for="issuable in pendingIssuables"
class="js-add-issuable-form-token-list-item add-issuable-form-token-list-item">
<issue-token
event-namespace="pendingIssuable"
:reference="issuable.reference"
:display-reference="issuable.displayReference"
:title="issuable.title"
:path="issuable.path"
:state="issuable.state"
:fetch-status="issuable.fetchStatus"
:can-remove="true" />
</li>
<li class="add-issuable-form-input-list-item">
<input
ref="input"
type="text"
class="add-issuable-form-input"
:value="inputValue"
placeholder="Search issues..."
@input="onInput"
@focus="onFocus"
@blur="onBlur" />
</li>
</ul>
</div>
<div class="clearfix prepend-top-10">
<button
ref="addButton"
type="button"
class="btn btn-new pull-left"
@click="onFormSubmit">
{{ addButtonLabel }}
</button>
<button
type="button"
class="btn btn-default pull-right"
@click="onFormCancel">
Cancel
</button>
</div>
</div>
</template>
......@@ -727,3 +727,42 @@
}
}
}
.add-issuable-form-input-wrapper {
height: auto;
padding: $gl-vert-padding $gl-vert-padding 0 $gl-input-padding;
&.focus,
&.focus:hover {
border-color: $dropdown-input-focus-border;
box-shadow: 0 0 4px $search-input-focus-shadow-color;
}
}
.add-issuable-form-input-token-list {
display: flex;
flex-wrap: wrap;
list-style: none;
margin-bottom: 0;
padding-left: 0;
}
.add-issuable-form-token-list-item {
margin-bottom: $gl-vert-padding;
margin-right: 1em;
}
.add-issuable-form-input-list-item {
flex: 1;
min-width: 200px;
margin-bottom: $gl-vert-padding;
}
.add-issuable-form-input {
width: 100%;
border: 0;
&:focus {
outline: none;
}
}
import Vue from 'vue';
import eventHub from '~/issuable/related_issues/event_hub';
import addIssuableForm from '~/issuable/related_issues/components/add_issuable_form.vue';
const issuable1 = {
reference: 'foo/bar#123',
title: 'some title',
path: '/foo/bar/issues/123',
state: 'opened',
};
const issuable2 = {
reference: 'foo/bar#124',
title: 'some other thing',
path: '/foo/bar/issues/124',
state: 'opened',
};
describe('AddIssuableForm', () => {
let AddIssuableForm;
let vm;
beforeEach(() => {
AddIssuableForm = Vue.extend(addIssuableForm);
});
afterEach(() => {
if (vm) {
vm.$destroy();
}
});
describe('with data', () => {
const inputValue = 'foo #123';
const addButtonLabel = 'Add issuable';
beforeEach(() => {
vm = new AddIssuableForm({
propsData: {
inputValue,
addButtonLabel,
pendingIssuables: [
issuable1,
issuable2,
],
},
}).$mount();
});
it('should put button label in place', () => {
expect(vm.$refs.addButton.textContent.trim()).toEqual(addButtonLabel);
});
it('should put input value in place', () => {
expect(vm.$refs.input.value).toEqual(inputValue);
});
it('should render pending issuables items', () => {
expect(vm.$el.querySelectorAll('.js-add-issuable-form-token-list-item').length).toEqual(2);
});
});
describe('methods', () => {
let addIssuableFormInputSpy;
let addIssuableFormBlurSpy;
let addIssuableFormSubmitSpy;
let addIssuableFormCancelSpy;
beforeEach(() => {
addIssuableFormInputSpy = jasmine.createSpy('spy');
addIssuableFormBlurSpy = jasmine.createSpy('spy');
addIssuableFormSubmitSpy = jasmine.createSpy('spy');
addIssuableFormCancelSpy = jasmine.createSpy('spy');
eventHub.$on('addIssuableFormInput', addIssuableFormInputSpy);
eventHub.$on('addIssuableFormBlur', addIssuableFormBlurSpy);
eventHub.$on('addIssuableFormSubmit', addIssuableFormSubmitSpy);
eventHub.$on('addIssuableFormCancel', addIssuableFormCancelSpy);
const el = document.createElement('div');
// We need to append to body to get focus tests working
document.body.appendChild(el);
vm = new AddIssuableForm({
propsData: {
inputValue: '',
addButtonLabel: 'Add issuable',
pendingIssuables: [
issuable1,
],
},
}).$mount(el);
spyOn(vm, 'onInputWrapperClick').and.callThrough();
});
afterEach(() => {
eventHub.$off('addIssuableFormInput', addIssuableFormInputSpy);
eventHub.$off('addIssuableFormBlur', addIssuableFormBlurSpy);
eventHub.$off('addIssuableFormSubmit', addIssuableFormSubmitSpy);
eventHub.$off('addIssuableFormCancel', addIssuableFormCancelSpy);
});
it('when clicking somewhere on the input wrapper should focus the input', () => {
expect(vm.onInputWrapperClick).not.toHaveBeenCalled();
vm.$refs.issuableFormWrapper.click();
Vue.nextTick(() => {
expect(vm.$refs.issuableFormWrapper.classList.contains('focus')).toEqual(true);
expect(vm.onInputWrapperClick).toHaveBeenCalled();
expect(document.activeElement).toEqual(vm.$refs.input);
});
});
it('when filling in the input', () => {
expect(addIssuableFormInputSpy).not.toHaveBeenCalled();
const newInputValue = 'filling in things';
vm.$refs.input.value = newInputValue;
vm.onInput();
expect(addIssuableFormInputSpy).toHaveBeenCalledWith(newInputValue, newInputValue.length);
});
it('when blurring the input', () => {
expect(addIssuableFormInputSpy).not.toHaveBeenCalled();
const newInputValue = 'filling in things';
vm.$refs.input.value = newInputValue;
vm.onBlur();
Vue.nextTick(() => {
expect(vm.$refs.issuableFormWrapper.classList.contains('focus')).toEqual(false);
expect(addIssuableFormBlurSpy).toHaveBeenCalledWith(newInputValue);
});
});
it('when submitting pending issues', () => {
expect(addIssuableFormSubmitSpy).not.toHaveBeenCalled();
vm.onFormSubmit();
expect(addIssuableFormSubmitSpy).toHaveBeenCalled();
});
it('when canceling form to collapse', () => {
expect(addIssuableFormCancelSpy).not.toHaveBeenCalled();
vm.onFormCancel();
expect(addIssuableFormCancelSpy).toHaveBeenCalled();
});
});
});
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