Commit 36177d1e authored by Mike Greiling's avatar Mike Greiling

Merge branch '196794-blob-header-filepath' into 'master'

Blob header file path component

See merge request gitlab-org/gitlab!24354
parents 961ffdb3 372bc9ff
<script>
import FileIcon from '~/vue_shared/components/file_icon.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
export default {
components: {
FileIcon,
ClipboardButton,
},
props: {
blob: {
type: Object,
required: true,
},
},
computed: {
blobSize() {
return numberToHumanSize(this.blob.size);
},
gfmCopyText() {
return `\`${this.blob.path}\``;
},
},
};
</script>
<template>
<div class="file-header-content d-flex align-items-center lh-100">
<slot name="filepathPrepend"></slot>
<file-icon :file-name="blob.path" :size="18" aria-hidden="true" css-classes="mr-2" />
<strong
v-if="blob.name"
class="file-title-name qa-file-title-name mr-1 js-blob-header-filepath"
>{{ blob.name }}</strong
>
<small class="mr-2">{{ blobSize }}</small>
<clipboard-button
:text="blob.path"
:gfm="gfmCopyText"
:title="__('Copy file path')"
css-class="btn-clipboard btn-transparent lh-100 position-static"
/>
</div>
</template>
...@@ -32,10 +32,6 @@ ...@@ -32,10 +32,6 @@
.snippet-file-content { .snippet-file-content {
border-radius: 3px; border-radius: 3px;
.file-title-flex-parent .btn-clipboard {
line-height: 28px;
}
} }
.snippet-header { .snippet-header {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Blob Header Filepath rendering matches the snapshot 1`] = `
<div
class="file-header-content d-flex align-items-center lh-100"
>
<file-icon-stub
aria-hidden="true"
cssclasses="mr-2"
filename="dummy.md"
size="18"
/>
<strong
class="file-title-name qa-file-title-name mr-1 js-blob-header-filepath"
>
dummy.md
</strong>
<small
class="mr-2"
>
a lot
</small>
<clipboard-button-stub
cssclass="btn-clipboard btn-transparent lh-100 position-static"
gfm="\`dummy.md\`"
text="dummy.md"
title="Copy file path"
tooltipplacement="top"
/>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import BlobHeaderFilepath from '~/blob/components/blob_header_filepath.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { Blob as MockBlob } from './mock_data';
import { numberToHumanSize } from '~/lib/utils/number_utils';
const mockHumanReadableSize = 'a lot';
jest.mock('~/lib/utils/number_utils', () => ({
numberToHumanSize: jest.fn(() => mockHumanReadableSize),
}));
describe('Blob Header Filepath', () => {
let wrapper;
function createComponent(blobProps = {}, options = {}) {
wrapper = shallowMount(BlobHeaderFilepath, {
propsData: {
blob: Object.assign({}, MockBlob, blobProps),
},
...options,
});
}
afterEach(() => {
wrapper.destroy();
});
describe('rendering', () => {
it('matches the snapshot', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
it('renders regular name', () => {
createComponent();
expect(
wrapper
.find('.js-blob-header-filepath')
.text()
.trim(),
).toBe(MockBlob.name);
});
it('does not fail if the name is empty', () => {
const emptyName = '';
createComponent({ name: emptyName });
expect(wrapper.find('.js-blob-header-filepath').exists()).toBe(false);
});
it('renders copy-to-clipboard icon that copies path of the Blob', () => {
createComponent();
const btn = wrapper.find(ClipboardButton);
expect(btn.exists()).toBe(true);
expect(btn.vm.text).toBe(MockBlob.path);
});
it('renders filesize in a human-friendly format', () => {
createComponent();
expect(numberToHumanSize).toHaveBeenCalled();
expect(wrapper.vm.blobSize).toBe(mockHumanReadableSize);
});
it('renders a slot and prepends its contents to the existing one', () => {
const slotContent = 'Foo Bar';
createComponent(
{},
{
scopedSlots: {
filepathPrepend: `<span>${slotContent}</span>`,
},
},
);
expect(wrapper.text()).toContain(slotContent);
expect(
wrapper
.text()
.trim()
.substring(0, slotContent.length),
).toBe(slotContent);
});
});
describe('functionality', () => {
it('sets gfm value correctly on the clipboard-button', () => {
createComponent();
expect(wrapper.vm.gfmCopyText).toBe('`dummy.md`');
});
});
});
export const Blob = {
binary: false,
highlightedData:
'<h1 data-sourcepos="1:1-1:19" dir="auto">\n<a id="user-content-this-one-is-dummy" class="anchor" href="#this-one-is-dummy" aria-hidden="true"></a>This one is dummy</h1>\n<h2 data-sourcepos="3:1-3:21" dir="auto">\n<a id="user-content-and-has-sub-header" class="anchor" href="#and-has-sub-header" aria-hidden="true"></a>And has sub-header</h2>\n<p data-sourcepos="5:1-5:27" dir="auto">Even some stupid text here</p>',
name: 'dummy.md',
path: 'dummy.md',
rawPath: '/flightjs/flight/snippets/51/raw',
size: 75,
simpleViewer: {
collapsed: false,
fileType: 'text',
loadAsync: true,
loadingPartialName: 'loading',
renderError: null,
tooLarge: false,
type: 'simple',
},
richViewer: {
collapsed: false,
fileType: 'markup',
loadAsync: true,
loadingPartialName: 'loading',
renderError: null,
tooLarge: false,
type: 'rich',
},
};
export default {};
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