summaryrefslogtreecommitdiff
path: root/app/assets/javascripts
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-05-18 15:10:46 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-05-18 15:10:46 +0000
commit346c2ebb5a818524c5d8d95dc6b9fc8c892e3b5c (patch)
tree63bf068a9d8cedf0b7f9e80cf99e491813617db1 /app/assets/javascripts
parent5f287a8e306da1e0b6152a0b7f8621a8e71a3fe4 (diff)
downloadgitlab-ce-346c2ebb5a818524c5d8d95dc6b9fc8c892e3b5c.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue16
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue225
-rw-r--r--app/assets/javascripts/security_configuration/components/feature_card.vue150
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue1
4 files changed, 328 insertions, 64 deletions
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index afd7467acf3..d9887c2ce05 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -5,6 +5,7 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import { hasDiff } from '~/helpers/diffs_helper';
import { diffViewerErrors } from '~/ide/constants';
+import { scrollToElement } from '~/lib/utils/common_utils';
import { sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import notesEventHub from '../../notes/event_hub';
@@ -233,15 +234,20 @@ export default {
eventHub.$emit(event);
});
},
- handleToggle() {
- const currentCollapsedFlag = this.isCollapsed;
+ handleToggle({ viaUserInteraction = false } = {}) {
+ const collapsingNow = !this.isCollapsed;
+ const contentElement = this.$el.querySelector(`#diff-content-${this.file.file_hash}`);
this.setFileCollapsedByUser({
filePath: this.file.file_path,
- collapsed: !currentCollapsedFlag,
+ collapsed: collapsingNow,
});
- if (!this.hasDiff && currentCollapsedFlag) {
+ if (collapsingNow && viaUserInteraction && contentElement) {
+ scrollToElement(contentElement, { duration: 1 });
+ }
+
+ if (!this.hasDiff && !collapsingNow) {
this.requestDiff();
}
},
@@ -300,7 +306,7 @@ export default {
:codequality-diff="codequalityDiffForFile"
class="js-file-title file-title gl-border-1 gl-border-solid gl-border-gray-100"
:class="hasBodyClasses.header"
- @toggleFile="handleToggle"
+ @toggleFile="handleToggle({ viaUserInteraction: true })"
@showForkMessage="showForkMessage"
/>
diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
index cc85582ec77..43753926039 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
@@ -1,5 +1,15 @@
<script>
-import { GlForm, GlIcon, GlLink, GlButton, GlSprintf, GlAlert, GlLoadingIcon } from '@gitlab/ui';
+import {
+ GlForm,
+ GlIcon,
+ GlLink,
+ GlButton,
+ GlSprintf,
+ GlAlert,
+ GlLoadingIcon,
+ GlModal,
+ GlModalDirective,
+} from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import csrf from '~/lib/utils/csrf';
import { setUrlFragment } from '~/lib/utils/url_utility';
@@ -15,6 +25,67 @@ const MARKDOWN_LINK_TEXT = {
};
export default {
+ i18n: {
+ title: {
+ label: s__('WikiPage|Title'),
+ placeholder: s__('WikiPage|Page title'),
+ helpText: {
+ existingPage: s__(
+ 'WikiPage|Tip: You can move this page by adding the path to the beginning of the title.',
+ ),
+ newPage: s__(
+ 'WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories.',
+ ),
+ moreInformation: s__('WikiPage|More Information.'),
+ },
+ },
+ format: {
+ label: s__('WikiPage|Format'),
+ },
+ content: {
+ label: s__('WikiPage|Content'),
+ placeholder: s__('WikiPage|Write your content or drag files here…'),
+ },
+ contentEditor: {
+ renderFailed: {
+ message: s__(
+ 'WikiPage|An error occured while trying to render the content editor. Please try again later.',
+ ),
+ primaryAction: s__('WikiPage|Retry'),
+ },
+ useNewEditor: s__('WikiPage|Use new editor'),
+ switchToOldEditor: {
+ label: s__('WikiPage|Switch to old editor'),
+ helpText: s__("WikiPage|Switching will discard any changes you've made in the new editor."),
+ modal: {
+ title: s__('WikiPage|Are you sure you want to switch to the old editor?'),
+ primary: s__('WikiPage|Switch to old editor'),
+ cancel: s__('WikiPage|Keep editing'),
+ text: s__(
+ "WikiPage|Switching to the old editor will discard any changes you've made in the new editor.",
+ ),
+ },
+ },
+ helpText: s__(
+ "WikiPage|This editor is in beta and may not display the page's contents properly.",
+ ),
+ },
+ linksHelpText: s__(
+ 'WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}.',
+ ),
+ commitMessage: {
+ label: s__('WikiPage|Commit message'),
+ value: {
+ existingPage: s__('WikiPage|Update %{pageTitle}'),
+ newPage: s__('WikiPage|Create %{pageTitle}'),
+ },
+ },
+ submitButton: {
+ existingPage: s__('WikiPage|Save changes'),
+ newPage: s__('WikiPage|Create page'),
+ },
+ cancel: s__('WikiPage|Cancel'),
+ },
components: {
GlAlert,
GlForm,
@@ -22,6 +93,7 @@ export default {
GlIcon,
GlLink,
GlButton,
+ GlModal,
MarkdownField,
GlLoadingIcon,
ContentEditor: () =>
@@ -29,6 +101,9 @@ export default {
/* webpackChunkName: 'content_editor' */ '~/content_editor/components/content_editor.vue'
),
},
+ directives: {
+ GlModalDirective,
+ },
mixins: [glFeatureFlagMixin()],
inject: ['formatOptions', 'pageInfo'],
data() {
@@ -41,6 +116,7 @@ export default {
commitMessage: '',
contentEditor: null,
isDirty: false,
+ contentEditorRenderFailed: false,
};
},
computed: {
@@ -58,15 +134,21 @@ export default {
},
commitMessageI18n() {
return this.pageInfo.persisted
- ? s__('WikiPage|Update %{pageTitle}')
- : s__('WikiPage|Create %{pageTitle}');
+ ? this.$options.i18n.commitMessage.value.existingPage
+ : this.$options.i18n.commitMessage.value.newPage;
},
linkExample() {
return MARKDOWN_LINK_TEXT[this.format];
},
submitButtonText() {
- if (this.pageInfo.persisted) return s__('WikiPage|Save changes');
- return s__('WikiPage|Create page');
+ return this.pageInfo.persisted
+ ? this.$options.i18n.submitButton.existingPage
+ : this.$options.i18n.submitButton.newPage;
+ },
+ titleHelpText() {
+ return this.pageInfo.persisted
+ ? this.$options.i18n.title.helpText.existingPage
+ : this.$options.i18n.title.helpText.newPage;
},
cancelFormPath() {
if (this.pageInfo.persisted) return this.pageInfo.path;
@@ -81,6 +163,9 @@ export default {
showContentEditorButton() {
return this.isMarkdownFormat && !this.useContentEditor && this.glFeatures.wikiContentEditor;
},
+ disableSubmitButton() {
+ return !this.content || !this.title || this.contentEditorRenderFailed;
+ },
isContentEditorActive() {
return this.isMarkdownFormat && this.useContentEditor;
},
@@ -147,14 +232,32 @@ export default {
onUpdate: () => this.handleContentChange(),
},
});
- await this.contentEditor.setSerializedContent(this.content);
- this.isContentEditorLoading = false;
+ try {
+ await this.contentEditor.setSerializedContent(this.content);
+ this.isContentEditorLoading = false;
+ } catch (e) {
+ this.contentEditorRenderFailed = true;
+ }
+ },
+
+ retryInitContentEditor() {
+ this.contentEditorRenderFailed = false;
+ this.initContentEditor();
},
switchToOldEditor() {
this.useContentEditor = false;
},
+
+ confirmSwitchToOldEditor() {
+ if (this.contentEditorRenderFailed) {
+ this.contentEditorRenderFailed = false;
+ this.switchToOldEditor();
+ } else {
+ this.$refs.confirmSwitchToOldEditorModal.show();
+ }
+ },
},
};
</script>
@@ -167,26 +270,15 @@ export default {
@submit="handleFormSubmit"
>
<gl-alert
- v-if="isContentEditorActive"
+ v-if="isContentEditorActive && contentEditorRenderFailed"
class="gl-mb-6"
:dismissible="false"
variant="danger"
- :primary-button-text="s__('WikiPage|Switch to old editor')"
- @primaryAction="switchToOldEditor()"
+ :primary-button-text="$options.i18n.contentEditor.renderFailed.primaryAction"
+ @primaryAction="retryInitContentEditor()"
>
<p>
- {{
- s__(
- "WikiPage|You are editing this page with Content Editor. This editor is in beta and may not display the page's contents properly.",
- )
- }}
- </p>
- <p>
- {{
- s__(
- "WikiPage|Switching to the old editor will discard any changes you've made in the new editor.",
- )
- }}
+ {{ $options.i18n.contentEditor.renderFailed.message }}
</p>
</gl-alert>
@@ -200,7 +292,9 @@ export default {
/>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
- <label class="control-label-full-width" for="wiki_title">{{ s__('WikiPage|Title') }}</label>
+ <label class="control-label-full-width" for="wiki_title">{{
+ $options.i18n.title.label
+ }}</label>
</div>
<div class="col-sm-10">
<input
@@ -212,22 +306,15 @@ export default {
data-qa-selector="wiki_title_textbox"
:required="true"
:autofocus="!pageInfo.persisted"
- :placeholder="s__('WikiPage|Page title')"
+ :placeholder="$options.i18n.title.placeholder"
@input="updateCommitMessage"
/>
<span class="gl-display-inline-block gl-max-w-full gl-mt-2 gl-text-gray-600">
<gl-icon class="gl-mr-n1" name="bulb" />
- {{
- pageInfo.persisted
- ? s__(
- 'WikiPage|Tip: You can move this page by adding the path to the beginning of the title.',
- )
- : s__(
- 'WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories.',
- )
- }}
+ {{ titleHelpText }}
<gl-link :href="helpPath" target="_blank"
- ><gl-icon name="question-o" /> {{ s__('WikiPage|More Information.') }}</gl-link
+ ><gl-icon name="question-o" />
+ {{ $options.i18n.title.helpText.moreInformation }}</gl-link
>
</span>
</div>
@@ -235,10 +322,10 @@ export default {
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_format">{{
- s__('WikiPage|Format')
+ $options.i18n.format.label
}}</label>
</div>
- <div class="col-sm-10 gl-display-flex gl-flex-wrap">
+ <div class="col-sm-10">
<select
id="wiki_format"
v-model="format"
@@ -250,20 +337,43 @@ export default {
{{ label }}
</option>
</select>
- <gl-button
- v-if="showContentEditorButton"
- category="secondary"
- variant="confirm"
- class="gl-mt-4"
- @click="initContentEditor"
- >{{ s__('WikiPage|Use new editor') }}</gl-button
- >
+ <div>
+ <gl-button
+ v-if="showContentEditorButton"
+ category="secondary"
+ variant="confirm"
+ class="gl-mt-4"
+ @click="initContentEditor"
+ >{{ $options.i18n.contentEditor.useNewEditor }}</gl-button
+ >
+ <div v-if="isContentEditorActive" class="gl-mt-4 gl-display-flex">
+ <div class="gl-mr-4">
+ <gl-button category="secondary" variant="confirm" @click="confirmSwitchToOldEditor">{{
+ $options.i18n.contentEditor.switchToOldEditor.label
+ }}</gl-button>
+ </div>
+ <div class="gl-mt-2">
+ <gl-icon name="warning" />
+ {{ $options.i18n.contentEditor.switchToOldEditor.helpText }}
+ </div>
+ </div>
+ <gl-modal
+ ref="confirmSwitchToOldEditorModal"
+ modal-id="confirm-switch-to-old-editor"
+ :title="$options.i18n.contentEditor.switchToOldEditor.modal.title"
+ :action-primary="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.primary }"
+ :action-cancel="{ text: $options.i18n.contentEditor.switchToOldEditor.modal.cancel }"
+ @primary="switchToOldEditor"
+ >
+ {{ $options.i18n.contentEditor.switchToOldEditor.modal.text }}
+ </gl-modal>
+ </div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_content">{{
- s__('WikiPage|Content')
+ $options.i18n.content.label
}}</label>
</div>
<div class="col-sm-10">
@@ -288,8 +398,8 @@ export default {
data-supports-quick-actions="false"
data-qa-selector="wiki_content_textarea"
:autofocus="pageInfo.persisted"
- :aria-label="s__('WikiPage|Content')"
- :placeholder="s__('WikiPage|Write your content or drag files here…')"
+ :aria-label="$options.i18n.content.label"
+ :placeholder="$options.i18n.content.placeholder"
@input="handleContentChange"
>
</textarea>
@@ -305,14 +415,8 @@ export default {
<div class="clearfix"></div>
<div class="error-alert"></div>
- <div v-if="!isContentEditorActive" class="form-text gl-text-gray-600">
- <gl-sprintf
- :message="
- s__(
- 'WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}.',
- )
- "
- >
+ <div class="form-text gl-text-gray-600">
+ <gl-sprintf v-if="!isContentEditorActive" :message="$options.i18n.linksHelpText">
<template #linkExample
><code>{{ linkExample }}</code></template
>
@@ -327,13 +431,16 @@ export default {
></template
>
</gl-sprintf>
+ <span v-else>
+ {{ $options.i18n.contentEditor.helpText }}
+ </span>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-sm-2 col-form-label">
<label class="control-label-full-width" for="wiki_message">{{
- s__('WikiPage|Commit message')
+ $options.i18n.commitMessage.label
}}</label>
</div>
<div class="col-sm-10">
@@ -344,7 +451,7 @@ export default {
type="text"
class="form-control"
data-qa-selector="wiki_message_textbox"
- :placeholder="s__('WikiPage|Commit message')"
+ :placeholder="$options.i18n.commitMessage.label"
/>
</div>
</div>
@@ -355,10 +462,10 @@ export default {
type="submit"
data-qa-selector="wiki_submit_button"
data-testid="wiki-submit-button"
- :disabled="!content || !title"
+ :disabled="disableSubmitButton"
>{{ submitButtonText }}</gl-button
>
- <gl-button :href="cancelFormPath" class="float-right">{{ s__('WikiPage|Cancel') }}</gl-button>
+ <gl-button :href="cancelFormPath" class="float-right">{{ $options.i18n.cancel }}</gl-button>
</div>
</gl-form>
</template>
diff --git a/app/assets/javascripts/security_configuration/components/feature_card.vue b/app/assets/javascripts/security_configuration/components/feature_card.vue
new file mode 100644
index 00000000000..518a6ede3de
--- /dev/null
+++ b/app/assets/javascripts/security_configuration/components/feature_card.vue
@@ -0,0 +1,150 @@
+<script>
+import { GlButton, GlCard, GlIcon, GlLink } from '@gitlab/ui';
+import { __, s__, sprintf } from '~/locale';
+import ManageViaMr from '~/vue_shared/security_configuration/components/manage_via_mr.vue';
+
+export default {
+ components: {
+ GlButton,
+ GlCard,
+ GlIcon,
+ GlLink,
+ ManageViaMr,
+ },
+ props: {
+ feature: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ available() {
+ return this.feature.available;
+ },
+ enabled() {
+ return this.available && this.feature.configured;
+ },
+ hasStatus() {
+ return !this.available || typeof this.feature.configured === 'boolean';
+ },
+ shortName() {
+ return this.feature.shortName ?? this.feature.name;
+ },
+ configurationButton() {
+ const button = this.enabled
+ ? {
+ text: this.$options.i18n.configureFeature,
+ category: 'secondary',
+ }
+ : {
+ text: this.$options.i18n.enableFeature,
+ category: 'primary',
+ };
+
+ button.text = sprintf(button.text, { feature: this.shortName });
+
+ return button;
+ },
+ showManageViaMr() {
+ const { available, configured, canEnableByMergeRequest } = this.feature;
+ return canEnableByMergeRequest && available && !configured;
+ },
+ cardClasses() {
+ return { 'gl-bg-gray-10': !this.available };
+ },
+ statusClasses() {
+ const { enabled } = this;
+
+ return {
+ 'gl-ml-auto': true,
+ 'gl-flex-shrink-0': true,
+ 'gl-text-gray-500': !enabled,
+ 'gl-text-green-500': enabled,
+ };
+ },
+ hasSecondary() {
+ const { name, description, configurationText } = this.feature.secondary ?? {};
+ return Boolean(name && description && configurationText);
+ },
+ },
+ i18n: {
+ enabled: s__('SecurityConfiguration|Enabled'),
+ notEnabled: s__('SecurityConfiguration|Not enabled'),
+ availableWith: s__('SecurityConfiguration|Available with Ultimate'),
+ configurationGuide: s__('SecurityConfiguration|Configuration guide'),
+ configureFeature: s__('SecurityConfiguration|Configure %{feature}'),
+ enableFeature: s__('SecurityConfiguration|Enable %{feature}'),
+ learnMore: __('Learn more'),
+ },
+};
+</script>
+
+<template>
+ <gl-card :class="cardClasses">
+ <div class="gl-display-flex gl-align-items-baseline">
+ <h3 class="gl-font-lg gl-m-0 gl-mr-3">{{ feature.name }}</h3>
+
+ <div :class="statusClasses" data-testid="feature-status">
+ <template v-if="hasStatus">
+ <template v-if="enabled">
+ <gl-icon name="check-circle-filled" />
+ <span class="gl-text-green-700">{{ $options.i18n.enabled }}</span>
+ </template>
+
+ <template v-else-if="available">
+ {{ $options.i18n.notEnabled }}
+ </template>
+
+ <template v-else>
+ {{ $options.i18n.availableWith }}
+ </template>
+ </template>
+ </div>
+ </div>
+
+ <p class="gl-mb-0 gl-mt-5">
+ {{ feature.description }}
+ <gl-link :href="feature.helpPath">{{ $options.i18n.learnMore }}</gl-link>
+ </p>
+
+ <template v-if="available">
+ <gl-button
+ v-if="feature.configurationPath"
+ :href="feature.configurationPath"
+ variant="confirm"
+ :category="configurationButton.category"
+ class="gl-mt-5"
+ >
+ {{ configurationButton.text }}
+ </gl-button>
+
+ <manage-via-mr
+ v-else-if="showManageViaMr"
+ :feature="feature"
+ variant="confirm"
+ category="primary"
+ class="gl-mt-5"
+ />
+
+ <gl-button v-else icon="external-link" :href="feature.configurationHelpPath" class="gl-mt-5">
+ {{ $options.i18n.configurationGuide }}
+ </gl-button>
+ </template>
+
+ <div v-if="hasSecondary" data-testid="secondary-feature">
+ <h4 class="gl-font-base gl-m-0 gl-mt-6">{{ feature.secondary.name }}</h4>
+
+ <p class="gl-mb-0 gl-mt-5">{{ feature.secondary.description }}</p>
+
+ <gl-button
+ v-if="available && feature.secondary.configurationPath"
+ :href="feature.secondary.configurationPath"
+ variant="confirm"
+ category="secondary"
+ class="gl-mt-5"
+ >
+ {{ feature.secondary.configurationText }}
+ </gl-button>
+ </div>
+ </gl-card>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
index 75831643f6a..0cd280c42d2 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_enabled.vue
@@ -169,6 +169,7 @@ export default {
role="button"
href="#"
class="btn btn-sm btn-default js-cancel-auto-merge"
+ data-qa-selector="cancel_auto_merge_button"
data-testid="cancelAutomaticMergeButton"
@click.prevent="cancelAutomaticMerge"
>