diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-18 23:02:30 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-18 23:02:30 +0300 |
commit | 41fe97390ceddf945f3d967b8fdb3de4c66b7dea (patch) | |
tree | 9c8d89a8624828992f06d892cd2f43818ff5dcc8 /app/assets/javascripts/boards | |
parent | 0804d2dc31052fb45a1efecedc8e06ce9bc32862 (diff) |
Add latest changes from gitlab-org/gitlab@14-9-stable-eev14.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/boards')
13 files changed, 103 insertions, 169 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index 7e4d3ebb686..96cc774a280 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -1,5 +1,6 @@ import { sortBy, cloneDeep } from 'lodash'; -import { isGid } from '~/graphql_shared/utils'; +import { TYPE_BOARD, TYPE_ITERATION, TYPE_MILESTONE, TYPE_USER } from '~/graphql_shared/constants'; +import { isGid, convertToGraphQLId } from '~/graphql_shared/utils'; import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants'; export function getMilestone() { @@ -80,19 +81,22 @@ export function formatListsPageInfo(lists) { } export function fullBoardId(boardId) { - return `gid://gitlab/Board/${boardId}`; + if (!boardId) { + return null; + } + return convertToGraphQLId(TYPE_BOARD, boardId); } export function fullIterationId(id) { - return `gid://gitlab/Iteration/${id}`; + return convertToGraphQLId(TYPE_ITERATION, id); } export function fullUserId(id) { - return `gid://gitlab/User/${id}`; + return convertToGraphQLId(TYPE_USER, id); } export function fullMilestoneId(id) { - return `gid://gitlab/Milestone/${id}`; + return convertToGraphQLId(TYPE_MILESTONE, id); } export function fullLabelId(label) { diff --git a/app/assets/javascripts/boards/components/board_filtered_search.vue b/app/assets/javascripts/boards/components/board_filtered_search.vue index 45192b5304a..95d4fd5bc0a 100644 --- a/app/assets/javascripts/boards/components/board_filtered_search.vue +++ b/app/assets/javascripts/boards/components/board_filtered_search.vue @@ -151,10 +151,10 @@ export default { }); } - if (this.filterParams['not[iteration_id]']) { + if (this.filterParams['not[iterationId]']) { filteredSearchValue.push({ - type: 'iteration_id', - value: { data: this.filterParams['not[iteration_id]'], operator: '!=' }, + type: 'iteration', + value: { data: this.filterParams['not[iterationId]'], operator: '!=' }, }); } diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue index cc048e2af1a..5fcf9514708 100644 --- a/app/assets/javascripts/boards/components/board_form.vue +++ b/app/assets/javascripts/boards/components/board_form.vue @@ -1,11 +1,9 @@ <script> import { GlModal, GlAlert } from '@gitlab/ui'; import { mapGetters, mapActions, mapState } from 'vuex'; -import { TYPE_USER, TYPE_ITERATION, TYPE_MILESTONE } from '~/graphql_shared/constants'; -import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getParameterByName, visitUrl } from '~/lib/utils/url_utility'; import { __, s__ } from '~/locale'; -import { fullLabelId } from '../boards_util'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { formType } from '../constants'; import createBoardMutation from '../graphql/board_create.mutation.graphql'; @@ -18,6 +16,7 @@ const boardDefaults = { name: '', labels: [], milestone: {}, + iterationCadence: {}, iteration: {}, assignee: {}, weight: null, @@ -44,6 +43,7 @@ export default { BoardConfigurationOptions, GlAlert, }, + mixins: [glFeatureFlagMixin()], inject: { fullPath: { default: '', @@ -158,33 +158,8 @@ export default { groupPath: this.isGroupBoard ? this.fullPath : undefined, }; }, - issueBoardScopeMutationVariables() { - return { - weight: this.board.weight, - assigneeId: this.board.assignee?.id - ? convertToGraphQLId(TYPE_USER, this.board.assignee.id) - : null, - // Temporarily converting to milestone ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779 - milestoneId: this.board.milestone?.id - ? convertToGraphQLId(TYPE_MILESTONE, getIdFromGraphQLId(this.board.milestone.id)) - : null, - // Temporarily converting to iteration ID due to https://gitlab.com/gitlab-org/gitlab/-/issues/344779 - iterationId: this.board.iteration?.id - ? convertToGraphQLId(TYPE_ITERATION, getIdFromGraphQLId(this.board.iteration.id)) - : null, - }; - }, - boardScopeMutationVariables() { - return { - labelIds: this.board.labels.map(fullLabelId), - ...(this.isIssueBoard && this.issueBoardScopeMutationVariables), - }; - }, mutationVariables() { - return { - ...this.baseMutationVariables, - ...(this.scopedIssueBoardFeatureEnabled ? this.boardScopeMutationVariables : {}), - }; + return this.baseMutationVariables; }, }, mounted() { @@ -259,9 +234,12 @@ export default { this.board = { ...boardDefaults, ...this.currentBoard }; } }, - setIteration(iterationId) { + setIteration(iteration) { + if (this.glFeatures.iterationCadences) { + this.board.iterationCadenceId = iteration.iterationCadenceId; + } this.$set(this.board, 'iteration', { - id: iterationId, + id: iteration.id, }); }, setBoardLabels(labels) { diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue index 6835d83a66c..46b28d20da9 100644 --- a/app/assets/javascripts/boards/components/board_list_header.vue +++ b/app/assets/javascripts/boards/components/board_list_header.vue @@ -89,10 +89,6 @@ export default { listTitle() { return this.list?.label?.description || this.list?.assignee?.name || this.list.title || ''; }, - listIterationPeriod() { - const iteration = this.list?.iteration; - return iteration ? this.getIterationPeriod(iteration) : ''; - }, isIterationList() { return this.listType === ListType.iteration; }, @@ -108,9 +104,6 @@ export default { showIterationListDetails() { return this.isIterationList && this.showListDetails; }, - iterationCadencesAvailable() { - return this.isIterationList && this.glFeatures.iterationCadences; - }, showListDetails() { return !this.list.collapsed || !this.isSwimlanesHeader; }, @@ -344,13 +337,6 @@ export default { class="board-title-main-text gl-text-truncate" > {{ listTitle }} - <span - v-if="iterationCadencesAvailable" - class="gl-display-inline-block gl-text-gray-400" - data-testid="board-list-iteration-period" - > - {{ listIterationPeriod }}</span - > </span> <span v-if="listType === 'assignee'" diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue index 6dbb1ea0050..91fdfd668fc 100644 --- a/app/assets/javascripts/boards/components/boards_selector.vue +++ b/app/assets/javascripts/boards/components/boards_selector.vue @@ -101,6 +101,7 @@ export default { }, update(data) { const board = data.workspace?.board; + this.setBoardConfig(board); return { ...board, labels: board?.labels?.nodes, @@ -170,7 +171,7 @@ export default { eventHub.$off('showBoardModal', this.showPage); }, methods: { - ...mapActions(['setError']), + ...mapActions(['setError', 'setBoardConfig']), showPage(page) { this.currentPage = page; }, @@ -315,9 +316,7 @@ export default { <gl-dropdown-item v-if="hasMissingBoards" class="no-pointer-events"> {{ - s__( - 'IssueBoards|Some of your boards are hidden, activate a license to see them again.', - ) + s__('IssueBoards|Some of your boards are hidden, add a license to see them again.') }} </gl-dropdown-item> </div> diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js deleted file mode 100644 index 72586970008..00000000000 --- a/app/assets/javascripts/boards/filtered_search_boards.js +++ /dev/null @@ -1,81 +0,0 @@ -import { transformBoardConfig } from 'ee_else_ce/boards/boards_util'; -import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager'; -import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys'; -import { updateHistory } from '~/lib/utils/url_utility'; -import FilteredSearchContainer from '../filtered_search/container'; -import vuexstore from './stores'; - -export default class FilteredSearchBoards extends FilteredSearchManager { - constructor(store, updateUrl = false, cantEdit = []) { - super({ - page: 'boards', - isGroupDecendent: true, - stateFiltersSelector: '.issues-state-filters', - isGroup: IS_EE, - useDefaultState: false, - filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys, - }); - - this.store = store; - this.updateUrl = updateUrl; - - // Issue boards is slightly different, we handle all the requests async - // instead or reloading the page, we just re-fire the list ajax requests - this.isHandledAsync = true; - this.cantEdit = cantEdit.filter((i) => typeof i === 'string'); - this.cantEditWithValue = cantEdit.filter((i) => typeof i === 'object'); - - if (vuexstore.state.boardConfig) { - const boardConfigPath = transformBoardConfig(vuexstore.state.boardConfig); - // TODO Refactor: https://gitlab.com/gitlab-org/gitlab/-/issues/329274 - // here we are using "window.location.search" as a temporary store - // only to unpack the params and do another validation inside - // 'performSearch' and 'setFilter' vuex actions. - if (boardConfigPath !== '') { - const filterPath = window.location.search ? `${window.location.search}&` : '?'; - updateHistory({ - url: `${filterPath}${transformBoardConfig(vuexstore.state.boardConfig)}`, - }); - } - } - } - - updateObject(path) { - const groupByParam = new URLSearchParams(window.location.search).get('group_by'); - this.store.path = `${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`; - - updateHistory({ - url: `?${path.substr(1)}${groupByParam ? `&group_by=${groupByParam}` : ''}`, - }); - vuexstore.dispatch('performSearch'); - } - - removeTokens() { - const tokens = FilteredSearchContainer.container.querySelectorAll('.js-visual-token'); - - // Remove all the tokens as they will be replaced by the search manager - [].forEach.call(tokens, (el) => { - el.parentNode.removeChild(el); - }); - - this.filteredSearchInput.value = ''; - } - - updateTokens() { - this.removeTokens(); - - this.loadSearchParamsFromURL(); - - // Get the placeholder back if search is empty - this.filteredSearchInput.dispatchEvent(new Event('input')); - } - - canEdit(tokenName, tokenValue) { - if (this.cantEdit.includes(tokenName)) return false; - return ( - this.cantEditWithValue.findIndex( - (token) => token.name === tokenName && token.value === tokenValue, - ) === -1 - ); - } -} diff --git a/app/assets/javascripts/boards/graphql.js b/app/assets/javascripts/boards/graphql.js index 95863d4d5ac..d066a5d002e 100644 --- a/app/assets/javascripts/boards/graphql.js +++ b/app/assets/javascripts/boards/graphql.js @@ -10,5 +10,6 @@ export const gqlClient = createDefaultClient( return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object); }, }, + batchMax: 2, }, ); diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index f6073f9d981..b31b56e6839 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -8,8 +8,6 @@ import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_t import BoardApp from '~/boards/components/board_app.vue'; import '~/boards/filters/due_date_filters'; import { issuableTypes } from '~/boards/constants'; -import eventHub from '~/boards/eventhub'; -import FilteredSearchBoards from '~/boards/filtered_search_boards'; import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards'; import store from '~/boards/stores'; import toggleFocusMode from '~/boards/toggle_focus'; @@ -30,6 +28,12 @@ const apolloProvider = new VueApollo({ function mountBoardApp(el) { const { boardId, groupId, fullPath, rootPath } = el.dataset; + store.dispatch('fetchBoard', { + fullPath, + fullBoardId: fullBoardId(boardId), + boardType: el.dataset.parent, + }); + store.dispatch('setInitialBoardData', { boardId, fullBoardId: fullBoardId(boardId), @@ -37,30 +41,8 @@ function mountBoardApp(el) { boardType: el.dataset.parent, disabled: parseBoolean(el.dataset.disabled) || true, issuableType: issuableTypes.issue, - boardConfig: { - milestoneId: parseInt(el.dataset.boardMilestoneId, 10), - milestoneTitle: el.dataset.boardMilestoneTitle || '', - iterationId: parseInt(el.dataset.boardIterationId, 10), - iterationTitle: el.dataset.boardIterationTitle || '', - assigneeId: el.dataset.boardAssigneeId, - assigneeUsername: el.dataset.boardAssigneeUsername, - labels: el.dataset.labels ? JSON.parse(el.dataset.labels) : [], - labelIds: el.dataset.labelIds ? JSON.parse(el.dataset.labelIds) : [], - weight: el.dataset.boardWeight ? parseInt(el.dataset.boardWeight, 10) : null, - }, }); - if (!gon?.features?.issueBoardsFilteredSearch) { - // Warning: FilteredSearchBoards has an implicit dependency on the Vuex state 'boardConfig' - // Improve this situation in the future. - const filterManager = new FilteredSearchBoards({ path: '' }, true, []); - filterManager.setup(); - - eventHub.$on('updateTokens', () => { - filterManager.updateTokens(); - }); - } - // eslint-disable-next-line no-new new Vue({ el, @@ -110,10 +92,14 @@ export default () => { } }); - if (gon?.features?.issueBoardsFilteredSearch) { - const { releasesFetchPath } = $boardApp.dataset; - initBoardsFilteredSearch(apolloProvider, isLoggedIn(), releasesFetchPath); - } + const { releasesFetchPath, epicFeatureAvailable, iterationFeatureAvailable } = $boardApp.dataset; + initBoardsFilteredSearch( + apolloProvider, + isLoggedIn(), + releasesFetchPath, + parseBoolean(epicFeatureAvailable), + parseBoolean(iterationFeatureAvailable), + ); mountBoardApp($boardApp); diff --git a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js index 327fb9ba8d7..bb659eb075a 100644 --- a/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js +++ b/app/assets/javascripts/boards/mount_filtered_search_issue_boards.js @@ -4,7 +4,13 @@ import store from '~/boards/stores'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { queryToObject } from '~/lib/utils/url_utility'; -export default (apolloProvider, isSignedIn, releasesFetchPath) => { +export default ( + apolloProvider, + isSignedIn, + releasesFetchPath, + epicFeatureAvailable, + iterationFeatureAvailable, +) => { const el = document.getElementById('js-issue-board-filtered-search'); const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true }); @@ -23,6 +29,8 @@ export default (apolloProvider, isSignedIn, releasesFetchPath) => { initialFilterParams, isSignedIn, releasesFetchPath, + epicFeatureAvailable, + iterationFeatureAvailable, }, store, // TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/324094 apolloProvider, diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 1ebfcfc331b..82307da2572 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -36,6 +36,8 @@ import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { queryToObject } from '~/lib/utils/url_utility'; import { s__ } from '~/locale'; import { gqlClient } from '../graphql'; +import projectBoardQuery from '../graphql/project_board.query.graphql'; +import groupBoardQuery from '../graphql/group_board.query.graphql'; import boardLabelsQuery from '../graphql/board_labels.query.graphql'; import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql'; import groupProjectsQuery from '../graphql/group_projects.query.graphql'; @@ -46,10 +48,44 @@ import projectBoardMilestonesQuery from '../graphql/project_board_milestones.que import * as types from './mutation_types'; export default { + fetchBoard: ({ commit, dispatch }, { fullPath, fullBoardId, boardType }) => { + const variables = { + fullPath, + boardId: fullBoardId, + }; + + return gqlClient + .query({ + query: boardType === BoardType.group ? groupBoardQuery : projectBoardQuery, + variables, + }) + .then(({ data }) => { + const board = data.workspace?.board; + commit(types.RECEIVE_BOARD_SUCCESS, board); + dispatch('setBoardConfig', board); + }) + .catch(() => commit(types.RECEIVE_BOARD_FAILURE)); + }, + setInitialBoardData: ({ commit }, data) => { commit(types.SET_INITIAL_BOARD_DATA, data); }, + setBoardConfig: ({ commit }, board) => { + const config = { + milestoneId: board.milestone?.id || null, + milestoneTitle: board.milestone?.title || null, + iterationId: board.iteration?.id || null, + iterationTitle: board.iteration?.title || null, + assigneeId: board.assignee?.id || null, + assigneeUsername: board.assignee?.username || null, + labels: board.labels?.nodes || [], + labelIds: board.labels?.nodes?.map((label) => label.id) || [], + weight: board.weight, + }; + commit(types.SET_BOARD_CONFIG, config); + }, + setActiveId({ commit }, { id, sidebarType }) { commit(types.SET_ACTIVE_ID, { id, sidebarType }); }, diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 31b78014525..668a3b5e0f9 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -1,4 +1,7 @@ +export const RECEIVE_BOARD_SUCCESS = 'RECEIVE_BOARD_SUCCESS'; +export const RECEIVE_BOARD_FAILURE = 'RECEIVE_BOARD_FAILURE'; export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA'; +export const SET_BOARD_CONFIG = 'SET_BOARD_CONFIG'; export const SET_FILTERS = 'SET_FILTERS'; export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS'; export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 2a2ce7652e6..9a50dcf05b8 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -33,10 +33,20 @@ export const addItemToList = ({ state, listId, itemId, moveBeforeId, moveAfterId }; export default { + [mutationTypes.RECEIVE_BOARD_SUCCESS]: (state, board) => { + state.board = { + ...board, + labels: board?.labels?.nodes || [], + }; + }, + + [mutationTypes.RECEIVE_BOARD_FAILURE]: (state) => { + state.error = s__('Boards|An error occurred while fetching the board. Please reload the page.'); + }, + [mutationTypes.SET_INITIAL_BOARD_DATA](state, data) { const { allowSubEpics, - boardConfig, boardId, boardType, disabled, @@ -45,7 +55,6 @@ export default { issuableType, } = data; state.allowSubEpics = allowSubEpics; - state.boardConfig = boardConfig; state.boardId = boardId; state.boardType = boardType; state.disabled = disabled; @@ -54,6 +63,10 @@ export default { state.issuableType = issuableType; }, + [mutationTypes.SET_BOARD_CONFIG](state, boardConfig) { + state.boardConfig = boardConfig; + }, + [mutationTypes.RECEIVE_BOARD_LISTS_SUCCESS]: (state, lists) => { state.boardLists = lists; }, diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index 80c51c966d2..7af4e5a8798 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -1,6 +1,7 @@ import { inactiveId, ListType } from '~/boards/constants'; export default () => ({ + board: {}, boardType: null, issuableType: null, fullPath: null, |