diff --git a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue index be2366108b31d405af3faa655595a00b704dac28..7e9faa154f3906984d5e1821fa216e1f8d86caf4 100644 --- a/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue +++ b/app/assets/javascripts/ci_variable_list/components/ci_variable_modal.vue @@ -26,6 +26,7 @@ import { AWS_TIP_DISMISSED_COOKIE_NAME, AWS_TIP_MESSAGE, CONTAINS_VARIABLE_REFERENCE_MESSAGE, + ENVIRONMENT_SCOPE_LINK_TITLE, EVENT_LABEL, EVENT_ACTION, } from '../constants'; @@ -40,6 +41,7 @@ export default { tokenList: awsTokenList, awsTipMessage: AWS_TIP_MESSAGE, containsVariableReferenceMessage: CONTAINS_VARIABLE_REFERENCE_MESSAGE, + environmentScopeLinkTitle: ENVIRONMENT_SCOPE_LINK_TITLE, components: { CiEnvironmentsDropdown, GlAlert, @@ -81,6 +83,7 @@ export default { 'containsVariableReferenceLink', 'protectedEnvironmentVariablesLink', 'maskedEnvironmentVariablesLink', + 'environmentScopeLink', ]), ...mapComputed( [ @@ -278,12 +281,18 @@ export default { <gl-form-select id="ci-variable-type" v-model="variable_type" :options="typeOptions" /> </gl-form-group> - <gl-form-group - :label="__('Environment scope')" - label-for="ci-variable-env" - class="w-50" - data-testid="environment-scope" - > + <gl-form-group label-for="ci-variable-env" class="w-50" data-testid="environment-scope"> + <template #label> + {{ __('Environment scope') }} + <gl-link + :title="$options.environmentScopeLinkTitle" + :href="environmentScopeLink" + target="_blank" + data-testid="environment-scope-link" + > + <gl-icon name="question" :size="12" /> + </gl-link> + </template> <ci-environments-dropdown v-if="scopedVariablesAvailable" class="w-100" diff --git a/app/assets/javascripts/ci_variable_list/constants.js b/app/assets/javascripts/ci_variable_list/constants.js index 663a912883bcafb4ea1b837677b1bca12ca18950..fa55b4d9e77fa2b6b712bf654d5f04a4603cdfd8 100644 --- a/app/assets/javascripts/ci_variable_list/constants.js +++ b/app/assets/javascripts/ci_variable_list/constants.js @@ -31,3 +31,5 @@ export const AWS_TOKEN_CONSTANTS = [AWS_ACCESS_KEY_ID, AWS_DEFAULT_REGION, AWS_S export const CONTAINS_VARIABLE_REFERENCE_MESSAGE = __( 'Values that contain the %{codeStart}$%{codeEnd} character can be considered a variable reference and expanded. %{docsLinkStart}Learn more.%{docsLinkEnd}', ); + +export const ENVIRONMENT_SCOPE_LINK_TITLE = __('Learn more'); diff --git a/app/assets/javascripts/ci_variable_list/index.js b/app/assets/javascripts/ci_variable_list/index.js index 7c40f8134d455303ae75f10cc7eab6574f455fec..f771751194cf70a5fdb20f805072e4c0b0ca4b34 100644 --- a/app/assets/javascripts/ci_variable_list/index.js +++ b/app/assets/javascripts/ci_variable_list/index.js @@ -17,6 +17,7 @@ const mountCiVariableListApp = (containerEl) => { containsVariableReferenceLink, protectedEnvironmentVariablesLink, maskedEnvironmentVariablesLink, + environmentScopeLink, } = containerEl.dataset; const isGroup = parseBoolean(group); const isProtectedByDefault = parseBoolean(protectedByDefault); @@ -34,6 +35,7 @@ const mountCiVariableListApp = (containerEl) => { containsVariableReferenceLink, protectedEnvironmentVariablesLink, maskedEnvironmentVariablesLink, + environmentScopeLink, }); return new Vue({ diff --git a/app/views/ci/variables/_index.html.haml b/app/views/ci/variables/_index.html.haml index f289e6a338647e5fe16748277caac9525ed9ff66..6fc85e73de658e0c04527971e8e3b610282b7d9d 100644 --- a/app/views/ci/variables/_index.html.haml +++ b/app/views/ci/variables/_index.html.haml @@ -19,7 +19,7 @@ contains_variable_reference_link: help_page_path('ci/variables/index', anchor: 'use-variables-in-other-variables'), protected_environment_variables_link: help_page_path('ci/variables/index', anchor: 'protect-a-cicd-variable'), masked_environment_variables_link: help_page_path('ci/variables/index', anchor: 'mask-a-cicd-variable'), -} } + environment_scope_link: help_page_path('ci/environments/index', anchor: 'scope-environments-with-specs') } } - if !@group && @project.group .settings-header.border-top.gl-mt-6 diff --git a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js index 2fedbbecd64e974ba9c4e319ef94a767b1e82744..d26378d938292654564591064c91f5ea3adc9ea6 100644 --- a/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js +++ b/spec/frontend/ci_variable_list/components/ci_variable_modal_spec.js @@ -5,7 +5,12 @@ import Vuex from 'vuex'; import { mockTracking } from 'helpers/tracking_helper'; import CiEnvironmentsDropdown from '~/ci_variable_list/components/ci_environments_dropdown.vue'; import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue'; -import { AWS_ACCESS_KEY_ID, EVENT_LABEL, EVENT_ACTION } from '~/ci_variable_list/constants'; +import { + AWS_ACCESS_KEY_ID, + EVENT_LABEL, + EVENT_ACTION, + ENVIRONMENT_SCOPE_LINK_TITLE, +} from '~/ci_variable_list/constants'; import createStore from '~/ci_variable_list/store'; import mockData from '../services/mock_data'; import ModalStub from '../stubs'; @@ -20,7 +25,11 @@ describe('Ci variable modal', () => { const maskableRegex = '^[a-zA-Z0-9_+=/@:.~-]{8,}$'; const createComponent = (method, options = {}) => { - store = createStore({ maskableRegex, isGroup: options.isGroup }); + store = createStore({ + maskableRegex, + isGroup: options.isGroup, + environmentScopeLink: '/help/environments', + }); wrapper = method(CiVariableModal, { attachTo: document.body, stubs: { @@ -213,6 +222,15 @@ describe('Ci variable modal', () => { }); }); }); + + it('renders a link to documentation on scopes', () => { + createComponent(mount); + + const link = wrapper.find('[data-testid="environment-scope-link"]'); + + expect(link.attributes('title')).toBe(ENVIRONMENT_SCOPE_LINK_TITLE); + expect(link.attributes('href')).toBe('/help/environments'); + }); }); describe('Validations', () => {