Commit e5e9466c authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch 'repo-list-row-component' into 'master'

Added tree list row component

See merge request gitlab-org/gitlab-ce!28542
parents 0c7300cd 5ddedb6b
......@@ -4,11 +4,13 @@ import { sprintf, __ } from '../../../locale';
import getRefMixin from '../../mixins/get_ref';
import getFiles from '../../queries/getFiles.graphql';
import TableHeader from './header.vue';
import TableRow from './row.vue';
export default {
components: {
GlLoadingIcon,
TableHeader,
TableRow,
},
mixins: [getRefMixin],
apollo: {
......@@ -57,7 +59,15 @@ export default {
}}
</caption>
<table-header />
<tbody></tbody>
<tbody>
<table-row
v-for="entry in files"
:id="entry.id"
:key="entry.id"
:path="entry.flatPath"
:type="entry.type"
/>
</tbody>
</table>
<gl-loading-icon v-if="isLoadingFiles" class="my-3" size="md" />
</div>
......
<script>
import { getIconName } from '../../utils/icon';
import getRefMixin from '../../mixins/get_ref';
export default {
mixins: [getRefMixin],
props: {
id: {
type: Number,
required: true,
},
path: {
type: String,
required: true,
},
type: {
type: String,
required: true,
},
},
computed: {
routerLinkTo() {
return this.isFolder ? { path: `/tree/${this.ref}/${this.path}` } : null;
},
iconName() {
return `fa-${getIconName(this.type, this.path)}`;
},
isFolder() {
return this.type === 'folder';
},
isSubmodule() {
return this.type === 'commit';
},
linkComponent() {
return this.isFolder ? 'router-link' : 'a';
},
},
methods: {
openRow() {
if (this.isFolder) {
this.$router.push(this.routerLinkTo);
}
},
},
};
</script>
<template>
<tr v-once :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component :is="linkComponent" :to="routerLinkTo" class="str-truncated">{{ path }}</component>
<template v-if="isSubmodule">
@ <a href="#" class="commit-sha">{{ id }}</a>
</template>
</td>
<td class="d-none d-sm-table-cell tree-commit"></td>
<td class="tree-time-ago text-right"></td>
</tr>
</template>
......@@ -7,7 +7,36 @@ Vue.use(VueApollo);
const defaultClient = createDefaultClient({
Query: {
files() {
return [];
return [
{
__typename: 'file',
id: 1,
name: 'app',
flatPath: 'app',
type: 'folder',
},
{
__typename: 'file',
id: 2,
name: 'gitlab-svg',
flatPath: 'gitlab-svg',
type: 'commit',
},
{
__typename: 'file',
id: 3,
name: 'index.js',
flatPath: 'index.js',
type: 'blob',
},
{
__typename: 'file',
id: 4,
name: 'test.pdf',
flatPath: 'fixtures/test.pdf',
type: 'blob',
},
];
},
},
});
......
......@@ -2,7 +2,7 @@
import FileTable from '../components/table/index.vue';
export default {
component: {
components: {
FileTable,
},
props: {
......
query getFiles($path: String!, $ref: String!) {
files(path: $path, ref: $ref) @client {
id
name
fullPath
flatPath
type
}
}
const entryTypeIcons = {
folder: 'folder',
commit: 'archive',
};
const fileTypeIcons = [
{ extensions: ['pdf'], name: 'file-pdf-o' },
{
extensions: [
'jpg',
'jpeg',
'jif',
'jfif',
'jp2',
'jpx',
'j2k',
'j2c',
'png',
'gif',
'tif',
'tiff',
'svg',
'ico',
'bmp',
],
name: 'file-image-o',
},
{
extensions: ['zip', 'zipx', 'tar', 'gz', 'bz', 'bzip', 'xz', 'rar', '7z'],
name: 'file-archive-o',
},
{ extensions: ['mp3', 'wma', 'ogg', 'oga', 'wav', 'flac', 'aac'], name: 'file-audio-o' },
{
extensions: [
'mp4',
'm4p',
'm4v',
'mpg',
'mp2',
'mpeg',
'mpe',
'mpv',
'm2v',
'avi',
'mkv',
'flv',
'ogv',
'mov',
'3gp',
'3g2',
],
name: 'file-video-o',
},
{ extensions: ['doc', 'dot', 'docx', 'docm', 'dotx', 'dotm', 'docb'], name: 'file-word-o' },
{
extensions: [
'xls',
'xlt',
'xlm',
'xlsx',
'xlsm',
'xltx',
'xltm',
'xlsb',
'xla',
'xlam',
'xll',
'xlw',
],
name: 'file-excel-o',
},
{
extensions: [
'ppt',
'pot',
'pps',
'pptx',
'pptm',
'potx',
'potm',
'ppam',
'ppsx',
'ppsm',
'sldx',
'sldm',
],
name: 'file-powerpoint-o',
},
];
// eslint-disable-next-line import/prefer-default-export
export const getIconName = (type, path) => {
if (entryTypeIcons[type]) return entryTypeIcons[type];
const extension = path.split('.').pop();
const file = fileTypeIcons.find(t => t.extensions.some(ext => ext === extension));
return file ? file.name : 'file-text-o';
};
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Repository table row component renders table row 1`] = `
<tr
class="tree-item file_1"
>
<td
class="tree-item-file-name"
>
<i
aria-label="file"
class="fa fa-fw fa-file-text-o"
role="img"
/>
<a
class="str-truncated"
>
test
</a>
<!---->
</td>
<td
class="d-none d-sm-table-cell tree-commit"
/>
<td
class="tree-time-ago text-right"
/>
</tr>
`;
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import TableRow from '~/repository/components/table/row.vue';
let vm;
let $router;
function factory(propsData = {}) {
$router = {
push: jest.fn(),
};
vm = shallowMount(TableRow, {
propsData,
mocks: {
$router,
},
stubs: {
RouterLink: RouterLinkStub,
},
});
vm.setData({ ref: 'master' });
}
describe('Repository table row component', () => {
afterEach(() => {
vm.destroy();
});
it('renders table row', () => {
factory({
id: 1,
path: 'test',
type: 'file',
});
expect(vm.element).toMatchSnapshot();
});
it.each`
type | component | componentName
${'folder'} | ${RouterLinkStub} | ${'RouterLink'}
${'file'} | ${'a'} | ${'hyperlink'}
${'commit'} | ${'a'} | ${'hyperlink'}
`('renders a $componentName for type $type', ({ type, component }) => {
factory({
id: 1,
path: 'test',
type,
});
expect(vm.find(component).exists()).toBe(true);
});
it.each`
type | pushes
${'folder'} | ${true}
${'file'} | ${false}
${'commit'} | ${false}
`('pushes new router if type $type is folder', ({ type, pushes }) => {
factory({
id: 1,
path: 'test',
type,
});
vm.trigger('click');
if (pushes) {
expect($router.push).toHaveBeenCalledWith({ path: '/tree/master/test' });
} else {
expect($router.push).not.toHaveBeenCalled();
}
});
it('renders commit ID for submodule', () => {
factory({
id: 1,
path: 'test',
type: 'commit',
});
expect(vm.find('.commit-sha').text()).toContain('1');
});
});
import { getIconName } from '~/repository/utils/icon';
describe('getIconName', () => {
// Tests the returning font awesome icon name
// We only test one for each file type to save testing a lot of different
// file types
it.each`
type | path | icon
${'folder'} | ${''} | ${'folder'}
${'commit'} | ${''} | ${'archive'}
${'file'} | ${'test.pdf'} | ${'file-pdf-o'}
${'file'} | ${'test.jpg'} | ${'file-image-o'}
${'file'} | ${'test.zip'} | ${'file-archive-o'}
${'file'} | ${'test.mp3'} | ${'file-audio-o'}
${'file'} | ${'test.flv'} | ${'file-video-o'}
${'file'} | ${'test.dotx'} | ${'file-word-o'}
${'file'} | ${'test.xlsb'} | ${'file-excel-o'}
${'file'} | ${'test.ppam'} | ${'file-powerpoint-o'}
${'file'} | ${'test.js'} | ${'file-text-o'}
`('returns $icon for $type with path $path', ({ type, path, icon }) => {
expect(getIconName(type, path)).toEqual(icon);
});
});
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