Commit c1a097e1 authored by Paul Slaughter's avatar Paul Slaughter

Refactor new_dropdown/index to receive isOpen prop

**Why?**

Previously it managed this as an internal data property, but there
was extra complexity involved because the component doesn't have
all the information it needs (i.e. the `mouseOver` prop).

This is a good opportunity to colocate the data prop with the component
that has the information to change it (i.e. `file_row`) and let
`new_dropdown/index` receive this value as a prop.
parent 9deaa79c
...@@ -23,7 +23,7 @@ export default { ...@@ -23,7 +23,7 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
mouseOver: { dropdownOpen: {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
...@@ -92,8 +92,9 @@ export default { ...@@ -92,8 +92,9 @@ export default {
<new-dropdown <new-dropdown
:type="file.type" :type="file.type"
:path="file.path" :path="file.path"
:mouse-over="mouseOver" :is-open="dropdownOpen"
class="prepend-left-8" class="prepend-left-8"
v-on="$listeners"
/> />
</div> </div>
</template> </template>
...@@ -21,38 +21,29 @@ export default { ...@@ -21,38 +21,29 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
mouseOver: { isOpen: {
type: Boolean, type: Boolean,
required: true, required: false,
default: false,
}, },
}, },
data() {
return {
dropdownOpen: false,
};
},
watch: { watch: {
dropdownOpen() { isOpen() {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.dropdownMenu.scrollIntoView({ this.$refs.dropdownMenu.scrollIntoView({
block: 'nearest', block: 'nearest',
}); });
}); });
}, },
mouseOver() {
if (!this.mouseOver) {
this.dropdownOpen = false;
}
},
}, },
methods: { methods: {
...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']), ...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']),
createNewItem(type) { createNewItem(type) {
this.openNewEntryModal({ type, path: this.path }); this.openNewEntryModal({ type, path: this.path });
this.dropdownOpen = false; this.$emit('toggle', false);
}, },
openDropdown() { openDropdown() {
this.dropdownOpen = !this.dropdownOpen; this.$emit('toggle', !this.isOpen);
}, },
}, },
modalTypes, modalTypes,
...@@ -63,7 +54,7 @@ export default { ...@@ -63,7 +54,7 @@ export default {
<div class="ide-new-btn"> <div class="ide-new-btn">
<div <div
:class="{ :class="{
show: dropdownOpen, show: isOpen,
}" }"
class="dropdown d-flex" class="dropdown d-flex"
> >
......
...@@ -39,7 +39,7 @@ export default { ...@@ -39,7 +39,7 @@ export default {
}, },
data() { data() {
return { return {
mouseOver: false, dropdownOpen: false,
}; };
}, },
computed: { computed: {
...@@ -123,8 +123,8 @@ export default { ...@@ -123,8 +123,8 @@ export default {
return this.$router.currentRoute.path === `/project${this.file.url}`; return this.$router.currentRoute.path === `/project${this.file.url}`;
}, },
toggleHover(over) { toggleDropdown(val) {
this.mouseOver = over; this.dropdownOpen = val;
}, },
}, },
}; };
...@@ -140,8 +140,7 @@ export default { ...@@ -140,8 +140,7 @@ export default {
class="file-row" class="file-row"
role="button" role="button"
@click="clickFile" @click="clickFile"
@mouseover="toggleHover(true)" @mouseleave="toggleDropdown(false)"
@mouseleave="toggleHover(false)"
> >
<div class="file-row-name-container"> <div class="file-row-name-container">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated"> <span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated">
...@@ -160,7 +159,8 @@ export default { ...@@ -160,7 +159,8 @@ export default {
:is="extraComponent" :is="extraComponent"
v-if="extraComponent && !(hideExtraOnTree && file.type === 'tree')" v-if="extraComponent && !(hideExtraOnTree && file.type === 'tree')"
:file="file" :file="file"
:mouse-over="mouseOver" :dropdown-open="dropdownOpen"
@toggle="toggleDropdown($event)"
/> />
</div> </div>
</div> </div>
......
...@@ -20,7 +20,7 @@ describe('IDE extra file row component', () => { ...@@ -20,7 +20,7 @@ describe('IDE extra file row component', () => {
file: { file: {
...file('test'), ...file('test'),
}, },
mouseOver: false, dropdownOpen: false,
}); });
spyOnProperty(vm, 'getUnstagedFilesCountForPath').and.returnValue(() => unstagedFilesCount); spyOnProperty(vm, 'getUnstagedFilesCountForPath').and.returnValue(() => unstagedFilesCount);
......
...@@ -56,11 +56,11 @@ describe('new dropdown component', () => { ...@@ -56,11 +56,11 @@ describe('new dropdown component', () => {
}); });
}); });
describe('dropdownOpen', () => { describe('isOpen', () => {
it('scrolls dropdown into view', done => { it('scrolls dropdown into view', done => {
spyOn(vm.$refs.dropdownMenu, 'scrollIntoView'); spyOn(vm.$refs.dropdownMenu, 'scrollIntoView');
vm.dropdownOpen = true; vm.isOpen = true;
setTimeout(() => { setTimeout(() => {
expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({ expect(vm.$refs.dropdownMenu.scrollIntoView).toHaveBeenCalledWith({
......
import Vue from 'vue'; import Vue from 'vue';
import FileRow from '~/vue_shared/components/file_row.vue'; import FileRow from '~/vue_shared/components/file_row.vue';
import FileRowExtra from '~/ide/components/file_row_extra.vue';
import { file } from 'spec/ide/helpers'; import { file } from 'spec/ide/helpers';
import mountComponent from '../../helpers/vue_mount_component_helper'; import mountComponent from '../../helpers/vue_mount_component_helper';
...@@ -16,6 +17,10 @@ describe('File row component', () => { ...@@ -16,6 +17,10 @@ describe('File row component', () => {
vm.$destroy(); vm.$destroy();
}); });
const findNewDropdown = () => vm.$el.querySelector('.ide-new-btn .dropdown');
const findNewDropdownButton = () => vm.$el.querySelector('.ide-new-btn .dropdown button');
const findFileRow = () => vm.$el.querySelector('.file-row');
it('renders name', () => { it('renders name', () => {
createComponent({ createComponent({
file: file('t4'), file: file('t4'),
...@@ -84,4 +89,59 @@ describe('File row component', () => { ...@@ -84,4 +89,59 @@ describe('File row component', () => {
expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null); expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null);
}); });
describe('new dropdown', () => {
beforeEach(() => {
createComponent({
file: file('t5'),
level: 1,
extraComponent: FileRowExtra,
});
});
it('renders in extra component', () => {
expect(findNewDropdown()).not.toBe(null);
});
it('is hidden at start', () => {
expect(findNewDropdown()).not.toHaveClass('show');
});
it('is opened when button is clicked', done => {
expect(vm.dropdownOpen).toBe(false);
findNewDropdownButton().dispatchEvent(new Event('click'));
vm.$nextTick()
.then(() => {
expect(vm.dropdownOpen).toBe(true);
expect(findNewDropdown()).toHaveClass('show');
})
.then(done)
.catch(done.fail);
});
describe('when opened', () => {
beforeEach(() => {
vm.dropdownOpen = true;
});
it('stays open when button triggers mouseout', () => {
findNewDropdownButton().dispatchEvent(new Event('mouseout'));
expect(vm.dropdownOpen).toBe(true);
});
it('stays open when button triggers mouseleave', () => {
findNewDropdownButton().dispatchEvent(new Event('mouseleave'));
expect(vm.dropdownOpen).toBe(true);
});
it('closes when row triggers mouseleave', () => {
findFileRow().dispatchEvent(new Event('mouseleave'));
expect(vm.dropdownOpen).toBe(false);
});
});
});
}); });
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