diff --git a/.rubocop.yml b/.rubocop.yml index a00ca6199c2feb3133639fc3270db1ef8cda02e1..3fce90ee7238f167b06aca632f4c80a0be2dd918 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -282,6 +282,14 @@ Cop/ActiveRecordAssociationReload: Gitlab/AvoidFeatureGet: Enabled: true +RSpec/TimecopFreeze: + Enabled: false + AutoCorrect: true + Include: + - 'spec/**/*.rb' + - 'ee/spec/**/*.rb' + - 'qa/spec/**/*.rb' + Naming/PredicateName: Enabled: true Exclude: diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue index c86db28515fc2e0796cee2eb1bc508931dea0b27..3b285e3bc31b90935d1d65099c38d48fb1473730 100644 --- a/app/assets/javascripts/clusters/components/application_row.vue +++ b/app/assets/javascripts/clusters/components/application_row.vue @@ -1,9 +1,8 @@ diff --git a/app/assets/javascripts/create_cluster/eks_cluster/constants.js b/app/assets/javascripts/create_cluster/eks_cluster/constants.js index 27547ed844949e29c73020f02960f8dd992cbaf8..471d6e1f0aa6ae4fd5dea42301e1092e64135005 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/constants.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/constants.js @@ -1 +1,6 @@ -export const KUBERNETES_VERSIONS = [{ name: '1.14', value: '1.14' }]; +export const KUBERNETES_VERSIONS = [ + { name: '1.14', value: '1.14' }, + { name: '1.15', value: '1.15' }, + { name: '1.16', value: '1.16', default: true }, + { name: '1.17', value: '1.17' }, +]; diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js index caf2729a4c76c2ef590a2b235f291ee6f504e5f2..5abff3c7831c7526668d26d06776c46d42a4826a 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/actions.js @@ -56,6 +56,7 @@ export const createCluster = ({ dispatch, state }) => { environment_scope: state.environmentScope, managed: state.gitlabManagedCluster, provider_aws_attributes: { + kubernetes_version: state.kubernetesVersion, region: state.selectedRegion, vpc_id: state.selectedVpc, subnet_ids: state.selectedSubnet, diff --git a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js index d1337e7ea4ab28b341448930d8038121c7755618..ed51e95e434ce81fa38a351bcb748a50f8d4cc7c 100644 --- a/app/assets/javascripts/create_cluster/eks_cluster/store/state.js +++ b/app/assets/javascripts/create_cluster/eks_cluster/store/state.js @@ -1,6 +1,6 @@ import { KUBERNETES_VERSIONS } from '../constants'; -const [{ value: kubernetesVersion }] = KUBERNETES_VERSIONS; +const kubernetesVersion = KUBERNETES_VERSIONS.find(version => version.default).value; export default () => ({ createRolePath: null, diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js new file mode 100644 index 0000000000000000000000000000000000000000..c17f2d2efe41e1f3145ac723fc6e218637aba2e1 --- /dev/null +++ b/app/assets/javascripts/deprecated_jquery_dropdown/gl_dropdown.js @@ -0,0 +1,689 @@ +/* eslint-disable consistent-return */ +import $ from 'jquery'; +import { escape } from 'lodash'; +import fuzzaldrinPlus from 'fuzzaldrin-plus'; +import { visitUrl } from '~/lib/utils/url_utility'; +import { isObject } from '~/lib/utils/type_utility'; +import renderItem from './render'; +import { GitLabDropdownRemote } from './gl_dropdown_remote'; +import { GitLabDropdownInput } from './gl_dropdown_input'; +import { GitLabDropdownFilter } from './gl_dropdown_filter'; + +const LOADING_CLASS = 'is-loading'; + +const PAGE_TWO_CLASS = 'is-page-two'; + +const ACTIVE_CLASS = 'is-active'; + +const INDETERMINATE_CLASS = 'is-indeterminate'; + +let currentIndex = -1; + +const NON_SELECTABLE_CLASSES = '.divider, .separator, .dropdown-header, .dropdown-menu-empty-item'; + +const SELECTABLE_CLASSES = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES}, .option-hidden)`; + +const CURSOR_SELECT_SCROLL_PADDING = 5; + +const FILTER_INPUT = '.dropdown-input .dropdown-input-field:not(.dropdown-no-filter)'; + +const NO_FILTER_INPUT = '.dropdown-input .dropdown-input-field.dropdown-no-filter'; + +export class GitLabDropdown { + constructor(el1, options) { + let selector; + let self; + this.el = el1; + this.options = options; + this.updateLabel = this.updateLabel.bind(this); + this.hidden = this.hidden.bind(this); + this.opened = this.opened.bind(this); + this.shouldPropagate = this.shouldPropagate.bind(this); + self = this; + selector = $(this.el).data('target'); + this.dropdown = selector != null ? $(selector) : $(this.el).parent(); + // Set Defaults + this.filterInput = this.options.filterInput || this.getElement(FILTER_INPUT); + this.noFilterInput = this.options.noFilterInput || this.getElement(NO_FILTER_INPUT); + this.highlight = Boolean(this.options.highlight); + this.icon = Boolean(this.options.icon); + this.filterInputBlur = + this.options.filterInputBlur != null ? this.options.filterInputBlur : true; + // If no input is passed create a default one + self = this; + // If selector was passed + if (typeof this.filterInput === 'string') { + this.filterInput = this.getElement(this.filterInput); + } + const searchFields = this.options.search ? this.options.search.fields : []; + if (this.options.data) { + // If we provided data + // data could be an array of objects or a group of arrays + if (typeof this.options.data === 'object' && !(this.options.data instanceof Function)) { + this.fullData = this.options.data; + currentIndex = -1; + this.parseData(this.options.data); + this.focusTextInput(); + } else { + this.remote = new GitLabDropdownRemote(this.options.data, { + dataType: this.options.dataType, + beforeSend: this.toggleLoading.bind(this), + success: data => { + this.fullData = data; + this.parseData(this.fullData); + this.focusTextInput(); + + // Update dropdown position since remote data may have changed dropdown size + this.dropdown.find('.dropdown-menu-toggle').dropdown('update'); + + if ( + this.options.filterable && + this.filter && + this.filter.input && + this.filter.input.val() && + this.filter.input.val().trim() !== '' + ) { + return this.filter.input.trigger('input'); + } + }, + instance: this, + }); + } + } + if (this.noFilterInput.length) { + this.plainInput = new GitLabDropdownInput(this.noFilterInput, this.options); + this.plainInput.onInput(this.addInput.bind(this)); + } + // Init filterable + if (this.options.filterable) { + this.filter = new GitLabDropdownFilter(this.filterInput, { + elIsInput: $(this.el).is('input'), + filterInputBlur: this.filterInputBlur, + filterByText: this.options.filterByText, + onFilter: this.options.onFilter, + remote: this.options.filterRemote, + query: this.options.data, + keys: searchFields, + instance: this, + elements: () => { + selector = `.dropdown-content li:not(${NON_SELECTABLE_CLASSES})`; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; + } + return $(selector, this.dropdown); + }, + data: () => this.fullData, + callback: data => { + this.parseData(data); + if (this.filterInput.val() !== '') { + selector = SELECTABLE_CLASSES; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = `.dropdown-page-one ${selector}`; + } + if ($(this.el).is('input')) { + currentIndex = -1; + } else { + $(selector, this.dropdown) + .first() + .find('a') + .addClass('is-focused'); + currentIndex = 0; + } + } + }, + }); + } + // Event listeners + this.dropdown.on('shown.bs.dropdown', this.opened); + this.dropdown.on('hidden.bs.dropdown', this.hidden); + $(this.el).on('update.label', this.updateLabel); + this.dropdown.on('click', '.dropdown-menu, .dropdown-menu-close', this.shouldPropagate); + this.dropdown.on('keyup', e => { + // Escape key + if (e.which === 27) { + return $('.dropdown-menu-close', this.dropdown).trigger('click'); + } + }); + this.dropdown.on('blur', 'a', e => { + let $dropdownMenu; + let $relatedTarget; + if (e.relatedTarget != null) { + $relatedTarget = $(e.relatedTarget); + $dropdownMenu = $relatedTarget.closest('.dropdown-menu'); + if ($dropdownMenu.length === 0) { + return this.dropdown.removeClass('show'); + } + } + }); + if (this.dropdown.find('.dropdown-toggle-page').length) { + this.dropdown.find('.dropdown-toggle-page, .dropdown-menu-back').on('click', e => { + e.preventDefault(); + e.stopPropagation(); + return this.togglePage(); + }); + } + if (this.options.selectable) { + selector = '.dropdown-content a'; + if (this.dropdown.find('.dropdown-toggle-page').length) { + selector = '.dropdown-page-one .dropdown-content a'; + } + this.dropdown.on('click', selector, e => { + const $el = $(e.currentTarget); + const selected = self.rowClicked($el); + const selectedObj = selected ? selected[0] : null; + const isMarking = selected ? selected[1] : null; + if (this.options.clicked) { + this.options.clicked.call(this, { + selectedObj, + $el, + e, + isMarking, + }); + } + + // Update label right after all modifications in dropdown has been done + if (this.options.toggleLabel) { + this.updateLabel(selectedObj, $el, this); + } + + $el.trigger('blur'); + }); + } + } + + // Finds an element inside wrapper element + getElement(selector) { + return this.dropdown.find(selector); + } + + toggleLoading() { + return $('.dropdown-menu', this.dropdown).toggleClass(LOADING_CLASS); + } + + togglePage() { + const menu = $('.dropdown-menu', this.dropdown); + if (menu.hasClass(PAGE_TWO_CLASS)) { + if (this.remote) { + this.remote.execute(); + } + } + menu.toggleClass(PAGE_TWO_CLASS); + // Focus first visible input on active page + return this.dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus(); + } + + parseData(data) { + let groupData; + let html; + this.renderedData = data; + if (this.options.filterable && data.length === 0) { + // render no matching results + html = [this.noResults()]; + } + // Handle array groups + else if (isObject(data)) { + html = []; + + Object.keys(data).forEach(name => { + groupData = data[name]; + html.push( + this.renderItem( + { + content: name, + type: 'header', + }, + name, + ), + ); + this.renderData(groupData, name).map(item => html.push(item)); + }); + } else { + // Render each row + html = this.renderData(data); + } + // Render the full menu + const fullHtml = this.renderMenu(html); + return this.appendMenu(fullHtml); + } + + renderData(data, group) { + return data.map((obj, index) => this.renderItem(obj, group || false, index)); + } + + shouldPropagate(e) { + let $target; + if (this.options.multiSelect || this.options.shouldPropagate === false) { + $target = $(e.target); + if ( + $target && + !$target.hasClass('dropdown-menu-close') && + !$target.hasClass('dropdown-menu-close-icon') && + !$target.data('isLink') + ) { + e.stopPropagation(); + + // This prevents automatic scrolling to the top + if ($target.closest('a').length) { + return false; + } + } + + return true; + } + } + + filteredFullData() { + return this.fullData.filter( + r => + typeof r === 'object' && + !Object.prototype.hasOwnProperty.call(r, 'beforeDivider') && + !Object.prototype.hasOwnProperty.call(r, 'header'), + ); + } + + opened(e) { + this.resetRows(); + this.addArrowKeyEvent(); + + const dropdownToggle = this.dropdown.find('.dropdown-menu-toggle'); + const hasFilterBulkUpdate = dropdownToggle.hasClass('js-filter-bulk-update'); + const shouldRefreshOnOpen = dropdownToggle.hasClass('js-gl-dropdown-refresh-on-open'); + const hasMultiSelect = dropdownToggle.hasClass('js-multiselect'); + + // Makes indeterminate items effective + if (this.fullData && (shouldRefreshOnOpen || hasFilterBulkUpdate)) { + this.parseData(this.fullData); + } + + // Process the data to make sure rendered data + // matches the correct layout + const inputValue = this.filterInput.val(); + if (this.fullData && hasMultiSelect && this.options.processData && inputValue.length === 0) { + this.options.processData.call( + this.options, + inputValue, + this.filteredFullData(), + this.parseData.bind(this), + ); + } + + const contentHtml = $('.dropdown-content', this.dropdown).html(); + if (this.remote && contentHtml === '') { + this.remote.execute(); + } else { + this.focusTextInput(); + } + + if (this.options.showMenuAbove) { + this.positionMenuAbove(); + } + + if (this.options.opened) { + if (this.options.preserveContext) { + this.options.opened(e); + } else { + this.options.opened.call(this, e); + } + } + + return this.dropdown.trigger('shown.gl.dropdown'); + } + + positionMenuAbove() { + const $menu = this.dropdown.find('.dropdown-menu'); + + $menu.addClass('dropdown-open-top'); + $menu.css('top', 'initial'); + $menu.css('bottom', '100%'); + } + + hidden(e) { + this.resetRows(); + this.removeArrowKeyEvent(); + const $input = this.dropdown.find('.dropdown-input-field'); + if (this.options.filterable) { + $input.blur(); + } + if (this.dropdown.find('.dropdown-toggle-page').length) { + $('.dropdown-menu', this.dropdown).removeClass(PAGE_TWO_CLASS); + } + if (this.options.hidden) { + this.options.hidden.call(this, e); + } + return this.dropdown.trigger('hidden.gl.dropdown'); + } + + // Render the full menu + renderMenu(html) { + if (this.options.renderMenu) { + return this.options.renderMenu(html); + } + return $('