Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-14 12:10:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-14 12:10:22 +0300
commit0ea7b5c8a3f7afaae6b03279af56cd880d538bd7 (patch)
tree92c0e70189cdde7a73fe3c1b19d7f0ee1c800297 /app/assets/javascripts/boards
parent298ae510cefeae8a8ffb9a868fefd9eceeac4122 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue_new.vue10
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue70
-rw-r--r--app/assets/javascripts/boards/components/project_select_deprecated.vue145
-rw-r--r--app/assets/javascripts/boards/graphql/group_projects.query.graphql17
-rw-r--r--app/assets/javascripts/boards/stores/actions.js32
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js4
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js21
-rw-r--r--app/assets/javascripts/boards/stores/state.js8
9 files changed, 261 insertions, 48 deletions
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 2b0ddbed7b3..90d8f6ebc58 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -3,7 +3,7 @@ import { GlButton } from '@gitlab/ui';
import { getMilestone } from 'ee_else_ce/boards/boards_util';
import ListIssue from 'ee_else_ce/boards/models/issue';
import eventHub from '../eventhub';
-import ProjectSelect from './project_select.vue';
+import ProjectSelect from './project_select_deprecated.vue';
import boardsStore from '../stores/boards_store';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
diff --git a/app/assets/javascripts/boards/components/board_new_issue_new.vue b/app/assets/javascripts/boards/components/board_new_issue_new.vue
index 674a49e01ef..5a1b8a7672f 100644
--- a/app/assets/javascripts/boards/components/board_new_issue_new.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue_new.vue
@@ -1,5 +1,5 @@
<script>
-import { mapActions } from 'vuex';
+import { mapActions, mapState } from 'vuex';
import { GlButton } from '@gitlab/ui';
import { getMilestone } from 'ee_else_ce/boards/boards_util';
import eventHub from '../eventhub';
@@ -28,10 +28,10 @@ export default {
data() {
return {
title: '',
- selectedProject: {},
};
},
computed: {
+ ...mapState(['selectedProject']),
disabled() {
if (this.groupId) {
return this.title === '' || !this.selectedProject.name;
@@ -45,7 +45,6 @@ export default {
},
mounted() {
this.$refs.input.focus();
- eventHub.$on('setSelectedProject', this.setSelectedProject);
},
methods: {
...mapActions(['addListNewIssue']),
@@ -68,7 +67,7 @@ export default {
labelIds: labels?.map((l) => l.id),
assigneeIds: assignees?.map((a) => a?.id),
milestoneId: milestone?.id,
- projectPath: this.selectedProject.path,
+ projectPath: this.selectedProject.fullPath,
weight: weight >= 0 ? weight : null,
},
list: this.list,
@@ -80,9 +79,6 @@ export default {
this.title = '';
eventHub.$emit(`toggle-issue-form-${this.list.id}`);
},
- setSelectedProject(selectedProject) {
- this.selectedProject = selectedProject;
- },
},
};
</script>
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index aecb2125e04..04699d0d3a4 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -1,14 +1,14 @@
<script>
+import { mapActions, mapState } from 'vuex';
import {
GlDropdown,
GlDropdownItem,
GlDropdownText,
GlSearchBoxByType,
+ GlIntersectionObserver,
GlLoadingIcon,
} from '@gitlab/ui';
-import eventHub from '../eventhub';
import { s__ } from '~/locale';
-import Api from '../../api';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import { ListType } from '../constants';
@@ -27,6 +27,7 @@ export default {
order_by: 'similarity',
},
components: {
+ GlIntersectionObserver,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
@@ -43,13 +44,12 @@ export default {
data() {
return {
initialLoading: true,
- isFetching: false,
- projects: [],
selectedProject: {},
searchTerm: '',
};
},
computed: {
+ ...mapState(['groupProjects', 'groupProjectsFlags']),
selectedProjectName() {
return this.selectedProject.name || this.$options.i18n.dropdownText;
},
@@ -65,47 +65,30 @@ export default {
};
},
isFetchResultEmpty() {
- return this.projects.length === 0;
+ return this.groupProjects.length === 0;
+ },
+ hasNextPage() {
+ return this.groupProjectsFlags.pageInfo?.hasNextPage;
},
},
watch: {
searchTerm() {
- this.fetchProjects();
+ this.fetchGroupProjects({ search: this.searchTerm });
},
},
- async mounted() {
- await this.fetchProjects();
+ mounted() {
+ this.fetchGroupProjects({});
this.initialLoading = false;
},
methods: {
- async fetchProjects() {
- this.isFetching = true;
- try {
- const projects = await Api.groupProjects(this.groupId, this.searchTerm, this.fetchOptions);
-
- this.projects = projects.map((project) => {
- return {
- id: project.id,
- name: project.name,
- namespacedName: project.name_with_namespace,
- path: project.path_with_namespace,
- };
- });
- } catch (err) {
- /* Handled in Api.groupProjects */
- } finally {
- this.isFetching = false;
- }
- },
+ ...mapActions(['fetchGroupProjects', 'setSelectedProject']),
selectProject(projectId) {
- this.selectedProject = this.projects.find((project) => project.id === projectId);
-
- /*
- TODO Remove eventhub, use Vuex for BoardNewIssue and GraphQL for BoardNewIssueNew
- https://gitlab.com/gitlab-org/gitlab/-/issues/276173
- */
- eventHub.$emit('setSelectedProject', this.selectedProject);
+ this.selectedProject = this.groupProjects.find((project) => project.id === projectId);
+ this.setSelectedProject(this.selectedProject);
+ },
+ loadMoreProjects() {
+ this.fetchGroupProjects({ search: this.searchTerm, fetchNext: true });
},
},
};
@@ -130,20 +113,29 @@ export default {
:placeholder="$options.i18n.searchPlaceholder"
/>
<gl-dropdown-item
- v-for="project in projects"
- v-show="!isFetching"
+ v-for="project in groupProjects"
+ v-show="!groupProjectsFlags.isLoading"
:key="project.id"
:name="project.name"
@click="selectProject(project.id)"
>
- {{ project.namespacedName }}
+ {{ project.nameWithNamespace }}
</gl-dropdown-item>
- <gl-dropdown-text v-show="isFetching" data-testid="dropdown-text-loading-icon">
+ <gl-dropdown-text
+ v-show="groupProjectsFlags.isLoading"
+ data-testid="dropdown-text-loading-icon"
+ >
<gl-loading-icon class="gl-mx-auto" />
</gl-dropdown-text>
- <gl-dropdown-text v-if="isFetchResultEmpty && !isFetching" data-testid="empty-result-message">
+ <gl-dropdown-text
+ v-if="isFetchResultEmpty && !groupProjectsFlags.isLoading"
+ data-testid="empty-result-message"
+ >
<span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span>
</gl-dropdown-text>
+ <gl-intersection-observer v-if="hasNextPage" @appear="loadMoreProjects">
+ <gl-loading-icon v-if="groupProjectsFlags.isLoadingMore" size="md" />
+ </gl-intersection-observer>
</gl-dropdown>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/project_select_deprecated.vue b/app/assets/javascripts/boards/components/project_select_deprecated.vue
new file mode 100644
index 00000000000..f14db42d43c
--- /dev/null
+++ b/app/assets/javascripts/boards/components/project_select_deprecated.vue
@@ -0,0 +1,145 @@
+<script>
+import {
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+ GlLoadingIcon,
+} from '@gitlab/ui';
+import eventHub from '../eventhub';
+import { s__ } from '~/locale';
+import Api from '../../api';
+import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
+import { ListType } from '../constants';
+
+export default {
+ name: 'ProjectSelect',
+ i18n: {
+ headerTitle: s__(`BoardNewIssue|Projects`),
+ dropdownText: s__(`BoardNewIssue|Select a project`),
+ searchPlaceholder: s__(`BoardNewIssue|Search projects`),
+ emptySearchResult: s__(`BoardNewIssue|No matching results`),
+ },
+ defaultFetchOptions: {
+ with_issues_enabled: true,
+ with_shared: false,
+ include_subgroups: true,
+ order_by: 'similarity',
+ },
+ components: {
+ GlLoadingIcon,
+ GlDropdown,
+ GlDropdownItem,
+ GlDropdownText,
+ GlSearchBoxByType,
+ },
+ props: {
+ list: {
+ type: Object,
+ required: true,
+ },
+ },
+ inject: ['groupId'],
+ data() {
+ return {
+ initialLoading: true,
+ isFetching: false,
+ projects: [],
+ selectedProject: {},
+ searchTerm: '',
+ };
+ },
+ computed: {
+ selectedProjectName() {
+ return this.selectedProject.name || this.$options.i18n.dropdownText;
+ },
+ fetchOptions() {
+ const additionalAttrs = {};
+ if (this.list.type && this.list.type !== ListType.backlog) {
+ additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
+ }
+
+ return {
+ ...this.$options.defaultFetchOptions,
+ ...additionalAttrs,
+ };
+ },
+ isFetchResultEmpty() {
+ return this.projects.length === 0;
+ },
+ },
+ watch: {
+ searchTerm() {
+ this.fetchProjects();
+ },
+ },
+ async mounted() {
+ await this.fetchProjects();
+
+ this.initialLoading = false;
+ },
+ methods: {
+ async fetchProjects() {
+ this.isFetching = true;
+ try {
+ const projects = await Api.groupProjects(this.groupId, this.searchTerm, this.fetchOptions);
+
+ this.projects = projects.map((project) => {
+ return {
+ id: project.id,
+ name: project.name,
+ namespacedName: project.name_with_namespace,
+ path: project.path_with_namespace,
+ };
+ });
+ } catch (err) {
+ /* Handled in Api.groupProjects */
+ } finally {
+ this.isFetching = false;
+ }
+ },
+ selectProject(projectId) {
+ this.selectedProject = this.projects.find((project) => project.id === projectId);
+
+ eventHub.$emit('setSelectedProject', this.selectedProject);
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <label class="gl-font-weight-bold gl-mt-3" data-testid="header-label">{{
+ $options.i18n.headerTitle
+ }}</label>
+ <gl-dropdown
+ data-testid="project-select-dropdown"
+ :text="selectedProjectName"
+ :header-text="$options.i18n.headerTitle"
+ block
+ menu-class="gl-w-full!"
+ :loading="initialLoading"
+ >
+ <gl-search-box-by-type
+ v-model.trim="searchTerm"
+ debounce="250"
+ :placeholder="$options.i18n.searchPlaceholder"
+ />
+ <gl-dropdown-item
+ v-for="project in projects"
+ v-show="!isFetching"
+ :key="project.id"
+ :name="project.name"
+ @click="selectProject(project.id)"
+ >
+ {{ project.namespacedName }}
+ </gl-dropdown-item>
+ <gl-dropdown-text v-show="isFetching" data-testid="dropdown-text-loading-icon">
+ <gl-loading-icon class="gl-mx-auto" />
+ </gl-dropdown-text>
+ <gl-dropdown-text v-if="isFetchResultEmpty && !isFetching" data-testid="empty-result-message">
+ <span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span>
+ </gl-dropdown-text>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/graphql/group_projects.query.graphql b/app/assets/javascripts/boards/graphql/group_projects.query.graphql
new file mode 100644
index 00000000000..1afa6e48547
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/group_projects.query.graphql
@@ -0,0 +1,17 @@
+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
+
+query getGroupProjects($fullPath: ID!, $search: String, $after: String) {
+ group(fullPath: $fullPath) {
+ projects(search: $search, after: $after, first: 100) {
+ nodes {
+ id
+ name
+ fullPath
+ nameWithNamespace
+ }
+ pageInfo {
+ ...PageInfo
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 46d551eb50c..0fe6d669f86 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -29,6 +29,7 @@ import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.grap
import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
import issueSetTitleMutation from '../graphql/issue_set_title.mutation.graphql';
+import groupProjectsQuery from '../graphql/group_projects.query.graphql';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -498,6 +499,37 @@ export default {
});
},
+ fetchGroupProjects: ({ commit, state }, { search = '', fetchNext = false }) => {
+ commit(types.REQUEST_GROUP_PROJECTS, fetchNext);
+
+ const { fullPath } = state;
+
+ const variables = {
+ fullPath,
+ search: search !== '' ? search : undefined,
+ after: fetchNext ? state.groupProjectsFlags.pageInfo.endCursor : undefined,
+ };
+
+ return gqlClient
+ .query({
+ query: groupProjectsQuery,
+ variables,
+ })
+ .then(({ data }) => {
+ const { projects } = data.group;
+ commit(types.RECEIVE_GROUP_PROJECTS_SUCCESS, {
+ projects: projects.nodes,
+ pageInfo: projects.pageInfo,
+ fetchNext,
+ });
+ })
+ .catch(() => commit(types.RECEIVE_GROUP_PROJECTS_FAILURE));
+ },
+
+ setSelectedProject: ({ commit }, project) => {
+ commit(types.SET_SELECTED_PROJECT, project);
+ },
+
fetchBacklog: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 2b2c2bee51c..4697f39498a 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -36,3 +36,7 @@ export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
export const UPDATE_ISSUE_BY_ID = 'UPDATE_ISSUE_BY_ID';
export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING';
export const RESET_ISSUES = 'RESET_ISSUES';
+export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS';
+export const RECEIVE_GROUP_PROJECTS_SUCCESS = 'RECEIVE_GROUP_PROJECTS_SUCCESS';
+export const RECEIVE_GROUP_PROJECTS_FAILURE = 'RECEIVE_GROUP_PROJECTS_FAILURE';
+export const SET_SELECTED_PROJECT = 'SET_SELECTED_PROJECT';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 62ac2ca1417..6c79b22d308 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -237,4 +237,25 @@ export default {
[mutationTypes.TOGGLE_EMPTY_STATE]: () => {
notImplemented();
},
+
+ [mutationTypes.REQUEST_GROUP_PROJECTS]: (state, fetchNext) => {
+ Vue.set(state, 'groupProjectsFlags', {
+ [fetchNext ? 'isLoadingMore' : 'isLoading']: true,
+ pageInfo: state.groupProjectsFlags.pageInfo,
+ });
+ },
+
+ [mutationTypes.RECEIVE_GROUP_PROJECTS_SUCCESS]: (state, { projects, pageInfo, fetchNext }) => {
+ Vue.set(state, 'groupProjects', fetchNext ? [...state.groupProjects, ...projects] : projects);
+ Vue.set(state, 'groupProjectsFlags', { isLoading: false, isLoadingMore: false, pageInfo });
+ },
+
+ [mutationTypes.RECEIVE_GROUP_PROJECTS_FAILURE]: (state) => {
+ state.error = s__('Boards|An error occurred while fetching group projects. Please try again.');
+ Vue.set(state, 'groupProjectsFlags', { isLoading: false, isLoadingMore: false });
+ },
+
+ [mutationTypes.SET_SELECTED_PROJECT]: (state, project) => {
+ state.selectedProject = project;
+ },
};
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index 573e98e56e0..aba7da373cf 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -1,7 +1,6 @@
import { inactiveId } from '~/boards/constants';
export default () => ({
- endpoints: {},
boardType: null,
disabled: false,
isShowingLabels: true,
@@ -15,6 +14,13 @@ export default () => ({
issues: {},
filterParams: {},
boardConfig: {},
+ groupProjects: [],
+ groupProjectsFlags: {
+ isLoading: false,
+ isLoadingMore: false,
+ pageInfo: {},
+ },
+ selectedProject: {},
error: undefined,
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,