Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-26 12:11:08 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-26 12:11:08 +0300
commit79cd3f3a38777b1436107bd1e3205f593e1a3bd1 (patch)
tree4bc714410905fed21c92057b2a83149150dbe18d /app
parenta8f6578cb24cb3a688b7a5be674867fa311b0b38 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/milestones/components/milestone_combobox.vue206
-rw-r--r--app/assets/javascripts/milestones/stores/mutations.js4
-rw-r--r--app/assets/javascripts/releases/components/app_edit_new.vue25
3 files changed, 84 insertions, 151 deletions
diff --git a/app/assets/javascripts/milestones/components/milestone_combobox.vue b/app/assets/javascripts/milestones/components/milestone_combobox.vue
index 2f7fb542d0e..a5e306b5372 100644
--- a/app/assets/javascripts/milestones/components/milestone_combobox.vue
+++ b/app/assets/javascripts/milestones/components/milestone_combobox.vue
@@ -1,19 +1,10 @@
<script>
-import {
- GlDropdown,
- GlDropdownDivider,
- GlDropdownSectionHeader,
- GlDropdownItem,
- GlLoadingIcon,
- GlSearchBoxByType,
- GlIcon,
-} from '@gitlab/ui';
+import { GlBadge, GlButton, GlCollapsibleListbox } from '@gitlab/ui';
import { debounce, isEqual } from 'lodash';
// eslint-disable-next-line no-restricted-imports
import { mapActions, mapGetters, mapState } from 'vuex';
import { s__, __, sprintf } from '~/locale';
import createStore from '../stores';
-import MilestoneResultsSection from './milestone_results_section.vue';
const SEARCH_DEBOUNCE_MS = 250;
@@ -21,14 +12,9 @@ export default {
name: 'MilestoneCombobox',
store: createStore(),
components: {
- GlDropdown,
- GlDropdownDivider,
- GlDropdownSectionHeader,
- GlDropdownItem,
- GlLoadingIcon,
- GlSearchBoxByType,
- GlIcon,
- MilestoneResultsSection,
+ GlCollapsibleListbox,
+ GlBadge,
+ GlButton,
},
props: {
value: {
@@ -56,27 +42,43 @@ export default {
required: false,
},
},
- data() {
- return {
- searchQuery: '',
- };
- },
translations: {
- milestone: s__('MilestoneCombobox|Milestone'),
selectMilestone: s__('MilestoneCombobox|Select milestone'),
noMilestone: s__('MilestoneCombobox|No milestone'),
- noResultsLabel: s__('MilestoneCombobox|No matching results'),
- searchMilestones: s__('MilestoneCombobox|Search Milestones'),
- searchErrorMessage: s__('MilestoneCombobox|An error occurred while searching for milestones'),
projectMilestones: s__('MilestoneCombobox|Project milestones'),
groupMilestones: s__('MilestoneCombobox|Group milestones'),
+ unselect: __('Unselect'),
},
computed: {
...mapState(['matches', 'selectedMilestones']),
- ...mapGetters(['isLoading', 'groupMilestonesEnabled']),
+ ...mapGetters(['isLoading']),
+ allMilestones() {
+ const { groupMilestones, projectMilestones } = this.matches || {};
+ const milestones = [];
+
+ if (projectMilestones?.totalCount) {
+ milestones.push({
+ id: 'project-milestones',
+ text: this.$options.translations.projectMilestones,
+ options: projectMilestones.list,
+ totalCount: projectMilestones.totalCount,
+ });
+ }
+
+ if (groupMilestones?.totalCount) {
+ milestones.push({
+ id: 'group-milestones',
+ text: this.$options.translations.groupMilestones,
+ options: groupMilestones.list,
+ totalCount: groupMilestones.totalCount,
+ });
+ }
+
+ return milestones;
+ },
selectedMilestonesLabel() {
const { selectedMilestones } = this;
- const firstMilestoneName = selectedMilestones[0];
+ const [firstMilestoneName] = selectedMilestones;
if (selectedMilestones.length === 0) {
return this.$options.translations.noMilestone;
@@ -92,20 +94,6 @@ export default {
numberOfOtherMilestones,
});
},
- showProjectMilestoneSection() {
- return Boolean(
- this.matches.projectMilestones.totalCount > 0 || this.matches.projectMilestones.error,
- );
- },
- showGroupMilestoneSection() {
- return (
- this.groupMilestonesEnabled &&
- Boolean(this.matches.groupMilestones.totalCount > 0 || this.matches.groupMilestones.error)
- );
- },
- showNoResults() {
- return !this.showProjectMilestoneSection && !this.showGroupMilestoneSection;
- },
},
watch: {
// Keep the Vuex store synchronized if the parent
@@ -127,8 +115,8 @@ export default {
// because we need to access the .cancel() method
// lodash attaches to the function, which is
// made inaccessible by Vue.
- this.debouncedSearch = debounce(function search() {
- this.search(this.searchQuery);
+ this.debouncedSearch = debounce(function search(q) {
+ this.search(q);
}, SEARCH_DEBOUNCE_MS);
this.setProjectId(this.projectId);
@@ -143,22 +131,14 @@ export default {
'setGroupMilestonesAvailable',
'setSelectedMilestones',
'clearSelectedMilestones',
- 'toggleMilestones',
'search',
'fetchMilestones',
]),
- focusSearchBox() {
- this.$refs.searchBox.$el.querySelector('input').focus();
- },
- onSearchBoxEnter() {
- this.debouncedSearch.cancel();
- this.search(this.searchQuery);
+ onSearchBoxInput(q) {
+ this.debouncedSearch(q);
},
- onSearchBoxInput() {
- this.debouncedSearch();
- },
- selectMilestone(milestone) {
- this.toggleMilestones(milestone);
+ selectMilestone(milestones) {
+ this.setSelectedMilestones(milestones);
this.$emit('input', this.selectedMilestones);
},
selectNoMilestone() {
@@ -170,84 +150,42 @@ export default {
</script>
<template>
- <gl-dropdown v-bind="$attrs" class="milestone-combobox" @shown="focusSearchBox">
- <template #button-content>
- <span data-testid="milestone-combobox-button-content" class="gl-flex-grow-1 text-muted">{{
- selectedMilestonesLabel
- }}</span>
- <gl-icon name="chevron-down" />
- </template>
-
- <gl-dropdown-section-header>
- <span class="text-center d-block">{{ $options.translations.selectMilestone }}</span>
- </gl-dropdown-section-header>
-
- <gl-dropdown-divider />
-
- <gl-search-box-by-type
- ref="searchBox"
- v-model.trim="searchQuery"
- class="gl-m-3"
- :placeholder="$options.translations.searchMilestones"
- @input="onSearchBoxInput"
- @keydown.enter.prevent="onSearchBoxEnter"
- />
-
- <gl-dropdown-item
- :is-checked="selectedMilestones.length === 0"
- is-check-item
- @click="selectNoMilestone()"
- >
- {{ $options.translations.noMilestone }}
- </gl-dropdown-item>
-
- <gl-dropdown-divider />
-
- <template v-if="isLoading">
- <gl-loading-icon size="sm" />
- <gl-dropdown-divider />
+ <gl-collapsible-listbox
+ :header-text="$options.translations.selectMilestone"
+ :items="allMilestones"
+ :reset-button-label="$options.translations.unselect"
+ :searching="isLoading"
+ :selected="selectedMilestones"
+ :toggle-text="selectedMilestonesLabel"
+ block
+ multiple
+ searchable
+ @reset="selectNoMilestone"
+ @search="onSearchBoxInput"
+ @select="selectMilestone"
+ >
+ <template #group-label="{ group }">
+ <span :data-testid="`${group.id}-section`"
+ >{{ group.text }}<gl-badge size="sm" class="gl-ml-2">{{ group.totalCount }}</gl-badge></span
+ >
</template>
- <template v-else-if="showNoResults">
- <div class="dropdown-item-space">
- <span data-testid="milestone-combobox-no-results" class="gl-pl-6">{{
- $options.translations.noResultsLabel
- }}</span>
+ <template #footer>
+ <div
+ class="gl-border-t-solid gl-border-t-1 gl-border-t-gray-200 gl-display-flex gl-flex-direction-column gl-p-2! gl-pt-0!"
+ >
+ <gl-button
+ v-for="(item, idx) in extraLinks"
+ :key="idx"
+ :href="item.url"
+ is-check-item
+ data-testid="milestone-combobox-extra-links"
+ category="tertiary"
+ block
+ class="gl-justify-content-start! gl-mt-2!"
+ >
+ {{ item.text }}
+ </gl-button>
</div>
- <gl-dropdown-divider />
- </template>
- <template v-else>
- <milestone-results-section
- v-if="showProjectMilestoneSection"
- :section-title="$options.translations.projectMilestones"
- :total-count="matches.projectMilestones.totalCount"
- :items="matches.projectMilestones.list"
- :selected-milestones="selectedMilestones"
- :error="matches.projectMilestones.error"
- :error-message="$options.translations.searchErrorMessage"
- data-testid="project-milestones-section"
- @selected="selectMilestone($event)"
- />
-
- <milestone-results-section
- v-if="showGroupMilestoneSection"
- :section-title="$options.translations.groupMilestones"
- :total-count="matches.groupMilestones.totalCount"
- :items="matches.groupMilestones.list"
- :selected-milestones="selectedMilestones"
- :error="matches.groupMilestones.error"
- :error-message="$options.translations.searchErrorMessage"
- data-testid="group-milestones-section"
- @selected="selectMilestone($event)"
- />
</template>
- <gl-dropdown-item
- v-for="(item, idx) in extraLinks"
- :key="idx"
- :href="item.url"
- is-check-item
- data-testid="milestone-combobox-extra-links"
- >
- {{ item.text }}
- </gl-dropdown-item>
- </gl-dropdown>
+ </gl-collapsible-listbox>
</template>
diff --git a/app/assets/javascripts/milestones/stores/mutations.js b/app/assets/javascripts/milestones/stores/mutations.js
index 1f88c0a1ea6..c0cd58cc5d2 100644
--- a/app/assets/javascripts/milestones/stores/mutations.js
+++ b/app/assets/javascripts/milestones/stores/mutations.js
@@ -37,7 +37,7 @@ export default {
},
[types.RECEIVE_PROJECT_MILESTONES_SUCCESS](state, response) {
state.matches.projectMilestones = {
- list: response.data.map(({ title }) => ({ title })),
+ list: response.data.map(({ title }) => ({ text: title, value: title })),
totalCount: parseInt(response.headers['x-total'], 10) || response.data.length,
error: null,
};
@@ -51,7 +51,7 @@ export default {
},
[types.RECEIVE_GROUP_MILESTONES_SUCCESS](state, response) {
state.matches.groupMilestones = {
- list: response.data.map(({ title }) => ({ title })),
+ list: response.data.map(({ title }) => ({ text: title, value: title })),
totalCount: parseInt(response.headers['x-total'], 10) || response.data.length,
error: null,
};
diff --git a/app/assets/javascripts/releases/components/app_edit_new.vue b/app/assets/javascripts/releases/components/app_edit_new.vue
index c68fbceb4f6..df9f333afe5 100644
--- a/app/assets/javascripts/releases/components/app_edit_new.vue
+++ b/app/assets/javascripts/releases/components/app_edit_new.vue
@@ -176,8 +176,7 @@ export default {
</p>
<form v-if="showForm" class="js-quick-submit" @submit.prevent="submitForm">
<tag-field />
- <gl-form-group>
- <label for="release-title">{{ __('Release title') }}</label>
+ <gl-form-group :label="__('Release title')">
<gl-form-input
id="release-title"
ref="releaseTitleInput"
@@ -186,17 +185,14 @@ export default {
class="form-control"
/>
</gl-form-group>
- <gl-form-group class="w-50" data-testid="milestones-field">
- <label>{{ __('Milestones') }}</label>
- <div class="d-flex flex-column col-md-6 col-sm-10 pl-0">
- <milestone-combobox
- v-model="releaseMilestones"
- :project-id="projectId"
- :group-id="groupId"
- :group-milestones-available="groupMilestonesAvailable"
- :extra-links="milestoneComboboxExtraLinks"
- />
- </div>
+ <gl-form-group :label="__('Milestones')" class="gl-w-30" data-testid="milestones-field">
+ <milestone-combobox
+ v-model="releaseMilestones"
+ :project-id="projectId"
+ :group-id="groupId"
+ :group-milestones-available="groupMilestonesAvailable"
+ :extra-links="milestoneComboboxExtraLinks"
+ />
</gl-form-group>
<gl-form-group :label="__('Release date')" label-for="release-released-at">
<template #label-description>
@@ -214,8 +210,7 @@ export default {
</template>
<gl-datepicker id="release-released-at" v-model="releasedAt" :default-date="releasedAt" />
</gl-form-group>
- <gl-form-group data-testid="release-notes">
- <label for="release-notes">{{ __('Release notes') }}</label>
+ <gl-form-group :label="__('Release notes')" data-testid="release-notes">
<div class="common-note-form">
<markdown-field
:can-attach-file="true"