summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/vue_shared/components/sidebar
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-08-19 09:08:42 +0000
commitb76ae638462ab0f673e5915986070518dd3f9ad3 (patch)
treebdab0533383b52873be0ec0eb4d3c66598ff8b91 /app/assets/javascripts/vue_shared/components/sidebar
parent434373eabe7b4be9593d18a585fb763f1e5f1a6f (diff)
downloadgitlab-ce-b76ae638462ab0f673e5915986070518dd3f9ad3.tar.gz
Add latest changes from gitlab-org/gitlab@14-2-stable-eev14.2.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/sidebar')
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js9
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js20
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue34
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue260
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue34
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js22
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js8
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue18
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js16
17 files changed, 274 insertions, 201 deletions
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
index 9914bfc6026..623e7799493 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
@@ -132,6 +132,9 @@ export default {
} else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
this.searchKey = '';
+
+ // Prevent parent form submission upon hitting enter.
+ e.preventDefault();
} else if (e.keyCode === ESC_KEY_CODE) {
this.toggleDropdownContents();
}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
index aad754e15b0..7989ad40b5a 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
@@ -28,8 +28,9 @@ export default {
<template v-if="allowLabelEdit">
<gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
<gl-button
- variant="link"
- class="float-right gl-text-gray-900! gl-hover-text-blue-800! js-sidebar-dropdown-toggle"
+ category="tertiary"
+ size="small"
+ class="float-right js-sidebar-dropdown-toggle gl-mr-n2"
data-qa-selector="labels_edit_button"
@click="toggleDropdownContents"
>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index 87af3ffc52c..4234bc72f3a 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -142,6 +142,7 @@ export default {
this.setInitialState({
selectedLabels,
});
+ setTimeout(() => this.updateLabelsSetState(), 100);
},
showDropdownContents(showDropdownContents) {
this.setContentIsOnViewport(showDropdownContents);
@@ -184,7 +185,7 @@ export default {
document.removeEventListener('click', this.handleDocumentClick);
},
methods: {
- ...mapActions(['setInitialState', 'toggleDropdownContents']),
+ ...mapActions(['setInitialState', 'toggleDropdownContents', 'updateLabelsSetState']),
/**
* This method differentiates between
* dispatched actions and calls necessary method.
@@ -315,7 +316,7 @@ export default {
</dropdown-value>
<dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
<dropdown-contents
- v-show="dropdownButtonVisible && showDropdownContents"
+ v-if="dropdownButtonVisible && showDropdownContents"
ref="dropdownContents"
:render-on-top="!contentIsOnViewport"
/>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
index 178be0f6da0..0c697e624ab 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
@@ -20,7 +20,11 @@ export const receiveLabelsFailure = ({ commit }) => {
message: __('Error fetching labels.'),
});
};
-export const fetchLabels = ({ state, dispatch }) => {
+export const fetchLabels = ({ state, dispatch }, options) => {
+ if (state.labelsFetched && (!options || !options.refetch)) {
+ return Promise.resolve();
+ }
+
dispatch('requestLabels');
return axios
.get(state.labelsFetchPath)
@@ -46,6 +50,7 @@ export const createLabel = ({ state, dispatch }, label) => {
})
.then(({ data }) => {
if (data.id) {
+ dispatch('fetchLabels', { refetch: true });
dispatch('receiveCreateLabelSuccess');
dispatch('toggleDropdownContentsCreateView');
} else {
@@ -60,3 +65,5 @@ export const createLabel = ({ state, dispatch }, label) => {
export const updateSelectedLabels = ({ commit }, labels) =>
commit(types.UPDATE_SELECTED_LABELS, { labels });
+
+export const updateLabelsSetState = ({ commit }) => commit(types.UPDATE_LABELS_SET_STATE);
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
index 2e044dc3b3c..f26e36031f4 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
@@ -18,3 +18,5 @@ export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS';
export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS';
export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW';
+
+export const UPDATE_LABELS_SET_STATE = 'UPDATE_LABELS_SET_STATE';
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
index 2e0a57f15dd..8853dc8b9e3 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
@@ -34,15 +34,12 @@ export default {
// Iterate over every label and add a `set` prop
// to determine whether it is already a part of
// selectedLabels array.
- const selectedLabelIds = state.selectedLabels.map((label) => label.id);
state.labelsFetchInProgress = false;
- state.labels = labels.reduce((allLabels, label) => {
- allLabels.push({
- ...label,
- set: selectedLabelIds.includes(label.id),
- });
- return allLabels;
- }, []);
+ state.labelsFetched = true;
+ state.labels = labels.map((label) => ({
+ ...label,
+ set: state.selectedLabels.some((selectedLabel) => selectedLabel.id === label.id),
+ }));
},
[types.RECEIVE_SET_LABELS_FAILURE](state) {
state.labelsFetchInProgress = false;
@@ -79,4 +76,11 @@ export default {
}
}
},
+
+ [types.UPDATE_LABELS_SET_STATE](state) {
+ state.labels = state.labels.map((label) => ({
+ ...label,
+ set: state.selectedLabels.some((selectedLabel) => selectedLabel.id === label.id),
+ }));
+ },
};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
index d66cfed4163..0185d5f88e1 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
@@ -1,6 +1,7 @@
export default () => ({
// Initial Data
labels: [],
+ labelsFetched: false,
selectedLabels: [],
labelsListTitle: '',
labelsCreateTitle: '',
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
index 1f0704f7308..6694e349b6e 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
@@ -21,9 +21,29 @@ export default {
type: String,
required: true,
},
+ selectedLabels: {
+ type: Array,
+ required: true,
+ },
+ allowMultiselect: {
+ type: Boolean,
+ required: true,
+ },
+ labelsListTitle: {
+ type: String,
+ required: true,
+ },
+ footerCreateLabelTitle: {
+ type: String,
+ required: true,
+ },
+ footerManageLabelTitle: {
+ type: String,
+ required: true,
+ },
},
computed: {
- ...mapState(['showDropdownContentsCreateView', 'labelsListTitle']),
+ ...mapState(['showDropdownContentsCreateView']),
...mapGetters(['isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
dropdownContentsView() {
if (this.showDropdownContentsCreateView) {
@@ -75,6 +95,16 @@ export default {
@click="toggleDropdownContents"
/>
</div>
- <component :is="dropdownContentsView" @hideCreateView="toggleDropdownContentsCreateView" />
+ <component
+ :is="dropdownContentsView"
+ :selected-labels="selectedLabels"
+ :allow-multiselect="allowMultiselect"
+ :labels-list-title="labelsListTitle"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
+ @hideCreateView="toggleDropdownContentsCreateView"
+ @closeDropdown="$emit('closeDropdown', $event)"
+ @toggleDropdownContentsCreateView="toggleDropdownContentsCreateView"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
index bff34743344..ffa37424c2c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
@@ -1,38 +1,91 @@
<script>
-import { GlIntersectionObserver, GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui';
+import { GlLoadingIcon, GlSearchBoxByType, GlLink } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { mapState, mapGetters, mapActions } from 'vuex';
-
+import { debounce } from 'lodash';
+import createFlash from '~/flash';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
-
+import { __ } from '~/locale';
+import { DropdownVariant } from './constants';
+import projectLabelsQuery from './graphql/project_labels.query.graphql';
import LabelItem from './label_item.vue';
export default {
components: {
- GlIntersectionObserver,
GlLoadingIcon,
GlSearchBoxByType,
GlLink,
LabelItem,
},
+ inject: ['projectPath', 'allowLabelCreate', 'labelsManagePath', 'variant'],
+ props: {
+ selectedLabels: {
+ type: Array,
+ required: true,
+ },
+ allowMultiselect: {
+ type: Boolean,
+ required: true,
+ },
+ labelsListTitle: {
+ type: String,
+ required: true,
+ },
+ footerCreateLabelTitle: {
+ type: String,
+ required: true,
+ },
+ footerManageLabelTitle: {
+ type: String,
+ required: true,
+ },
+ },
data() {
return {
searchKey: '',
+ labels: [],
currentHighlightItem: -1,
+ localSelectedLabels: [...this.selectedLabels],
};
},
+ apollo: {
+ labels: {
+ query: projectLabelsQuery,
+ variables() {
+ return {
+ fullPath: this.projectPath,
+ searchTerm: this.searchKey,
+ };
+ },
+ skip() {
+ return this.searchKey.length === 1;
+ },
+ update: (data) => data.workspace?.labels?.nodes || [],
+ async result() {
+ if (this.$refs.searchInput) {
+ await this.$nextTick();
+ this.$refs.searchInput.focusInput();
+ }
+ },
+ error() {
+ createFlash({ message: __('Error fetching labels.') });
+ },
+ },
+ },
computed: {
- ...mapState([
- 'allowLabelCreate',
- 'allowMultiselect',
- 'labelsManagePath',
- 'labels',
- 'labelsFetchInProgress',
- 'labelsListTitle',
- 'footerCreateLabelTitle',
- 'footerManageLabelTitle',
- ]),
- ...mapGetters(['selectedLabelsList', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
+ isDropdownVariantSidebar() {
+ return this.variant === DropdownVariant.Sidebar;
+ },
+ isDropdownVariantEmbedded() {
+ return this.variant === DropdownVariant.Embedded;
+ },
+ labelsFetchInProgress() {
+ return this.$apollo.queries.labels.loading;
+ },
+ localSelectedLabelsIds() {
+ return this.localSelectedLabels.map((label) => label.id);
+ },
visibleLabels() {
if (this.searchKey) {
return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
@@ -55,17 +108,16 @@ export default {
}
},
},
+ created() {
+ this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
+ },
+ beforeDestroy() {
+ this.$emit('closeDropdown', this.localSelectedLabels);
+ this.debouncedSearchKeyUpdate.cancel();
+ },
methods: {
- ...mapActions([
- 'toggleDropdownContents',
- 'toggleDropdownContentsCreateView',
- 'fetchLabels',
- 'receiveLabelsSuccess',
- 'updateSelectedLabels',
- 'toggleDropdownContents',
- ]),
isLabelSelected(label) {
- return this.selectedLabelsList.includes(label.id);
+ return this.localSelectedLabelsIds.includes(getIdFromGraphQLId(label.id));
},
/**
* This method scrolls item from dropdown into
@@ -86,23 +138,17 @@ export default {
}
}
},
- handleComponentAppear() {
- // We can avoid putting `catch` block here
- // as failure is handled within actions.js already.
- return this.fetchLabels().then(() => {
- this.$refs.searchInput.focusInput();
- });
- },
- /**
- * We want to remove loaded labels to ensure component
- * fetches fresh set of labels every time when shown.
- */
- handleComponentDisappear() {
- this.receiveLabelsSuccess([]);
- },
- handleCreateLabelClick() {
- this.receiveLabelsSuccess([]);
- this.toggleDropdownContentsCreateView();
+ updateSelectedLabels(label) {
+ if (this.isLabelSelected(label)) {
+ this.localSelectedLabels = this.localSelectedLabels.filter(
+ ({ id }) => id !== getIdFromGraphQLId(label.id),
+ );
+ } else {
+ this.localSelectedLabels.push({
+ ...label,
+ id: getIdFromGraphQLId(label.id),
+ });
+ }
},
/**
* This method enables keyboard navigation support for
@@ -117,10 +163,10 @@ export default {
) {
this.currentHighlightItem += 1;
} else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
- this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
+ this.updateSelectedLabels(this.visibleLabels[this.currentHighlightItem]);
this.searchKey = '';
} else if (e.keyCode === ESC_KEY_CODE) {
- this.toggleDropdownContents();
+ this.$emit('closeDropdown', this.localSelectedLabels);
}
if (e.keyCode !== ESC_KEY_CODE) {
@@ -132,68 +178,82 @@ export default {
}
},
handleLabelClick(label) {
- this.updateSelectedLabels([label]);
- if (!this.allowMultiselect) this.toggleDropdownContents();
+ this.updateSelectedLabels(label);
+ if (!this.allowMultiselect) {
+ this.$emit('closeDropdown', this.localSelectedLabels);
+ }
+ },
+ setSearchKey(value) {
+ this.searchKey = value;
},
},
};
</script>
<template>
- <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear">
- <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
- <div class="dropdown-input" @click.stop="() => {}">
- <gl-search-box-by-type
- ref="searchInput"
- v-model="searchKey"
- :disabled="labelsFetchInProgress"
- data-qa-selector="dropdown_input_field"
- />
- </div>
- <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading gl-align-items-center w-100 h-100"
- size="md"
+ <div
+ class="labels-select-contents-list js-labels-list"
+ data-testid="dropdown-wrapper"
+ @keydown="handleKeyDown"
+ >
+ <div class="dropdown-input" @click.stop="() => {}">
+ <gl-search-box-by-type
+ ref="searchInput"
+ :value="searchKey"
+ :disabled="labelsFetchInProgress"
+ data-qa-selector="dropdown_input_field"
+ data-testid="dropdown-input-field"
+ @input="debouncedSearchKeyUpdate"
+ />
+ </div>
+ <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
+ <gl-loading-icon
+ v-if="labelsFetchInProgress"
+ class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full"
+ size="md"
+ />
+ <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word" data-testid="labels-list">
+ <label-item
+ v-for="(label, index) in visibleLabels"
+ :key="label.id"
+ :label="label"
+ :is-label-set="isLabelSelected(label)"
+ :highlight="index === currentHighlightItem"
+ @clickLabel="handleLabelClick(label)"
/>
- <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
- <label-item
- v-for="(label, index) in visibleLabels"
- :key="label.id"
- :label="label"
- :is-label-set="label.set"
- :highlight="index === currentHighlightItem"
- @clickLabel="handleLabelClick(label)"
- />
- <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
- {{ __('No matching results') }}
- </li>
- </ul>
- </div>
- <div
- v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
- class="dropdown-footer"
- data-testid="dropdown-footer"
- >
- <ul class="list-unstyled">
- <li v-if="allowLabelCreate">
- <gl-link
- class="gl-display-flex w-100 flex-row text-break-word label-item"
- @click="handleCreateLabelClick"
- >
- {{ footerCreateLabelTitle }}
- </gl-link>
- </li>
- <li>
- <gl-link
- :href="labelsManagePath"
- class="gl-display-flex flex-row text-break-word label-item"
- >
- {{ footerManageLabelTitle }}
- </gl-link>
- </li>
- </ul>
- </div>
+ <li
+ v-show="showNoMatchingResultsMessage"
+ class="gl-p-3 gl-text-center"
+ data-testid="no-results"
+ >
+ {{ __('No matching results') }}
+ </li>
+ </ul>
+ </div>
+ <div
+ v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
+ class="dropdown-footer"
+ data-testid="dropdown-footer"
+ >
+ <ul class="list-unstyled">
+ <li v-if="allowLabelCreate">
+ <gl-link
+ class="gl-display-flex gl-flex-direction-row gl-w-full gl-overflow-break-word label-item"
+ data-testid="create-label-button"
+ @click="$emit('toggleDropdownContentsCreateView')"
+ >
+ {{ footerCreateLabelTitle }}
+ </gl-link>
+ </li>
+ <li>
+ <gl-link
+ :href="labelsManagePath"
+ class="gl-display-flex gl-flex-direction-row gl-w-full gl-overflow-break-word label-item"
+ >
+ {{ footerManageLabelTitle }}
+ </gl-link>
+ </li>
+ </ul>
</div>
- </gl-intersection-observer>
+ </div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue
index b6d14965cfa..46edfa1c42a 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_title.vue
@@ -28,8 +28,9 @@ export default {
<template v-if="allowLabelEdit">
<gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
<gl-button
- variant="link"
- class="float-right js-sidebar-dropdown-toggle"
+ category="tertiary"
+ size="small"
+ class="float-right js-sidebar-dropdown-toggle gl-mr-n2"
data-qa-selector="labels_edit_button"
@click="toggleDropdownContents"
>{{ __('Edit') }}</gl-button
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
new file mode 100644
index 00000000000..dc39220487d
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
@@ -0,0 +1,12 @@
+query projectLabels($fullPath: ID!, $searchTerm: String) {
+ workspace: project(fullPath: $fullPath) {
+ labels(searchTerm: $searchTerm, includeAncestorGroups: true) {
+ nodes {
+ id
+ title
+ color
+ description
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
index 87f36a5bb72..0499dfe468f 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
@@ -197,23 +197,6 @@ export default {
methods: {
...mapActions(['setInitialState', 'toggleDropdownContents']),
/**
- * This method differentiates between
- * dispatched actions and calls necessary method.
- */
- handleVuexActionDispatch(action, state) {
- if (
- action.type === 'toggleDropdownContents' &&
- !state.showDropdownButton &&
- !state.showDropdownContents
- ) {
- let filterFn = (label) => label.touched;
- if (this.isDropdownVariantEmbedded) {
- filterFn = (label) => label.set;
- }
- this.handleDropdownClose(state.labels.filter(filterFn));
- }
- },
- /**
* This method stores a mousedown event's target.
* Required by the click listener because the click
* event itself has no reference to this element.
@@ -276,6 +259,9 @@ export default {
handleDropdownClose(labels) {
// Only emit label updates if there are any labels to update
// on UI.
+ if (this.showDropdownContents) {
+ this.toggleDropdownContents();
+ }
if (labels.length) this.$emit('updateSelectedLabels', labels);
this.$emit('onDropdownClose');
},
@@ -330,10 +316,16 @@ export default {
</dropdown-value>
<dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
<dropdown-contents
- v-show="dropdownButtonVisible && showDropdownContents"
+ v-if="dropdownButtonVisible && showDropdownContents"
ref="dropdownContents"
+ :allow-multiselect="allowMultiselect"
+ :labels-list-title="labelsListTitle"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
:render-on-top="!contentIsOnViewport"
:labels-create-title="labelsCreateTitle"
+ :selected-labels="selectedLabels"
+ @closeDropdown="handleDropdownClose"
/>
</template>
<template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded">
@@ -341,7 +333,13 @@ export default {
<dropdown-contents
v-if="dropdownButtonVisible && showDropdownContents"
ref="dropdownContents"
+ :allow-multiselect="allowMultiselect"
+ :labels-list-title="labelsListTitle"
+ :footer-create-label-title="footerCreateLabelTitle"
+ :footer-manage-label-title="footerManageLabelTitle"
:render-on-top="!contentIsOnViewport"
+ :selected-labels="selectedLabels"
+ @closeDropdown="handleDropdownClose"
/>
</template>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js
index 935f020f559..b3d4a204a81 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/actions.js
@@ -1,6 +1,3 @@
-import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
import * as types from './mutation_types';
export const setInitialState = ({ commit }, props) => commit(types.SET_INITIAL_STATE, props);
@@ -11,24 +8,5 @@ export const toggleDropdownContents = ({ commit }) => commit(types.TOGGLE_DROPDO
export const toggleDropdownContentsCreateView = ({ commit }) =>
commit(types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW);
-export const requestLabels = ({ commit }) => commit(types.REQUEST_LABELS);
-export const receiveLabelsSuccess = ({ commit }, labels) =>
- commit(types.RECEIVE_SET_LABELS_SUCCESS, labels);
-export const receiveLabelsFailure = ({ commit }) => {
- commit(types.RECEIVE_SET_LABELS_FAILURE);
- createFlash({
- message: __('Error fetching labels.'),
- });
-};
-export const fetchLabels = ({ state, dispatch }) => {
- dispatch('requestLabels');
- return axios
- .get(state.labelsFetchPath)
- .then(({ data }) => {
- dispatch('receiveLabelsSuccess', data);
- })
- .catch(() => dispatch('receiveLabelsFailure'));
-};
-
export const updateSelectedLabels = ({ commit }, labels) =>
commit(types.UPDATE_SELECTED_LABELS, { labels });
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js
index b8da7a90b36..bd71c3b85f1 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutation_types.js
@@ -1,13 +1,5 @@
export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
-export const REQUEST_LABELS = 'REQUEST_LABELS';
-export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS';
-export const RECEIVE_LABELS_FAILURE = 'RECEIVE_LABELS_FAILURE';
-
-export const REQUEST_SET_LABELS = 'REQUEST_SET_LABELS';
-export const RECEIVE_SET_LABELS_SUCCESS = 'RECEIVE_SET_LABELS_SUCCESS';
-export const RECEIVE_SET_LABELS_FAILURE = 'RECEIVE_SET_LABELS_FAILURE';
-
export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY';
export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS';
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js
index 1c03d95f37b..45ec4d7ae04 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/store/mutations.js
@@ -26,27 +26,6 @@ export default {
[types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state) {
state.showDropdownContentsCreateView = !state.showDropdownContentsCreateView;
},
-
- [types.REQUEST_LABELS](state) {
- state.labelsFetchInProgress = true;
- },
- [types.RECEIVE_SET_LABELS_SUCCESS](state, labels) {
- // Iterate over every label and add a `set` prop
- // to determine whether it is already a part of
- // selectedLabels array.
- const selectedLabelIds = state.selectedLabels.map((label) => label.id);
- state.labelsFetchInProgress = false;
- state.labels = labels.reduce((allLabels, label) => {
- allLabels.push({
- ...label,
- set: selectedLabelIds.includes(label.id),
- });
- return allLabels;
- }, []);
- },
- [types.RECEIVE_SET_LABELS_FAILURE](state) {
- state.labelsFetchInProgress = false;
- },
[types.UPDATE_SELECTED_LABELS](state, { labels }) {
// Find the label to update from all the labels
// and change `set` prop value to represent their current state.
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
index e6229cf0a93..cdc7422c7df 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
@@ -1,6 +1,6 @@
<script>
import { GlButton } from '@gitlab/ui';
-import { todoLabel } from './utils';
+import { todoLabel, updateGlobalTodoCount } from './utils';
export default {
components: {
@@ -19,23 +19,11 @@ export default {
},
},
methods: {
- updateGlobalTodoCount(additionalTodoCount) {
- const countContainer = document.querySelector('.js-todos-count');
- if (countContainer === null) return;
- const currentCount = parseInt(countContainer.innerText, 10);
- const todoToggleEvent = new CustomEvent('todo:toggle', {
- detail: {
- count: Math.max(currentCount + additionalTodoCount, 0),
- },
- });
-
- document.dispatchEvent(todoToggleEvent);
- },
incrementGlobalTodoCount() {
- this.updateGlobalTodoCount(1);
+ updateGlobalTodoCount(1);
},
decrementGlobalTodoCount() {
- this.updateGlobalTodoCount(-1);
+ updateGlobalTodoCount(-1);
},
onToggle(event) {
if (this.isTodo) {
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
index 59e72a2ffe3..098ab72dfb5 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
+++ b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
@@ -3,3 +3,19 @@ import { __ } from '~/locale';
export const todoLabel = (hasTodo) => {
return hasTodo ? __('Mark as done') : __('Add a to do');
};
+
+export const updateGlobalTodoCount = (additionalTodoCount) => {
+ const countContainer = document.querySelector('.js-todos-count');
+
+ if (countContainer === null) return;
+
+ const currentCount = parseInt(countContainer.innerText, 10);
+
+ const todoToggleEvent = new CustomEvent('todo:toggle', {
+ detail: {
+ count: Math.max(currentCount + additionalTodoCount, 0),
+ },
+ });
+
+ document.dispatchEvent(todoToggleEvent);
+};