Commit 3a727aa6 authored by Tim Zallmann's avatar Tim Zallmann Committed by Sean McGivern

Multi File Editor File icons

parent fced41b1
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -3,6 +3,7 @@
import timeAgoMixin from '../../vue_shared/mixins/timeago';
import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
import newDropdown from './new_dropdown/index.vue';
import fileIcon from '../../vue_shared/components/file_icon.vue';
export default {
mixins: [
......@@ -11,6 +12,7 @@
components: {
skeletonLoadingContainer,
newDropdown,
fileIcon,
},
props: {
file: {
......@@ -26,13 +28,6 @@
...mapState([
'leftPanelCollapsed',
]),
fileIcon() {
return {
'fa-spinner fa-spin': this.file.loading,
[this.file.icon]: !this.file.loading,
'fa-folder-open': !this.file.loading && this.file.opened,
};
},
isSubmodule() {
return this.file.type === 'submodule';
},
......@@ -94,16 +89,18 @@
class="multi-file-table-name"
:colspan="submoduleColSpan"
>
<i
class="fa fa-fw file-icon"
:class="fileIcon"
:style="levelIndentation"
aria-hidden="true"
>
</i>
<a
class="repo-file-name"
>
<file-icon
:file-name="file.name"
:loading="file.loading"
:folder="file.type === 'tree'"
:opened="file.opened"
:style="levelIndentation"
:size="16"
>
</file-icon>
{{ file.name }}
</a>
<new-dropdown
......
<script>
import { mapActions } from 'vuex';
import fileIcon from '../../vue_shared/components/file_icon.vue';
export default {
props: {
......@@ -8,7 +9,9 @@ export default {
required: true,
},
},
components: {
fileIcon,
},
computed: {
closeLabel() {
if (this.tab.changed || this.tab.tempFile) {
......@@ -63,6 +66,11 @@ export default {
:class="{active : tab.active }"
:title="tab.url"
>
<file-icon
:file-name="tab.name"
:size="16"
>
</file-icon>
{{ tab.name }}
</div>
</li>
......
<script>
import getIconForFile from './file_icon/file_icon_map';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import icon from '../../vue_shared/components/icon.vue';
/* This is a re-usable vue component for rendering a svg sprite
icon
Sample configuration:
<file-icon
name="retry"
:size="32"
css-classes="top"
/>
*/
export default {
props: {
fileName: {
type: String,
required: true,
},
folder: {
type: Boolean,
required: false,
default: false,
},
opened: {
type: Boolean,
required: false,
default: false,
},
loading: {
type: Boolean,
required: false,
default: false,
},
size: {
type: Number,
required: false,
default: 16,
},
cssClasses: {
type: String,
required: false,
default: '',
},
},
components: {
loadingIcon,
icon,
},
computed: {
spriteHref() {
const iconName = getIconForFile(this.fileName) || 'file';
return `${gon.sprite_file_icons}#${iconName}`;
},
folderIconName() {
// We don't have a open folder icon yet
return this.opened ? 'folder' : 'folder';
},
iconSizeClass() {
return this.size ? `s${this.size}` : '';
},
},
};
</script>
<template>
<span>
<svg
:class="[iconSizeClass, cssClasses]"
v-if="!loading && !folder">
<use
v-bind="{'xlink:href':spriteHref}"/>
</svg>
<icon
v-if="!loading && folder"
:name="folderIconName"
:size="size"
/>
<loading-icon
v-if="loading"
:inline="true"
/>
</span>
</template>
......@@ -96,8 +96,14 @@
padding: 6px 12px;
}
.multi-file-table-name {
table.table tr td.multi-file-table-name {
width: 350px;
padding: 6px 12px;
svg {
vertical-align: middle;
margin-right: 2px;
}
}
.multi-file-table-col-commit-message {
......@@ -132,6 +138,10 @@
border-bottom: 1px solid $white-dark;
cursor: pointer;
svg {
vertical-align: middle;
}
&.active {
background-color: $white-light;
border-bottom-color: $white-light;
......
......@@ -30,6 +30,13 @@ module IconsHelper
ActionController::Base.helpers.image_path('icons.svg', host: sprite_base_url)
end
def sprite_file_icons_path
# SVG Sprites currently don't work across domains, so in the case of a CDN
# we have to set the current path deliberately to prevent addition of asset_host
sprite_base_url = Gitlab.config.gitlab.url if ActionController::Base.asset_host
ActionController::Base.helpers.image_path('file_icons.svg', host: sprite_base_url)
end
def sprite_icon(icon_name, size: nil, css_class: nil)
css_classes = size ? "s#{size}" : ""
css_classes << " #{css_class}" unless css_class.blank?
......
......@@ -21,6 +21,7 @@ module Gitlab
gon.revision = Gitlab::REVISION
gon.gitlab_logo = ActionController::Base.helpers.asset_path('gitlab_logo.png')
gon.sprite_icons = IconsHelper.sprite_icon_path
gon.sprite_file_icons = IconsHelper.sprite_file_icons_path
if current_user
gon.current_user_id = current_user.id
......
......@@ -32,13 +32,9 @@ describe('RepoFile', () => {
vm.$mount();
const name = vm.$el.querySelector('.repo-file-name');
const fileIcon = vm.$el.querySelector('.file-icon');
expect(vm.$el.querySelector(`.${vm.file.icon}`).style.marginLeft).toEqual('0px');
expect(name.href).toMatch('');
expect(name.textContent.trim()).toEqual(vm.file.name);
expect(fileIcon.classList.contains(vm.file.icon)).toBeTruthy();
expect(fileIcon.style.marginLeft).toEqual(`${vm.file.level * 10}px`);
});
it('does render if hasFiles is true and is loading tree', () => {
......@@ -49,17 +45,6 @@ describe('RepoFile', () => {
expect(vm.$el.querySelector('.fa-spin.fa-spinner')).toBeFalsy();
});
it('renders a spinner if the file is loading', () => {
const f = file();
f.loading = true;
vm = createComponent({
file: f,
});
expect(vm.$el.querySelector('.fa-spin.fa-spinner')).not.toBeNull();
expect(vm.$el.querySelector('.fa-spin.fa-spinner').style.marginLeft).toEqual(`${vm.file.level * 16}px`);
});
it('does not render commit message and datetime if mini', (done) => {
vm = createComponent({
file: file(),
......
import Vue from 'vue';
import fileIcon from '~/vue_shared/components/file_icon.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('File Icon component', () => {
let vm;
let FileIcon;
beforeEach(() => {
FileIcon = Vue.extend(fileIcon);
});
afterEach(() => {
vm.$destroy();
});
it('should render a span element with an svg', () => {
vm = mountComponent(FileIcon, {
fileName: 'test.js',
});
expect(vm.$el.tagName).toEqual('SPAN');
expect(vm.$el.querySelector('span > svg')).toBeDefined();
});
it('should render a javascript icon based on file ending', () => {
vm = mountComponent(FileIcon, {
fileName: 'test.js',
});
expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#javascript`);
});
it('should render a image icon based on file ending', () => {
vm = mountComponent(FileIcon, {
fileName: 'test.png',
});
expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#image`);
});
it('should render a webpack icon based on file namer', () => {
vm = mountComponent(FileIcon, {
fileName: 'webpack.js',
});
expect(vm.$el.firstChild.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#webpack`);
});
it('should render a standard folder icon', () => {
vm = mountComponent(FileIcon, {
fileName: 'js',
folder: true,
});
expect(vm.$el.querySelector('span > svg > use').getAttribute('xlink:href')).toBe(`${gon.sprite_file_icons}#folder`);
});
it('should render a loading icon', () => {
vm = mountComponent(FileIcon, {
fileName: 'test.js',
loading: true,
});
expect(
vm.$el.querySelector('i').getAttribute('class'),
).toEqual('fa fa-spin fa-spinner fa-1x');
});
it('should add a special class and a size class', () => {
vm = mountComponent(FileIcon, {
fileName: 'test.js',
cssClasses: 'extraclasses',
size: 120,
});
const classList = vm.$el.firstChild.classList;
const containsSizeClass = classList.contains('s120');
const containsCustomClass = classList.contains('extraclasses');
expect(containsSizeClass).toBe(true);
expect(containsCustomClass).toBe(true);
});
});
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