diff options
Diffstat (limited to 'app/assets/javascripts/boards/components')
13 files changed, 188 insertions, 209 deletions
diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue index 1cfa35ffd91..4d915ff341a 100644 --- a/app/assets/javascripts/boards/components/board_app.vue +++ b/app/assets/javascripts/boards/components/board_app.vue @@ -1,6 +1,7 @@ <script> // eslint-disable-next-line no-restricted-imports import { mapGetters } from 'vuex'; +import { omit } from 'lodash'; import { refreshCurrentPage, queryToObject } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import BoardContent from '~/boards/components/board_content.vue'; @@ -115,9 +116,8 @@ export default { return this.activeListId ? this.boardListsApollo[this.activeListId] : undefined; }, formattedFilterParams() { - if (this.filterParams.groupBy) delete this.filterParams.groupBy; return filterVariables({ - filters: this.filterParams, + filters: omit(this.filterParams, 'groupBy'), issuableType: this.issuableType, filterInfo: FiltersInfo, filterFields: FilterFields, diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 05865dc7305..fd45d2d31c3 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -2,6 +2,9 @@ // eslint-disable-next-line no-restricted-imports import { mapActions, mapState } from 'vuex'; import Tracking from '~/tracking'; +import setSelectedBoardItemsMutation from '~/boards/graphql/client/set_selected_board_items.mutation.graphql'; +import unsetSelectedBoardItemsMutation from '~/boards/graphql/client/unset_selected_board_items.mutation.graphql'; +import selectedBoardItemsQuery from '~/boards/graphql/client/selected_board_items.query.graphql'; import setActiveBoardItemMutation from 'ee_else_ce/boards/graphql/client/set_active_board_item.mutation.graphql'; import activeBoardItemQuery from 'ee_else_ce/boards/graphql/client/active_board_item.query.graphql'; import BoardCardInner from './board_card_inner.vue'; @@ -52,9 +55,12 @@ export default { return !this.isApolloBoard; }, }, + selectedBoardItems: { + query: selectedBoardItemsQuery, + }, }, computed: { - ...mapState(['selectedBoardItems', 'activeId']), + ...mapState(['activeId']), activeItemId() { return this.isApolloBoard ? this.activeBoardItem?.id : this.activeId; }, @@ -62,10 +68,7 @@ export default { return this.item.id === this.activeItemId; }, multiSelectVisible() { - return ( - !this.activeItemId && - this.selectedBoardItems.findIndex((boardItem) => boardItem.id === this.item.id) > -1 - ); + return !this.activeItemId && this.selectedBoardItems?.includes(this.item.id); }, isDisabled() { return this.disabled || !this.item.id || this.item.isLoading || !this.canAdmin; @@ -93,7 +96,7 @@ export default { }, }, methods: { - ...mapActions(['toggleBoardItemMultiSelection', 'toggleBoardItem']), + ...mapActions(['toggleBoardItem']), toggleIssue(e) { // Don't do anything if this happened on a no trigger element if (e.target.closest('.js-no-trigger')) return; @@ -110,7 +113,10 @@ export default { this.track('click_card', { label: 'right_sidebar' }); } }, - toggleItem() { + async toggleItem() { + await this.$apollo.mutate({ + mutation: unsetSelectedBoardItemsMutation, + }); this.$apollo.mutate({ mutation: setActiveBoardItemMutation, variables: { @@ -119,13 +125,32 @@ export default { }, }); }, + async toggleBoardItemMultiSelection(item) { + if (this.activeItemId) { + await this.$apollo.mutate({ + mutation: setSelectedBoardItemsMutation, + variables: { + itemId: this.activeItemId, + }, + }); + await this.$apollo.mutate({ + mutation: setActiveBoardItemMutation, + variables: { boardItem: null }, + }); + } + this.$apollo.mutate({ + mutation: setSelectedBoardItemsMutation, + variables: { + itemId: item.id, + }, + }); + }, }, }; </script> <template> <li - data-qa-selector="board_card" :class="[ { 'multi-select gl-bg-blue-50 gl-border-blue-200': multiSelectVisible, @@ -141,7 +166,7 @@ export default { :data-item-iid="item.iid" :data-item-path="item.referencePath" :style="cardStyle" - data-testid="board_card" + data-testid="board-card" class="board-card gl-p-5 gl-rounded-base gl-line-height-normal gl-relative gl-mb-3" @click="toggleIssue($event)" > diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue index c441a718dd8..c10ff2e08da 100644 --- a/app/assets/javascripts/boards/components/board_card_inner.vue +++ b/app/assets/javascripts/boards/components/board_card_inner.vue @@ -9,11 +9,12 @@ import { } from '@gitlab/ui'; import { sortBy } from 'lodash'; // eslint-disable-next-line no-restricted-imports -import { mapActions, mapState } from 'vuex'; +import { mapActions } from 'vuex'; import boardCardInner from 'ee_else_ce/boards/mixins/board_card_inner'; import { isScopedLabel } from '~/lib/utils/common_utils'; import { updateHistory } from '~/lib/utils/url_utility'; import { sprintf, __, n__ } from '~/locale'; +import isShowingLabelsQuery from '~/graphql_shared/client/is_showing_labels.query.graphql'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; @@ -86,8 +87,13 @@ export default { maxCounter: 99, }; }, + apollo: { + isShowingLabels: { + query: isShowingLabelsQuery, + update: (data) => data.isShowingLabels, + }, + }, computed: { - ...mapState(['isShowingLabels']), isLoading() { return this.item.isLoading || this.item.iid === '-1'; }, @@ -252,7 +258,7 @@ export default { v-if="item.hidden" v-gl-tooltip name="spam" - :title="__('This issue is hidden because its author has been banned')" + :title="__('This issue is hidden because its author has been banned.')" class="gl-mr-2 hidden-icon gl-text-orange-500 gl-cursor-help" data-testid="hidden-icon" /> diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue index bcd7db8dcb4..67a4c5eba45 100644 --- a/app/assets/javascripts/boards/components/board_column.vue +++ b/app/assets/javascripts/boards/components/board_column.vue @@ -93,7 +93,7 @@ export default { }" :data-list-id="list.id" class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal is-expandable" - data-qa-selector="board_list" + data-testid="board-list" > <div class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50" diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue index 3c2659b00c9..554f3bfa416 100644 --- a/app/assets/javascripts/boards/components/board_content.vue +++ b/app/assets/javascripts/boards/components/board_content.vue @@ -219,7 +219,7 @@ export default { <template> <div v-cloak - data-qa-selector="boards_list" + data-testid="boards-list" class="gl-flex-grow-1 gl-display-flex gl-flex-direction-column gl-min-h-0" > <gl-alert v-if="errorToDisplay" variant="danger" :dismissible="true" @dismiss="dismissError"> diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue index d12478b42d8..a3d55ac8306 100644 --- a/app/assets/javascripts/boards/components/board_form.vue +++ b/app/assets/javascripts/boards/components/board_form.vue @@ -1,13 +1,15 @@ <script> import { GlModal, GlAlert } from '@gitlab/ui'; // eslint-disable-next-line no-restricted-imports -import { mapActions, mapState } from 'vuex'; +import { mapActions } from 'vuex'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { visitUrl, updateHistory, getParameterByName } from '~/lib/utils/url_utility'; import { __, s__ } from '~/locale'; import eventHub from '~/boards/eventhub'; import { formType } from '../constants'; +import { setError } from '../graphql/cache_updates'; +import errorQuery from '../graphql/client/error.query.graphql'; import createBoardMutation from '../graphql/board_create.mutation.graphql'; import destroyBoardMutation from '../graphql/board_destroy.mutation.graphql'; import updateBoardMutation from '../graphql/board_update.mutation.graphql'; @@ -93,8 +95,13 @@ export default { isLoading: false, }; }, + apollo: { + error: { + query: errorQuery, + update: (data) => data.boardsAppError, + }, + }, computed: { - ...mapState(['error']), isNewForm() { return this.currentPage === formType.new; }, @@ -133,7 +140,7 @@ export default { variant: this.buttonKind, disabled: this.submitDisabled, loading: this.isLoading, - 'data-qa-selector': 'save_changes_button', + 'data-testid': 'save-changes-button', }, }; }, @@ -177,7 +184,8 @@ export default { } }, methods: { - ...mapActions(['setError', 'unsetError', 'setBoard']), + ...mapActions(['setBoard']), + setError, isFocusMode() { return Boolean(document.querySelector('.content-wrapper > .js-focus-mode-board.is-focused')); }, @@ -211,8 +219,8 @@ export default { try { await this.deleteBoard(); visitUrl(this.boardBaseUrl); - } catch { - this.setError({ message: this.$options.i18n.deleteErrorMessage }); + } catch (error) { + setError({ error, message: this.$options.i18n.deleteErrorMessage }); } finally { this.isLoading = false; } @@ -236,8 +244,8 @@ export default { : ''; updateHistory({ url: `${this.boardBaseUrl}/${getIdFromGraphQLId(board.id)}${param}` }); } - } catch { - this.setError({ message: this.$options.i18n.saveErrorMessage }); + } catch (error) { + setError({ error, message: this.$options.i18n.saveErrorMessage }); } finally { this.isLoading = false; } @@ -295,11 +303,11 @@ export default { @hide.prevent > <gl-alert - v-if="!isApolloBoard && error" + v-if="error" class="gl-mb-3" variant="danger" :dismissible="true" - @dismiss="unsetError" + @dismiss="() => setError({ message: null, captureError: false })" > {{ error }} </gl-alert> @@ -316,7 +324,7 @@ export default { ref="name" v-model="board.name" class="form-control" - data-qa-selector="board_name_field" + data-testid="board-name-field" type="text" :placeholder="$options.i18n.titleFieldPlaceholder" @keyup.enter="submit" diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue index 1bb7e88122a..2693a6bb5ea 100644 --- a/app/assets/javascripts/boards/components/board_list.vue +++ b/app/assets/javascripts/boards/components/board_list.vue @@ -653,7 +653,7 @@ export default { <div v-show="!list.collapsed" class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column gl-min-h-0" - data-qa-selector="board_list_cards_area" + data-testid="board-list-cards-area" > <div v-if="loading" diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue index 42c30dc8245..0235edd69ac 100644 --- a/app/assets/javascripts/boards/components/board_list_header.vue +++ b/app/assets/javascripts/boards/components/board_list_header.vue @@ -201,8 +201,8 @@ export default { }) ); }, - totalWeight() { - return this.boardList?.totalWeight; + totalIssueWeight() { + return this.boardList?.totalIssueWeight; }, canShowTotalWeight() { return this.weightFeatureAvailable && !this.isLoading; @@ -365,7 +365,6 @@ export default { }" :style="headerStyle" class="board-header gl-relative" - data-qa-selector="board_list_header" data-testid="board-list-header" > <h3 @@ -473,8 +472,8 @@ export default { <div v-else>• {{ itemsTooltipLabel }}</div> <div v-if="weightFeatureAvailable && !isLoading"> • - <gl-sprintf :message="__('%{totalWeight} total weight')"> - <template #totalWeight>{{ totalWeight }}</template> + <gl-sprintf :message="__('%{totalIssueWeight} total weight')"> + <template #totalIssueWeight>{{ totalIssueWeight }}</template> </gl-sprintf> </div> </gl-tooltip> @@ -507,7 +506,7 @@ export default { <gl-tooltip :target="() => $refs.weightTooltip" :title="weightCountToolTip" /> <span ref="weightTooltip" class="gl-display-inline-flex gl-ml-3" data-testid="weight"> <gl-icon class="gl-mr-2" name="weight" :size="14" /> - {{ totalWeight }} + {{ totalIssueWeight }} </span> </template> <!-- EE end --> diff --git a/app/assets/javascripts/boards/components/board_top_bar.vue b/app/assets/javascripts/boards/components/board_top_bar.vue index 7fd1a934381..31664c28831 100644 --- a/app/assets/javascripts/boards/components/board_top_bar.vue +++ b/app/assets/javascripts/boards/components/board_top_bar.vue @@ -4,6 +4,7 @@ import { s__ } from '~/locale'; import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue'; import IssueBoardFilteredSearch from 'ee_else_ce/boards/components/issue_board_filtered_search.vue'; import { getBoardQuery } from 'ee_else_ce/boards/boards_util'; +import ToggleLabels from '~/vue_shared/components/toggle_labels.vue'; import { setError } from '../graphql/cache_updates'; import ConfigToggle from './config_toggle.vue'; import NewBoardButton from './new_board_button.vue'; @@ -17,7 +18,7 @@ export default { ConfigToggle, NewBoardButton, ToggleFocus, - ToggleLabels: () => import('ee_component/boards/components/toggle_labels.vue'), + ToggleLabels, ToggleEpicsSwimlanes: () => import('ee_component/boards/components/toggle_epics_swimlanes.vue'), EpicBoardFilteredSearch: () => import('ee_component/boards/components/epic_filtered_search.vue'), diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index cc6fde92f9b..cd2a4a02b2e 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -1,15 +1,7 @@ <script> -import { - GlLoadingIcon, - GlSearchBoxByType, - GlDropdown, - GlDropdownDivider, - GlDropdownSectionHeader, - GlDropdownItem, - GlModalDirective, -} from '@gitlab/ui'; +import { GlButton, GlCollapsibleListbox, GlModalDirective } from '@gitlab/ui'; import { produce } from 'immer'; -import { throttle } from 'lodash'; +import { differenceBy, debounce } from 'lodash'; // eslint-disable-next-line no-restricted-imports import { mapActions, mapState } from 'vuex'; @@ -18,7 +10,8 @@ import BoardForm from 'ee_else_ce/boards/components/board_form.vue'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { isMetaKey } from '~/lib/utils/common_utils'; import { updateHistory } from '~/lib/utils/url_utility'; -import { s__ } from '~/locale'; +import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; +import { s__, __ } from '~/locale'; import eventHub from '../eventhub'; import groupBoardsQuery from '../graphql/group_boards.query.graphql'; @@ -34,15 +27,16 @@ export default { name: 'BoardsSelector', i18n: { fetchBoardsError: s__('Boards|An error occurred while fetching boards. Please try again.'), + headerText: s__('IssueBoards|Switch board'), + noResultsText: s__('IssueBoards|No matching boards found'), + hiddenBoardsText: s__( + 'IssueBoards|Some of your boards are hidden, add a license to see them again.', + ), }, components: { BoardForm, - GlLoadingIcon, - GlSearchBoxByType, - GlDropdown, - GlDropdownDivider, - GlDropdownSectionHeader, - GlDropdownItem, + GlButton, + GlCollapsibleListbox, }, directives: { GlModalDirective, @@ -60,11 +54,6 @@ export default { 'isApolloBoard', ], props: { - throttleDuration: { - type: Number, - default: 200, - required: false, - }, boardApollo: { type: Object, required: false, @@ -78,13 +67,10 @@ export default { }, data() { return { - hasScrollFade: false, - scrollFadeInitialized: false, boards: [], recentBoards: [], loadingBoards: false, loadingRecentBoards: false, - throttledSetScrollFade: throttle(this.setScrollFade, this.throttleDuration), contentClientHeight: 0, maxPosition: 0, filterTerm: '', @@ -97,6 +83,12 @@ export default { boardToUse() { return this.isApolloBoard ? this.boardApollo : this.board; }, + boardToUseName() { + return this.boardToUse?.name || s__('IssueBoards|Select board'); + }, + boardToUseId() { + return getIdFromGraphQLId(this.boardToUse.id) || ''; + }, isBoardToUseLoading() { return this.isApolloBoard ? this.isCurrentBoardLoading : this.isBoardLoading; }, @@ -112,6 +104,26 @@ export default { loading() { return this.loadingRecentBoards || this.loadingBoards; }, + listBoxItems() { + const mapItems = ({ id, name }) => ({ text: name, value: id }); + + if (this.showRecentSection) { + const notRecent = differenceBy(this.filteredBoards, this.recentBoards, 'id'); + + return [ + { + text: __('Recent'), + options: this.recentBoards.map(mapItems), + }, + { + text: __('All'), + options: notRecent.map(mapItems), + }, + ]; + } + + return this.filteredBoards.map(mapItems); + }, filteredBoards() { return this.boards.filter((board) => board.name.toLowerCase().includes(this.filterTerm.toLowerCase()), @@ -126,34 +138,25 @@ export default { showDropdown() { return this.showCreate || this.hasMissingBoards; }, - scrollFadeClass() { - return { - 'fade-out': !this.hasScrollFade, - }; - }, showRecentSection() { return ( - this.recentBoards.length && + this.recentBoards.length > 0 && this.boards.length > MIN_BOARDS_TO_VIEW_RECENT && !this.filterTerm.length ); }, }, watch: { - filteredBoards() { - this.scrollFadeInitialized = false; - this.$nextTick(this.setScrollFade); - }, - recentBoards() { - this.scrollFadeInitialized = false; - this.$nextTick(this.setScrollFade); - }, boardToUse(newBoard) { document.title = newBoard.name; }, }, created() { eventHub.$on('showBoardModal', this.showPage); + this.handleSearch = debounce(this.setFilterTerm, DEFAULT_DEBOUNCE_AND_THROTTLE_MS); + }, + destroyed() { + this.handleSearch.cancel(); }, beforeDestroy() { eventHub.$off('showBoardModal', this.showPage); @@ -248,34 +251,6 @@ export default { this.$emit('switchBoard', board.id); }, - isScrolledUp() { - const { content } = this.$refs; - - if (!content) { - return false; - } - - const currentPosition = this.contentClientHeight + content.scrollTop; - - return currentPosition < this.maxPosition; - }, - initScrollFade() { - const { content } = this.$refs; - - if (!content) { - return; - } - - this.scrollFadeInitialized = true; - - this.contentClientHeight = content.clientHeight; - this.maxPosition = content.scrollHeight; - }, - setScrollFade() { - if (!this.scrollFadeInitialized) this.initScrollFade(); - - this.hasScrollFade = this.isScrolledUp(); - }, fetchCurrentBoard(boardId) { this.fetchBoard({ fullPath: this.fullPath, @@ -283,17 +258,24 @@ export default { boardType: this.boardType, }); }, - async switchBoard(boardId, e) { + setFilterTerm(value) { + this.filterTerm = value; + }, + async switchBoardKeyEvent(boardId, e) { if (isMetaKey(e)) { + e.stopPropagation(); window.open(`${this.boardBaseUrl}/${boardId}`, '_blank'); - } else if (this.isApolloBoard) { + } + }, + switchBoardGroup(value) { + if (this.isApolloBoard) { // Epic board ID is supported in EE version of this file - this.$emit('switchBoard', this.fullBoardId(boardId)); - updateHistory({ url: `${this.boardBaseUrl}/${boardId}` }); + this.$emit('switchBoard', this.fullBoardId(value)); + updateHistory({ url: `${this.boardBaseUrl}/${value}` }); } else { this.unsetActiveId(); - this.fetchCurrentBoard(boardId); - updateHistory({ url: `${this.boardBaseUrl}/${boardId}` }); + this.fetchCurrentBoard(value); + updateHistory({ url: `${this.boardBaseUrl}/${value}` }); } }, }, @@ -303,105 +285,65 @@ export default { <template> <div class="boards-switcher gl-mr-3" data-testid="boards-selector"> <span class="boards-selector-wrapper"> - <gl-dropdown + <gl-collapsible-listbox v-if="showDropdown" + block data-testid="boards-dropdown" - data-qa-selector="boards_dropdown" - toggle-class="dropdown-menu-toggle" - menu-class="flex-column dropdown-extended-height" + searchable + :searching="loading" + toggle-class="gl-min-w-20" + :header-text="$options.i18n.headerText" + :no-results-text="$options.i18n.noResultsText" :loading="isBoardToUseLoading" - :text="boardToUse.name" - @show="loadBoards" + :items="listBoxItems" + :toggle-text="boardToUseName" + :selected="boardToUseId" + @search="handleSearch" + @select="switchBoardGroup" + @shown="loadBoards" > - <p class="gl-dropdown-header-top" @mousedown.prevent> - {{ s__('IssueBoards|Switch board') }} - </p> - <gl-search-box-by-type ref="searchBox" v-model="filterTerm" class="m-2" /> - - <div - v-if="!loading" - ref="content" - data-qa-selector="boards_dropdown_content" - class="dropdown-content flex-fill" - @scroll.passive="throttledSetScrollFade" - > - <gl-dropdown-item - v-show="filteredBoards.length === 0" - class="gl-pointer-events-none text-secondary" - > - {{ s__('IssueBoards|No matching boards found') }} - </gl-dropdown-item> - - <gl-dropdown-section-header v-if="showRecentSection"> - {{ __('Recent') }} - </gl-dropdown-section-header> - - <template v-if="showRecentSection"> - <gl-dropdown-item - v-for="recentBoard in recentBoards" - :key="`recent-${recentBoard.id}`" - data-testid="dropdown-item" - @click.prevent="switchBoard(recentBoard.id, $event)" - > - {{ recentBoard.name }} - </gl-dropdown-item> - </template> - - <gl-dropdown-divider v-if="showRecentSection" /> - - <gl-dropdown-section-header v-if="showRecentSection"> - {{ __('All') }} - </gl-dropdown-section-header> - - <gl-dropdown-item - v-for="otherBoard in filteredBoards" - :key="otherBoard.id" - data-testid="dropdown-item" - @click.prevent="switchBoard(otherBoard.id, $event)" - > - {{ otherBoard.name }} - </gl-dropdown-item> - - <gl-dropdown-item v-if="hasMissingBoards" class="no-pointer-events"> + <template #list-item="{ item }"> + <div data-testid="dropdown-item-recent" @click="switchBoardKeyEvent(item.value, $event)"> + {{ item.text }} + </div> + </template> + + <template #footer> + <div v-if="hasMissingBoards" class="gl-border-t gl-font-sm gl-px-4 gl-pt-4 gl-pb-3"> {{ s__('IssueBoards|Some of your boards are hidden, add a license to see them again.') }} - </gl-dropdown-item> - </div> - - <div - v-show="filteredBoards.length > 0" - class="dropdown-content-faded-mask" - :class="scrollFadeClass" - ></div> - - <gl-loading-icon v-if="loading" size="sm" /> - - <div v-if="canAdminBoard"> - <gl-dropdown-divider /> - - <gl-dropdown-item - v-if="showCreate" - v-gl-modal-directive="'board-config-modal'" - data-qa-selector="create_new_board_button" - data-track-action="click_button" - data-track-label="create_new_board" - data-track-property="dropdown" - @click.prevent="showPage('new')" - > - {{ s__('IssueBoards|Create new board') }} - </gl-dropdown-item> - - <gl-dropdown-item - v-if="showDelete" - v-gl-modal-directive="'board-config-modal'" - class="text-danger" - @click.prevent="showPage('delete')" - > - {{ s__('IssueBoards|Delete board') }} - </gl-dropdown-item> - </div> - </gl-dropdown> + </div> + <div v-if="canAdminBoard" class="gl-border-t gl-py-2 gl-px-2"> + <gl-button + v-if="showCreate" + v-gl-modal-directive="'board-config-modal'" + block + class="gl-justify-content-start!" + category="tertiary" + data-testid="create-new-board-button" + data-track-action="click_button" + data-track-label="create_new_board" + data-track-property="dropdown" + @click="showPage('new')" + > + {{ s__('IssueBoards|Create new board') }} + </gl-button> + + <gl-button + v-if="showDelete" + v-gl-modal-directive="'board-config-modal'" + block + category="tertiary" + variant="danger" + class="gl-mt-0! gl-justify-content-start!" + @click="showPage('delete')" + > + {{ s__('IssueBoards|Delete board') }} + </gl-button> + </div> + </template> + </gl-collapsible-listbox> <board-form v-if="currentPage" diff --git a/app/assets/javascripts/boards/components/config_toggle.vue b/app/assets/javascripts/boards/components/config_toggle.vue index bc896932ffc..69e6cc870d2 100644 --- a/app/assets/javascripts/boards/components/config_toggle.vue +++ b/app/assets/javascripts/boards/components/config_toggle.vue @@ -49,7 +49,7 @@ export default { v-gl-tooltip :title="tooltipTitle" :class="{ 'dot-highlight': hasScope || boardHasScope }" - data-qa-selector="boards_config_button" + data-testid="boards-config-button" @click.prevent="showPage" > {{ buttonText }} diff --git a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue index a7b3f5536a4..c28415de620 100644 --- a/app/assets/javascripts/boards/components/issue_board_filtered_search.vue +++ b/app/assets/javascripts/boards/components/issue_board_filtered_search.vue @@ -62,11 +62,7 @@ export default { tokensCE() { const { issue, incident } = this.$options.i18n; const { types } = this.$options; - const { fetchUsers, fetchLabels } = issueBoardFilters( - this.$apollo, - this.fullPath, - this.isGroupBoard, - ); + const { fetchLabels } = issueBoardFilters(this.$apollo, this.fullPath, this.isGroupBoard); const tokens = [ { @@ -77,7 +73,8 @@ export default { token: UserToken, dataType: 'user', unique: true, - fetchUsers, + isProject: !this.isGroupBoard, + fullPath: this.fullPath, preloadedUsers: this.preloadedUsers(), }, { @@ -89,7 +86,8 @@ export default { token: UserToken, dataType: 'user', unique: true, - fetchUsers, + isProject: !this.isGroupBoard, + fullPath: this.fullPath, preloadedUsers: this.preloadedUsers(), }, { diff --git a/app/assets/javascripts/boards/components/toggle_focus.vue b/app/assets/javascripts/boards/components/toggle_focus.vue index 990a6fa63d4..a886abf9e61 100644 --- a/app/assets/javascripts/boards/components/toggle_focus.vue +++ b/app/assets/javascripts/boards/components/toggle_focus.vue @@ -38,7 +38,7 @@ export default { v-gl-tooltip category="tertiary" :icon="isFullscreen ? 'minimize' : 'maximize'" - data-qa-selector="focus_mode_button" + data-testid="focus-mode-button" :title="$options.i18n.toggleFocusMode" :aria-label="$options.i18n.toggleFocusMode" @click="toggleFocusMode" |