Commit 7c7bcd85 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'cngo-issues-list-sort-milestones' into 'master'

Sort milestones correctly on project/issues list refactor

See merge request gitlab-org/gitlab!79945
parents c5dfee02 1beed5d9
...@@ -10,16 +10,30 @@ import { ...@@ -10,16 +10,30 @@ import {
} from '@gitlab/ui'; } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import fuzzaldrinPlus from 'fuzzaldrin-plus'; import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { orderBy } from 'lodash'; import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql'; import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql'; import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import createFlash, { FLASH_TYPES } from '~/flash'; import createFlash, { FLASH_TYPES } from '~/flash';
import { TYPE_USER } from '~/graphql_shared/constants'; import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { ITEM_TYPE } from '~/groups/constants'; import { ITEM_TYPE } from '~/groups/constants';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue'; import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue'; import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
import axios from '~/lib/utils/axios_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
DEFAULT_NONE_ANY,
OPERATOR_IS_ONLY,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants'; import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
import { import {
...@@ -40,7 +54,13 @@ import { ...@@ -40,7 +54,13 @@ import {
TOKEN_TYPE_TYPE, TOKEN_TYPE_TYPE,
UPDATED_DESC, UPDATED_DESC,
urlSortParams, urlSortParams,
} from '~/issues/list/constants'; } from '../constants';
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
import searchMilestonesQuery from '../queries/search_milestones.query.graphql';
import searchUsersQuery from '../queries/search_users.query.graphql';
import setSortPreferenceMutation from '../queries/set_sort_preference.mutation.graphql';
import { import {
convertToApiParams, convertToApiParams,
convertToSearchQuery, convertToSearchQuery,
...@@ -50,28 +70,7 @@ import { ...@@ -50,28 +70,7 @@ import {
getInitialPageParams, getInitialPageParams,
getSortKey, getSortKey,
getSortOptions, getSortOptions,
} from '~/issues/list/utils'; } from '../utils';
import axios from '~/lib/utils/axios_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName, joinPaths } from '~/lib/utils/url_utility';
import {
DEFAULT_NONE_ANY,
OPERATOR_IS_ONLY,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
} from '~/vue_shared/components/filtered_search_bar/constants';
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
import setSortPreferenceMutation from '../queries/set_sort_preference.mutation.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
import searchMilestonesQuery from '../queries/search_milestones.query.graphql';
import searchUsersQuery from '../queries/search_users.query.graphql';
import NewIssueDropdown from './new_issue_dropdown.vue'; import NewIssueDropdown from './new_issue_dropdown.vue';
const AuthorToken = () => const AuthorToken = () =>
...@@ -103,77 +102,31 @@ export default { ...@@ -103,77 +102,31 @@ export default {
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
}, },
inject: { inject: [
autocompleteAwardEmojisPath: { 'autocompleteAwardEmojisPath',
default: '', 'calendarPath',
}, 'canBulkUpdate',
calendarPath: { 'emptyStateSvgPath',
default: '', 'exportCsvPath',
}, 'fullPath',
canBulkUpdate: { 'hasAnyIssues',
default: false, 'hasAnyProjects',
}, 'hasBlockedIssuesFeature',
emptyStateSvgPath: { 'hasIssueWeightsFeature',
default: '', 'hasMultipleIssueAssigneesFeature',
}, 'initialEmail',
exportCsvPath: { 'initialSort',
default: '', 'isAnonymousSearchDisabled',
}, 'isIssueRepositioningDisabled',
fullPath: { 'isProject',
default: '', 'isSignedIn',
}, 'jiraIntegrationPath',
hasAnyIssues: { 'newIssuePath',
default: false, 'releasesPath',
}, 'rssPath',
hasAnyProjects: { 'showNewIssueLink',
default: false, 'signInPath',
}, ],
hasBlockedIssuesFeature: {
default: false,
},
hasIssueWeightsFeature: {
default: false,
},
hasMultipleIssueAssigneesFeature: {
default: false,
},
initialEmail: {
default: '',
},
initialSort: {
default: '',
},
isAnonymousSearchDisabled: {
default: false,
},
isIssueRepositioningDisabled: {
default: false,
},
isProject: {
default: false,
},
isSignedIn: {
default: false,
},
jiraIntegrationPath: {
default: '',
},
newIssuePath: {
default: '',
},
releasesPath: {
default: '',
},
rssPath: {
default: '',
},
showNewIssueLink: {
default: false,
},
signInPath: {
default: '',
},
},
props: { props: {
eeSearchTokens: { eeSearchTokens: {
type: Array, type: Array,
...@@ -349,6 +302,7 @@ export default { ...@@ -349,6 +302,7 @@ export default {
token: MilestoneToken, token: MilestoneToken,
fetchMilestones: this.fetchMilestones, fetchMilestones: this.fetchMilestones,
recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`, recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`,
shouldSkipSort: true,
}, },
{ {
type: TOKEN_TYPE_LABEL, type: TOKEN_TYPE_LABEL,
...@@ -414,7 +368,7 @@ export default { ...@@ -414,7 +368,7 @@ export default {
tokens.sort((a, b) => a.title.localeCompare(b.title)); tokens.sort((a, b) => a.title.localeCompare(b.title));
return orderBy(tokens, ['title']); return tokens;
}, },
showPaginationControls() { showPaginationControls() {
return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage); return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
......
...@@ -7,10 +7,10 @@ import { ...@@ -7,10 +7,10 @@ import {
GlSearchBoxByType, GlSearchBoxByType,
} from '@gitlab/ui'; } from '@gitlab/ui';
import createFlash from '~/flash'; import createFlash from '~/flash';
import searchProjectsQuery from '~/issues/list/queries/search_projects.query.graphql';
import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility';
import { __, sprintf } from '~/locale'; import { __, sprintf } from '~/locale';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
import searchProjectsQuery from '../queries/search_projects.query.graphql';
export default { export default {
i18n: { i18n: {
......
...@@ -3,7 +3,12 @@ ...@@ -3,7 +3,12 @@
query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = false) { query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = false) {
group(fullPath: $fullPath) @skip(if: $isProject) { group(fullPath: $fullPath) @skip(if: $isProject) {
id id
milestones(searchTitle: $search, includeAncestors: true, includeDescendants: true) { milestones(
searchTitle: $search
includeAncestors: true
includeDescendants: true
sort: EXPIRED_LAST_DUE_DATE_ASC
) {
nodes { nodes {
...Milestone ...Milestone
} }
...@@ -11,7 +16,7 @@ query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = fa ...@@ -11,7 +16,7 @@ query searchMilestones($fullPath: ID!, $search: String, $isProject: Boolean = fa
} }
project(fullPath: $fullPath) @include(if: $isProject) { project(fullPath: $fullPath) @include(if: $isProject) {
id id
milestones(searchTitle: $search, includeAncestors: true) { milestones(searchTitle: $search, includeAncestors: true, sort: EXPIRED_LAST_DUE_DATE_ASC) {
nodes { nodes {
...Milestone ...Milestone
} }
......
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
OPERATOR_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import { import {
API_PARAM, API_PARAM,
BLOCKING_ISSUES_ASC, BLOCKING_ISSUES_ASC,
...@@ -36,13 +42,7 @@ import { ...@@ -36,13 +42,7 @@ import {
urlSortParams, urlSortParams,
WEIGHT_ASC, WEIGHT_ASC,
WEIGHT_DESC, WEIGHT_DESC,
} from '~/issues/list/constants'; } from './constants';
import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
OPERATOR_IS_NOT,
} from '~/vue_shared/components/filtered_search_bar/constants';
export const getInitialPageParams = (sortKey) => export const getInitialPageParams = (sortKey) =>
sortKey === RELATIVE_POSITION_ASC ? largePageSizeParams : defaultPageSizeParams; sortKey === RELATIVE_POSITION_ASC ? largePageSizeParams : defaultPageSizeParams;
......
...@@ -22,23 +22,7 @@ export default { ...@@ -22,23 +22,7 @@ export default {
BlockingIssuesCount, BlockingIssuesCount,
IssuesListApp, IssuesListApp,
}, },
inject: { inject: ['fullPath', 'groupPath', 'hasIssueWeightsFeature', 'hasIterationsFeature', 'isProject'],
fullPath: {
default: '',
},
groupPath: {
default: '',
},
hasIssueWeightsFeature: {
default: false,
},
hasIterationsFeature: {
default: false,
},
isProject: {
default: false,
},
},
computed: { computed: {
namespace() { namespace() {
return this.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP; return this.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
......
...@@ -10,6 +10,7 @@ import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issue ...@@ -10,6 +10,7 @@ import { getIssuesCountsQueryResponse, getIssuesQueryResponse } from 'jest/issue
import { convertToGraphQLId } from '~/graphql_shared/utils'; import { convertToGraphQLId } from '~/graphql_shared/utils';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { import {
CREATED_DESC,
TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR, TOKEN_TYPE_AUTHOR,
TOKEN_TYPE_CONFIDENTIAL, TOKEN_TYPE_CONFIDENTIAL,
...@@ -30,6 +31,35 @@ describe('EE IssuesListApp component', () => { ...@@ -30,6 +31,35 @@ describe('EE IssuesListApp component', () => {
Vue.use(VueApollo); Vue.use(VueApollo);
const defaultProvide = {
autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path',
calendarPath: 'calendar/path',
canBulkUpdate: false,
emptyStateSvgPath: 'empty-state.svg',
exportCsvPath: 'export/csv/path',
fullPath: 'path/to/project',
groupPath: 'group/path',
hasAnyIssues: true,
hasAnyProjects: true,
hasBlockedIssuesFeature: true,
hasIssuableHealthStatusFeature: true,
hasIssueWeightsFeature: true,
hasIterationsFeature: true,
hasMultipleIssueAssigneesFeature: true,
initialEmail: 'email@example.com',
initialSort: CREATED_DESC,
isAnonymousSearchDisabled: false,
isIssueRepositioningDisabled: false,
isProject: true,
isSignedIn: true,
jiraIntegrationPath: 'jira/integration/path',
newIssuePath: 'new/issue/path',
releasesPath: 'releases/path',
rssPath: 'rss/path',
showNewIssueLink: true,
signInPath: 'sign/in/path',
};
const defaultQueryResponse = cloneDeep(getIssuesQueryResponse); const defaultQueryResponse = cloneDeep(getIssuesQueryResponse);
defaultQueryResponse.data.project.issues.nodes[0].blockingCount = 1; defaultQueryResponse.data.project.issues.nodes[0].blockingCount = 1;
defaultQueryResponse.data.project.issues.nodes[0].healthStatus = null; defaultQueryResponse.data.project.issues.nodes[0].healthStatus = null;
...@@ -51,9 +81,7 @@ describe('EE IssuesListApp component', () => { ...@@ -51,9 +81,7 @@ describe('EE IssuesListApp component', () => {
return mount(IssuesListApp, { return mount(IssuesListApp, {
apolloProvider, apolloProvider,
provide: { provide: {
hasAnyIssues: true, ...defaultProvide,
hasIssuableHealthStatusFeature: true,
isProject: true,
...provide, ...provide,
}, },
}); });
......
...@@ -62,6 +62,7 @@ describe('CE IssuesListApp component', () => { ...@@ -62,6 +62,7 @@ describe('CE IssuesListApp component', () => {
Vue.use(VueApollo); Vue.use(VueApollo);
const defaultProvide = { const defaultProvide = {
autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path',
calendarPath: 'calendar/path', calendarPath: 'calendar/path',
canBulkUpdate: false, canBulkUpdate: false,
emptyStateSvgPath: 'empty-state.svg', emptyStateSvgPath: 'empty-state.svg',
...@@ -73,10 +74,16 @@ describe('CE IssuesListApp component', () => { ...@@ -73,10 +74,16 @@ describe('CE IssuesListApp component', () => {
hasIssuableHealthStatusFeature: true, hasIssuableHealthStatusFeature: true,
hasIssueWeightsFeature: true, hasIssueWeightsFeature: true,
hasIterationsFeature: true, hasIterationsFeature: true,
hasMultipleIssueAssigneesFeature: true,
initialEmail: 'email@example.com',
initialSort: CREATED_DESC,
isAnonymousSearchDisabled: false,
isIssueRepositioningDisabled: false,
isProject: true, isProject: true,
isSignedIn: true, isSignedIn: true,
jiraIntegrationPath: 'jira/integration/path', jiraIntegrationPath: 'jira/integration/path',
newIssuePath: 'new/issue/path', newIssuePath: 'new/issue/path',
releasesPath: 'releases/path',
rssPath: 'rss/path', rssPath: 'rss/path',
showNewIssueLink: true, showNewIssueLink: true,
signInPath: 'sign/in/path', signInPath: 'sign/in/path',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment