diff --git a/app/assets/javascripts/behaviors/components/global_alerts.vue b/app/assets/javascripts/behaviors/components/global_alerts.vue new file mode 100644 index 00000000000..d7333619110 --- /dev/null +++ b/app/assets/javascripts/behaviors/components/global_alerts.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/assets/javascripts/behaviors/global_alerts.js b/app/assets/javascripts/behaviors/global_alerts.js new file mode 100644 index 00000000000..476291e6b47 --- /dev/null +++ b/app/assets/javascripts/behaviors/global_alerts.js @@ -0,0 +1,17 @@ +import Vue from 'vue'; + +import GlobalAlerts from './components/global_alerts.vue'; + +export const initGlobalAlerts = () => { + const el = document.getElementById('js-global-alerts'); + + if (!el) return false; + + return new Vue({ + el, + name: 'GlobalAlertsRoot', + render(createElement) { + return createElement(GlobalAlerts); + }, + }); +}; diff --git a/app/assets/javascripts/behaviors/index.js b/app/assets/javascripts/behaviors/index.js index 871b1279ce6..dc9153e61f7 100644 --- a/app/assets/javascripts/behaviors/index.js +++ b/app/assets/javascripts/behaviors/index.js @@ -9,6 +9,7 @@ import './quick_submit'; import './requires_input'; import initPageShortcuts from './shortcuts'; import { initToastMessages } from './toasts'; +import { initGlobalAlerts } from './global_alerts'; import './toggler_behavior'; import './preview_markdown'; @@ -24,6 +25,8 @@ initCollapseSidebarOnWindowResize(); initToastMessages(); +initGlobalAlerts(); + window.requestIdleCallback( () => { // Check if we have to Load GFM Input diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_environments_dropdown.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_environments_dropdown.vue index a25f871ac92..abd272ee24b 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_environments_dropdown.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_environments_dropdown.vue @@ -36,6 +36,7 @@ export default { }, data() { return { + customEnvScope: null, isDropdownShown: false, selectedEnvironment: '', searchTerm: '', @@ -68,13 +69,20 @@ export default { filtered = uniq([...filtered, '*']); } + // add custom env scope if it matches the search term + if (this.customEnvScope && this.customEnvScope.startsWith(this.searchTerm)) { + filtered = uniq([...filtered, this.customEnvScope]); + } + return filtered.sort().map((environment) => ({ value: environment, text: environment, })); }, shouldRenderCreateButton() { - return this.searchTerm && !this.environments.includes(this.searchTerm); + return ( + this.searchTerm && ![...this.environments, this.customEnvScope].includes(this.searchTerm) + ); }, shouldRenderDivider() { return ( @@ -98,7 +106,7 @@ export default { this.selectedEnvironment = selected; }, createEnvironmentScope() { - this.$emit('create-environment-scope', this.searchTerm); + this.customEnvScope = this.searchTerm; this.selectEnvironment(this.searchTerm); }, toggleDropdownShown(isShown) { diff --git a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue index c609e05bbb7..14410d8b344 100644 --- a/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue +++ b/app/assets/javascripts/ci/ci_variable_list/components/ci_variable_drawer.vue @@ -11,9 +11,11 @@ import { GlFormTextarea, GlIcon, GlLink, + GlModal, + GlModalDirective, GlSprintf, } from '@gitlab/ui'; -import { __, s__ } from '~/locale'; +import { __, s__, sprintf } from '~/locale'; import { DRAWER_Z_INDEX } from '~/lib/utils/constants'; import { getContentWrapperHeight } from '~/lib/utils/dom_utils'; import { helpPagePath } from '~/helpers/help_page_helper'; @@ -36,10 +38,11 @@ import { awsTokenList } from './ci_variable_autocomplete_tokens'; const trackingMixin = Tracking.mixin({ label: DRAWER_EVENT_LABEL }); export const i18n = { - addVariable: s__('CiVariables|Add Variable'), + addVariable: s__('CiVariables|Add variable'), cancel: __('Cancel'), defaultScope: allEnvironments.text, - editVariable: s__('CiVariables|Edit Variable'), + deleteVariable: s__('CiVariables|Delete variable'), + editVariable: s__('CiVariables|Edit variable'), environments: __('Environments'), environmentScopeLinkTitle: ENVIRONMENT_SCOPE_LINK_TITLE, expandedField: s__('CiVariables|Expand variable reference'), @@ -51,6 +54,7 @@ export const i18n = { maskedDescription: s__( 'CiVariables|Variable will be masked in job logs. Requires values to meet regular expression requirements.', ), + modalDeleteMessage: s__('CiVariables|Do you want to delete the variable %{key}?'), protectedField: s__('CiVariables|Protect variable'), protectedDescription: s__( 'CiVariables|Export variable to pipelines running on protected branches and tags only.', @@ -86,8 +90,12 @@ export default { GlFormTextarea, GlIcon, GlLink, + GlModal, GlSprintf, }, + directives: { + GlModalDirective, + }, mixins: [trackingMixin], inject: ['environmentScopeLink', 'isProtectedByDefault', 'maskableRawRegex', 'maskableRegex'], props: { @@ -170,6 +178,9 @@ export default { modalActionText() { return this.isEditing ? this.$options.i18n.editVariable : this.$options.i18n.addVariable; }, + removeVariableMessage() { + return sprintf(this.$options.i18n.modalDeleteMessage, { key: this.variable.key }); + }, }, watch: { variable: { @@ -188,6 +199,13 @@ export default { close() { this.$emit('close-form'); }, + deleteVariable() { + this.$emit('delete-variable', this.variable); + this.close(); + }, + setEnvironmentScope(scope) { + this.variable = { ...this.variable, environmentScope: scope }; + }, getTrackingErrorProperty() { if (this.isValueEmpty) { return null; @@ -225,164 +243,206 @@ export default { }), i18n, variableOptions, + deleteModal: { + actionPrimary: { + text: __('Delete'), + attributes: { + variant: 'danger', + }, + }, + actionSecondary: { + text: __('Cancel'), + attributes: { + variant: 'default', + }, + }, + }, };