Commit 3ba10b63 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge branch '323210-vue-blob-header' into 'master'

Use BlobHeader vue component for repo files

See merge request gitlab-org/gitlab!60883
parents 2cd392f6 854bcedd
...@@ -3,6 +3,7 @@ import { GlLoadingIcon } from '@gitlab/ui'; ...@@ -3,6 +3,7 @@ import { GlLoadingIcon } from '@gitlab/ui';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import BlobContent from '~/blob/components/blob_content.vue'; import BlobContent from '~/blob/components/blob_content.vue';
import BlobHeader from '~/blob/components/blob_header.vue'; import BlobHeader from '~/blob/components/blob_header.vue';
import { SIMPLE_BLOB_VIEWER, RICH_BLOB_VIEWER } from '~/blob/components/constants';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import blobInfoQuery from '../queries/blob_info.query.graphql'; import blobInfoQuery from '../queries/blob_info.query.graphql';
...@@ -22,6 +23,11 @@ export default { ...@@ -22,6 +23,11 @@ export default {
filePath: this.path, filePath: this.path,
}; };
}, },
result() {
this.switchViewer(
this.hasRichViewer && !window.location.hash ? RICH_BLOB_VIEWER : SIMPLE_BLOB_VIEWER,
);
},
error() { error() {
createFlash({ message: __('An error occurred while loading the file. Please try again.') }); createFlash({ message: __('An error occurred while loading the file. Please try again.') });
}, },
...@@ -44,6 +50,7 @@ export default { ...@@ -44,6 +50,7 @@ export default {
}, },
data() { data() {
return { return {
activeViewerType: SIMPLE_BLOB_VIEWER,
project: { project: {
repository: { repository: {
blobs: { blobs: {
...@@ -69,7 +76,7 @@ export default { ...@@ -69,7 +76,7 @@ export default {
canModifyBlob: true, canModifyBlob: true,
forkPath: '', forkPath: '',
simpleViewer: {}, simpleViewer: {},
richViewer: {}, richViewer: null,
}, },
], ],
}, },
...@@ -87,10 +94,19 @@ export default { ...@@ -87,10 +94,19 @@ export default {
return nodes[0] || {}; return nodes[0] || {};
}, },
viewer() { viewer() {
const viewer = this.blobInfo.richViewer || this.blobInfo.simpleViewer; const { richViewer, simpleViewer } = this.blobInfo;
const { fileType, tooLarge, type } = viewer; return this.activeViewerType === RICH_BLOB_VIEWER ? richViewer : simpleViewer;
},
return { fileType, tooLarge, type }; hasRichViewer() {
return Boolean(this.blobInfo.richViewer);
},
hasRenderError() {
return Boolean(this.viewer.renderError);
},
},
methods: {
switchViewer(newViewer) {
this.activeViewerType = newViewer || SIMPLE_BLOB_VIEWER;
}, },
}, },
}; };
...@@ -99,8 +115,14 @@ export default { ...@@ -99,8 +115,14 @@ export default {
<template> <template>
<div> <div>
<gl-loading-icon v-if="isLoading" /> <gl-loading-icon v-if="isLoading" />
<div v-if="blobInfo && !isLoading"> <div v-if="blobInfo && !isLoading" class="file-holder">
<blob-header :blob="blobInfo" /> <blob-header
:blob="blobInfo"
:hide-viewer-switcher="!hasRichViewer"
:active-viewer-type="viewer.type"
:has-render-error="hasRenderError"
@viewer-changed="switchViewer"
/>
<blob-content <blob-content
:blob="blobInfo" :blob="blobInfo"
:content="blobInfo.rawTextBlob" :content="blobInfo.rawTextBlob"
......
...@@ -6,6 +6,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) { ...@@ -6,6 +6,7 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) {
nodes { nodes {
webPath webPath
name name
size
rawSize rawSize
rawTextBlob rawTextBlob
fileType fileType
...@@ -18,11 +19,13 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) { ...@@ -18,11 +19,13 @@ query getBlobInfo($projectPath: ID!, $filePath: String!) {
fileType fileType
tooLarge tooLarge
type type
renderError
} }
richViewer { richViewer {
fileType fileType
tooLarge tooLarge
type type
renderError
} }
} }
} }
......
...@@ -36,6 +36,10 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -36,6 +36,10 @@ class Projects::BlobController < Projects::ApplicationController
feature_category :source_code_management feature_category :source_code_management
before_action do
push_frontend_feature_flag(:refactor_blob_viewer, @project, default_enabled: :yaml)
end
def new def new
commit unless @repository.empty? commit unless @repository.empty?
end end
......
...@@ -5,9 +5,10 @@ import BlobHeader from '~/blob/components/blob_header.vue'; ...@@ -5,9 +5,10 @@ import BlobHeader from '~/blob/components/blob_header.vue';
import BlobContentViewer from '~/repository/components/blob_content_viewer.vue'; import BlobContentViewer from '~/repository/components/blob_content_viewer.vue';
let wrapper; let wrapper;
const mockData = { const simpleMockData = {
name: 'some_file.js', name: 'some_file.js',
size: 123, size: 123,
rawSize: 123,
rawTextBlob: 'raw content', rawTextBlob: 'raw content',
type: 'text', type: 'text',
fileType: 'text', fileType: 'text',
...@@ -29,15 +30,26 @@ const mockData = { ...@@ -29,15 +30,26 @@ const mockData = {
fileType: 'text', fileType: 'text',
tooLarge: false, tooLarge: false,
type: 'simple', type: 'simple',
renderError: null,
}, },
richViewer: null, richViewer: null,
}; };
const richMockData = {
...simpleMockData,
richViewer: {
fileType: 'markup',
tooLarge: false,
type: 'rich',
renderError: null,
},
};
function factory(path, loading = false) { function factory({ props = {}, mockData = {} } = {}, loading = false) {
wrapper = shallowMount(BlobContentViewer, { wrapper = shallowMount(BlobContentViewer, {
propsData: { propsData: {
path, path: 'some_file.js',
projectPath: 'some/path', projectPath: 'some/path',
...props,
}, },
mocks: { mocks: {
$apollo: { $apollo: {
...@@ -50,7 +62,7 @@ function factory(path, loading = false) { ...@@ -50,7 +62,7 @@ function factory(path, loading = false) {
}, },
}); });
wrapper.setData({ blobInfo: mockData }); wrapper.setData(mockData);
} }
describe('Blob content viewer component', () => { describe('Blob content viewer component', () => {
...@@ -58,34 +70,84 @@ describe('Blob content viewer component', () => { ...@@ -58,34 +70,84 @@ describe('Blob content viewer component', () => {
const findBlobHeader = () => wrapper.find(BlobHeader); const findBlobHeader = () => wrapper.find(BlobHeader);
const findBlobContent = () => wrapper.find(BlobContent); const findBlobContent = () => wrapper.find(BlobContent);
afterEach(() => { beforeEach(() => {
wrapper.destroy(); factory({ mockData: { blobInfo: simpleMockData } });
}); });
beforeEach(() => { afterEach(() => {
factory('some_file.js'); wrapper.destroy();
}); });
it('renders a GlLoadingIcon component', () => { it('renders a GlLoadingIcon component', () => {
factory('some_file.js', true); factory({ mockData: { blobInfo: simpleMockData } }, true);
expect(findLoadingIcon().exists()).toBe(true); expect(findLoadingIcon().exists()).toBe(true);
}); });
it('renders a BlobHeader component', () => { describe('simple viewer', () => {
expect(findBlobHeader().exists()).toBe(true); it('renders a BlobHeader component', () => {
expect(findBlobHeader().props('activeViewerType')).toEqual('simple');
expect(findBlobHeader().props('hasRenderError')).toEqual(false);
expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(true);
expect(findBlobHeader().props('blob')).toEqual(simpleMockData);
});
it('renders a BlobContent component', () => {
expect(findBlobContent().props('loading')).toEqual(false);
expect(findBlobContent().props('content')).toEqual('raw content');
expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'text',
tooLarge: false,
type: 'simple',
renderError: null,
});
});
}); });
it('renders a BlobContent component', () => { describe('rich viewer', () => {
expect(findBlobContent().exists()).toBe(true); beforeEach(() => {
factory({
mockData: { blobInfo: richMockData, activeViewerType: 'rich' },
});
});
it('renders a BlobHeader component', () => {
expect(findBlobHeader().props('activeViewerType')).toEqual('rich');
expect(findBlobHeader().props('hasRenderError')).toEqual(false);
expect(findBlobHeader().props('hideViewerSwitcher')).toEqual(false);
expect(findBlobHeader().props('blob')).toEqual(richMockData);
});
it('renders a BlobContent component', () => {
expect(findBlobContent().props('loading')).toEqual(false);
expect(findBlobContent().props('content')).toEqual('raw content');
expect(findBlobContent().props('isRawContent')).toBe(true);
expect(findBlobContent().props('activeViewer')).toEqual({
fileType: 'markup',
tooLarge: false,
type: 'rich',
renderError: null,
});
});
it('updates viewer type when viewer changed is clicked', async () => {
expect(findBlobContent().props('activeViewer')).toEqual(
expect.objectContaining({
type: 'rich',
}),
);
expect(findBlobHeader().props('activeViewerType')).toEqual('rich');
findBlobHeader().vm.$emit('viewer-changed', 'simple');
await wrapper.vm.$nextTick();
expect(findBlobContent().props('loading')).toEqual(false); expect(findBlobHeader().props('activeViewerType')).toEqual('simple');
expect(findBlobContent().props('content')).toEqual('raw content'); expect(findBlobContent().props('activeViewer')).toEqual(
expect(findBlobContent().props('isRawContent')).toBe(true); expect.objectContaining({
expect(findBlobContent().props('activeViewer')).toEqual({ type: 'simple',
fileType: 'text', }),
tooLarge: false, );
type: 'simple',
}); });
}); });
}); });
...@@ -11,7 +11,9 @@ describe('Repository blob page component', () => { ...@@ -11,7 +11,9 @@ describe('Repository blob page component', () => {
const path = 'file.js'; const path = 'file.js';
beforeEach(() => { beforeEach(() => {
wrapper = shallowMount(BlobPage, { propsData: { path, projectPath: 'some/path' } }); wrapper = shallowMount(BlobPage, {
propsData: { path, projectPath: 'some/path' },
});
}); });
afterEach(() => { afterEach(() => {
......
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