diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 7424ab7ea26..5fcc55bb0b0 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -3666,7 +3666,6 @@ Layout/LineLength: - 'qa/qa/ee/page/project/secure/security_dashboard.rb' - 'qa/qa/ee/page/project/secure/show.rb' - 'qa/qa/ee/resource/license.rb' - - 'qa/qa/fixtures/auto_devops_rack/config.ru' - 'qa/qa/flow/sign_up.rb' - 'qa/qa/git/repository.rb' - 'qa/qa/page/base.rb' diff --git a/.rubocop_todo/style/lambda.yml b/.rubocop_todo/style/lambda.yml index 5b898417d96..525e2c31797 100644 --- a/.rubocop_todo/style/lambda.yml +++ b/.rubocop_todo/style/lambda.yml @@ -217,7 +217,6 @@ Style/Lambda: - 'lib/gitlab/sidekiq_signals.rb' - 'lib/gitlab/utils/measuring.rb' - 'lib/gitlab/visibility_level.rb' - - 'qa/qa/fixtures/auto_devops_rack/config.ru' - 'rubocop/cop/rspec/modify_sidekiq_middleware.rb' - 'rubocop/cop/rspec/timecop_freeze.rb' - 'rubocop/cop/rspec/timecop_travel.rb' diff --git a/Gemfile.lock b/Gemfile.lock index 7b78495dea2..4ba19e79aea 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1281,7 +1281,7 @@ GEM shellany (0.0.1) shoulda-matchers (5.1.0) activesupport (>= 5.2.0) - sidekiq (6.4.0) + sidekiq (6.4.2) connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.2.0) diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue index cd5521c599e..0bd7371d39b 100644 --- a/app/assets/javascripts/groups/components/app.vue +++ b/app/assets/javascripts/groups/components/app.vue @@ -17,11 +17,6 @@ export default { GlLoadingIcon, EmptyState, }, - inject: { - renderEmptyState: { - default: false, - }, - }, props: { action: { type: String, @@ -45,6 +40,11 @@ export default { type: Boolean, required: true, }, + renderEmptyState: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -224,6 +224,9 @@ export default { }, showLegacyEmptyState() { const { containerEl } = this; + + if (!containerEl) return; + const contentListEl = containerEl.querySelector(CONTENT_LIST_CLASS); const emptyStateEl = containerEl.querySelector('.empty-state'); diff --git a/app/assets/javascripts/groups/components/overview_tabs.vue b/app/assets/javascripts/groups/components/overview_tabs.vue new file mode 100644 index 00000000000..53efb354f5c --- /dev/null +++ b/app/assets/javascripts/groups/components/overview_tabs.vue @@ -0,0 +1,80 @@ + + + diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js index dc2909f2621..c3bf3f28509 100644 --- a/app/assets/javascripts/groups/index.js +++ b/app/assets/javascripts/groups/index.js @@ -52,7 +52,6 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { newSubgroupIllustration, newProjectIllustration, emptySubgroupIllustration, - renderEmptyState, canCreateSubgroups, canCreateProjects, currentGroupVisibility, @@ -65,7 +64,6 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { newSubgroupIllustration, newProjectIllustration, emptySubgroupIllustration, - renderEmptyState: parseBoolean(renderEmptyState), canCreateSubgroups: parseBoolean(canCreateSubgroups), canCreateProjects: parseBoolean(canCreateProjects), currentGroupVisibility, @@ -75,6 +73,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { const { dataset } = dataEl || this.$options.el; const hideProjects = parseBoolean(dataset.hideProjects); const showSchemaMarkup = parseBoolean(dataset.showSchemaMarkup); + const renderEmptyState = parseBoolean(dataset.renderEmptyState); const service = new GroupsService(endpoint || dataset.endpoint); const store = new GroupsStore({ hideProjects, showSchemaMarkup }); @@ -83,6 +82,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { store, service, hideProjects, + renderEmptyState, loading: true, containerId, }; @@ -119,6 +119,7 @@ export default (containerId = 'js-groups-tree', endpoint, action = '') => { store: this.store, service: this.service, hideProjects: this.hideProjects, + renderEmptyState: this.renderEmptyState, containerId: this.containerId, }, }); diff --git a/app/assets/javascripts/groups/init_overview_tabs.js b/app/assets/javascripts/groups/init_overview_tabs.js new file mode 100644 index 00000000000..5f568d10a42 --- /dev/null +++ b/app/assets/javascripts/groups/init_overview_tabs.js @@ -0,0 +1,57 @@ +import Vue from 'vue'; +import { GlToast } from '@gitlab/ui'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import GroupFolder from './components/group_folder.vue'; +import GroupItem from './components/group_item.vue'; +import { + ACTIVE_TAB_SUBGROUPS_AND_PROJECTS, + ACTIVE_TAB_SHARED, + ACTIVE_TAB_ARCHIVED, +} from './constants'; +import OverviewTabs from './components/overview_tabs.vue'; + +export const initGroupOverviewTabs = () => { + const el = document.getElementById('js-group-overview-tabs'); + + if (!el) return false; + + Vue.component('GroupFolder', GroupFolder); + Vue.component('GroupItem', GroupItem); + Vue.use(GlToast); + + const { + newSubgroupPath, + newProjectPath, + newSubgroupIllustration, + newProjectIllustration, + emptySubgroupIllustration, + canCreateSubgroups, + canCreateProjects, + currentGroupVisibility, + subgroupsAndProjectsEndpoint, + sharedProjectsEndpoint, + archivedProjectsEndpoint, + } = el.dataset; + + return new Vue({ + el, + provide: { + newSubgroupPath, + newProjectPath, + newSubgroupIllustration, + newProjectIllustration, + emptySubgroupIllustration, + canCreateSubgroups: parseBoolean(canCreateSubgroups), + canCreateProjects: parseBoolean(canCreateProjects), + currentGroupVisibility, + endpoints: { + [ACTIVE_TAB_SUBGROUPS_AND_PROJECTS]: subgroupsAndProjectsEndpoint, + [ACTIVE_TAB_SHARED]: sharedProjectsEndpoint, + [ACTIVE_TAB_ARCHIVED]: archivedProjectsEndpoint, + }, + }, + render(createElement) { + return createElement(OverviewTabs); + }, + }); +}; diff --git a/app/assets/javascripts/issuable/issuable_form.js b/app/assets/javascripts/issuable/issuable_form.js index cc2608b5c62..4c6685820cf 100644 --- a/app/assets/javascripts/issuable/issuable_form.js +++ b/app/assets/javascripts/issuable/issuable_form.js @@ -39,6 +39,11 @@ function format(searchTerm, isFallbackKey = false) { return formattedQuery; } +function getSearchTerm(newIssuePath) { + const { search, pathname } = document.location; + return newIssuePath === pathname ? '' : format(search); +} + function getFallbackKey() { const searchTerm = format(document.location.search, true); return ['autosave', document.location.pathname, searchTerm].join('/'); @@ -72,7 +77,8 @@ export default class IssuableForm { this.reviewersSelect = new UsersSelect(undefined, '.js-reviewer-search'); this.zenMode = new ZenMode(); - this.newIssuePath = form[0].getAttribute(DATA_ISSUES_NEW_PATH); + this.searchTerm = getSearchTerm(form[0].getAttribute(DATA_ISSUES_NEW_PATH)); + this.fallbackKey = getFallbackKey(); this.titleField = this.form.find('input[name*="[title]"]'); this.descriptionField = this.form.find('textarea[name*="[description]"]'); if (!(this.titleField.length && this.descriptionField.length)) { @@ -109,20 +115,16 @@ export default class IssuableForm { } initAutosave() { - const { search, pathname } = document.location; - const searchTerm = this.newIssuePath === pathname ? '' : format(search); - const fallbackKey = getFallbackKey(); - - this.autosave = new Autosave( + this.autosaveTitle = new Autosave( this.titleField, - [document.location.pathname, searchTerm, 'title'], - `${fallbackKey}=title`, + [document.location.pathname, this.searchTerm, 'title'], + `${this.fallbackKey}=title`, ); - return new Autosave( + this.autosaveDescription = new Autosave( this.descriptionField, - [document.location.pathname, searchTerm, 'description'], - `${fallbackKey}=description`, + [document.location.pathname, this.searchTerm, 'description'], + `${this.fallbackKey}=description`, ); } @@ -131,8 +133,8 @@ export default class IssuableForm { } resetAutosave() { - this.titleField.data('autosave').reset(); - return this.descriptionField.data('autosave').reset(); + this.autosaveTitle.reset(); + this.autosaveDescription.reset(); } initWip() { diff --git a/app/assets/javascripts/pages/groups/details/index.js b/app/assets/javascripts/pages/groups/details/index.js index 0417134f2a7..92490368b15 100644 --- a/app/assets/javascripts/pages/groups/details/index.js +++ b/app/assets/javascripts/pages/groups/details/index.js @@ -1,3 +1,5 @@ +import { initGroupOverviewTabs } from '~/groups/init_overview_tabs'; import initGroupDetails from '../shared/group_details'; initGroupDetails('details'); +initGroupOverviewTabs(); diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js index e4a84dd5eec..161fca83a58 100644 --- a/app/assets/javascripts/pages/groups/show/index.js +++ b/app/assets/javascripts/pages/groups/show/index.js @@ -1,5 +1,7 @@ import leaveByUrl from '~/namespaces/leave_by_url'; +import { initGroupOverviewTabs } from '~/groups/init_overview_tabs'; import initGroupDetails from '../shared/group_details'; leaveByUrl('group'); initGroupDetails(); +initGroupOverviewTabs(); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue index aa5ab87597f..300e2a672cb 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue @@ -13,6 +13,7 @@ import Poll from '~/lib/utils/poll'; import { normalizeHeaders } from '~/lib/utils/common_utils'; import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants'; import Actions from '../action_buttons.vue'; +import StateContainer from '../state_container.vue'; import StatusIcon from './status_icon.vue'; import ChildContent from './child_content.vue'; import { createTelemetryHub } from './telemetry'; @@ -36,6 +37,7 @@ export default { ChildContent, DynamicScroller, DynamicScrollerItem, + StateContainer, }, directives: { SafeHtml: GlSafeHtmlDirective, @@ -312,18 +314,15 @@ export default { data-testid="widget-extension" data-qa-selector="mr_widget_extension" > -
-
- +
- +
+ +
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue index 2bba8d2dc82..03728e2831b 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/state_container.vue @@ -44,7 +44,14 @@ export default {