diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-28 18:09:45 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-28 18:09:45 +0300 |
commit | d62fd6e04c272d48dccde4033529ca97c27502f6 (patch) | |
tree | e3bbea524f4bccb92048fd8a52a42b757618b57b /app | |
parent | aaff41e10e8c03e545af9ba157e79f67686972a0 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
29 files changed, 335 insertions, 422 deletions
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index 719f252781d..5cf0f4ad5ab 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -464,6 +464,7 @@ export default { </template> <gl-dropdown-item v-if="showToggleIssueStateButton && glFeatures.moveCloseIntoDropdown" + data-testid="toggle-issue-state-button" @click="toggleIssueState" > {{ buttonText }} diff --git a/app/assets/javascripts/search/sidebar/components/app.vue b/app/assets/javascripts/search/sidebar/components/app.vue index cd289be4c05..cd0976a9fd6 100644 --- a/app/assets/javascripts/search/sidebar/components/app.vue +++ b/app/assets/javascripts/search/sidebar/components/app.vue @@ -5,7 +5,8 @@ import ScopeSidebarNavigation from '~/search/sidebar/components/scope_sidebar_na import SidebarPortal from '~/super_sidebar/components/sidebar_portal.vue'; import { SCOPE_ISSUES, SCOPE_MERGE_REQUESTS, SCOPE_BLOB } from '../constants'; import IssuesFilters from './issues_filters.vue'; -import LanguageFilter from './language_filter/index.vue'; +import MergeRequestsFilters from './merge_requests_filters.vue'; +import BlobsFilters from './blobs_filters.vue'; export default { name: 'GlobalSearchSidebar', @@ -13,25 +14,27 @@ export default { IssuesFilters, ScopeLegacyNavigation, ScopeSidebarNavigation, - LanguageFilter, SidebarPortal, + MergeRequestsFilters, + BlobsFilters, }, computed: { // useSidebarNavigation refers to whether the new left sidebar navigation is enabled ...mapState(['useSidebarNavigation']), ...mapGetters(['currentScope']), - showIssueAndMergeFilters() { - return this.currentScope === SCOPE_ISSUES || this.currentScope === SCOPE_MERGE_REQUESTS; + showIssuesFilters() { + return this.currentScope === SCOPE_ISSUES; }, - showBlobFilter() { - return this.currentScope === SCOPE_BLOB; + showMergeRequestFilters() { + return this.currentScope === SCOPE_MERGE_REQUESTS; }, - showLabelFilter() { - return this.currentScope === SCOPE_ISSUES; + showBlobFilters() { + return this.currentScope === SCOPE_BLOB; }, showScopeNavigation() { // showScopeNavigation refers to whether the scope navigation should be shown - // while the legacy navigation is being used and there are no search results the scope navigation has to be hidden + // while the legacy navigation is being used and there are no search results + // the scope navigation has to be hidden return Boolean(this.currentScope); }, }, @@ -42,8 +45,9 @@ export default { <section v-if="useSidebarNavigation"> <sidebar-portal> <scope-sidebar-navigation /> - <issues-filters v-if="showIssueAndMergeFilters" /> - <language-filter v-if="showBlobFilter" /> + <issues-filters v-if="showIssuesFilters" /> + <merge-requests-filters v-if="showMergeRequestFilters" /> + <blobs-filters v-if="showBlobFilters" /> </sidebar-portal> </section> <section @@ -51,7 +55,8 @@ export default { class="search-sidebar gl-display-flex gl-flex-direction-column gl-md-mr-5 gl-mb-6 gl-mt-5" > <scope-legacy-navigation /> - <issues-filters v-if="showIssueAndMergeFilters" /> - <language-filter v-if="showBlobFilter" /> + <issues-filters v-if="showIssuesFilters" /> + <merge-requests-filters v-if="showMergeRequestFilters" /> + <blobs-filters v-if="showBlobFilters" /> </section> </template> diff --git a/app/assets/javascripts/search/sidebar/components/blobs_filters.vue b/app/assets/javascripts/search/sidebar/components/blobs_filters.vue new file mode 100644 index 00000000000..5f4d6fbd56c --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/blobs_filters.vue @@ -0,0 +1,18 @@ +<script> +import LanguageFilter from './language_filter/index.vue'; +import FiltersTemplate from './filters_template.vue'; + +export default { + name: 'BlobsFilters', + components: { + LanguageFilter, + FiltersTemplate, + }, +}; +</script> + +<template> + <filters-template> + <language-filter class="gl-mb-5" /> + </filters-template> +</template> diff --git a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue b/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue deleted file mode 100644 index bca049e56c7..00000000000 --- a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue +++ /dev/null @@ -1,96 +0,0 @@ -<script> -import Vue from 'vue'; -import { GlFormCheckboxGroup, GlFormCheckbox } from '@gitlab/ui'; -import { mapState, mapActions, mapGetters } from 'vuex'; -import { intersection } from 'lodash'; -import Tracking from '~/tracking'; -import { NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES } from '../constants'; -import { formatSearchResultCount } from '../../store/utils'; - -export const TRACKING_LABEL_SET = 'set'; -export const TRACKING_LABEL_CHECKBOX = 'checkbox'; - -export default { - name: 'CheckboxFilter', - components: { - GlFormCheckboxGroup, - GlFormCheckbox, - }, - props: { - filtersData: { - type: Object, - required: true, - }, - trackingNamespace: { - type: String, - required: true, - }, - }, - computed: { - ...mapState(['query', 'useSidebarNavigation']), - ...mapGetters(['queryLanguageFilters']), - dataFilters() { - return Object.values(this.filtersData?.filters || []); - }, - flatDataFilterValues() { - return this.dataFilters.map(({ value }) => value); - }, - selectedFilter: { - get() { - return intersection(this.flatDataFilterValues, this.queryLanguageFilters); - }, - async set(value) { - this.setQuery({ key: this.filtersData?.filterParam, value }); - - await Vue.nextTick(); - this.trackSelectCheckbox(); - }, - }, - labelCountClasses() { - return [...NAV_LINK_COUNT_DEFAULT_CLASSES, 'gl-text-gray-500']; - }, - }, - methods: { - ...mapActions(['setQuery']), - getFormattedCount(count) { - return formatSearchResultCount(count); - }, - trackSelectCheckbox() { - Tracking.event(this.trackingNamespace, TRACKING_LABEL_CHECKBOX, { - label: TRACKING_LABEL_SET, - property: this.selectedFilter, - }); - }, - }, - NAV_LINK_COUNT_DEFAULT_CLASSES, - LABEL_DEFAULT_CLASSES, -}; -</script> - -<template> - <div class="gl-mx-5"> - <h5 class="gl-mt-0" :class="{ 'gl-font-sm': useSidebarNavigation }"> - {{ filtersData.header }} - </h5> - <gl-form-checkbox-group v-model="selectedFilter"> - <gl-form-checkbox - v-for="f in dataFilters" - :key="f.label" - :value="f.label" - class="gl-flex-grow-1 gl-display-inline-flex gl-justify-content-space-between gl-w-full" - :class="$options.LABEL_DEFAULT_CLASSES" - > - <span - class="gl-flex-grow-1 gl-display-inline-flex gl-justify-content-space-between gl-w-full" - > - <span data-testid="label"> - {{ f.label }} - </span> - <span v-if="f.count" :class="labelCountClasses" data-testid="labelCount"> - {{ getFormattedCount(f.count) }} - </span> - </span> - </gl-form-checkbox> - </gl-form-checkbox-group> - </div> -</template> diff --git a/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue b/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue index 312092b9904..ea3a27168c8 100644 --- a/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue @@ -1,6 +1,5 @@ <script> import { mapState } from 'vuex'; -import { HR_DEFAULT_CLASSES } from '../../constants'; import RadioFilter from '../radio_filter.vue'; import { confidentialFilterData } from './data'; @@ -13,7 +12,6 @@ export default { ...mapState(['useSidebarNavigation']), }, confidentialFilterData, - HR_DEFAULT_CLASSES, }; </script> diff --git a/app/assets/javascripts/search/sidebar/components/filters_template.vue b/app/assets/javascripts/search/sidebar/components/filters_template.vue new file mode 100644 index 00000000000..29c62096dcf --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/filters_template.vue @@ -0,0 +1,59 @@ +<script> +import { GlButton, GlLink, GlForm } from '@gitlab/ui'; +import { mapActions, mapState, mapGetters } from 'vuex'; +import Tracking from '~/tracking'; + +import { + HR_DEFAULT_CLASSES, + TRACKING_ACTION_CLICK, + TRACKING_LABEL_APPLY, + TRACKING_LABEL_RESET, +} from '../constants/index'; + +export default { + name: 'FiltersTemplate', + components: { + GlButton, + GlLink, + GlForm, + }, + computed: { + ...mapState(['sidebarDirty', 'useSidebarNavigation']), + ...mapGetters(['currentScope']), + hrClasses() { + return [...HR_DEFAULT_CLASSES, 'gl-display-none', 'gl-md-display-block']; + }, + }, + methods: { + ...mapActions(['applyQuery', 'resetQuery']), + applyQueryWithTracking() { + Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, { + label: this.currentScope, + }); + this.applyQuery(); + }, + resetQueryWithTracking() { + Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_RESET, { + label: this.currentScope, + }); + this.resetQuery(); + }, + }, +}; +</script> + +<template> + <gl-form class="issue-filters gl-px-5 gl-pt-0" @submit.prevent="applyQueryWithTracking"> + <hr v-if="!useSidebarNavigation" :class="hrClasses" /> + <slot></slot> + <hr v-if="!useSidebarNavigation" :class="hrClasses" /> + <div class="gl-display-flex gl-align-items-center gl-mt-4"> + <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty"> + {{ __('Apply') }} + </gl-button> + <gl-link v-if="sidebarDirty" class="gl-ml-auto" @click="resetQueryWithTracking">{{ + __('Reset filters') + }}</gl-link> + </div> + </gl-form> +</template> diff --git a/app/assets/javascripts/search/sidebar/components/issues_filters.vue b/app/assets/javascripts/search/sidebar/components/issues_filters.vue index 5fd8b6418d7..df3fd464a2d 100644 --- a/app/assets/javascripts/search/sidebar/components/issues_filters.vue +++ b/app/assets/javascripts/search/sidebar/components/issues_filters.vue @@ -1,15 +1,7 @@ <script> -import { GlButton, GlLink } from '@gitlab/ui'; -import { mapActions, mapState, mapGetters } from 'vuex'; -import Tracking from '~/tracking'; +import { mapGetters, mapState } from 'vuex'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { - HR_DEFAULT_CLASSES, - TRACKING_ACTION_CLICK, - TRACKING_LABEL_APPLY, - TRACKING_CATEGORY, - TRACKING_LABEL_RESET, -} from '../constants/index'; +import { HR_DEFAULT_CLASSES } from '../constants/index'; import { confidentialFilterData } from './confidentiality_filter/data'; import { statusFilterData } from './status_filter/data'; import ConfidentialityFilter from './confidentiality_filter/index.vue'; @@ -17,22 +9,20 @@ import { labelFilterData } from './label_filter/data'; import LabelFilter from './label_filter/index.vue'; import StatusFilter from './status_filter/index.vue'; +import FiltersTemplate from './filters_template.vue'; + export default { name: 'IssuesFilters', components: { - GlButton, - GlLink, StatusFilter, ConfidentialityFilter, LabelFilter, + FiltersTemplate, }, mixins: [glFeatureFlagsMixin()], computed: { - ...mapState(['urlQuery', 'sidebarDirty', 'useSidebarNavigation']), ...mapGetters(['currentScope']), - showReset() { - return this.urlQuery.state || this.urlQuery.confidential || this.urlQuery.labels; - }, + ...mapState(['useSidebarNavigation']), showConfidentialityFilter() { return Object.values(confidentialFilterData.scopes).includes(this.currentScope); }, @@ -45,46 +35,22 @@ export default { this.glFeatures.searchIssueLabelAggregation ); }, + showDivider() { + return !this.useSidebarNavigation; + }, hrClasses() { return [...HR_DEFAULT_CLASSES, 'gl-display-none', 'gl-md-display-block']; }, }, - methods: { - ...mapActions(['applyQuery', 'resetQuery']), - applyQueryWithTracking() { - Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, { - label: TRACKING_CATEGORY, - }); - this.applyQuery(); - }, - resetQueryWithTracking() { - Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_RESET, { - label: TRACKING_CATEGORY, - }); - this.resetQuery(); - }, - }, }; </script> <template> - <form class="issue-filters gl-px-5 gl-pt-0" @submit.prevent="applyQueryWithTracking"> - <hr v-if="!useSidebarNavigation" :class="hrClasses" /> + <filters-template> <status-filter v-if="showStatusFilter" class="gl-mb-5" /> - <hr v-if="!useSidebarNavigation" :class="hrClasses" /> + <hr v-if="showConfidentialityFilter && showDivider" :class="hrClasses" /> <confidentiality-filter v-if="showConfidentialityFilter" class="gl-mb-5" /> - <hr - v-if="!useSidebarNavigation && showConfidentialityFilter && showLabelFilter" - :class="hrClasses" - /> + <hr v-if="showLabelFilter && showDivider" :class="hrClasses" /> <label-filter v-if="showLabelFilter" /> - <div class="gl-display-flex gl-align-items-center gl-mt-4"> - <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty"> - {{ __('Apply') }} - </gl-button> - <gl-link v-if="showReset" class="gl-ml-auto" @click="resetQueryWithTracking">{{ - __('Reset filters') - }}</gl-link> - </div> - </form> + </filters-template> </template> diff --git a/app/assets/javascripts/search/sidebar/components/label_filter/index.vue b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue index eb3556ac2cf..f14c95b41aa 100644 --- a/app/assets/javascripts/search/sidebar/components/label_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/label_filter/index.vue @@ -19,8 +19,6 @@ import { sprintf } from '~/locale'; import DropdownKeyboardNavigation from '~/vue_shared/components/dropdown_keyboard_navigation.vue'; import { I18N } from '~/vue_shared/global_search/constants'; - -import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../../constants'; import LabelDropdownItems from './label_dropdown_items.vue'; import { @@ -62,7 +60,6 @@ export default { 'filteredUnselectedLabels', 'filteredAppliedSelectedLabels', 'appliedSelectedLabels', - 'filteredUnappliedSelectedLabels', ]), searchInputDescribeBy() { if (this.isLoggedIn) { @@ -107,9 +104,6 @@ export default { hasUnselectedLabels() { return this.filteredUnselectedLabels.length > 0; }, - dividerClasses() { - return [...HR_DEFAULT_CLASSES, ...ONLY_SHOW_MD]; - }, labelSearchBox() { return this.$refs.searchLabelInputBox?.$el.querySelector('[role=searchbox]'); }, @@ -277,6 +271,5 @@ export default { <gl-loading-icon v-if="aggregations.fetching" size="lg" class="my-4" /> </div> </div> - <hr v-if="!useSidebarNavigation" :class="dividerClasses" /> </div> </template> diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue b/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue index b42fe9185cb..404cfd9a1e5 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue @@ -1,7 +1,7 @@ <script> import Vue from 'vue'; import { GlFormCheckboxGroup, GlFormCheckbox } from '@gitlab/ui'; -import { mapState, mapActions, mapGetters } from 'vuex'; +import { mapActions, mapGetters } from 'vuex'; import { intersection } from 'lodash'; import Tracking from '~/tracking'; import { NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES } from '../../constants'; @@ -27,7 +27,6 @@ export default { }, }, computed: { - ...mapState(['query', 'useSidebarNavigation']), ...mapGetters(['queryLanguageFilters']), dataFilters() { return Object.values(this.filtersData?.filters || []); @@ -62,7 +61,6 @@ export default { }); }, }, - NAV_LINK_COUNT_DEFAULT_CLASSES, LABEL_DEFAULT_CLASSES, }; </script> diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue index e5560dd5b55..b958c4d8eeb 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue @@ -1,17 +1,10 @@ <script> -import { GlButton, GlAlert, GlForm } from '@gitlab/ui'; +import { GlButton, GlAlert } from '@gitlab/ui'; import { mapState, mapActions, mapGetters } from 'vuex'; -import { __, s__, sprintf } from '~/locale'; -import { HR_DEFAULT_CLASSES, ONLY_SHOW_MD } from '../../constants'; +import { s__, sprintf } from '~/locale'; import { convertFiltersData } from '../../utils'; import CheckboxFilter from './checkbox_filter.vue'; -import { - trackShowMore, - trackShowHasOverMax, - trackSubmitQuery, - trackResetQuery, - TRACKING_ACTION_SELECT, -} from './tracking'; +import { trackShowMore, trackShowHasOverMax, TRACKING_ACTION_SELECT } from './tracking'; import { DEFAULT_ITEM_LENGTH, MAX_ITEM_LENGTH, languageFilterData } from './data'; @@ -21,7 +14,6 @@ export default { CheckboxFilter, GlButton, GlAlert, - GlForm, }, data() { return { @@ -30,18 +22,12 @@ export default { }, i18n: { showMore: s__('GlobalSearch|Show more'), - apply: __('Apply'), showingMax: sprintf(s__('GlobalSearch|Showing top %{maxItems}'), { maxItems: MAX_ITEM_LENGTH }), loadError: s__('GlobalSearch|Aggregations load error.'), - reset: s__('GlobalSearch|Reset filters'), }, computed: { - ...mapState(['aggregations', 'sidebarDirty', 'useSidebarNavigation']), - ...mapGetters([ - 'languageAggregationBuckets', - 'currentUrlQueryHasLanguageFilters', - 'queryLanguageFilters', - ]), + ...mapState(['aggregations', 'useSidebarNavigation']), + ...mapGetters(['languageAggregationBuckets']), hasBuckets() { return this.languageAggregationBuckets.length > 0; }, @@ -63,26 +49,12 @@ export default { hasOverMax() { return this.languageAggregationBuckets.length > MAX_ITEM_LENGTH; }, - dividerClassesTop() { - return [...HR_DEFAULT_CLASSES, ...ONLY_SHOW_MD]; - }, - dividerClassesBottom() { - return [...HR_DEFAULT_CLASSES, 'gl-mt-5']; - }, - hasQueryFilters() { - return this.queryLanguageFilters.length > 0; - }, }, async created() { await this.fetchAllAggregation(); }, methods: { - ...mapActions([ - 'applyQuery', - 'resetLanguageQuery', - 'resetLanguageQueryWithRedirect', - 'fetchAllAggregation', - ]), + ...mapActions(['fetchAllAggregation']), onShowMore() { this.showAll = true; trackShowMore(); @@ -91,91 +63,47 @@ export default { trackShowHasOverMax(); } }, - submitQuery() { - trackSubmitQuery(); - this.applyQuery(); - }, trimBuckets(length) { return this.languageAggregationBuckets.slice(0, length); }, - cleanResetFilters() { - trackResetQuery(); - if (this.currentUrlQueryHasLanguageFilters) { - return this.resetLanguageQueryWithRedirect(); - } - this.showAll = false; - return this.resetLanguageQuery(); - }, }, - HR_DEFAULT_CLASSES, TRACKING_ACTION_SELECT, languageFilterData, }; </script> <template> - <div> - <gl-form - v-if="hasBuckets" - class="gl-m-5 gl-my-0 language-filter-checkbox" - @submit.prevent="submitQuery" + <div v-if="hasBuckets" class="gl-my-0 language-filter-checkbox"> + <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useSidebarNavigation }"> + {{ $options.languageFilterData.header }} + </h5> + <div + v-if="!aggregations.error" + class="gl-overflow-x-hidden gl-overflow-y-auto" + :class="{ 'language-filter-max-height': showAll }" > - <hr v-if="!useSidebarNavigation" :class="dividerClassesTop" /> - <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useSidebarNavigation }"> - {{ $options.languageFilterData.header }} - </h5> - <div - v-if="!aggregations.error" - class="gl-overflow-x-hidden gl-overflow-y-auto" - :class="{ 'language-filter-max-height': showAll }" + <checkbox-filter + :filters-data="filtersData" + :tracking-namespace="$options.TRACKING_ACTION_SELECT" + /> + <span v-if="showAll && hasOverMax" data-testid="has-over-max-text">{{ + $options.i18n.showingMax + }}</span> + </div> + <gl-alert v-else class="gl-mx-5" variant="danger" :dismissible="false">{{ + $options.i18n.loadError + }}</gl-alert> + <div v-if="hasShowMore && !showAll" class="language-filter-show-all"> + <gl-button + data-testid="show-more-button" + category="tertiary" + variant="link" + size="small" + button-text-classes="gl-font-sm" + @click="onShowMore" > - <checkbox-filter - :filters-data="filtersData" - :tracking-namespace="$options.TRACKING_ACTION_SELECT" - /> - <span v-if="showAll && hasOverMax" data-testid="has-over-max-text">{{ - $options.i18n.showingMax - }}</span> - </div> - <gl-alert v-else class="gl-mx-5" variant="danger" :dismissible="false">{{ - $options.i18n.loadError - }}</gl-alert> - <div v-if="hasShowMore && !showAll" class="gl-px-5 language-filter-show-all"> - <gl-button - data-testid="show-more-button" - category="tertiary" - variant="link" - size="small" - button-text-classes="gl-font-sm" - @click="onShowMore" - > - {{ $options.i18n.showMore }} - </gl-button> - </div> - <div v-if="!aggregations.error"> - <hr v-if="!useSidebarNavigation" :class="dividerClassesBottom" /> - <div class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-mt-4"> - <gl-button - category="primary" - variant="confirm" - type="submit" - :disabled="!sidebarDirty" - data-testid="apply-button" - > - {{ $options.i18n.apply }} - </gl-button> - <gl-button - v-if="hasQueryFilters && sidebarDirty" - category="tertiary" - variant="link" - size="small" - data-testid="reset-button" - @click="cleanResetFilters" - > - {{ $options.i18n.reset }} - </gl-button> - </div> - </div> - </gl-form> + {{ $options.i18n.showMore }} + </gl-button> + </div> </div> </template> diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js b/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js index db107830329..5f085c7df7e 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js +++ b/app/assets/javascripts/search/sidebar/components/language_filter/tracking.js @@ -27,13 +27,3 @@ export const trackShowHasOverMax = () => label: TRACKING_LABEL_MAX, property: TRACKING_PROPERTY_MAX, }); - -export const trackSubmitQuery = () => - Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_APPLY, { - label: TRACKING_CATEGORY, - }); - -export const trackResetQuery = () => - Tracking.event(TRACKING_ACTION_CLICK, TRACKING_LABEL_RESET, { - label: TRACKING_CATEGORY, - }); diff --git a/app/assets/javascripts/search/sidebar/components/merge_requests_filters.vue b/app/assets/javascripts/search/sidebar/components/merge_requests_filters.vue new file mode 100644 index 00000000000..bc5b797dd56 --- /dev/null +++ b/app/assets/javascripts/search/sidebar/components/merge_requests_filters.vue @@ -0,0 +1,18 @@ +<script> +import StatusFilter from './status_filter/index.vue'; +import FiltersTemplate from './filters_template.vue'; + +export default { + name: 'MergeRequestsFilters', + components: { + StatusFilter, + FiltersTemplate, + }, +}; +</script> + +<template> + <filters-template> + <status-filter class="gl-mb-5" /> + </filters-template> +</template> diff --git a/app/assets/javascripts/search/sidebar/components/results_filters.vue b/app/assets/javascripts/search/sidebar/components/results_filters.vue deleted file mode 100644 index 88e434cf99e..00000000000 --- a/app/assets/javascripts/search/sidebar/components/results_filters.vue +++ /dev/null @@ -1,54 +0,0 @@ -<script> -import { GlButton, GlLink } from '@gitlab/ui'; -import { mapActions, mapState, mapGetters } from 'vuex'; -import { HR_DEFAULT_CLASSES } from '../constants/index'; -import { confidentialFilterData } from './confidentiality_filter/data'; -import { statusFilterData } from './status_filter/data'; -import ConfidentialityFilter from './confidentiality_filter/index.vue'; -import StatusFilter from './status_filter/index.vue'; - -export default { - name: 'ResultsFilters', - components: { - GlButton, - GlLink, - StatusFilter, - ConfidentialityFilter, - }, - computed: { - ...mapState(['urlQuery', 'sidebarDirty', 'useSidebarNavigation']), - ...mapGetters(['currentScope']), - showReset() { - return this.urlQuery.state || this.urlQuery.confidential; - }, - showConfidentialityFilter() { - return Object.values(confidentialFilterData.scopes).includes(this.currentScope); - }, - showStatusFilter() { - return Object.values(statusFilterData.scopes).includes(this.currentScope); - }, - hrClasses() { - return [...HR_DEFAULT_CLASSES, 'gl-display-none', 'gl-md-display-block']; - }, - }, - methods: { - ...mapActions(['applyQuery', 'resetQuery']), - }, -}; -</script> - -<template> - <form class="gl-pt-5 gl-md-pt-0" @submit.prevent="applyQuery"> - <hr v-if="!useSidebarNavigation" :class="hrClasses" /> - <status-filter v-if="showStatusFilter" /> - <confidentiality-filter v-if="showConfidentialityFilter" /> - <div class="gl-display-flex gl-align-items-center gl-mt-4 gl-px-5"> - <gl-button category="primary" variant="confirm" type="submit" :disabled="!sidebarDirty"> - {{ __('Apply') }} - </gl-button> - <gl-link v-if="showReset" class="gl-ml-auto" @click="resetQuery">{{ - __('Reset filters') - }}</gl-link> - </div> - </form> -</template> diff --git a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue index 3707e152e47..70fad49724c 100644 --- a/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue +++ b/app/assets/javascripts/search/sidebar/components/scope_sidebar_navigation.vue @@ -1,7 +1,6 @@ <script> import { mapActions, mapState, mapGetters } from 'vuex'; import { s__ } from '~/locale'; -import Tracking from '~/tracking'; import NavItem from '~/super_sidebar/components/nav_item.vue'; import { NAV_LINK_DEFAULT_CLASSES, NAV_LINK_COUNT_DEFAULT_CLASSES } from '../constants'; @@ -13,7 +12,6 @@ export default { components: { NavItem, }, - mixins: [Tracking.mixin()], computed: { ...mapState(['navigation', 'urlQuery']), ...mapGetters(['navigationItems']), diff --git a/app/assets/javascripts/search/sidebar/components/status_filter/index.vue b/app/assets/javascripts/search/sidebar/components/status_filter/index.vue index 010cfbad590..a5f717dcf06 100644 --- a/app/assets/javascripts/search/sidebar/components/status_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/status_filter/index.vue @@ -1,5 +1,4 @@ <script> -import { mapState } from 'vuex'; import { HR_DEFAULT_CLASSES } from '../../constants'; import RadioFilter from '../radio_filter.vue'; import { statusFilterData } from './data'; @@ -9,16 +8,11 @@ export default { components: { RadioFilter, }, - computed: { - ...mapState(['useSidebarNavigation']), - }, statusFilterData, HR_DEFAULT_CLASSES, }; </script> <template> - <div> - <radio-filter :filter-data="$options.statusFilterData" /> - </div> + <radio-filter :filter-data="$options.statusFilterData" /> </template> diff --git a/app/assets/javascripts/search/store/actions.js b/app/assets/javascripts/search/store/actions.js index 077c46bbe22..f4928834bf3 100644 --- a/app/assets/javascripts/search/store/actions.js +++ b/app/assets/javascripts/search/store/actions.js @@ -4,7 +4,6 @@ import axios from '~/lib/utils/axios_utils'; import { visitUrl, setUrlParams } from '~/lib/utils/url_utility'; import { logError } from '~/lib/logger'; import { __ } from '~/locale'; -import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { labelFilterData } from '~/search/sidebar/components/label_filter/data'; import { GROUPS_LOCAL_STORAGE_KEY, PROJECTS_LOCAL_STORAGE_KEY, SIDEBAR_PARAMS } from './constants'; import * as types from './mutation_types'; @@ -127,14 +126,6 @@ export const setLabelFilterSearch = ({ commit }, { value }) => { commit(types.SET_LABEL_SEARCH_STRING, value); }; -export const resetLanguageQueryWithRedirect = ({ state }) => { - visitUrl(setUrlParams({ ...state.query, language: null }, undefined, true)); -}; - -export const resetLanguageQuery = ({ commit }) => { - commit(types.SET_QUERY, { key: languageFilterData?.filterParam, value: [] }); -}; - export const fetchSidebarCount = ({ commit, state }) => { const promises = Object.values(state.navigation).map((navItem) => { // active nav item has count already so we skip it diff --git a/app/assets/javascripts/search/store/getters.js b/app/assets/javascripts/search/store/getters.js index c7cb595f42f..6c9834d0d6c 100644 --- a/app/assets/javascripts/search/store/getters.js +++ b/app/assets/javascripts/search/store/getters.js @@ -1,4 +1,4 @@ -import { findKey, has } from 'lodash'; +import { findKey } from 'lodash'; import { languageFilterData } from '~/search/sidebar/components/language_filter/data'; import { labelFilterData } from '~/search/sidebar/components/label_filter/data'; import { formatSearchResultCount, addCountOverLimit } from '~/search/store/utils'; @@ -62,10 +62,6 @@ export const currentScope = (state) => findKey(state.navigation, { active: true export const queryLanguageFilters = (state) => state.query[languageFilterData.filterParam] || []; -export const currentUrlQueryHasLanguageFilters = (state) => - has(state.urlQuery, languageFilterData.filterParam) && - state.urlQuery[languageFilterData.filterParam]?.length > 0; - export const navigationItems = (state) => Object.values(state.navigation).map((item) => ({ title: item.label, diff --git a/app/assets/javascripts/super_sidebar/components/context_header.vue b/app/assets/javascripts/super_sidebar/components/context_header.vue new file mode 100644 index 00000000000..11b9840a409 --- /dev/null +++ b/app/assets/javascripts/super_sidebar/components/context_header.vue @@ -0,0 +1,56 @@ +<script> +import { GlTruncate, GlAvatar, GlIcon } from '@gitlab/ui'; + +export default { + components: { + GlTruncate, + GlAvatar, + GlIcon, + }, + props: { + /* + * Contains metadata about the current view, e.g. `id`, `title` and `avatar` + */ + context: { + type: Object, + required: true, + }, + tag: { + type: String, + required: false, + default: 'div', + }, + }, + computed: { + avatarShape() { + return this.context.avatar_shape || 'rect'; + }, + }, +}; +</script> + +<template> + <component + :is="tag" + class="border-top border-bottom gl-border-gray-a-08! gl-display-flex gl-align-items-center gl-gap-3 gl-font-weight-bold gl-w-full gl-h-8 gl-px-4 gl-flex-shrink-0" + > + <span + v-if="context.icon" + class="gl-avatar avatar-container gl-bg-t-gray-a-08 icon-avatar rect-avatar s24" + > + <gl-icon class="gl-text-gray-700" :name="context.icon" :size="16" /> + </span> + <gl-avatar + v-else + :size="24" + :shape="avatarShape" + :entity-name="context.title" + :entity-id="context.id" + :src="context.avatar" + /> + <div class="gl-flex-grow-1 gl-overflow-auto gl-text-gray-900"> + <gl-truncate :text="context.title" /> + </div> + <slot name="end"></slot> + </component> +</template> diff --git a/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue b/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue index 17227a2b123..f6aa2b637eb 100644 --- a/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue +++ b/app/assets/javascripts/super_sidebar/components/context_switcher_toggle.vue @@ -1,11 +1,11 @@ <script> -import { GlTruncate, GlAvatar, GlIcon } from '@gitlab/ui'; +import { GlIcon } from '@gitlab/ui'; +import ContextHeader from './context_header.vue'; export default { components: { - GlTruncate, - GlAvatar, GlIcon, + ContextHeader, }, props: { /* @@ -24,39 +24,20 @@ export default { collapseIcon() { return this.expanded ? 'chevron-up' : 'chevron-down'; }, - avatarShape() { - return this.context.avatar_shape || 'rect'; - }, }, }; </script> <template> - <button + <context-header + :context="context" + tag="button" type="button" - class="context-switcher-toggle gl-p-0 gl-bg-transparent gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-border-0 border-top border-bottom gl-border-gray-a-08! gl-box-shadow-none gl-display-flex gl-align-items-center gl-font-weight-bold gl-w-full gl-h-8 gl-flex-shrink-0" + class="context-switcher-toggle gl-p-0 gl-bg-transparent gl-hover-bg-t-gray-a-08 gl-focus-bg-t-gray-a-08 gl-border-0 gl-box-shadow-none gl-text-left" data-qa-selector="context_switcher" > - <span - v-if="context.icon" - class="gl-avatar avatar-container gl-bg-t-gray-a-08 icon-avatar rect-avatar s24 gl-mr-3 gl-ml-4" - > - <gl-icon class="gl-text-gray-700" :name="context.icon" :size="16" /> - </span> - <gl-avatar - v-else - :size="24" - :shape="avatarShape" - :entity-name="context.title" - :entity-id="context.id" - :src="context.avatar" - class="gl-mr-3 gl-ml-4" - /> - <div class="gl-overflow-auto gl-text-gray-900"> - <gl-truncate :text="context.title" /> - </div> - <span class="gl-flex-grow-1 gl-text-right gl-mr-4"> + <template #end> <gl-icon class="gl-text-gray-400" :name="collapseIcon" /> - </span> - </button> + </template> + </context-header> </template> diff --git a/app/assets/javascripts/super_sidebar/components/global_search/store/getters.js b/app/assets/javascripts/super_sidebar/components/global_search/store/getters.js index 4a42f416206..89bd41ea6c4 100644 --- a/app/assets/javascripts/super_sidebar/components/global_search/store/getters.js +++ b/app/assets/javascripts/super_sidebar/components/global_search/store/getters.js @@ -1,6 +1,8 @@ import { omitBy, isNil } from 'lodash'; import { objectToQuery } from '~/lib/utils/url_utility'; import { + ISSUES_CATEGORY, + MERGE_REQUEST_CATEGORY, MSG_ISSUES_ASSIGNED_TO_ME, MSG_ISSUES_IVE_CREATED, MSG_MR_ASSIGNED_TO_ME, @@ -61,6 +63,19 @@ export const scopedMRPath = (state) => { export const defaultSearchOptions = (state, getters) => { const userName = gon.current_username; + if (!userName) { + return [ + { + text: ISSUES_CATEGORY, + href: getters.scopedIssuesPath, + }, + { + text: MERGE_REQUEST_CATEGORY, + href: getters.scopedMRPath, + }, + ]; + } + const issues = [ { text: MSG_ISSUES_ASSIGNED_TO_ME, diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue index 86fe5c9ad5c..821b9dbcb7b 100644 --- a/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue +++ b/app/assets/javascripts/super_sidebar/components/sidebar_menu.vue @@ -30,6 +30,10 @@ export default { type: Array, required: true, }, + isLoggedIn: { + type: Boolean, + required: true, + }, pinnedItemIds: { type: Array, required: false, @@ -42,7 +46,8 @@ export default { }, updatePinsUrl: { type: String, - required: true, + required: false, + default: '', }, }, @@ -97,7 +102,7 @@ export default { .filter(Boolean); }, supportsPins() { - return PANELS_WITH_PINS.includes(this.panelType); + return this.isLoggedIn && PANELS_WITH_PINS.includes(this.panelType); }, hasStaticItems() { return this.staticItems.length > 0; diff --git a/app/assets/javascripts/super_sidebar/components/sidebar_peek_behavior.vue b/app/assets/javascripts/super_sidebar/components/sidebar_peek_behavior.vue index 6058ed3a1cd..ec728b4af9e 100644 --- a/app/assets/javascripts/super_sidebar/components/sidebar_peek_behavior.vue +++ b/app/assets/javascripts/super_sidebar/components/sidebar_peek_behavior.vue @@ -11,6 +11,12 @@ export const STATE_WILL_CLOSE = 'will-close'; export default { name: 'SidebarPeek', mixins: [Tracking.mixin()], + props: { + isMouseOverSidebar: { + type: Boolean, + required: true, + }, + }, created() { // Nothing needs to observe these properties, so they are not reactive. this.state = null; @@ -57,6 +63,11 @@ export default { this.close(); } } else if (this.state === STATE_OPEN) { + // Do not close the sidebar if it or one of its child elements still + // has mouseover. This allows to move the mouse from the sidebar to + // one of its flyout menus. + if (this.isMouseOverSidebar) return; + if (clientX >= this.xAwayFromSidebar) { this.close(); } else if (clientX >= this.xSidebarEdge) { diff --git a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue index c194401ce95..dd07d2f21e0 100644 --- a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue +++ b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue @@ -8,6 +8,7 @@ import { sidebarState } from '../constants'; import { isCollapsed, toggleSuperSidebarCollapsed } from '../super_sidebar_collapsed_state_manager'; import UserBar from './user_bar.vue'; import SidebarPortalTarget from './sidebar_portal_target.vue'; +import ContextHeader from './context_header.vue'; import ContextSwitcher from './context_switcher.vue'; import HelpCenter from './help_center.vue'; import SidebarMenu from './sidebar_menu.vue'; @@ -17,6 +18,7 @@ export default { components: { GlButton, UserBar, + ContextHeader, ContextSwitcher, HelpCenter, SidebarMenu, @@ -42,6 +44,7 @@ export default { return { sidebarState, showPeekHint: false, + isMouseover: false, }; }, computed: { @@ -57,7 +60,7 @@ export default { }, watch: { 'sidebarState.isCollapsed': function isCollapsedWatcher(newIsCollapsed) { - if (newIsCollapsed) { + if (newIsCollapsed && this.$refs['context-switcher']) { this.$refs['context-switcher'].close(); } }, @@ -118,6 +121,8 @@ export default { data-testid="super-sidebar" data-qa-selector="navbar" :inert="sidebarState.isCollapsed" + @mouseenter="isMouseover = true" + @mouseleave="isMouseover = false" > <user-bar :has-collapse-button="!sidebarState.isPeek" :sidebar-data="sidebarData" /> <div v-if="showTrialStatusWidget" class="gl-px-2 gl-py-2"> @@ -133,6 +138,7 @@ export default { data-testid="nav-container" > <context-switcher + v-if="sidebarData.is_logged_in" ref="context-switcher" :persistent-links="sidebarData.context_switcher_links" :username="sidebarData.username" @@ -142,9 +148,11 @@ export default { :context-header="sidebarData.current_context_header" @toggle="onContextSwitcherToggled" /> + <context-header v-else :context="sidebarData.current_context_header" /> <sidebar-menu v-if="menuItems.length" :items="menuItems" + :is-logged-in="sidebarData.is_logged_in" :panel-type="sidebarData.panel_type" :pinned-item-ids="sidebarData.pinned_items" :update-pins-url="sidebarData.update_pins_url" @@ -170,6 +178,10 @@ export default { Only mount SidebarPeekBehavior if the sidebar is peekable, to avoid setting up event listeners unnecessarily. --> - <sidebar-peek-behavior v-if="sidebarState.isPeekable" @change="onPeekChange" /> + <sidebar-peek-behavior + v-if="sidebarState.isPeekable" + :is-mouse-over-sidebar="isMouseover" + @change="onPeekChange" + /> </div> </template> diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue index a882df057fa..0188585df40 100644 --- a/app/assets/javascripts/super_sidebar/components/user_bar.vue +++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue @@ -105,17 +105,20 @@ export default { <template> <div class="user-bar"> <div class="gl-display-flex gl-align-items-center gl-px-3 gl-py-2"> - <brand-logo :logo-url="sidebarData.logo_url" /> - <gl-badge - v-if="sidebarData.gitlab_com_and_canary" - variant="success" - :href="sidebarData.canary_toggle_com_url" - size="sm" - class="gl-ml-2" - > - {{ $options.NEXT_LABEL }} - </gl-badge> - <div class="gl-flex-grow-1"></div> + <template v-if="sidebarData.is_logged_in"> + <brand-logo :logo-url="sidebarData.logo_url" /> + <gl-badge + v-if="sidebarData.gitlab_com_and_canary" + variant="success" + :href="sidebarData.canary_toggle_com_url" + size="sm" + class="gl-ml-2" + > + {{ $options.NEXT_LABEL }} + </gl-badge> + <div class="gl-flex-grow-1"></div> + </template> + <super-sidebar-toggle v-if="hasCollapseButton" :class="$options.JS_TOGGLE_COLLAPSE_CLASS" @@ -123,7 +126,7 @@ export default { tooltip-container="super-sidebar" data-testid="super-sidebar-collapse-button" /> - <create-menu :groups="sidebarData.create_new_menu_groups" /> + <create-menu v-if="sidebarData.is_logged_in" :groups="sidebarData.create_new_menu_groups" /> <gl-button id="super-sidebar-search" @@ -136,7 +139,7 @@ export default { /> <search-modal @shown="hideSearchTooltip" @hidden="showSearchTooltip" /> - <user-menu :data="sidebarData" /> + <user-menu v-if="sidebarData.is_logged_in" :data="sidebarData" /> <gl-button v-if="isImpersonating" @@ -151,7 +154,10 @@ export default { data-testid="stop-impersonation-btn" /> </div> - <div class="gl-display-flex gl-justify-content-space-between gl-px-3 gl-py-2 gl-gap-2"> + <div + v-if="sidebarData.is_logged_in" + class="gl-display-flex gl-justify-content-space-between gl-px-3 gl-py-2 gl-gap-2" + > <counter v-gl-tooltip:super-sidebar.hover.bottom="$options.i18n.issues" class="gl-flex-basis-third dashboard-shortcuts-issues" diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss index 358f599e0e9..3b0cacb2350 100644 --- a/app/assets/stylesheets/framework/emojis.scss +++ b/app/assets/stylesheets/framework/emojis.scss @@ -45,6 +45,14 @@ gl-emoji { .emoji-picker-category-tab { border-bottom-color: transparent; + + &:hover { + @include gl-text-gray-900; + + &:not(.emoji-picker-category-active) { + @include gl-border-b-gray-200; + } + } } .emoji-picker-category-active { diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index fd699737b63..6af81e2fa06 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -499,7 +499,8 @@ module ApplicationSettingsHelper :gitlab_dedicated_instance, :ci_max_includes, :allow_account_deletion, - :gitlab_shell_operation_limit + :gitlab_shell_operation_limit, + :namespace_aggregation_schedule_lease_duration_in_seconds ].tap do |settings| next if Gitlab.com? diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index c7864c1d45f..4cbd5029ac9 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -90,7 +90,7 @@ module NavHelper # The new sidebar is not enabled for anonymous use # Once we enable the new sidebar by default, this # should return true - return false unless user + return Feature.enabled?(:super_sidebar_logged_out) unless user # Users who got the special `super_sidebar_nav_enrolled` enabled, # see the new nav as long as they don't explicitly opt-out via the toggle diff --git a/app/helpers/sidebars_helper.rb b/app/helpers/sidebars_helper.rb index 90917cb96e0..0329a3e136b 100644 --- a/app/helpers/sidebars_helper.rb +++ b/app/helpers/sidebars_helper.rb @@ -45,9 +45,31 @@ module SidebarsHelper end def super_sidebar_context(user, group:, project:, panel:, panel_type:) # rubocop:disable Metrics/AbcSize + return super_sidebar_logged_out_context(panel: panel, panel_type: panel_type) unless user + + super_sidebar_logged_in_context(user, group: group, project: project, panel: panel, panel_type: panel_type) + end + + def super_sidebar_logged_out_context(panel:, panel_type:) # rubocop:disable Metrics/AbcSize { + is_logged_in: false, current_menu_items: panel.super_sidebar_menu_items, current_context_header: panel.super_sidebar_context_header, + support_path: support_url, + display_whats_new: display_whats_new?, + whats_new_most_recent_release_items_count: whats_new_most_recent_release_items_count, + whats_new_version_digest: whats_new_version_digest, + show_version_check: show_version_check?, + gitlab_version: Gitlab.version_info, + gitlab_version_check: gitlab_version_check, + search: search_data, + panel_type: panel_type + } + end + + def super_sidebar_logged_in_context(user, group:, project:, panel:, panel_type:) # rubocop:disable Metrics/AbcSize + super_sidebar_logged_out_context(panel: panel, panel_type: panel_type).merge({ + is_logged_in: true, name: user.name, username: user.username, avatar_url: user.avatar_url, @@ -75,26 +97,17 @@ module SidebarsHelper merge_request_menu: create_merge_request_menu(user), projects_path: dashboard_projects_path, groups_path: dashboard_groups_path, - support_path: support_url, - display_whats_new: display_whats_new?, - whats_new_most_recent_release_items_count: whats_new_most_recent_release_items_count, - whats_new_version_digest: whats_new_version_digest, - show_version_check: show_version_check?, - gitlab_version: Gitlab.version_info, - gitlab_version_check: gitlab_version_check, gitlab_com_but_not_canary: Gitlab.com_but_not_canary?, gitlab_com_and_canary: Gitlab.com_and_canary?, canary_toggle_com_url: Gitlab::Saas.canary_toggle_com_url, current_context: super_sidebar_current_context(project: project, group: group), context_switcher_links: context_switcher_links, - search: search_data, pinned_items: user.pinned_nav_items[panel_type] || super_sidebar_default_pins(panel_type), - panel_type: panel_type, update_pins_url: pins_url, is_impersonating: impersonating?, stop_impersonation_path: admin_impersonation_path, shortcut_links: shortcut_links(user, project: project) - } + }) end def super_sidebar_nav_panel( diff --git a/app/helpers/users/callouts_helper.rb b/app/helpers/users/callouts_helper.rb index 0f4cbd6642b..12f78d9bd16 100644 --- a/app/helpers/users/callouts_helper.rb +++ b/app/helpers/users/callouts_helper.rb @@ -89,6 +89,8 @@ module Users end def gitlab_com_user_created_after_new_nav_rollout? + return true unless current_user + Gitlab.com? && current_user.created_at >= Date.new(2023, 6, 2) end |