diff options
author | Filipa Lacerda <filipa@gitlab.com> | 2019-05-22 09:50:18 +0000 |
---|---|---|
committer | Filipa Lacerda <filipa@gitlab.com> | 2019-05-22 09:50:18 +0000 |
commit | e5e9466c9c7489816a3271575e582f560bb4709f (patch) | |
tree | 83ecf9880ae80d9bc5f0134d038264808fe2b506 | |
parent | 0c7300cdde8b14f1538f3221beedcd824f0c7c7c (diff) | |
parent | 5ddedb6b8bf3f47c400e3d38e415462abefa3b50 (diff) | |
download | gitlab-ce-e5e9466c9c7489816a3271575e582f560bb4709f.tar.gz |
Merge branch 'repo-list-row-component' into 'master'
Added tree list row component
See merge request gitlab-org/gitlab-ce!28542
9 files changed, 343 insertions, 5 deletions
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index 7119861c7b3..ad3d8f9329d 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -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> diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue new file mode 100644 index 00000000000..0ad0fdbd605 --- /dev/null +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -0,0 +1,60 @@ +<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> diff --git a/app/assets/javascripts/repository/graphql.js b/app/assets/javascripts/repository/graphql.js index 0aedc73fc12..c85db5c01e5 100644 --- a/app/assets/javascripts/repository/graphql.js +++ b/app/assets/javascripts/repository/graphql.js @@ -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', + }, + ]; }, }, }); diff --git a/app/assets/javascripts/repository/pages/tree.vue b/app/assets/javascripts/repository/pages/tree.vue index 413102b2cd3..3b898d1aa91 100644 --- a/app/assets/javascripts/repository/pages/tree.vue +++ b/app/assets/javascripts/repository/pages/tree.vue @@ -2,7 +2,7 @@ import FileTable from '../components/table/index.vue'; export default { - component: { + components: { FileTable, }, props: { diff --git a/app/assets/javascripts/repository/queries/getFiles.graphql b/app/assets/javascripts/repository/queries/getFiles.graphql index 5ceaf67ea82..fb446780ed1 100644 --- a/app/assets/javascripts/repository/queries/getFiles.graphql +++ b/app/assets/javascripts/repository/queries/getFiles.graphql @@ -1,8 +1,7 @@ query getFiles($path: String!, $ref: String!) { files(path: $path, ref: $ref) @client { id - name - fullPath + flatPath type } } diff --git a/app/assets/javascripts/repository/utils/icon.js b/app/assets/javascripts/repository/utils/icon.js new file mode 100644 index 00000000000..3e93ff0ec39 --- /dev/null +++ b/app/assets/javascripts/repository/utils/icon.js @@ -0,0 +1,99 @@ +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'; +}; diff --git a/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap new file mode 100644 index 00000000000..f0b72343b6e --- /dev/null +++ b/spec/frontend/repository/components/table/__snapshots__/row_spec.js.snap @@ -0,0 +1,33 @@ +// 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> +`; diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js new file mode 100644 index 00000000000..216128dce25 --- /dev/null +++ b/spec/frontend/repository/components/table/row_spec.js @@ -0,0 +1,85 @@ +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'); + }); +}); diff --git a/spec/frontend/repository/utils/icon_spec.js b/spec/frontend/repository/utils/icon_spec.js new file mode 100644 index 00000000000..52787327bef --- /dev/null +++ b/spec/frontend/repository/utils/icon_spec.js @@ -0,0 +1,23 @@ +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); + }); +}); |