summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue
blob: e749579af8049514c503588b5ae6a9e850aecfca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
<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;
    },
    projectsWithIssuesEnabled() {
      return this.projects.filter((project) => project.issuesEnabled);
    },
    showNoSearchResultsText() {
      return !this.projectsWithIssuesEnabled.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 projectsWithIssuesEnabled"
        :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>