From 757a048ca0b7a2102eff3305cbba327baf1e057c Mon Sep 17 00:00:00 2001 From: JeremyWuuuuu Date: Thu, 9 Sep 2021 09:57:32 +0800 Subject: [PATCH 01/11] Feature ZenTao integration list part Abstract duplicate code from Jira to ExternalIssue List. Add ZenTao logo --- app/assets/images/logos/zentao.svg | 14 + .../external_issues_list_empty_state.vue | 70 +++++ .../components/external_issues_list_root.vue | 284 ++++++++++++++++++ .../external_issue/issues_list/constants.js | 3 + .../issues_list/external_issue_list_bundle.js | 66 ++++ .../issues_list/graphql/index.js | 19 ++ .../stylesheets/page_bundles/issues_list.scss | 6 + 7 files changed, 462 insertions(+) create mode 100644 app/assets/images/logos/zentao.svg create mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue create mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue create mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/constants.js create mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/external_issue_list_bundle.js create mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/graphql/index.js diff --git a/app/assets/images/logos/zentao.svg b/app/assets/images/logos/zentao.svg new file mode 100644 index 00000000000000..d2115b72aee056 --- /dev/null +++ b/app/assets/images/logos/zentao.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue b/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue new file mode 100644 index 00000000000000..264326fc081634 --- /dev/null +++ b/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue @@ -0,0 +1,70 @@ + + + diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue b/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue new file mode 100644 index 00000000000000..acb2a6c7833646 --- /dev/null +++ b/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue @@ -0,0 +1,284 @@ + + + diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/constants.js b/app/assets/javascripts/integrations/external_issue/issues_list/constants.js new file mode 100644 index 00000000000000..b8d2532e108b11 --- /dev/null +++ b/app/assets/javascripts/integrations/external_issue/issues_list/constants.js @@ -0,0 +1,3 @@ +import { __ } from '~/locale'; + +export const ISSUES_LIST_FETCH_ERROR = __('An error occurred while loading issues'); diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/external_issue_list_bundle.js b/app/assets/javascripts/integrations/external_issue/issues_list/external_issue_list_bundle.js new file mode 100644 index 00000000000000..f752b9cbc50516 --- /dev/null +++ b/app/assets/javascripts/integrations/external_issue/issues_list/external_issue_list_bundle.js @@ -0,0 +1,66 @@ +import Vue from 'vue'; + +import { IssuableStates } from '~/issuable_list/constants'; +import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; +import { queryToObject } from '~/lib/utils/url_utility'; + +import externalIssuesListApp from './components/external_issues_list_root.vue'; +import getApolloProvider from './graphql'; + +/** + * + * @param {provides} provides necessary attributes for issues_list + * make sure + * { + * 'getIssuesQuery', + * 'externalIssuesLogo', + * 'externalIssueName', + * 'searchInputPlaceholderText', + * 'recentSearchesStorageKey', + * 'createNewIssueText', + * 'logoContainerClass' + * } + * is provided + */ +export default function externalIssuesListFactory({ provides, query }) { + return function initExternalIssuesList({ mountPointSelector }) { + const mountPointEl = document.querySelector(mountPointSelector); + + if (!mountPointEl) { + return null; + } + + const { + page = 1, + initialState = IssuableStates.Opened, + initialSortBy = 'created_desc', + } = mountPointEl.dataset; + + const initialFilterParams = Object.assign( + convertObjectPropsToCamelCase( + queryToObject(window.location.search.substring(1), { gatherArrays: true }), + { + dropKeys: ['scope', 'utf8', 'state', 'sort'], // These keys are unsupported/unnecessary + }, + ), + ); + + return new Vue({ + el: mountPointEl, + provide: { + ...mountPointEl.dataset, + page: parseInt(page, 10), + initialState, + initialSortBy, + ...provides, + }, + apolloProvider: getApolloProvider(query), + render: (createElement) => + createElement(externalIssuesListApp, { + props: { + initialFilterParams, + }, + }), + }); + }; +} diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/graphql/index.js b/app/assets/javascripts/integrations/external_issue/issues_list/graphql/index.js new file mode 100644 index 00000000000000..996f62db1c0ecc --- /dev/null +++ b/app/assets/javascripts/integrations/external_issue/issues_list/graphql/index.js @@ -0,0 +1,19 @@ +import Vue from 'vue'; +import VueApollo from 'vue-apollo'; +import createDefaultClient from '~/lib/graphql'; + +Vue.use(VueApollo); + +export default (externalIssues) => { + const resolvers = { + Query: { + externalIssues, + }, + }; + + const defaultClient = createDefaultClient(resolvers, { assumeImmutableResults: true }); + + return new VueApollo({ + defaultClient, + }); +}; diff --git a/app/assets/stylesheets/page_bundles/issues_list.scss b/app/assets/stylesheets/page_bundles/issues_list.scss index 8a958bdf0c5750..5af8902949a63f 100644 --- a/app/assets/stylesheets/page_bundles/issues_list.scss +++ b/app/assets/stylesheets/page_bundles/issues_list.scss @@ -43,3 +43,9 @@ opacity: 0.3; pointer-events: none; } + +.svg-container.logo-container { + svg { + vertical-align: text-bottom; + } +} -- GitLab From e8648d184de2b75af71b4e5c6e140562e12c1610 Mon Sep 17 00:00:00 2001 From: JeremyWuuuuu Date: Fri, 10 Sep 2021 15:23:04 +0800 Subject: [PATCH 02/11] Move external_issue/list to external_issue_list --- .../external_issues_list_empty_state.vue | 27 +- .../components/external_issues_list_root.vue | 14 +- .../graphql/index.js | 4 +- .../index.js} | 49 +-- .../external_issue/issues_list/constants.js | 3 - .../external_issues_list_root_spec.js.snap | 169 +++++++++ .../external_issues_list_empty_state_spec.js | 207 +++++++++++ .../external_issues_list_root_spec.js | 334 ++++++++++++++++++ .../external_issues_list/mock_data.js | 102 ++++++ .../mock_get_jira_issues.query.graphql | 48 +++ 10 files changed, 916 insertions(+), 41 deletions(-) rename app/assets/javascripts/{integrations/external_issue/issues_list => external_issues_list}/components/external_issues_list_empty_state.vue (67%) rename app/assets/javascripts/{integrations/external_issue/issues_list => external_issues_list}/components/external_issues_list_root.vue (94%) rename app/assets/javascripts/{integrations/external_issue/issues_list => external_issues_list}/graphql/index.js (76%) rename app/assets/javascripts/{integrations/external_issue/issues_list/external_issue_list_bundle.js => external_issues_list/index.js} (62%) delete mode 100644 app/assets/javascripts/integrations/external_issue/issues_list/constants.js create mode 100644 spec/frontend/external_issues_list/components/__snapshots__/external_issues_list_root_spec.js.snap create mode 100644 spec/frontend/external_issues_list/components/external_issues_list_empty_state_spec.js create mode 100644 spec/frontend/external_issues_list/components/external_issues_list_root_spec.js create mode 100644 spec/frontend/external_issues_list/mock_data.js create mode 100644 spec/frontend/external_issues_list/mock_get_jira_issues.query.graphql diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue b/app/assets/javascripts/external_issues_list/components/external_issues_list_empty_state.vue similarity index 67% rename from app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue rename to app/assets/javascripts/external_issues_list/components/external_issues_list_empty_state.vue index 264326fc081634..dc8bf4fc76678d 100644 --- a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_empty_state.vue +++ b/app/assets/javascripts/external_issues_list/components/external_issues_list_empty_state.vue @@ -4,10 +4,6 @@ import { IssuableStates } from '~/issuable_list/constants'; import { __, s__ } from '~/locale'; export default { - FilterStateEmptyMessage: { - [IssuableStates.Opened]: __('There are no open issues'), - [IssuableStates.Closed]: __('There are no closed issues'), - }, components: { GlEmptyState, GlButton, @@ -36,22 +32,37 @@ export default { return this.issuesCount[IssuableStates.Opened] + this.issuesCount[IssuableStates.Closed] > 0; }, emptyStateTitle() { + const { titleWhenFilters, filterStateEmptyMessage } = this.$options.i18n; + if (this.hasFiltersApplied) { - return __('Sorry, your filter produced no results'); + return titleWhenFilters; } else if (this.hasIssues) { - return this.$options.FilterStateEmptyMessage[this.currentState]; + return filterStateEmptyMessage[this.currentState]; } + return this.emptyStateNoIssueText; }, emptyStateDescription() { + const { descriptionWhenFilters, descriptionWhenNoIssues } = this.$options.i18n; + if (this.hasFiltersApplied) { - return __('To widen your search, change or remove filters above'); + return descriptionWhenFilters; } else if (!this.hasIssues) { - return s__('Integrations|To keep this project going, create a new issue.'); + return descriptionWhenNoIssues; } + return ''; }, }, + i18n: { + titleWhenFilters: __('Sorry, your filter produced no results'), + descriptionWhenFilters: __('To widen your search, change or remove filters above'), + descriptionWhenNoIssues: s__('Integrations|To keep this project going, create a new issue.'), + filterStateEmptyMessage: { + [IssuableStates.Opened]: __('There are no open issues'), + [IssuableStates.Closed]: __('There are no closed issues'), + }, + }, }; diff --git a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue b/app/assets/javascripts/external_issues_list/components/external_issues_list_root.vue similarity index 94% rename from app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue rename to app/assets/javascripts/external_issues_list/components/external_issues_list_root.vue index acb2a6c7833646..0de509959ab516 100644 --- a/app/assets/javascripts/integrations/external_issue/issues_list/components/external_issues_list_root.vue +++ b/app/assets/javascripts/external_issues_list/components/external_issues_list_root.vue @@ -9,6 +9,7 @@ import { AvailableSortOptions, DEFAULT_PAGE_SIZE, } from '~/issuable_list/constants'; +import { i18n } from '~/issues_list/constants'; import { FILTERED_SEARCH_LABELS, FILTERED_SEARCH_TERM, @@ -17,7 +18,6 @@ import { } from '~/vue_shared/components/filtered_search_bar/constants'; import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; -import { ISSUES_LIST_FETCH_ERROR } from '../constants'; import ExternalIssuesListEmptyState from './external_issues_list_empty_state.vue'; export default { @@ -45,11 +45,10 @@ export default { 'issueCreateUrl', 'getIssuesQuery', 'externalIssuesLogo', - 'externalIssueName', + 'externalIssueTrackerName', 'searchInputPlaceholderText', 'recentSearchesStorageKey', 'createNewIssueText', - 'logoContainerClass', ], props: { initialFilterParams: { @@ -126,7 +125,7 @@ export default { this.issuesCount[this.currentState] = nodes.length; }, error() { - this.onExternalIssuesQueryError(new Error(ISSUES_LIST_FETCH_ERROR)); + this.onExternalIssuesQueryError(new Error(this.$options.i18n.errorFetchingIssues)); }, }, }, @@ -222,6 +221,9 @@ export default { this.filterParams = filterParams; }, }, + i18n: { + errorFetchingIssues: i18n.errorFetchingIssues, + }, }; @@ -258,11 +260,11 @@ export default {