diff options
Diffstat (limited to 'app/assets/javascripts/blob/components')
7 files changed, 154 insertions, 12 deletions
diff --git a/app/assets/javascripts/blob/components/blob_content.vue b/app/assets/javascripts/blob/components/blob_content.vue index 7d5d48cfc31..4f433bd8dfd 100644 --- a/app/assets/javascripts/blob/components/blob_content.vue +++ b/app/assets/javascripts/blob/components/blob_content.vue @@ -3,12 +3,19 @@ import { GlLoadingIcon } from '@gitlab/ui'; import { RichViewer, SimpleViewer } from '~/vue_shared/components/blob_viewers'; import BlobContentError from './blob_content_error.vue'; +import { BLOB_RENDER_EVENT_LOAD, BLOB_RENDER_EVENT_SHOW_SOURCE } from './constants'; + export default { components: { GlLoadingIcon, BlobContentError, }, props: { + blob: { + type: Object, + required: false, + default: () => ({}), + }, content: { type: String, default: '', @@ -37,6 +44,8 @@ export default { return this.activeViewer.renderError; }, }, + BLOB_RENDER_EVENT_LOAD, + BLOB_RENDER_EVENT_SHOW_SOURCE, }; </script> <template> @@ -44,7 +53,13 @@ export default { <gl-loading-icon v-if="loading" size="md" color="dark" class="my-4 mx-auto" /> <template v-else> - <blob-content-error v-if="viewerError" :viewer-error="viewerError" /> + <blob-content-error + v-if="viewerError" + :viewer-error="viewerError" + :blob="blob" + @[$options.BLOB_RENDER_EVENT_LOAD]="$emit($options.BLOB_RENDER_EVENT_LOAD)" + @[$options.BLOB_RENDER_EVENT_SHOW_SOURCE]="$emit($options.BLOB_RENDER_EVENT_SHOW_SOURCE)" + /> <component :is="viewer" v-else diff --git a/app/assets/javascripts/blob/components/blob_content_error.vue b/app/assets/javascripts/blob/components/blob_content_error.vue index 0f1af0a962d..44dc4a6c727 100644 --- a/app/assets/javascripts/blob/components/blob_content_error.vue +++ b/app/assets/javascripts/blob/components/blob_content_error.vue @@ -1,15 +1,84 @@ <script> +import { __ } from '~/locale'; +import { GlSprintf, GlLink } from '@gitlab/ui'; +import { BLOB_RENDER_ERRORS } from './constants'; + export default { + components: { + GlSprintf, + GlLink, + }, props: { viewerError: { type: String, required: true, }, + blob: { + type: Object, + required: false, + default: () => ({}), + }, + }, + computed: { + notStoredExternally() { + return this.viewerError !== BLOB_RENDER_ERRORS.REASONS.EXTERNAL.id; + }, + renderErrorReason() { + const defaultReasonPath = Object.keys(BLOB_RENDER_ERRORS.REASONS).find( + reason => BLOB_RENDER_ERRORS.REASONS[reason].id === this.viewerError, + ); + const defaultReason = BLOB_RENDER_ERRORS.REASONS[defaultReasonPath].text; + return this.notStoredExternally + ? defaultReason + : defaultReason[this.blob.externalStorage || 'default']; + }, + renderErrorOptions() { + const load = { + ...BLOB_RENDER_ERRORS.OPTIONS.LOAD, + condition: this.shouldShowLoadBtn, + }; + const showSource = { + ...BLOB_RENDER_ERRORS.OPTIONS.SHOW_SOURCE, + condition: this.shouldShowSourceBtn, + }; + const download = { + ...BLOB_RENDER_ERRORS.OPTIONS.DOWNLOAD, + href: this.blob.rawPath, + }; + return [load, showSource, download]; + }, + shouldShowLoadBtn() { + return this.viewerError === BLOB_RENDER_ERRORS.REASONS.COLLAPSED.id; + }, + shouldShowSourceBtn() { + return this.blob.richViewer && this.blob.renderedAsText && this.notStoredExternally; + }, }, + errorMessage: __( + 'This content could not be displayed because %{reason}. You can %{options} instead.', + ), }; </script> <template> <div class="file-content code"> - <div class="text-center py-4" v-html="viewerError"></div> + <div class="text-center py-4"> + <gl-sprintf :message="$options.errorMessage"> + <template #reason>{{ renderErrorReason }}</template> + <template #options> + <template v-for="option in renderErrorOptions"> + <span v-if="option.condition" :key="option.text"> + <gl-link + :href="option.href" + :target="option.target" + :data-test-id="`option-${option.id}`" + @click="option.event && $emit(option.event)" + >{{ option.text }}</gl-link + > + {{ option.conjunction }} + </span> + </template> + </template> + </gl-sprintf> + </div> </div> </template> diff --git a/app/assets/javascripts/blob/components/blob_edit_header.vue b/app/assets/javascripts/blob/components/blob_edit_header.vue index e9b5ceda479..e1e1d76f721 100644 --- a/app/assets/javascripts/blob/components/blob_edit_header.vue +++ b/app/assets/javascripts/blob/components/blob_edit_header.vue @@ -5,6 +5,7 @@ export default { components: { GlFormInput, }, + inheritAttrs: false, props: { value: { type: String, @@ -27,8 +28,9 @@ export default { s__('Snippets|Give your file a name to add code highlighting, e.g. example.rb for Ruby') " name="snippet_file_name" - class="form-control js-snippet-file-name qa-snippet-file-name" + class="form-control js-snippet-file-name" type="text" + v-bind="$attrs" @change="$emit('input', name)" /> </div> diff --git a/app/assets/javascripts/blob/components/blob_header.vue b/app/assets/javascripts/blob/components/blob_header.vue index b7d9600ec40..e5e01caa9a5 100644 --- a/app/assets/javascripts/blob/components/blob_header.vue +++ b/app/assets/javascripts/blob/components/blob_header.vue @@ -66,7 +66,7 @@ export default { </template> </blob-filepath> - <div class="file-actions d-none d-sm-block"> + <div class="file-actions d-none d-sm-flex"> <viewer-switcher v-if="showViewerSwitcher" v-model="viewer" /> <slot name="actions"></slot> diff --git a/app/assets/javascripts/blob/components/blob_header_filepath.vue b/app/assets/javascripts/blob/components/blob_header_filepath.vue index 6c6a22e2b36..e9be7fbcf9b 100644 --- a/app/assets/javascripts/blob/components/blob_header_filepath.vue +++ b/app/assets/javascripts/blob/components/blob_header_filepath.vue @@ -28,12 +28,14 @@ export default { <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 - > + <template v-if="blob.path"> + <file-icon :file-name="blob.path" :size="18" aria-hidden="true" css-classes="mr-2" /> + <strong + class="file-title-name mr-1 js-blob-header-filepath" + data-qa-selector="file_title_name" + >{{ blob.path }}</strong + > + </template> <small class="mr-2">{{ blobSize }}</small> diff --git a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue index 7155a1d35b1..ed03213d7cf 100644 --- a/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue +++ b/app/assets/javascripts/blob/components/blob_header_viewer_switcher.vue @@ -45,7 +45,7 @@ export default { }; </script> <template> - <gl-button-group class="js-blob-viewer-switcher ml-2"> + <gl-button-group class="js-blob-viewer-switcher mx-2"> <gl-deprecated-button v-gl-tooltip.hover :aria-label="$options.SIMPLE_BLOB_VIEWER_TITLE" diff --git a/app/assets/javascripts/blob/components/constants.js b/app/assets/javascripts/blob/components/constants.js index d3fed9e51e9..93dceacabdd 100644 --- a/app/assets/javascripts/blob/components/constants.js +++ b/app/assets/javascripts/blob/components/constants.js @@ -1,4 +1,5 @@ -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; +import { numberToHumanSize } from '~/lib/utils/number_utils'; export const BTN_COPY_CONTENTS_TITLE = __('Copy file contents'); export const BTN_RAW_TITLE = __('Open raw'); @@ -9,3 +10,56 @@ export const SIMPLE_BLOB_VIEWER_TITLE = __('Display source'); export const RICH_BLOB_VIEWER = 'rich'; export const RICH_BLOB_VIEWER_TITLE = __('Display rendered file'); + +export const BLOB_RENDER_EVENT_LOAD = 'force-content-fetch'; +export const BLOB_RENDER_EVENT_SHOW_SOURCE = 'force-switch-viewer'; + +export const BLOB_RENDER_ERRORS = { + REASONS: { + COLLAPSED: { + id: 'collapsed', + text: sprintf(__('it is larger than %{limit}'), { + limit: numberToHumanSize(1048576), // 1MB in bytes + }), + }, + TOO_LARGE: { + id: 'too_large', + text: sprintf(__('it is larger than %{limit}'), { + limit: numberToHumanSize(104857600), // 100MB in bytes + }), + }, + EXTERNAL: { + id: 'server_side_but_stored_externally', + text: { + lfs: __('it is stored in LFS'), + build_artifact: __('it is stored as a job artifact'), + default: __('it is stored externally'), + }, + }, + }, + OPTIONS: { + LOAD: { + id: 'load', + text: __('load it anyway'), + conjunction: __('or'), + href: '#', + target: '', + event: BLOB_RENDER_EVENT_LOAD, + }, + SHOW_SOURCE: { + id: 'show_source', + text: __('view the source'), + conjunction: __('or'), + href: '#', + target: '', + event: BLOB_RENDER_EVENT_SHOW_SOURCE, + }, + DOWNLOAD: { + id: 'download', + text: __('download it'), + conjunction: '', + target: '_blank', + condition: true, + }, + }, +}; |