diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-19 04:45:44 +0300 |
commit | 85dc423f7090da0a52c73eb66faf22ddb20efff9 (patch) | |
tree | 9160f299afd8c80c038f08e1545be119f5e3f1e1 /app/assets/javascripts/snippets | |
parent | 15c2c8c66dbe422588e5411eee7e68f1fa440bb8 (diff) |
Add latest changes from gitlab-org/gitlab@13-4-stable-ee
Diffstat (limited to 'app/assets/javascripts/snippets')
12 files changed, 201 insertions, 41 deletions
diff --git a/app/assets/javascripts/snippets/components/edit.vue b/app/assets/javascripts/snippets/components/edit.vue index 0978fcc7f93..1a539aa0876 100644 --- a/app/assets/javascripts/snippets/components/edit.vue +++ b/app/assets/javascripts/snippets/components/edit.vue @@ -4,21 +4,23 @@ import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { deprecatedCreateFlash as Flash } from '~/flash'; import { __, sprintf } from '~/locale'; import TitleField from '~/vue_shared/components/form/title.vue'; -import { redirectTo } from '~/lib/utils/url_utility'; +import { redirectTo, joinPaths } from '~/lib/utils/url_utility'; import FormFooterActions from '~/vue_shared/components/form/form_footer_actions.vue'; +import { SNIPPET_MARK_EDIT_APP_START } from '~/performance_constants'; import UpdateSnippetMutation from '../mutations/updateSnippet.mutation.graphql'; import CreateSnippetMutation from '../mutations/createSnippet.mutation.graphql'; import { getSnippetMixin } from '../mixins/snippets'; import { - SNIPPET_VISIBILITY_PRIVATE, SNIPPET_CREATE_MUTATION_ERROR, SNIPPET_UPDATE_MUTATION_ERROR, + SNIPPET_VISIBILITY_PRIVATE, } from '../constants'; +import defaultVisibilityQuery from '../queries/snippet_visibility.query.graphql'; + import SnippetBlobActionsEdit from './snippet_blob_actions_edit.vue'; import SnippetVisibilityEdit from './snippet_visibility_edit.vue'; import SnippetDescriptionEdit from './snippet_description_edit.vue'; -import { SNIPPET_MARK_EDIT_APP_START } from '~/performance_constants'; export default { components: { @@ -31,6 +33,15 @@ export default { GlLoadingIcon, }, mixins: [getSnippetMixin], + apollo: { + defaultVisibility: { + query: defaultVisibilityQuery, + manual: true, + result({ data: { selectedLevel } }) { + this.selectedLevelDefault = selectedLevel; + }, + }, + }, props: { markdownPreviewPath: { type: String, @@ -56,6 +67,7 @@ export default { isUpdating: false, newSnippet: false, actions: [], + selectedLevelDefault: SNIPPET_VISIBILITY_PRIVATE, }; }, computed: { @@ -88,7 +100,7 @@ export default { }, cancelButtonHref() { if (this.newSnippet) { - return this.projectPath ? `/${this.projectPath}/-/snippets` : `/-/snippets`; + return joinPaths('/', gon.relative_url_root, this.projectPath, '-/snippets'); } return this.snippet.webUrl; }, @@ -98,6 +110,13 @@ export default { descriptionFieldId() { return `${this.isProjectSnippet ? 'project' : 'personal'}_snippet_description`; }, + newSnippetSchema() { + return { + title: '', + description: '', + visibilityLevel: this.selectedLevelDefault, + }; + }, }, beforeCreate() { performance.mark(SNIPPET_MARK_EDIT_APP_START); @@ -126,7 +145,7 @@ export default { }, onNewSnippetFetched() { this.newSnippet = true; - this.snippet = this.$options.newSnippetSchema; + this.snippet = this.newSnippetSchema; }, onExistingSnippetFetched() { this.newSnippet = false; @@ -184,11 +203,6 @@ export default { this.actions = actions; }, }, - newSnippetSchema: { - title: '', - description: '', - visibilityLevel: SNIPPET_VISIBILITY_PRIVATE, - }, }; </script> <template> @@ -202,7 +216,7 @@ export default { v-if="isLoading" :label="__('Loading snippet')" size="lg" - class="loading-animation prepend-top-20 append-bottom-20" + class="loading-animation prepend-top-20 gl-mb-6" /> <template v-else> <title-field diff --git a/app/assets/javascripts/snippets/components/embed_dropdown.vue b/app/assets/javascripts/snippets/components/embed_dropdown.vue new file mode 100644 index 00000000000..589754a8b19 --- /dev/null +++ b/app/assets/javascripts/snippets/components/embed_dropdown.vue @@ -0,0 +1,78 @@ +<script> +import { escape as esc } from 'lodash'; +import { + GlButton, + GlDropdown, + GlDropdownSectionHeader, + GlDropdownText, + GlFormInputGroup, + GlTooltipDirective, +} from '@gitlab/ui'; +import { __ } from '~/locale'; + +const MSG_EMBED = __('Embed'); +const MSG_SHARE = __('Share'); +const MSG_COPY = __('Copy'); + +export default { + components: { + GlButton, + GlDropdown, + GlDropdownSectionHeader, + GlDropdownText, + GlFormInputGroup, + }, + directives: { + GlTooltip: GlTooltipDirective, + }, + props: { + url: { + type: String, + required: true, + }, + }, + computed: { + sections() { + return [ + // eslint-disable-next-line no-useless-escape + { name: MSG_EMBED, value: `<script src="${esc(this.url)}.js"><\/script>` }, + { name: MSG_SHARE, value: this.url }, + ]; + }, + }, + MSG_EMBED, + MSG_COPY, +}; +</script> +<template> + <gl-dropdown + right + :text="$options.MSG_EMBED" + menu-class="gl-px-1! gl-pb-5! gl-dropdown-menu-wide" + > + <template v-for="{ name, value } in sections"> + <gl-dropdown-section-header :key="`header_${name}`" data-testid="header">{{ + name + }}</gl-dropdown-section-header> + <gl-dropdown-text + :key="`input_${name}`" + tag="div" + class="gl-dropdown-text-py-0 gl-dropdown-text-block" + data-testid="input" + > + <gl-form-input-group :value="value" readonly select-on-click> + <template #append> + <gl-button + v-gl-tooltip.hover + :title="$options.MSG_COPY" + :data-clipboard-text="value" + icon="copy-to-clipboard" + data-qa-selector="copy_button" + :data-qa-action="name" + /> + </template> + </gl-form-input-group> + </gl-dropdown-text> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/snippets/components/show.vue b/app/assets/javascripts/snippets/components/show.vue index ca41fd0a2b1..43be2cb7ed8 100644 --- a/app/assets/javascripts/snippets/components/show.vue +++ b/app/assets/javascripts/snippets/components/show.vue @@ -1,6 +1,6 @@ <script> import { GlLoadingIcon } from '@gitlab/ui'; -import BlobEmbeddable from '~/blob/components/blob_embeddable.vue'; +import EmbedDropdown from './embed_dropdown.vue'; import SnippetHeader from './snippet_header.vue'; import SnippetTitle from './snippet_title.vue'; import SnippetBlob from './snippet_blob_view.vue'; @@ -13,7 +13,7 @@ import { SNIPPET_MARK_VIEW_APP_START } from '~/performance_constants'; export default { components: { - BlobEmbeddable, + EmbedDropdown, SnippetHeader, SnippetTitle, GlLoadingIcon, @@ -40,13 +40,17 @@ export default { v-if="isLoading" :label="__('Loading snippet')" size="lg" - class="loading-animation prepend-top-20 append-bottom-20" + class="loading-animation prepend-top-20 gl-mb-6" /> <template v-else> <snippet-header :snippet="snippet" /> <snippet-title :snippet="snippet" /> <div class="gl-display-flex gl-justify-content-end gl-mb-5"> - <blob-embeddable v-if="embeddable" class="gl-flex-fill-1" :url="snippet.webUrl" /> + <embed-dropdown + v-if="embeddable" + :url="snippet.webUrl" + data-qa-selector="snippet_embed_dropdown" + /> <clone-dropdown-button v-if="canBeCloned" class="gl-ml-3" diff --git a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue index ff03432f942..f3f894ed649 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_edit.vue @@ -53,7 +53,10 @@ export default { const url = joinPaths(baseUrl, this.blob.rawPath); axios - .get(url) + .get(url, { + // This prevents axios from automatically JSON.parse response + transformResponse: [f => f], + }) .then(res => { this.notifyAboutUpdates({ content: res.data }); }) @@ -80,7 +83,7 @@ export default { v-if="!blob.isLoaded" :label="__('Loading snippet')" size="lg" - class="loading-animation prepend-top-20 append-bottom-20" + class="loading-animation prepend-top-20 gl-mb-6" /> <blob-content-edit v-else diff --git a/app/assets/javascripts/snippets/components/snippet_description_view.vue b/app/assets/javascripts/snippets/components/snippet_description_view.vue index a5107f09fc7..e462f20535b 100644 --- a/app/assets/javascripts/snippets/components/snippet_description_view.vue +++ b/app/assets/javascripts/snippets/components/snippet_description_view.vue @@ -1,4 +1,5 @@ <script> +/* eslint-disable vue/no-v-html */ import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue'; export default { diff --git a/app/assets/javascripts/snippets/components/snippet_header.vue b/app/assets/javascripts/snippets/components/snippet_header.vue index ed087dcfaf9..0ca69f3161a 100644 --- a/app/assets/javascripts/snippets/components/snippet_header.vue +++ b/app/assets/javascripts/snippets/components/snippet_header.vue @@ -17,6 +17,7 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import DeleteSnippetMutation from '../mutations/deleteSnippet.mutation.graphql'; import CanCreatePersonalSnippet from '../queries/userPermissions.query.graphql'; import CanCreateProjectSnippet from '../queries/projectPermissions.query.graphql'; +import { joinPaths } from '~/lib/utils/url_utility'; export default { components: { @@ -96,8 +97,8 @@ export default { condition: this.canCreateSnippet, text: __('New snippet'), href: this.snippet.project - ? `${this.snippet.project.webUrl}/-/snippets/new` - : '/-/snippets/new', + ? joinPaths(this.snippet.project.webUrl, '-/snippets/new') + : joinPaths('/', gon.relative_url_root, '/-/snippets/new'), variant: 'success', category: 'secondary', cssClass: 'ml-2', @@ -137,7 +138,7 @@ export default { redirectToSnippets() { window.location.pathname = this.snippet.project ? `${this.snippet.project.fullPath}/-/snippets` - : 'dashboard/snippets'; + : `${gon.relative_url_root}dashboard/snippets`; }, closeDeleteModal() { this.$refs.deleteModal.hide(); diff --git a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue index 299bb8fcfad..25ad7c214b2 100644 --- a/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue +++ b/app/assets/javascripts/snippets/components/snippet_visibility_edit.vue @@ -1,11 +1,8 @@ <script> import { GlIcon, GlFormGroup, GlFormRadio, GlFormRadioGroup, GlLink } from '@gitlab/ui'; -import { - SNIPPET_VISIBILITY, - SNIPPET_VISIBILITY_PRIVATE, - SNIPPET_VISIBILITY_INTERNAL, - SNIPPET_VISIBILITY_PUBLIC, -} from '~/snippets/constants'; +import defaultVisibilityQuery from '../queries/snippet_visibility.query.graphql'; +import { defaultSnippetVisibilityLevels } from '../utils/blob'; +import { SNIPPET_LEVELS_RESTRICTED, SNIPPET_LEVELS_DISABLED } from '~/snippets/constants'; export default { components: { @@ -15,6 +12,16 @@ export default { GlFormRadioGroup, GlLink, }, + apollo: { + defaultVisibility: { + query: defaultVisibilityQuery, + manual: true, + result({ data: { visibilityLevels, multipleLevelsRestricted } }) { + this.visibilityLevels = defaultSnippetVisibilityLevels(visibilityLevels); + this.multipleLevelsRestricted = multipleLevelsRestricted; + }, + }, + }, props: { helpLink: { type: String, @@ -28,19 +35,17 @@ export default { }, value: { type: String, - required: false, - default: SNIPPET_VISIBILITY_PRIVATE, + required: true, }, }, - computed: { - visibilityOptions() { - return [ - SNIPPET_VISIBILITY_PRIVATE, - SNIPPET_VISIBILITY_INTERNAL, - SNIPPET_VISIBILITY_PUBLIC, - ].map(key => ({ value: key, ...SNIPPET_VISIBILITY[key] })); - }, + data() { + return { + visibilityLevels: [], + multipleLevelsRestricted: false, + }; }, + SNIPPET_LEVELS_DISABLED, + SNIPPET_LEVELS_RESTRICTED, }; </script> <template> @@ -51,10 +56,10 @@ export default { ><gl-icon :size="12" name="question" /></gl-link> </label> - <gl-form-group id="visibility-level-setting"> - <gl-form-radio-group v-bind="$attrs" :checked="value" stacked v-on="$listeners"> + <gl-form-group id="visibility-level-setting" class="gl-mb-0"> + <gl-form-radio-group :checked="value" stacked v-bind="$attrs" v-on="$listeners"> <gl-form-radio - v-for="option in visibilityOptions" + v-for="option in visibilityLevels" :key="option.value" :value="option.value" class="mb-3" @@ -71,5 +76,12 @@ export default { </gl-form-radio> </gl-form-radio-group> </gl-form-group> + + <div class="text-muted" data-testid="restricted-levels-info"> + <template v-if="!visibilityLevels.length">{{ $options.SNIPPET_LEVELS_DISABLED }}</template> + <template v-else-if="multipleLevelsRestricted">{{ + $options.SNIPPET_LEVELS_RESTRICTED + }}</template> + </div> </div> </template> diff --git a/app/assets/javascripts/snippets/constants.js b/app/assets/javascripts/snippets/constants.js index 12b83525bf7..e75922df15f 100644 --- a/app/assets/javascripts/snippets/constants.js +++ b/app/assets/javascripts/snippets/constants.js @@ -33,3 +33,15 @@ export const SNIPPET_BLOB_ACTION_MOVE = 'move'; export const SNIPPET_BLOB_ACTION_DELETE = 'delete'; export const SNIPPET_MAX_BLOBS = 10; + +export const SNIPPET_LEVELS_MAP = { + 0: SNIPPET_VISIBILITY_PRIVATE, + 10: SNIPPET_VISIBILITY_INTERNAL, + 20: SNIPPET_VISIBILITY_PUBLIC, +}; +export const SNIPPET_LEVELS_RESTRICTED = __( + 'Other visibility settings have been disabled by the administrator.', +); +export const SNIPPET_LEVELS_DISABLED = __( + 'Visibility settings have been disabled by the administrator.', +); diff --git a/app/assets/javascripts/snippets/index.js b/app/assets/javascripts/snippets/index.js index bb5e7d6e3f0..c70ad9b95f8 100644 --- a/app/assets/javascripts/snippets/index.js +++ b/app/assets/javascripts/snippets/index.js @@ -5,6 +5,7 @@ import createDefaultClient from '~/lib/graphql'; import SnippetsShow from './components/show.vue'; import SnippetsEdit from './components/edit.vue'; +import { SNIPPET_LEVELS_MAP, SNIPPET_VISIBILITY_PRIVATE } from '~/snippets/constants'; Vue.use(VueApollo); Vue.use(Translate); @@ -18,13 +19,28 @@ function appFactory(el, Component) { defaultClient: createDefaultClient(), }); + const { + visibilityLevels = '[]', + selectedLevel, + multipleLevelsRestricted, + ...restDataset + } = el.dataset; + + apolloProvider.clients.defaultClient.cache.writeData({ + data: { + visibilityLevels: JSON.parse(visibilityLevels), + selectedLevel: SNIPPET_LEVELS_MAP[selectedLevel] ?? SNIPPET_VISIBILITY_PRIVATE, + multipleLevelsRestricted: 'multipleLevelsRestricted' in el.dataset, + }, + }); + return new Vue({ el, apolloProvider, render(createElement) { return createElement(Component, { props: { - ...el.dataset, + ...restDataset, }, }); }, diff --git a/app/assets/javascripts/snippets/mixins/snippets.js b/app/assets/javascripts/snippets/mixins/snippets.js index 3f5d64a768f..15daaa8d84a 100644 --- a/app/assets/javascripts/snippets/mixins/snippets.js +++ b/app/assets/javascripts/snippets/mixins/snippets.js @@ -2,7 +2,6 @@ import GetSnippetQuery from '../queries/snippet.query.graphql'; const blobsDefault = []; -// eslint-disable-next-line import/prefer-default-export export const getSnippetMixin = { apollo: { snippet: { diff --git a/app/assets/javascripts/snippets/queries/snippet_visibility.query.graphql b/app/assets/javascripts/snippets/queries/snippet_visibility.query.graphql new file mode 100644 index 00000000000..5bd6c131bab --- /dev/null +++ b/app/assets/javascripts/snippets/queries/snippet_visibility.query.graphql @@ -0,0 +1,5 @@ +query defaultSnippetVisibility { + visibilityLevels @client + selectedLevel @client + multipleLevelsRestricted @client +} diff --git a/app/assets/javascripts/snippets/utils/blob.js b/app/assets/javascripts/snippets/utils/blob.js index fd5ff9a3d2e..21f52671801 100644 --- a/app/assets/javascripts/snippets/utils/blob.js +++ b/app/assets/javascripts/snippets/utils/blob.js @@ -4,6 +4,8 @@ import { SNIPPET_BLOB_ACTION_UPDATE, SNIPPET_BLOB_ACTION_MOVE, SNIPPET_BLOB_ACTION_DELETE, + SNIPPET_LEVELS_MAP, + SNIPPET_VISIBILITY, } from '../constants'; const createLocalId = () => uniqueId('blob_local_'); @@ -64,3 +66,16 @@ export const diffAll = (blobs, origBlobs) => { return [...deletedEntries, ...newEntries]; }; + +export const defaultSnippetVisibilityLevels = arr => { + if (Array.isArray(arr)) { + return arr.map(l => { + const translatedLevel = SNIPPET_LEVELS_MAP[l]; + return { + value: translatedLevel, + ...SNIPPET_VISIBILITY[translatedLevel], + }; + }); + } + return []; +}; |