From 6286bab9668b6c6d3c43409606ceef6b9f526811 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Tue, 9 May 2023 19:31:25 +0100 Subject: [PATCH 1/9] Add basic support for issue filter by date Introduces a dumb (plain text) widget on the filtered search bar for issues (dashboard, group and project level) to allow filtering issues based on created and/or closed date. Changelog: added --- .../components/issues_dashboard_app.vue | 40 +++++++++++++++++ .../queries/get_issues.query.graphql | 4 ++ .../queries/get_issues_counts.query.graphql | 16 +++++++ .../list/components/issues_list_app.vue | 40 +++++++++++++++++ .../javascripts/issues/list/constants.js | 44 +++++++++++++++++++ .../list/queries/get_issues.query.graphql | 12 +++++ .../queries/get_issues_counts.query.graphql | 28 ++++++++++++ .../filtered_search_bar/constants.js | 8 ++++ .../queries/get_issues.query.graphql | 8 ++++ .../list/queries/get_issues.query.graphql | 12 +++++ .../queries/get_issues_counts.query.graphql | 28 ++++++++++++ locale/gitlab.pot | 12 +++++ 12 files changed, 252 insertions(+) diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index eb73f8e0182d1b..3257ee8b420bcb 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -44,6 +44,10 @@ import { TOKEN_TITLE_MY_REACTION, TOKEN_TITLE_SEARCH_WITHIN, TOKEN_TITLE_TYPE, + TOKEN_TITLE_CREATED_BEFORE, + TOKEN_TITLE_CREATED_AFTER, + TOKEN_TITLE_CLOSED_BEFORE, + TOKEN_TITLE_CLOSED_AFTER, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -52,6 +56,10 @@ import { TOKEN_TYPE_MY_REACTION, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; @@ -318,6 +326,38 @@ export default { fetchEmojis: this.fetchEmojis, recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-my_reaction', }); + + tokens.push({ + type: TOKEN_TYPE_CREATED_BEFORE, + title: TOKEN_TITLE_CREATED_BEFORE, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CREATED_AFTER, + title: TOKEN_TITLE_CREATED_AFTER, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CLOSED_BEFORE, + title: TOKEN_TITLE_CLOSED_BEFORE, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CLOSED_AFTER, + title: TOKEN_TITLE_CLOSED_AFTER, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); } tokens.sort((a, b) => a.title.localeCompare(b.title)); diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql index 5c331fe95e2f4b..a34c96bfbc032f 100644 --- a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql @@ -23,6 +23,10 @@ query getDashboardIssues( $beforeCursor: String $firstPageSize: Int $lastPageSize: Int + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { issues( search: $search diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues_counts.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues_counts.query.graphql index b36f546e4ab62e..a91f15f0c047e8 100644 --- a/app/assets/javascripts/issues/dashboard/queries/get_issues_counts.query.graphql +++ b/app/assets/javascripts/issues/dashboard/queries/get_issues_counts.query.graphql @@ -12,6 +12,10 @@ query getDashboardIssuesCount( $in: [IssuableSearchableField!] $not: NegatedIssueFilterInput $or: UnionedIssueFilterInput + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { openedIssues: issues( state: opened @@ -28,6 +32,10 @@ query getDashboardIssuesCount( in: $in not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -46,6 +54,10 @@ query getDashboardIssuesCount( in: $in not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -64,6 +76,10 @@ query getDashboardIssuesCount( in: $in not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index f7693dd7102b8e..e19033f73b9135 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -47,6 +47,10 @@ import { TOKEN_TITLE_RELEASE, TOKEN_TITLE_SEARCH_WITHIN, TOKEN_TITLE_TYPE, + TOKEN_TITLE_CREATED_BEFORE, + TOKEN_TITLE_CREATED_AFTER, + TOKEN_TITLE_CLOSED_BEFORE, + TOKEN_TITLE_CLOSED_AFTER, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -58,6 +62,10 @@ import { TOKEN_TYPE_RELEASE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; @@ -446,6 +454,38 @@ export default { { icon: 'eye', value: 'no', title: this.$options.i18n.confidentialNo }, ], }); + + tokens.push({ + type: TOKEN_TYPE_CREATED_BEFORE, + title: TOKEN_TITLE_CREATED_BEFORE, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CREATED_AFTER, + title: TOKEN_TITLE_CREATED_AFTER, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CLOSED_BEFORE, + title: TOKEN_TITLE_CLOSED_BEFORE, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); + + tokens.push({ + type: TOKEN_TYPE_CLOSED_AFTER, + title: TOKEN_TITLE_CLOSED_AFTER, + icon: 'clock', + token: GlFilteredSearchToken, + unique: true, + }); } if (this.canReadCrmContact) { diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index 1a3d97277c7879..6914b2165e4553 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -24,6 +24,10 @@ import { TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, TOKEN_TYPE_SEARCH_WITHIN, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, } from '~/vue_shared/components/filtered_search_bar/constants'; import { WORK_ITEM_TYPE_ENUM_INCIDENT, @@ -415,4 +419,44 @@ export const filtersMap = { }, }, }, + [TOKEN_TYPE_CREATED_BEFORE]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'createdBefore', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'created_before', + }, + }, + }, + [TOKEN_TYPE_CREATED_AFTER]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'createdAfter', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'created_after', + }, + }, + }, + [TOKEN_TYPE_CLOSED_BEFORE]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'closedBefore', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'closed_before', + }, + }, + }, + [TOKEN_TYPE_CLOSED_AFTER]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'closedAfter', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'closed_after', + }, + }, + }, }; diff --git a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql index 1018848fb5339b..23410ea0f819dc 100644 --- a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql @@ -30,6 +30,10 @@ query getIssues( $afterCursor: String $firstPageSize: Int $lastPageSize: Int + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { group(fullPath: $fullPath) @skip(if: $isProject) @persist { id @@ -57,6 +61,10 @@ query getIssues( after: $afterCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { __persist pageInfo { @@ -96,6 +104,10 @@ query getIssues( after: $afterCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { __persist pageInfo { diff --git a/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql index fdb0eeb597038b..7953dc423b6676 100644 --- a/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql +++ b/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql @@ -18,6 +18,10 @@ query getIssuesCount( $crmOrganizationId: String $not: NegatedIssueFilterInput $or: UnionedIssueFilterInput + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { group(fullPath: $fullPath) @skip(if: $isProject) { id @@ -39,6 +43,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -60,6 +68,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -81,6 +93,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -106,6 +122,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -128,6 +148,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -150,6 +174,10 @@ query getIssuesCount( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 5b98af8c732ea7..512f5a12fef83c 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -62,6 +62,10 @@ export const TOKEN_TITLE_STATUS = __('Status'); export const TOKEN_TITLE_TARGET_BRANCH = __('Target Branch'); export const TOKEN_TITLE_TYPE = __('Type'); export const TOKEN_TITLE_SEARCH_WITHIN = __('Search Within'); +export const TOKEN_TITLE_CREATED_BEFORE = __('Created before'); +export const TOKEN_TITLE_CREATED_AFTER = __('Created after'); +export const TOKEN_TITLE_CLOSED_BEFORE = __('Closed before'); +export const TOKEN_TITLE_CLOSED_AFTER = __('Closed after'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; @@ -88,3 +92,7 @@ export const TOKEN_TYPE_TARGET_BRANCH = 'target-branch'; export const TOKEN_TYPE_TYPE = 'type'; export const TOKEN_TYPE_WEIGHT = 'weight'; export const TOKEN_TYPE_SEARCH_WITHIN = 'in'; +export const TOKEN_TYPE_CREATED_BEFORE = 'created-before'; +export const TOKEN_TYPE_CREATED_AFTER = 'created-after'; +export const TOKEN_TYPE_CLOSED_BEFORE = 'closed-before'; +export const TOKEN_TYPE_CLOSED_AFTER = 'closed-after'; diff --git a/ee/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/ee/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql index 3ff440245b4605..47a75d1f1e22d7 100644 --- a/ee/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql +++ b/ee/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql @@ -23,6 +23,10 @@ query getDashboardIssuesEE( $beforeCursor: String $firstPageSize: Int $lastPageSize: Int + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { issues( search: $search @@ -44,6 +48,10 @@ query getDashboardIssuesEE( before: $beforeCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) @persist { nodes { __persist diff --git a/ee/app/assets/javascripts/issues/list/queries/get_issues.query.graphql b/ee/app/assets/javascripts/issues/list/queries/get_issues.query.graphql index c81d76adc309d5..f71de6f720311f 100644 --- a/ee/app/assets/javascripts/issues/list/queries/get_issues.query.graphql +++ b/ee/app/assets/javascripts/issues/list/queries/get_issues.query.graphql @@ -35,6 +35,10 @@ query getIssuesEE( $afterCursor: String $firstPageSize: Int $lastPageSize: Int + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { group(fullPath: $fullPath) @skip(if: $isProject) @persist { id @@ -68,6 +72,10 @@ query getIssuesEE( after: $afterCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { __persist pageInfo { @@ -116,6 +124,10 @@ query getIssuesEE( after: $afterCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { __persist pageInfo { diff --git a/ee/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql b/ee/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql index 067ff8e2ad3b42..2e4c980abb915f 100644 --- a/ee/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql +++ b/ee/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql @@ -23,6 +23,10 @@ query getIssuesCountEE( $crmOrganizationId: String $not: NegatedIssueFilterInput $or: UnionedIssueFilterInput + $createdAfter: Time + $createdBefore: Time + $closedAfter: Time + $closedBefore: Time ) { group(fullPath: $fullPath) @skip(if: $isProject) { id @@ -50,6 +54,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -77,6 +85,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -104,6 +116,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -135,6 +151,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -163,6 +183,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } @@ -191,6 +215,10 @@ query getIssuesCountEE( crmOrganizationId: $crmOrganizationId not: $not or: $or + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) { count } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 0b1099f4a1d3eb..b89a6340ae2dc3 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10191,6 +10191,12 @@ msgstr "" msgid "Closed (moved)" msgstr "" +msgid "Closed after" +msgstr "" + +msgid "Closed before" +msgstr "" + msgid "Closed date" msgstr "" @@ -13507,6 +13513,12 @@ msgstr "" msgid "Created a branch and a merge request to resolve this issue." msgstr "" +msgid "Created after" +msgstr "" + +msgid "Created before" +msgstr "" + msgid "Created branch '%{branch_name}' and a merge request to resolve this issue." msgstr "" -- GitLab From efc6953ca3035c4d8cc6a2d8ee160f8b5936b6f0 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Tue, 9 May 2023 20:12:15 +0100 Subject: [PATCH 2/9] Update vue specs --- .../issues/list/components/issues_list_app_spec.js | 8 ++++++++ .../dashboard/components/issues_dashboard_app_spec.js | 8 ++++++++ .../issues/list/components/issues_list_app_spec.js | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js index 4ef3cc38059e24..6bf0467a18ea88 100644 --- a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -36,6 +36,10 @@ import { TOKEN_TYPE_TYPE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_WEIGHT, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, } from 'ee/vue_shared/components/filtered_search_bar/constants'; import BlockingIssuesCount from 'ee/issues/components/blocking_issues_count.vue'; import IssuesListApp from 'ee/issues/list/components/issues_list_app.vue'; @@ -256,8 +260,12 @@ describe('EE IssuesListApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, + { type: TOKEN_TYPE_CLOSED_AFTER }, + { type: TOKEN_TYPE_CLOSED_BEFORE }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_CONTACT }, + { type: TOKEN_TYPE_CREATED_AFTER }, + { type: TOKEN_TYPE_CREATED_BEFORE }, { type: TOKEN_TYPE_EPIC }, { type: TOKEN_TYPE_HEALTH }, { type: TOKEN_TYPE_ITERATION }, diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js index 148c6230b9f6a4..36f84036d775a5 100644 --- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js +++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js @@ -35,6 +35,10 @@ import { TOKEN_TYPE_MY_REACTION, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { @@ -365,7 +369,11 @@ describe('IssuesDashboardApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, + { type: TOKEN_TYPE_CLOSED_AFTER }, + { type: TOKEN_TYPE_CLOSED_BEFORE }, { type: TOKEN_TYPE_CONFIDENTIAL }, + { type: TOKEN_TYPE_CREATED_AFTER }, + { type: TOKEN_TYPE_CREATED_BEFORE }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_MY_REACTION }, diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index 72bf48260568d8..bb2e2ea00ca8f2 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -69,6 +69,10 @@ import { TOKEN_TYPE_RELEASE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, + TOKEN_TYPE_CREATED_AFTER, + TOKEN_TYPE_CREATED_BEFORE, + TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CLOSED_BEFORE, } from '~/vue_shared/components/filtered_search_bar/constants'; import('~/issuable'); @@ -634,8 +638,12 @@ describe('CE IssuesListApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, + { type: TOKEN_TYPE_CLOSED_AFTER }, + { type: TOKEN_TYPE_CLOSED_BEFORE }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_CONTACT }, + { type: TOKEN_TYPE_CREATED_AFTER }, + { type: TOKEN_TYPE_CREATED_BEFORE }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_MY_REACTION }, -- GitLab From 152acff696e222ee3bca9ff7b67c31ca5aa52675 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Wed, 10 May 2023 08:36:22 +0100 Subject: [PATCH 3/9] Consolide created/closed and add new operators --- .../components/issues_dashboard_app.vue | 41 +++++------------ .../list/components/issues_list_app.vue | 41 +++++------------ .../javascripts/issues/list/constants.js | 44 +++++++------------ app/assets/javascripts/issues/list/utils.js | 4 +- .../filtered_search_bar/constants.js | 19 ++++---- .../list/components/issues_list_app_spec.js | 12 ++--- locale/gitlab.pot | 18 +++----- .../components/issues_dashboard_app_spec.js | 12 ++--- .../list/components/issues_list_app_spec.js | 12 ++--- 9 files changed, 70 insertions(+), 133 deletions(-) diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index 3257ee8b420bcb..a8d129cb1fe829 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -36,6 +36,7 @@ import { getParameterByName } from '~/lib/utils/url_utility'; import { OPERATORS_IS, OPERATORS_IS_NOT_OR, + OPERATORS_AFTER_BEFORE, TOKEN_TITLE_ASSIGNEE, TOKEN_TITLE_AUTHOR, TOKEN_TITLE_CONFIDENTIAL, @@ -44,10 +45,8 @@ import { TOKEN_TITLE_MY_REACTION, TOKEN_TITLE_SEARCH_WITHIN, TOKEN_TITLE_TYPE, - TOKEN_TITLE_CREATED_BEFORE, - TOKEN_TITLE_CREATED_AFTER, - TOKEN_TITLE_CLOSED_BEFORE, - TOKEN_TITLE_CLOSED_AFTER, + TOKEN_TITLE_CREATED, + TOKEN_TITLE_CLOSED, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -56,10 +55,8 @@ import { TOKEN_TYPE_MY_REACTION, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; @@ -328,35 +325,19 @@ export default { }); tokens.push({ - type: TOKEN_TYPE_CREATED_BEFORE, - title: TOKEN_TITLE_CREATED_BEFORE, + type: TOKEN_TYPE_CREATED, + title: TOKEN_TITLE_CREATED, icon: 'clock', token: GlFilteredSearchToken, - unique: true, - }); - - tokens.push({ - type: TOKEN_TYPE_CREATED_AFTER, - title: TOKEN_TITLE_CREATED_AFTER, - icon: 'clock', - token: GlFilteredSearchToken, - unique: true, + operators: OPERATORS_AFTER_BEFORE, }); tokens.push({ - type: TOKEN_TYPE_CLOSED_BEFORE, - title: TOKEN_TITLE_CLOSED_BEFORE, + type: TOKEN_TYPE_CLOSED, + title: TOKEN_TITLE_CLOSED, icon: 'clock', token: GlFilteredSearchToken, - unique: true, - }); - - tokens.push({ - type: TOKEN_TYPE_CLOSED_AFTER, - title: TOKEN_TITLE_CLOSED_AFTER, - icon: 'clock', - token: GlFilteredSearchToken, - unique: true, + operators: OPERATORS_AFTER_BEFORE, }); } diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index e19033f73b9135..4259bb7a30959e 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -36,6 +36,7 @@ import { OPERATORS_IS, OPERATORS_IS_NOT, OPERATORS_IS_NOT_OR, + OPERATORS_AFTER_BEFORE, TOKEN_TITLE_ASSIGNEE, TOKEN_TITLE_AUTHOR, TOKEN_TITLE_CONFIDENTIAL, @@ -47,10 +48,8 @@ import { TOKEN_TITLE_RELEASE, TOKEN_TITLE_SEARCH_WITHIN, TOKEN_TITLE_TYPE, - TOKEN_TITLE_CREATED_BEFORE, - TOKEN_TITLE_CREATED_AFTER, - TOKEN_TITLE_CLOSED_BEFORE, - TOKEN_TITLE_CLOSED_AFTER, + TOKEN_TITLE_CREATED, + TOKEN_TITLE_CLOSED, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -62,10 +61,8 @@ import { TOKEN_TYPE_RELEASE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { DEFAULT_PAGE_SIZE, issuableListTabs } from '~/vue_shared/issuable/list/constants'; @@ -456,35 +453,19 @@ export default { }); tokens.push({ - type: TOKEN_TYPE_CREATED_BEFORE, - title: TOKEN_TITLE_CREATED_BEFORE, + type: TOKEN_TYPE_CREATED, + title: TOKEN_TITLE_CREATED, icon: 'clock', token: GlFilteredSearchToken, - unique: true, - }); - - tokens.push({ - type: TOKEN_TYPE_CREATED_AFTER, - title: TOKEN_TITLE_CREATED_AFTER, - icon: 'clock', - token: GlFilteredSearchToken, - unique: true, + operators: OPERATORS_AFTER_BEFORE, }); tokens.push({ - type: TOKEN_TYPE_CLOSED_BEFORE, - title: TOKEN_TITLE_CLOSED_BEFORE, + type: TOKEN_TYPE_CLOSED, + title: TOKEN_TITLE_CLOSED, icon: 'clock', token: GlFilteredSearchToken, - unique: true, - }); - - tokens.push({ - type: TOKEN_TYPE_CLOSED_AFTER, - title: TOKEN_TITLE_CLOSED_AFTER, - icon: 'clock', - token: GlFilteredSearchToken, - unique: true, + operators: OPERATORS_AFTER_BEFORE, }); } diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index 6914b2165e4553..996bbed823f52e 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -9,6 +9,8 @@ import { OPERATOR_IS, OPERATOR_NOT, OPERATOR_OR, + OPERATOR_AFTER, + OPERATOR_BEFORE, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -24,10 +26,8 @@ import { TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, TOKEN_TYPE_SEARCH_WITHIN, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from '~/vue_shared/components/filtered_search_bar/constants'; import { WORK_ITEM_TYPE_ENUM_INCIDENT, @@ -419,43 +419,31 @@ export const filtersMap = { }, }, }, - [TOKEN_TYPE_CREATED_BEFORE]: { + [TOKEN_TYPE_CREATED]: { [API_PARAM]: { [NORMAL_FILTER]: 'createdBefore', + [ALTERNATIVE_FILTER]: 'createdAfter', }, [URL_PARAM]: { - [OPERATOR_IS]: { - [NORMAL_FILTER]: 'created_before', + [OPERATOR_AFTER]: { + [ALTERNATIVE_FILTER]: 'created_after', }, - }, - }, - [TOKEN_TYPE_CREATED_AFTER]: { - [API_PARAM]: { - [NORMAL_FILTER]: 'createdAfter', - }, - [URL_PARAM]: { - [OPERATOR_IS]: { - [NORMAL_FILTER]: 'created_after', + [OPERATOR_BEFORE]: { + [NORMAL_FILTER]: 'created_before', }, }, }, - [TOKEN_TYPE_CLOSED_BEFORE]: { + [TOKEN_TYPE_CLOSED]: { [API_PARAM]: { [NORMAL_FILTER]: 'closedBefore', + [ALTERNATIVE_FILTER]: 'closedAfter', }, [URL_PARAM]: { - [OPERATOR_IS]: { - [NORMAL_FILTER]: 'closed_before', + [OPERATOR_AFTER]: { + [ALTERNATIVE_FILTER]: 'closed_after', }, - }, - }, - [TOKEN_TYPE_CLOSED_AFTER]: { - [API_PARAM]: { - [NORMAL_FILTER]: 'closedAfter', - }, - [URL_PARAM]: { - [OPERATOR_IS]: { - [NORMAL_FILTER]: 'closed_after', + [OPERATOR_BEFORE]: { + [NORMAL_FILTER]: 'closed_before', }, }, }, diff --git a/app/assets/javascripts/issues/list/utils.js b/app/assets/javascripts/issues/list/utils.js index d053400dd033f5..ab97ca01e4faae 100644 --- a/app/assets/javascripts/issues/list/utils.js +++ b/app/assets/javascripts/issues/list/utils.js @@ -5,6 +5,7 @@ import { FILTERED_SEARCH_TERM, OPERATOR_NOT, OPERATOR_OR, + OPERATOR_AFTER, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -236,8 +237,9 @@ const isSpecialFilter = (type, data) => { const getFilterType = ({ type, value: { data, operator } }) => { const isUnionedAuthor = type === TOKEN_TYPE_AUTHOR && operator === OPERATOR_OR; const isUnionedLabel = type === TOKEN_TYPE_LABEL && operator === OPERATOR_OR; + const isAfter = operator === OPERATOR_AFTER; - if (isUnionedAuthor || isUnionedLabel) { + if (isUnionedAuthor || isUnionedLabel || isAfter) { return ALTERNATIVE_FILTER; } if (isSpecialFilter(type, data)) { diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 512f5a12fef83c..3f3a7a2718fa00 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -17,12 +17,19 @@ export const OPERATOR_NOT = '!='; export const OPERATOR_NOT_TEXT = __('is not one of'); export const OPERATOR_OR = '||'; export const OPERATOR_OR_TEXT = __('is one of'); +export const OPERATOR_AFTER = '>'; +export const OPERATOR_AFTER_TEXT = __('is after'); +export const OPERATOR_BEFORE = '<'; +export const OPERATOR_BEFORE_TEXT = __('is before'); export const OPERATORS_IS = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }]; export const OPERATORS_NOT = [{ value: OPERATOR_NOT, description: OPERATOR_NOT_TEXT }]; export const OPERATORS_OR = [{ value: OPERATOR_OR, description: OPERATOR_OR_TEXT }]; +export const OPERATORS_AFTER = [{ value: OPERATOR_AFTER, description: OPERATOR_AFTER_TEXT }]; +export const OPERATORS_BEFORE = [{ value: OPERATOR_BEFORE, description: OPERATOR_BEFORE_TEXT }]; export const OPERATORS_IS_NOT = [...OPERATORS_IS, ...OPERATORS_NOT]; export const OPERATORS_IS_NOT_OR = [...OPERATORS_IS, ...OPERATORS_NOT, ...OPERATORS_OR]; +export const OPERATORS_AFTER_BEFORE = [...OPERATORS_AFTER, ...OPERATORS_BEFORE]; export const OPTION_NONE = { value: FILTER_NONE, text: __('None'), title: __('None') }; export const OPTION_ANY = { value: FILTER_ANY, text: __('Any'), title: __('Any') }; @@ -62,10 +69,8 @@ export const TOKEN_TITLE_STATUS = __('Status'); export const TOKEN_TITLE_TARGET_BRANCH = __('Target Branch'); export const TOKEN_TITLE_TYPE = __('Type'); export const TOKEN_TITLE_SEARCH_WITHIN = __('Search Within'); -export const TOKEN_TITLE_CREATED_BEFORE = __('Created before'); -export const TOKEN_TITLE_CREATED_AFTER = __('Created after'); -export const TOKEN_TITLE_CLOSED_BEFORE = __('Closed before'); -export const TOKEN_TITLE_CLOSED_AFTER = __('Closed after'); +export const TOKEN_TITLE_CREATED = __('Created'); +export const TOKEN_TITLE_CLOSED = __('Closed'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; @@ -92,7 +97,5 @@ export const TOKEN_TYPE_TARGET_BRANCH = 'target-branch'; export const TOKEN_TYPE_TYPE = 'type'; export const TOKEN_TYPE_WEIGHT = 'weight'; export const TOKEN_TYPE_SEARCH_WITHIN = 'in'; -export const TOKEN_TYPE_CREATED_BEFORE = 'created-before'; -export const TOKEN_TYPE_CREATED_AFTER = 'created-after'; -export const TOKEN_TYPE_CLOSED_BEFORE = 'closed-before'; -export const TOKEN_TYPE_CLOSED_AFTER = 'closed-after'; +export const TOKEN_TYPE_CREATED = 'created'; +export const TOKEN_TYPE_CLOSED = 'closed'; diff --git a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js index 6bf0467a18ea88..7b84e753c65db6 100644 --- a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -36,10 +36,8 @@ import { TOKEN_TYPE_TYPE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_WEIGHT, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from 'ee/vue_shared/components/filtered_search_bar/constants'; import BlockingIssuesCount from 'ee/issues/components/blocking_issues_count.vue'; import IssuesListApp from 'ee/issues/list/components/issues_list_app.vue'; @@ -260,12 +258,10 @@ describe('EE IssuesListApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, - { type: TOKEN_TYPE_CLOSED_AFTER }, - { type: TOKEN_TYPE_CLOSED_BEFORE }, + { type: TOKEN_TYPE_CLOSED }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_CONTACT }, - { type: TOKEN_TYPE_CREATED_AFTER }, - { type: TOKEN_TYPE_CREATED_BEFORE }, + { type: TOKEN_TYPE_CREATED }, { type: TOKEN_TYPE_EPIC }, { type: TOKEN_TYPE_HEALTH }, { type: TOKEN_TYPE_ITERATION }, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b89a6340ae2dc3..ddc09822f2a8bc 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10191,12 +10191,6 @@ msgstr "" msgid "Closed (moved)" msgstr "" -msgid "Closed after" -msgstr "" - -msgid "Closed before" -msgstr "" - msgid "Closed date" msgstr "" @@ -13513,12 +13507,6 @@ msgstr "" msgid "Created a branch and a merge request to resolve this issue." msgstr "" -msgid "Created after" -msgstr "" - -msgid "Created before" -msgstr "" - msgid "Created branch '%{branch_name}' and a merge request to resolve this issue." msgstr "" @@ -54788,6 +54776,9 @@ msgstr "" msgid "is" msgstr "" +msgid "is after" +msgstr "" + msgid "is already associated to a GitLab Issue. New issue will not be associated." msgstr "" @@ -54800,6 +54791,9 @@ msgstr "" msgid "is an invalid IP address range" msgstr "" +msgid "is before" +msgstr "" + msgid "is blocked by" msgstr "" diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js index 36f84036d775a5..586b36605f571b 100644 --- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js +++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js @@ -35,10 +35,8 @@ import { TOKEN_TYPE_MY_REACTION, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; import { @@ -369,11 +367,9 @@ describe('IssuesDashboardApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, - { type: TOKEN_TYPE_CLOSED_AFTER }, - { type: TOKEN_TYPE_CLOSED_BEFORE }, + { type: TOKEN_TYPE_CLOSED }, { type: TOKEN_TYPE_CONFIDENTIAL }, - { type: TOKEN_TYPE_CREATED_AFTER }, - { type: TOKEN_TYPE_CREATED_BEFORE }, + { type: TOKEN_TYPE_CREATED }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_MY_REACTION }, diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index bb2e2ea00ca8f2..c9fe696709525b 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -69,10 +69,8 @@ import { TOKEN_TYPE_RELEASE, TOKEN_TYPE_SEARCH_WITHIN, TOKEN_TYPE_TYPE, - TOKEN_TYPE_CREATED_AFTER, - TOKEN_TYPE_CREATED_BEFORE, - TOKEN_TYPE_CLOSED_AFTER, - TOKEN_TYPE_CLOSED_BEFORE, + TOKEN_TYPE_CREATED, + TOKEN_TYPE_CLOSED, } from '~/vue_shared/components/filtered_search_bar/constants'; import('~/issuable'); @@ -638,12 +636,10 @@ describe('CE IssuesListApp component', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ { type: TOKEN_TYPE_ASSIGNEE, preloadedUsers }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, - { type: TOKEN_TYPE_CLOSED_AFTER }, - { type: TOKEN_TYPE_CLOSED_BEFORE }, + { type: TOKEN_TYPE_CLOSED }, { type: TOKEN_TYPE_CONFIDENTIAL }, { type: TOKEN_TYPE_CONTACT }, - { type: TOKEN_TYPE_CREATED_AFTER }, - { type: TOKEN_TYPE_CREATED_BEFORE }, + { type: TOKEN_TYPE_CREATED }, { type: TOKEN_TYPE_LABEL }, { type: TOKEN_TYPE_MILESTONE }, { type: TOKEN_TYPE_MY_REACTION }, -- GitLab From c64d1f0f95096af9f13b40f89d1baf7406d0ed82 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Sat, 20 May 2023 20:58:44 +0100 Subject: [PATCH 4/9] Implement DateToken GlFilteredSearch component --- .../components/issues_dashboard_app.vue | 9 ++-- .../list/components/issues_list_app.vue | 4 +- .../filtered_search_bar/constants.js | 4 +- .../filtered_search_bar/tokens/date_token.vue | 50 +++++++++++++++++++ .../filtered_search_bar/tokens/datepicker.vue | 33 ++++++++++++ 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue create mode 100644 app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index a8d129cb1fe829..04058cb35f6959 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -68,6 +68,7 @@ const EmojiToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'); const LabelToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'); +const DateToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/date_token.vue'); const MilestoneToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'); @@ -327,16 +328,16 @@ export default { tokens.push({ type: TOKEN_TYPE_CREATED, title: TOKEN_TITLE_CREATED, - icon: 'clock', - token: GlFilteredSearchToken, + icon: 'history', + token: DateToken, operators: OPERATORS_AFTER_BEFORE, }); tokens.push({ type: TOKEN_TYPE_CLOSED, title: TOKEN_TITLE_CLOSED, - icon: 'clock', - token: GlFilteredSearchToken, + icon: 'history', + token: DateToken, operators: OPERATORS_AFTER_BEFORE, }); } diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index 4259bb7a30959e..0ab249c46934f3 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -455,7 +455,7 @@ export default { tokens.push({ type: TOKEN_TYPE_CREATED, title: TOKEN_TITLE_CREATED, - icon: 'clock', + icon: 'history', token: GlFilteredSearchToken, operators: OPERATORS_AFTER_BEFORE, }); @@ -463,7 +463,7 @@ export default { tokens.push({ type: TOKEN_TYPE_CLOSED, title: TOKEN_TITLE_CLOSED, - icon: 'clock', + icon: 'history', token: GlFilteredSearchToken, operators: OPERATORS_AFTER_BEFORE, }); diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 3f3a7a2718fa00..a65cbe1dd9814d 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -69,8 +69,8 @@ export const TOKEN_TITLE_STATUS = __('Status'); export const TOKEN_TITLE_TARGET_BRANCH = __('Target Branch'); export const TOKEN_TITLE_TYPE = __('Type'); export const TOKEN_TITLE_SEARCH_WITHIN = __('Search Within'); -export const TOKEN_TITLE_CREATED = __('Created'); -export const TOKEN_TITLE_CLOSED = __('Closed'); +export const TOKEN_TITLE_CREATED = __('Created date'); +export const TOKEN_TITLE_CLOSED = __('Closed date'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue new file mode 100644 index 00000000000000..2fab045205bd9b --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue @@ -0,0 +1,50 @@ + + + diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue new file mode 100644 index 00000000000000..b284a34bb5c914 --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue @@ -0,0 +1,33 @@ + + + -- GitLab From abb8d7e0dc9c279bfce3aefa22c12ee72277cb30 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Sun, 21 May 2023 13:59:47 +0100 Subject: [PATCH 5/9] Add date_token and datepicker specs --- .../list/components/issues_list_app.vue | 5 +- .../filtered_search_bar/constants.js | 4 +- locale/gitlab.pot | 12 ++--- .../tokens/date_token_spec.js | 50 +++++++++++++++++++ .../tokens/datepicker_spec.js | 50 +++++++++++++++++++ 5 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js create mode 100644 spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index 0ab249c46934f3..586d3011c92883 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -121,6 +121,7 @@ const CrmContactToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue'); const CrmOrganizationToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue'); +const DateToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/date_token.vue'); export default { i18n, @@ -456,7 +457,7 @@ export default { type: TOKEN_TYPE_CREATED, title: TOKEN_TITLE_CREATED, icon: 'history', - token: GlFilteredSearchToken, + token: DateToken, operators: OPERATORS_AFTER_BEFORE, }); @@ -464,7 +465,7 @@ export default { type: TOKEN_TYPE_CLOSED, title: TOKEN_TITLE_CLOSED, icon: 'history', - token: GlFilteredSearchToken, + token: DateToken, operators: OPERATORS_AFTER_BEFORE, }); } diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index a65cbe1dd9814d..ef5081f6392494 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -18,9 +18,9 @@ export const OPERATOR_NOT_TEXT = __('is not one of'); export const OPERATOR_OR = '||'; export const OPERATOR_OR_TEXT = __('is one of'); export const OPERATOR_AFTER = '>'; -export const OPERATOR_AFTER_TEXT = __('is after'); +export const OPERATOR_AFTER_TEXT = __('after'); export const OPERATOR_BEFORE = '<'; -export const OPERATOR_BEFORE_TEXT = __('is before'); +export const OPERATOR_BEFORE_TEXT = __('before'); export const OPERATORS_IS = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }]; export const OPERATORS_NOT = [{ value: OPERATOR_NOT, description: OPERATOR_NOT_TEXT }]; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index ddc09822f2a8bc..cb5727800e179f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -53852,6 +53852,9 @@ msgstr "" msgid "added a Zoom call to this issue" msgstr "" +msgid "after" +msgstr "" + msgid "ago" msgstr "" @@ -53938,6 +53941,9 @@ msgstr "" msgid "banned user already exists" msgstr "" +msgid "before" +msgstr "" + msgid "beta" msgstr "" @@ -54776,9 +54782,6 @@ msgstr "" msgid "is" msgstr "" -msgid "is after" -msgstr "" - msgid "is already associated to a GitLab Issue. New issue will not be associated." msgstr "" @@ -54791,9 +54794,6 @@ msgstr "" msgid "is an invalid IP address range" msgstr "" -msgid "is before" -msgstr "" - msgid "is blocked by" msgstr "" diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js new file mode 100644 index 00000000000000..4d3f9e5dc3455b --- /dev/null +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js @@ -0,0 +1,50 @@ +import { GlFilteredSearchToken } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import DateToken from '~/vue_shared/components/filtered_search_bar/tokens/date_token.vue'; + +const propsData = { + active: true, + config: {}, + value: { operator: null, data: null }, +}; + +function createComponent() { + return shallowMount(DateToken, { + propsData, + }); +} + +describe('DateToken', () => { + let wrapper; + + const findGlFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken); + + beforeEach(() => { + wrapper = createComponent(); + }); + + it('renders GlFilteredSearchToken', () => { + expect(findGlFilteredSearchToken().exists()).toBe(true); + }); + + it('emits `destroy` when GlFilteredSearchToken emits `destroy`', () => { + findGlFilteredSearchToken().vm.$emit('destroy'); + + expect(wrapper.emitted('destroy')).toBeDefined(); + }); + + it('sets `value.operator` when findGlFilteredSearchToken emits `input`', async () => { + findGlFilteredSearchToken().vm.$emit('input', { operator: '>' }); + + await nextTick(); + expect(propsData.value.operator).toBe('>'); + }); + + it('sets `value.data` when findGlFilteredSearchToken emits `select`', async () => { + findGlFilteredSearchToken().vm.$emit('select', '2023-05-21'); + + await nextTick(); + expect(propsData.value.data).toBe('2023-05-21'); + }); +}); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js new file mode 100644 index 00000000000000..6429bdc9ed8b2a --- /dev/null +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js @@ -0,0 +1,50 @@ +import { GlDatepicker } from '@gitlab/ui'; +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import Datepicker from '~/vue_shared/components/filtered_search_bar/tokens/datepicker.vue'; + +const mockInstance = { + $emit: jest.fn(), +}; + +function createComponent() { + return shallowMount(Datepicker, { + propsData: { + value: null, + }, + provide: { + filteredSearchSuggestionListInstance: mockInstance, + }, + }); +} + +describe('Datepicker', () => { + let wrapper; + + const findDatepicker = () => wrapper.findComponent(GlDatepicker); + + beforeEach(() => { + wrapper = createComponent(); + }); + + it('renders GlDatepicker', () => { + expect(findDatepicker().exists()).toBe(true); + }); + + it('emits `suggestion` with the formatted date from filteredSearchSuggestionListInstance when a value is selected selected', () => { + jest.spyOn(mockInstance, '$emit'); + + findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); + + expect(mockInstance.$emit).toHaveBeenCalledWith('suggestion', '2014-10-13'); + }); + + it('emits `destroy` when a value is selected', async () => { + jest.spyOn(mockInstance, '$emit'); + + findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); + + await nextTick(); + expect(wrapper.emitted('destroy')).toBeDefined(); + }); +}); -- GitLab From c621d44f39cbb25ac66f6f061b60ad13a5a0bc4e Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Thu, 20 Jul 2023 23:24:07 +0100 Subject: [PATCH 6/9] Use new submitValue method --- .../queries/get_issues.query.graphql | 4 ++ .../filtered_search_bar/tokens/date_token.vue | 44 ++++++++++------ .../filtered_search_bar/tokens/datepicker.vue | 33 ------------ .../tokens/date_token_spec.js | 42 ++++++++-------- .../tokens/datepicker_spec.js | 50 ------------------- 5 files changed, 53 insertions(+), 120 deletions(-) delete mode 100644 app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue delete mode 100644 spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql index a34c96bfbc032f..51e38d44c853f5 100644 --- a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql @@ -48,6 +48,10 @@ query getDashboardIssues( before: $beforeCursor first: $firstPageSize last: $lastPageSize + createdAfter: $createdAfter + createdBefore: $createdBefore + closedAfter: $closedAfter + closedBefore: $closedBefore ) @persist { nodes { __persist diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue index 2fab045205bd9b..6129f9cf1f5b6f 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue @@ -1,11 +1,11 @@ @@ -39,12 +43,22 @@ export default { :config="config" :value="value" :active="active" - @destroy="$emit('destroy')" - @input="selectOperator" - @select="selectDate" + :data-segment-input-attributes="$options.dataSegmentInputAttributes" + v-bind="{ ...$props, ...$attrs }" + v-on="$listeners" > - diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue deleted file mode 100644 index b284a34bb5c914..00000000000000 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/datepicker.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js index 4d3f9e5dc3455b..bee8f21cfc52d1 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js @@ -1,17 +1,21 @@ -import { GlFilteredSearchToken } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; +import { GlDatepicker, GlFilteredSearchToken } from '@gitlab/ui'; +import { mount } from '@vue/test-utils'; import DateToken from '~/vue_shared/components/filtered_search_bar/tokens/date_token.vue'; const propsData = { active: true, config: {}, - value: { operator: null, data: null }, + value: { operator: '>', data: null }, }; function createComponent() { - return shallowMount(DateToken, { + return mount(DateToken, { propsData, + provide: { + portalName: 'fake target', + alignSuggestions: function fakeAlignSuggestions() {}, + termsAsTokens: () => false, + }, }); } @@ -19,32 +23,26 @@ describe('DateToken', () => { let wrapper; const findGlFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken); + const findDatepicker = () => wrapper.findComponent(GlDatepicker); beforeEach(() => { wrapper = createComponent(); }); - it('renders GlFilteredSearchToken', () => { - expect(findGlFilteredSearchToken().exists()).toBe(true); - }); - - it('emits `destroy` when GlFilteredSearchToken emits `destroy`', () => { - findGlFilteredSearchToken().vm.$emit('destroy'); - - expect(wrapper.emitted('destroy')).toBeDefined(); + it('renders GlDatepicker', () => { + expect(findDatepicker().exists()).toBe(true); }); - it('sets `value.operator` when findGlFilteredSearchToken emits `input`', async () => { - findGlFilteredSearchToken().vm.$emit('input', { operator: '>' }); - - await nextTick(); - expect(propsData.value.operator).toBe('>'); + it('renders GlFilteredSearchToken', () => { + expect(findGlFilteredSearchToken().exists()).toBe(true); }); - it('sets `value.data` when findGlFilteredSearchToken emits `select`', async () => { - findGlFilteredSearchToken().vm.$emit('select', '2023-05-21'); + it('emits `complete` and `select` with the formatted date when a value is selected', () => { + findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); - await nextTick(); - expect(propsData.value.data).toBe('2023-05-21'); + expect(findGlFilteredSearchToken().emitted()).toEqual({ + complete: [[]], + select: [['2014-10-13']], + }); }); }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js deleted file mode 100644 index 6429bdc9ed8b2a..00000000000000 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/datepicker_spec.js +++ /dev/null @@ -1,50 +0,0 @@ -import { GlDatepicker } from '@gitlab/ui'; -import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; -import Datepicker from '~/vue_shared/components/filtered_search_bar/tokens/datepicker.vue'; - -const mockInstance = { - $emit: jest.fn(), -}; - -function createComponent() { - return shallowMount(Datepicker, { - propsData: { - value: null, - }, - provide: { - filteredSearchSuggestionListInstance: mockInstance, - }, - }); -} - -describe('Datepicker', () => { - let wrapper; - - const findDatepicker = () => wrapper.findComponent(GlDatepicker); - - beforeEach(() => { - wrapper = createComponent(); - }); - - it('renders GlDatepicker', () => { - expect(findDatepicker().exists()).toBe(true); - }); - - it('emits `suggestion` with the formatted date from filteredSearchSuggestionListInstance when a value is selected selected', () => { - jest.spyOn(mockInstance, '$emit'); - - findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); - - expect(mockInstance.$emit).toHaveBeenCalledWith('suggestion', '2014-10-13'); - }); - - it('emits `destroy` when a value is selected', async () => { - jest.spyOn(mockInstance, '$emit'); - - findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); - - await nextTick(); - expect(wrapper.emitted('destroy')).toBeDefined(); - }); -}); -- GitLab From be394e379821e85a270cf69f4ca4eb9787c6cc87 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Wed, 26 Jul 2023 15:11:22 +0100 Subject: [PATCH 7/9] Apply UX improvements --- .../filtered_search_bar/constants.js | 4 +- .../filtered_search_bar/tokens/date_token.vue | 41 +++++++++++-------- locale/gitlab.pot | 6 +-- .../tokens/date_token_spec.js | 1 + 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index ef5081f6392494..39fd3d62c3bbc9 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -17,8 +17,8 @@ export const OPERATOR_NOT = '!='; export const OPERATOR_NOT_TEXT = __('is not one of'); export const OPERATOR_OR = '||'; export const OPERATOR_OR_TEXT = __('is one of'); -export const OPERATOR_AFTER = '>'; -export const OPERATOR_AFTER_TEXT = __('after'); +export const OPERATOR_AFTER = '≥'; +export const OPERATOR_AFTER_TEXT = __('on or after'); export const OPERATOR_BEFORE = '<'; export const OPERATOR_BEFORE_TEXT = __('before'); diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue index 6129f9cf1f5b6f..24a9cd72be9fc2 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue @@ -1,10 +1,10 @@ @@ -45,19 +58,15 @@ export default { :active="active" :data-segment-input-attributes="$options.dataSegmentInputAttributes" v-bind="{ ...$props, ...$attrs }" - v-on="$listeners" + v-on="handle()" > diff --git a/locale/gitlab.pot b/locale/gitlab.pot index cb5727800e179f..ddd52f1bd75a55 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -53852,9 +53852,6 @@ msgstr "" msgid "added a Zoom call to this issue" msgstr "" -msgid "after" -msgstr "" - msgid "ago" msgstr "" @@ -55472,6 +55469,9 @@ msgstr "" msgid "nounSeries|%{item}, and %{lastItem}" msgstr "" +msgid "on or after" +msgstr "" + msgid "only available on top-level groups." msgstr "" diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js index bee8f21cfc52d1..56a59790210142 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/date_token_spec.js @@ -39,6 +39,7 @@ describe('DateToken', () => { it('emits `complete` and `select` with the formatted date when a value is selected', () => { findDatepicker().vm.$emit('input', new Date('October 13, 2014 11:13:00')); + findDatepicker().vm.$emit('close'); expect(findGlFilteredSearchToken().emitted()).toEqual({ complete: [[]], -- GitLab From b905f0554e1a6a78a823ef6ab88c5c997b8c07a6 Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Fri, 28 Jul 2023 16:08:25 +0100 Subject: [PATCH 8/9] Fix UTC date bug --- .../components/filtered_search_bar/tokens/date_token.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue index 24a9cd72be9fc2..4446886dc889e8 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/date_token.vue @@ -28,7 +28,7 @@ export default { }, methods: { selectValue(value) { - this.selectedDate = formatDate(value, 'yyyy-mm-dd', true); + this.selectedDate = formatDate(value, 'yyyy-mm-dd'); }, close(submitValue) { if (this.selectedDate == null) { -- GitLab From 73acd852815258b5e8f14caff184379955d2f71b Mon Sep 17 00:00:00 2001 From: Lee Tickett Date: Fri, 28 Jul 2023 18:02:22 +0100 Subject: [PATCH 9/9] Add issue_date_filter feature flag --- .../components/issues_dashboard_app.vue | 31 ++++++++++--------- .../list/components/issues_list_app.vue | 31 ++++++++++--------- app/helpers/issues_helper.rb | 3 +- .../development/issue_date_filter.yml | 8 +++++ .../list/components/issues_list_app_spec.js | 1 + .../components/issues_dashboard_app_spec.js | 1 + .../list/components/issues_list_app_spec.js | 1 + 7 files changed, 47 insertions(+), 29 deletions(-) create mode 100644 config/feature_flags/development/issue_date_filter.yml diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue index 04058cb35f6959..9febebf7e55382 100644 --- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue +++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue @@ -95,6 +95,7 @@ export default { 'emptyStateWithoutFilterSvgPath', 'hasBlockedIssuesFeature', 'hasIssuableHealthStatusFeature', + 'hasIssueDateFilterFeature', 'hasIssueWeightsFeature', 'hasScopedLabelsFeature', 'initialSort', @@ -325,21 +326,23 @@ export default { recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-my_reaction', }); - tokens.push({ - type: TOKEN_TYPE_CREATED, - title: TOKEN_TITLE_CREATED, - icon: 'history', - token: DateToken, - operators: OPERATORS_AFTER_BEFORE, - }); + if (this.hasIssueDateFilterFeature) { + tokens.push({ + type: TOKEN_TYPE_CREATED, + title: TOKEN_TITLE_CREATED, + icon: 'history', + token: DateToken, + operators: OPERATORS_AFTER_BEFORE, + }); - tokens.push({ - type: TOKEN_TYPE_CLOSED, - title: TOKEN_TITLE_CLOSED, - icon: 'history', - token: DateToken, - operators: OPERATORS_AFTER_BEFORE, - }); + tokens.push({ + type: TOKEN_TYPE_CLOSED, + title: TOKEN_TITLE_CLOSED, + icon: 'history', + token: DateToken, + operators: OPERATORS_AFTER_BEFORE, + }); + } } tokens.sort((a, b) => a.title.localeCompare(b.title)); diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index 586d3011c92883..4be58e63bd1ddf 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -160,6 +160,7 @@ export default { 'hasAnyProjects', 'hasBlockedIssuesFeature', 'hasIssuableHealthStatusFeature', + 'hasIssueDateFilterFeature', 'hasIssueWeightsFeature', 'hasScopedLabelsFeature', 'initialEmail', @@ -453,21 +454,23 @@ export default { ], }); - tokens.push({ - type: TOKEN_TYPE_CREATED, - title: TOKEN_TITLE_CREATED, - icon: 'history', - token: DateToken, - operators: OPERATORS_AFTER_BEFORE, - }); + if (this.hasIssueDateFilterFeature) { + tokens.push({ + type: TOKEN_TYPE_CREATED, + title: TOKEN_TITLE_CREATED, + icon: 'history', + token: DateToken, + operators: OPERATORS_AFTER_BEFORE, + }); - tokens.push({ - type: TOKEN_TYPE_CLOSED, - title: TOKEN_TITLE_CLOSED, - icon: 'history', - token: DateToken, - operators: OPERATORS_AFTER_BEFORE, - }); + tokens.push({ + type: TOKEN_TYPE_CLOSED, + title: TOKEN_TITLE_CLOSED, + icon: 'history', + token: DateToken, + operators: OPERATORS_AFTER_BEFORE, + }); + } } if (this.canReadCrmContact) { diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index d9b9b27d16c5d5..a0d5d7806d3eec 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -195,7 +195,8 @@ def common_issues_list_data(namespace, current_user) is_signed_in: current_user.present?.to_s, jira_integration_path: help_page_url('integration/jira/issues', anchor: 'view-jira-issues'), rss_path: url_for(safe_params.merge(rss_url_options)), - sign_in_path: new_user_session_path + sign_in_path: new_user_session_path, + has_issue_date_filter_feature: Feature.enabled?(:issue_date_filter, namespace) } end diff --git a/config/feature_flags/development/issue_date_filter.yml b/config/feature_flags/development/issue_date_filter.yml new file mode 100644 index 00000000000000..1b6cb2c4bed3ae --- /dev/null +++ b/config/feature_flags/development/issue_date_filter.yml @@ -0,0 +1,8 @@ +--- +name: issue_date_filter +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120160 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/420173 +milestone: '16.2' +type: development +group: group::project management +default_enabled: false diff --git a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js index 7b84e753c65db6..98c8b9c751e906 100644 --- a/ee/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/ee/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -61,6 +61,7 @@ describe('EE IssuesListApp component', () => { hasAnyIssues: true, hasAnyProjects: true, hasBlockedIssuesFeature: true, + hasIssueDateFilterFeature: true, hasIssuableHealthStatusFeature: true, hasIssueWeightsFeature: true, hasIterationsFeature: true, diff --git a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js index 586b36605f571b..4686a4fe0c42bc 100644 --- a/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js +++ b/spec/frontend/issues/dashboard/components/issues_dashboard_app_spec.js @@ -63,6 +63,7 @@ describe('IssuesDashboardApp component', () => { emptyStateWithFilterSvgPath: 'empty/state/with/filter/svg/path.svg', emptyStateWithoutFilterSvgPath: 'empty/state/with/filter/svg/path.svg', hasBlockedIssuesFeature: true, + hasIssueDateFilterFeature: true, hasIssuableHealthStatusFeature: true, hasIssueWeightsFeature: true, hasScopedLabelsFeature: true, diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index c9fe696709525b..da9a3514b4dd26 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -100,6 +100,7 @@ describe('CE IssuesListApp component', () => { hasAnyIssues: true, hasAnyProjects: true, hasBlockedIssuesFeature: true, + hasIssueDateFilterFeature: true, hasIssuableHealthStatusFeature: true, hasIssueWeightsFeature: true, hasIterationsFeature: true, -- GitLab