diff --git a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue index 722dc29d7465959bb7a191d553e845763887b547..f4ce8f900ec4fbb919dda5c93e971961d0a7fc11 100644 --- a/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue +++ b/app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue @@ -3,8 +3,7 @@ import { GlAlert, GlIcon, GlButton, - GlDropdown, - GlDropdownItem, + GlCollapsibleListbox, GlForm, GlFormGroup, GlFormInput, @@ -50,10 +49,6 @@ const i18n = { }; export default { - typeOptions: { - [VARIABLE_TYPE]: __('Variable'), - [FILE_TYPE]: __('File'), - }, i18n, formElementClasses: 'gl-mr-3 gl-mb-3 gl-flex-basis-quarter gl-flex-shrink-0 gl-flex-grow-0', // this height value is used inline on the textarea to match the input field height @@ -63,8 +58,7 @@ export default { GlAlert, GlIcon, GlButton, - GlDropdown, - GlDropdownItem, + GlCollapsibleListbox, GlForm, GlFormGroup, GlFormInput, @@ -227,6 +221,18 @@ export default { ccRequiredError() { return this.error === CC_VALIDATION_REQUIRED_ERROR && !this.ccAlertDismissed; }, + variableTypeListboxItems() { + return [ + { + value: VARIABLE_TYPE, + text: s__('Pipeline|Variable'), + }, + { + value: FILE_TYPE, + text: s__('Pipeline|File'), + }, + ]; + }, }, methods: { addEmptyVariable(refValue) { @@ -369,6 +375,12 @@ export default { this.ccAlertDismissed = true; this.error = null; }, + createListItemsFromVariableOptions(key) { + return this.configVariablesWithDescription.options[key].map((option) => ({ + text: option, + value: option, + })); + }, }, }; </script> @@ -443,19 +455,15 @@ export default { <div class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row" > - <gl-dropdown - :text="$options.typeOptions[variable.variable_type]" + <gl-collapsible-listbox + :items="variableTypeListboxItems" + :selected="variable.variable_type" + block + fluid-width :class="$options.formElementClasses" data-testid="pipeline-form-ci-variable-type" - > - <gl-dropdown-item - v-for="type in Object.keys($options.typeOptions)" - :key="type" - @click="setVariableAttribute(variable.key, 'variable_type', type)" - > - {{ $options.typeOptions[type] }} - </gl-dropdown-item> - </gl-dropdown> + @select="setVariableAttribute(variable.key, 'variable_type', $event)" + /> <gl-form-input v-model="variable.key" :placeholder="s__('CiVariables|Input variable key')" @@ -463,22 +471,17 @@ export default { data-testid="pipeline-form-ci-variable-key-field" @change="addEmptyVariable(refFullName)" /> - <gl-dropdown + <gl-collapsible-listbox v-if="shouldShowValuesDropdown(variable.key)" - :text="variable.value" + :items="createListItemsFromVariableOptions(variable.key)" + :selected="variable.value" + block + fluid-width :class="$options.formElementClasses" class="gl-flex-grow-1 gl-mr-0!" data-testid="pipeline-form-ci-variable-value-dropdown" - > - <gl-dropdown-item - v-for="option in configVariablesWithDescription.options[variable.key]" - :key="option" - data-testid="ci-variable-value-dropdown-item" - @click="setVariableAttribute(variable.key, 'value', option)" - > - {{ option }} - </gl-dropdown-item> - </gl-dropdown> + @select="setVariableAttribute(variable.key, 'value', $event)" + /> <gl-form-textarea v-else v-model="variable.value" diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2c249a38de7c688ef69a040d655fb0626b3e4e6f..ba6e997710b42ac45e50729af4a2a5e4167d26a5 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -36586,6 +36586,9 @@ msgstr "" msgid "Pipeline|Failed" msgstr "" +msgid "Pipeline|File" +msgstr "" + msgid "Pipeline|Manual" msgstr "" @@ -36703,6 +36706,9 @@ msgstr "" msgid "Pipeline|Trigger author" msgstr "" +msgid "Pipeline|Variable" +msgstr "" + msgid "Pipeline|Variables" msgstr "" diff --git a/qa/qa/page/project/pipeline/new.rb b/qa/qa/page/project/pipeline/new.rb index 33aa93d620435199a7e612834f92dac06aca5638..7005c1a3c6546904e1b729b9e015237fdb839bfd 100644 --- a/qa/qa/page/project/pipeline/new.rb +++ b/qa/qa/page/project/pipeline/new.rb @@ -11,7 +11,6 @@ class New < QA::Page::Base element 'pipeline-form-ci-variable-key-field' element 'pipeline-form-ci-variable-value-field' element 'pipeline-form-ci-variable-value-dropdown' - element 'ci-variable-value-dropdown-item' end def click_run_pipeline_button @@ -44,8 +43,8 @@ def variable_dropdown def variable_dropdown_item_with_index(index) return unless has_variable_dropdown? - within_element_by_index('ci-variable-value-dropdown-item', index) do - find('p') + within_element_by_index('.gl-new-dropdown-item', index) do + find('.gl-new-dropdown-item-text-wrapper') end end end diff --git a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js index 2807cc0f2a1927c06cebbceb0643717eed0cf749..369c8794fa9d592454d98e7fe6be38995b7b5047 100644 --- a/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js +++ b/spec/frontend/ci/pipeline_new/components/pipeline_new_form_spec.js @@ -1,6 +1,6 @@ import Vue, { nextTick } from 'vue'; import VueApollo from 'vue-apollo'; -import { GlForm, GlDropdownItem, GlSprintf, GlLoadingIcon } from '@gitlab/ui'; +import { GlForm, GlSprintf, GlLoadingIcon } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; import CreditCardValidationRequiredAlert from 'ee_component/billings/components/cc_validation_required_alert.vue'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -58,12 +58,12 @@ describe('Pipeline New Form', () => { const findSubmitButton = () => wrapper.findByTestId('run-pipeline-button'); const findVariableRows = () => wrapper.findAllByTestId('ci-variable-row-container'); const findRemoveIcons = () => wrapper.findAllByTestId('remove-ci-variable-row'); - const findVariableTypes = () => wrapper.findAllByTestId('pipeline-form-ci-variable-type'); + const findCollapsableListsWithVariableTypes = () => + wrapper.findAllByTestId('pipeline-form-ci-variable-type'); const findKeyInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-key-field'); const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value-field'); - const findValueDropdowns = () => - wrapper.findAllByTestId('pipeline-form-ci-variable-value-dropdown'); - const findValueDropdownItems = (dropdown) => dropdown.findAllComponents(GlDropdownItem); + const findCollapsableListWithVariableOptions = () => + wrapper.findAllByTestId('pipeline-form-ci-variable-value-dropdown').at(0); const findErrorAlert = () => wrapper.findByTestId('run-pipeline-error-alert'); const findPipelineConfigButton = () => wrapper.findByTestId('ci-cd-pipeline-configuration'); const findWarningAlert = () => wrapper.findByTestId('run-pipeline-warning-alert'); @@ -137,8 +137,10 @@ describe('Pipeline New Form', () => { }); it('displays the correct values for the provided query params', () => { - expect(findVariableTypes().at(0).props('text')).toBe('Variable'); - expect(findVariableTypes().at(1).props('text')).toBe('File'); + const collapsableLists = findCollapsableListsWithVariableTypes(); + + expect(collapsableLists.at(0).props('selected')).toBe('env_var'); + expect(collapsableLists.at(1).props('selected')).toBe('file'); expect(findRefsDropdown().props('value')).toEqual({ shortName: 'tag-1' }); expect(findVariableRows()).toHaveLength(3); }); @@ -149,9 +151,11 @@ describe('Pipeline New Form', () => { }); it('displays an empty variable for the user to fill out', () => { + const collapsableLists = findCollapsableListsWithVariableTypes(); + expect(findKeyInputs().at(2).attributes('value')).toBe(''); expect(findValueInputs().at(2).attributes('value')).toBe(''); - expect(findVariableTypes().at(2).props('text')).toBe('Variable'); + expect(collapsableLists.at(2).props('selected')).toBe('env_var'); }); it('does not display remove icon for last row', () => { @@ -359,9 +363,11 @@ describe('Pipeline New Form', () => { createComponentWithApollo(); await waitForPromises(); + const collapsableLists = findCollapsableListsWithVariableTypes(); + expect(findKeyInputs().at(0).attributes('value')).toBe(''); expect(findValueInputs().at(0).attributes('value')).toBe(''); - expect(findVariableTypes().at(0).props('text')).toBe('Variable'); + expect(collapsableLists.at(0).props('selected')).toBe('env_var'); }); }); @@ -379,21 +385,21 @@ describe('Pipeline New Form', () => { expect(findValueInputs().at(1).attributes('value')).toBe(mockYamlVariables[1].value); }); - it('multiple predefined values are rendered as a dropdown', () => { - const dropdown = findValueDropdowns().at(0); - const dropdownItems = findValueDropdownItems(dropdown); - const { valueOptions } = mockYamlVariables[2]; + it('passes the correct data to the collapsible list, which will be displayed as items of the collapsible list', () => { + const collapsableList = findCollapsableListWithVariableOptions(); + const expectedItems = mockYamlVariables[2].valueOptions.map((item) => ({ + text: item, + value: item, + })); - expect(dropdownItems.at(0).text()).toBe(valueOptions[0]); - expect(dropdownItems.at(1).text()).toBe(valueOptions[1]); - expect(dropdownItems.at(2).text()).toBe(valueOptions[2]); + expect(collapsableList.props('items')).toMatchObject(expectedItems); }); - it('variable with multiple predefined values sets value as the default', () => { - const dropdown = findValueDropdowns().at(0); + it('passes the correct default variable option value to the collapsable list', () => { + const collapsableList = findCollapsableListWithVariableOptions(); const { valueOptions } = mockYamlVariables[2]; - expect(dropdown.props('text')).toBe(valueOptions[1]); + expect(collapsableList.props('selected')).toBe(valueOptions[1]); }); });