diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
commit | d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch) | |
tree | 2341ef426af70ad1e289c38036737e04b0aa5007 /app/assets/javascripts/issues_list | |
parent | d6e514dd13db8947884cd58fe2a9c2a063400a9b (diff) |
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/issues_list')
5 files changed, 174 insertions, 34 deletions
diff --git a/app/assets/javascripts/issues_list/components/issue_card_time_info.vue b/app/assets/javascripts/issues_list/components/issue_card_time_info.vue index a687a58a6ad..4a2f7861492 100644 --- a/app/assets/javascripts/issues_list/components/issue_card_time_info.vue +++ b/app/assets/javascripts/issues_list/components/issue_card_time_info.vue @@ -85,7 +85,7 @@ export default { <span> <span v-if="issue.milestone" - class="issuable-milestone gl-display-none gl-sm-display-inline-block! gl-mr-3" + class="issuable-milestone gl-mr-3" data-testid="issuable-milestone" > <gl-link v-gl-tooltip :href="milestoneLink" :title="milestoneDate"> @@ -96,7 +96,7 @@ export default { <span v-if="issue.dueDate" v-gl-tooltip - class="issuable-due-date gl-display-none gl-sm-display-inline-block! gl-mr-3" + class="issuable-due-date gl-mr-3" :class="{ 'gl-text-red-500': showDueDateInRed }" :title="__('Due date')" data-testid="issuable-due-date" @@ -107,21 +107,14 @@ export default { <span v-if="timeEstimate" v-gl-tooltip - class="gl-display-none gl-sm-display-inline-block! gl-mr-3" + class="gl-mr-3" :title="__('Estimate')" data-testid="time-estimate" > <gl-icon name="timer" /> {{ timeEstimate }} </span> - <weight-count - class="issuable-weight gl-display-none gl-sm-display-inline-block gl-mr-3" - :weight="issue.weight" - /> - <issue-health-status - v-if="showHealthStatus" - class="gl-display-none gl-sm-display-inline-block" - :health-status="healthStatus" - /> + <weight-count class="issuable-weight gl-mr-3" :weight="issue.weight" /> + <issue-health-status v-if="showHealthStatus" :health-status="healthStatus" /> </span> </template> 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 8e37339fca6..7b51f6ee46a 100644 --- a/app/assets/javascripts/issues_list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue @@ -68,13 +68,6 @@ import { TOKEN_TITLE_TYPE, TOKEN_TITLE_WEIGHT, } from '~/vue_shared/components/filtered_search_bar/constants'; -import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; -import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'; -import EpicToken from '~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue'; -import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue'; -import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; -import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; -import WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue'; import eventHub from '../eventhub'; import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql'; import searchIterationsQuery from '../queries/search_iterations.query.graphql'; @@ -82,6 +75,21 @@ 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 IssueCardTimeInfo from './issue_card_time_info.vue'; +import NewIssueDropdown from './new_issue_dropdown.vue'; + +const AuthorToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'); +const EmojiToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'); +const EpicToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue'); +const IterationToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue'); +const LabelToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'); +const MilestoneToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'); +const WeightToken = () => + import('~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue'); export default { i18n, @@ -96,6 +104,7 @@ export default { IssuableByEmail, IssuableList, IssueCardTimeInfo, + NewIssueDropdown, BlockingIssuesCount: () => import('ee_component/issues/components/blocking_issues_count.vue'), }, directives: { @@ -120,12 +129,15 @@ export default { fullPath: { default: '', }, - groupEpicsPath: { + groupPath: { default: '', }, hasAnyIssues: { default: false, }, + hasAnyProjects: { + default: false, + }, hasBlockedIssuesFeature: { default: false, }, @@ -253,6 +265,9 @@ export default { showCsvButtons() { return this.isProject && this.isSignedIn; }, + showNewIssueDropdown() { + return !this.isProject && this.hasAnyProjects; + }, apiFilterParams() { return convertToApiParams(this.filterTokens); }, @@ -363,16 +378,18 @@ export default { }); } - if (this.groupEpicsPath) { + if (this.groupPath) { tokens.push({ type: TOKEN_TYPE_EPIC, title: TOKEN_TITLE_EPIC, icon: 'epic', token: EpicToken, unique: true, + symbol: '&', idProperty: 'id', useIdValue: true, - fetchEpics: this.fetchEpics, + recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-epic_id`, + fullPath: this.groupPath, }); } @@ -442,16 +459,6 @@ export default { fetchEmojis(search) { return this.fetchWithCache(this.autocompleteAwardEmojisPath, 'emojis', 'name', search); }, - async fetchEpics({ search }) { - const epics = await this.fetchWithCache(this.groupEpicsPath, 'epics'); - if (!search) { - return epics.slice(0, MAX_LIST_SIZE); - } - const number = Number(search); - return Number.isNaN(number) - ? fuzzaldrinPlus.filter(epics, search, { key: 'title' }) - : epics.filter((epic) => epic.id === number); - }, fetchLabels(search) { return this.$apollo .query({ @@ -662,6 +669,7 @@ export default { <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm"> {{ $options.i18n.newIssueLabel }} </gl-button> + <new-issue-dropdown v-if="showNewIssueDropdown" /> </template> <template #timeframe="{ issuable = {} }"> @@ -765,6 +773,7 @@ export default { :export-csv-path="exportCsvPathWithQuery" :issuable-count="currentTabCount" /> + <new-issue-dropdown v-if="showNewIssueDropdown" /> </template> </gl-empty-state> <hr /> diff --git a/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue b/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue new file mode 100644 index 00000000000..037fd9be542 --- /dev/null +++ b/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue @@ -0,0 +1,124 @@ +<script> +import { + GlDropdown, + GlDropdownItem, + GlDropdownText, + GlLoadingIcon, + GlSearchBoxByType, +} from '@gitlab/ui'; +import createFlash from '~/flash'; +import searchProjectsQuery from '~/issues_list/queries/search_projects.query.graphql'; +import { DASH_SCOPE, joinPaths } from '~/lib/utils/url_utility'; +import { __, sprintf } from '~/locale'; +import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants'; + +export default { + i18n: { + defaultDropdownText: __('Select project to create issue'), + noMatchesFound: __('No matches found'), + toggleButtonLabel: __('Toggle project select'), + }, + components: { + GlDropdown, + GlDropdownItem, + GlDropdownText, + GlLoadingIcon, + GlSearchBoxByType, + }, + inject: ['fullPath'], + data() { + return { + projects: [], + search: '', + selectedProject: {}, + shouldSkipQuery: true, + }; + }, + apollo: { + projects: { + query: searchProjectsQuery, + variables() { + return { + fullPath: this.fullPath, + search: this.search, + }; + }, + update: ({ group }) => group.projects.nodes ?? [], + error(error) { + createFlash({ + message: __('An error occurred while loading projects.'), + captureError: true, + error, + }); + }, + skip() { + return this.shouldSkipQuery; + }, + debounce: DEBOUNCE_DELAY, + }, + }, + computed: { + dropdownHref() { + return this.hasSelectedProject + ? joinPaths(this.selectedProject.webUrl, DASH_SCOPE, 'issues/new') + : undefined; + }, + dropdownText() { + return this.hasSelectedProject + ? sprintf(__('New issue in %{project}'), { project: this.selectedProject.name }) + : this.$options.i18n.defaultDropdownText; + }, + hasSelectedProject() { + return this.selectedProject.id; + }, + showNoSearchResultsText() { + return !this.projects.length && this.search; + }, + }, + methods: { + handleDropdownClick() { + if (!this.dropdownHref) { + this.$refs.dropdown.show(); + } + }, + handleDropdownShown() { + if (this.shouldSkipQuery) { + this.shouldSkipQuery = false; + } + this.$refs.search.focusInput(); + }, + selectProject(project) { + this.selectedProject = project; + }, + }, +}; +</script> + +<template> + <gl-dropdown + ref="dropdown" + right + split + :split-href="dropdownHref" + :text="dropdownText" + :toggle-text="$options.i18n.toggleButtonLabel" + variant="confirm" + @click="handleDropdownClick" + @shown="handleDropdownShown" + > + <gl-search-box-by-type ref="search" v-model.trim="search" /> + <gl-loading-icon v-if="$apollo.queries.projects.loading" /> + <template v-else> + <gl-dropdown-item + v-for="project of projects" + :key="project.id" + @click="selectProject(project)" + > + {{ project.nameWithNamespace }} + </gl-dropdown-item> + <gl-dropdown-text v-if="showNoSearchResultsText"> + {{ $options.i18n.noMatchesFound }} + </gl-dropdown-text> + </template> + </gl-dropdown> +</template> diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index e89e3e8e681..47af20f5271 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -119,8 +119,9 @@ export function mountIssuesListApp() { emptyStateSvgPath, exportCsvPath, fullPath, - groupEpicsPath, + groupPath, hasAnyIssues, + hasAnyProjects, hasBlockedIssuesFeature, hasIssuableHealthStatusFeature, hasIssueWeightsFeature, @@ -151,8 +152,9 @@ export function mountIssuesListApp() { canBulkUpdate: parseBoolean(canBulkUpdate), emptyStateSvgPath, fullPath, - groupEpicsPath, + groupPath, hasAnyIssues: parseBoolean(hasAnyIssues), + hasAnyProjects: parseBoolean(hasAnyProjects), hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature), hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature), hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature), diff --git a/app/assets/javascripts/issues_list/queries/search_projects.query.graphql b/app/assets/javascripts/issues_list/queries/search_projects.query.graphql new file mode 100644 index 00000000000..df1f330139a --- /dev/null +++ b/app/assets/javascripts/issues_list/queries/search_projects.query.graphql @@ -0,0 +1,12 @@ +query searchProjects($fullPath: ID!, $search: String) { + group(fullPath: $fullPath) { + projects(search: $search, includeSubgroups: true) { + nodes { + id + name + nameWithNamespace + webUrl + } + } + } +} |