Commit e1361e31 authored by Samantha Ming's avatar Samantha Ming Committed by Paul Slaughter

Refactored recursive part of file_row to file_tree

**Note:**
- Also, introduces some components which will be used for
  IDE and diff row specific things.

https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
parent e342328a
<script>
/**
* This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue`
* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
*/
import FileRow from '~/vue_shared/components/file_row.vue';
export default {
components: {
FileRow,
},
};
</script>
<template>
<file-row v-bind="$attrs" v-on="$listeners" />
</template>
......@@ -3,7 +3,8 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
import FileTree from '~/vue_shared/components/file_tree.vue';
import DiffFileRow from './diff_file_row.vue';
import FileRowStats from './file_row_stats.vue';
export default {
......@@ -12,7 +13,7 @@ export default {
},
components: {
Icon,
FileRow,
FileTree,
},
props: {
hideFileStats: {
......@@ -61,6 +62,7 @@ export default {
searchPlaceholder: sprintf(s__('MergeRequest|Search files (%{modifier_key}P)'), {
modifier_key: /Mac/i.test(navigator.userAgent) ? '' : 'Ctrl+',
}),
DiffFileRow,
};
</script>
......@@ -91,7 +93,7 @@ export default {
</div>
<div :class="{ 'pt-0 tree-list-blobs': !renderTreeList }" class="tree-list-scroll">
<template v-if="filteredTreeList.length">
<file-row
<file-tree
v-for="file in filteredTreeList"
:key="file.key"
:file="file"
......@@ -99,6 +101,7 @@ export default {
:hide-extra-on-tree="true"
:extra-component="fileRowExtraComponent"
:show-changed-icon="true"
:file-row-component="$options.DiffFileRow"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile"
/>
......
<script>
/**
* This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue`
* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
*/
import FileRow from '~/vue_shared/components/file_row.vue';
export default {
components: {
FileRow,
},
};
</script>
<template>
<file-row v-bind="$attrs" v-on="$listeners" />
</template>
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
import { GlSkeletonLoading } from '@gitlab/ui';
import FileRow from '~/vue_shared/components/file_row.vue';
import FileTree from '~/vue_shared/components/file_tree.vue';
import IdeFileRow from './ide_file_row.vue';
import NavDropdown from './nav_dropdown.vue';
import FileRowExtra from './file_row_extra.vue';
......@@ -9,7 +10,7 @@ export default {
components: {
GlSkeletonLoading,
NavDropdown,
FileRow,
FileTree,
},
props: {
viewerType: {
......@@ -36,6 +37,7 @@ export default {
...mapActions(['updateViewer', 'toggleTreeOpen']),
},
FileRowExtra,
IdeFileRow,
};
</script>
......@@ -53,12 +55,13 @@ export default {
</header>
<div class="ide-tree-body h-100">
<template v-if="currentTree.tree.length">
<file-row
<file-tree
v-for="file in currentTree.tree"
:key="file.key"
:file="file"
:level="0"
:extra-component="$options.FileRowExtra"
:file-row-component="$options.IdeFileRow"
@toggleTreeOpen="toggleTreeOpen"
/>
</template>
......
......@@ -62,9 +62,6 @@ export default {
'is-open': this.file.opened,
};
},
childFilesLevel() {
return this.file.isHeader ? 0 : this.level + 1;
},
},
watch: {
'file.active': function fileActiveWatch(active) {
......@@ -131,53 +128,38 @@ export default {
</script>
<template>
<div>
<file-header v-if="file.isHeader" :path="file.path" />
<div
v-else
:class="fileClass"
:title="file.name"
class="file-row"
role="button"
@click="clickFile"
@mouseleave="toggleDropdown(false)"
>
<div class="file-row-name-container">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated">
<file-icon
v-if="!showChangedIcon || file.type === 'tree'"
class="file-row-icon"
:file-name="file.name"
:loading="file.loading"
:folder="isTree"
:opened="file.opened"
:size="16"
/>
<changed-file-icon v-else :file="file" :size="16" class="append-right-5" />
{{ file.name }}
</span>
<component
:is="extraComponent"
v-if="extraComponent && !(hideExtraOnTree && file.type === 'tree')"
:file="file"
:dropdown-open="dropdownOpen"
@toggle="toggleDropdown($event)"
<file-header v-if="file.isHeader" :path="file.path" />
<div
v-else
:class="fileClass"
:title="file.name"
class="file-row"
role="button"
@click="clickFile"
@mouseleave="toggleDropdown(false)"
>
<div class="file-row-name-container">
<span ref="textOutput" :style="levelIndentation" class="file-row-name str-truncated">
<file-icon
v-if="!showChangedIcon || file.type === 'tree'"
class="file-row-icon"
:file-name="file.name"
:loading="file.loading"
:folder="isTree"
:opened="file.opened"
:size="16"
/>
</div>
</div>
<template v-if="file.opened || file.isHeader">
<file-row
v-for="childFile in file.tree"
:key="childFile.key"
:file="childFile"
:level="childFilesLevel"
:hide-extra-on-tree="hideExtraOnTree"
:extra-component="extraComponent"
:show-changed-icon="showChangedIcon"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="clickedFile"
<changed-file-icon v-else :file="file" :size="16" class="append-right-5" />
{{ file.name }}
</span>
<component
:is="extraComponent"
v-if="extraComponent && !(hideExtraOnTree && file.type === 'tree')"
:file="file"
:dropdown-open="dropdownOpen"
@toggle="toggleDropdown($event)"
/>
</template>
</div>
</div>
</template>
......
<script>
export default {
name: 'FileTree',
props: {
fileRowComponent: {
type: Object,
required: true,
},
level: {
type: Number,
required: true,
},
file: {
type: Object,
required: true,
},
},
computed: {
childFilesLevel() {
return this.file.isHeader ? 0 : this.level + 1;
},
},
};
</script>
<template>
<div>
<component
:is="fileRowComponent"
:level="level"
:file="file"
v-bind="$attrs"
v-on="$listeners"
/>
<template v-if="file.opened || file.isHeader">
<file-tree
v-for="childFile in file.tree"
:key="childFile.key"
:file-row-component="fileRowComponent"
:level="childFilesLevel"
:file="childFile"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
</div>
</template>
import { shallowMount } from '@vue/test-utils';
import DiffFileRow from '~/diffs/components/diff_file_row.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
describe('Diff File Row component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(DiffFileRow, {
propsData: { ...props },
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders file row component', () => {
createComponent({
level: 4,
file: {},
});
expect(wrapper.find(FileRow).exists()).toEqual(true);
});
});
import { shallowMount } from '@vue/test-utils';
import IdeFileRow from '~/ide/components/ide_file_row.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
describe('Ide File Row component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(IdeFileRow, {
propsData: { ...props },
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders file row component', () => {
createComponent({
level: 4,
file: {},
});
expect(wrapper.find(FileRow).exists()).toEqual(true);
});
});
import { pick } from 'lodash';
import { shallowMount } from '@vue/test-utils';
import FileTree from '~/vue_shared/components/file_tree.vue';
const MockFileRow = {
name: 'MockFileRow',
render() {
return this.$slots.default;
},
};
const TEST_LEVEL = 4;
const TEST_EXTA_ARGS = {
foo: 'lorem-ipsum',
bar: 'zoo',
};
describe('File Tree component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(FileTree, {
propsData: { level: TEST_LEVEL, fileRowComponent: MockFileRow, ...props },
attrs: { ...TEST_EXTA_ARGS },
});
};
const findFileRow = () => wrapper.find(MockFileRow);
const findChildrenTrees = () => wrapper.findAll(FileTree).wrappers.slice(1);
const findChildrenTreeProps = () =>
findChildrenTrees().map(x => ({
...x.props(),
...pick(x.attributes(), Object.keys(TEST_EXTA_ARGS)),
}));
afterEach(() => {
wrapper.destroy();
});
describe('file row component', () => {
beforeEach(() => {
createComponent({ file: {} });
});
it('renders file row component', () => {
expect(findFileRow().exists()).toEqual(true);
});
it('contains the required attribute keys', () => {
const fileRow = findFileRow();
// Checking strings b/c value in attributes are always strings
expect(fileRow.attributes()).toEqual({
file: {}.toString(),
level: TEST_LEVEL.toString(),
...TEST_EXTA_ARGS,
});
});
});
describe('file tree', () => {
const createChildren = () => [{ id: 1 }, { id: 2 }];
const createChildrenExpectation = (props = {}) =>
createChildren().map(file => ({
fileRowComponent: MockFileRow,
file,
...TEST_EXTA_ARGS,
...props,
}));
it.each`
key | value | desc | expectedChildren
${'isHeader'} | ${true} | ${'is shown if file is header'} | ${createChildrenExpectation({ level: 0 })}
${'opened'} | ${true} | ${'is shown if file is open'} | ${createChildrenExpectation({ level: TEST_LEVEL + 1 })}
${'isHeader'} | ${false} | ${'is hidden if file is header'} | ${[]}
${'opened'} | ${false} | ${'is hidden if file is open'} | ${[]}
`('$desc', ({ key, value, expectedChildren }) => {
createComponent({
file: {
[key]: value,
tree: createChildren(),
},
});
expect(findChildrenTreeProps()).toEqual(expectedChildren);
});
});
});
......@@ -19,7 +19,6 @@ describe('File row component', () => {
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', () => {
createComponent({
......@@ -42,7 +41,7 @@ describe('File row component', () => {
});
spyOn(vm, '$emit').and.stub();
vm.$el.querySelector('.file-row').click();
vm.$el.click();
expect(vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', vm.file.path);
});
......@@ -87,7 +86,7 @@ describe('File row component', () => {
level: 0,
});
expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null);
expect(vm.$el.classList).toContain('js-file-row-header');
});
describe('new dropdown', () => {
......@@ -138,7 +137,7 @@ describe('File row component', () => {
});
it('closes when row triggers mouseleave', () => {
findFileRow().dispatchEvent(new Event('mouseleave'));
vm.$el.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