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-06 06:10:22 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-06 06:10:22 +0300
commitb1e7ea9111b4c466c30202464ad3172219e865a9 (patch)
treeec28f239942c7ac7ef8f45fdea2642a7fc42f3ad /app/assets/javascripts/boards
parentbc439e2eed92ed397ac40e5f09a1b36cf42b2c83 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_editable_item.vue37
-rw-r--r--app/assets/javascripts/boards/components/sidebar/board_sidebar_issue_title.vue171
-rw-r--r--app/assets/javascripts/boards/graphql/issue_set_title.mutation.graphql8
-rw-r--r--app/assets/javascripts/boards/stores/actions.js25
4 files changed, 236 insertions, 5 deletions
diff --git a/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue b/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
index ce267be6d45..b4fe16de695 100644
--- a/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
+++ b/app/assets/javascripts/boards/components/sidebar/board_editable_item.vue
@@ -14,6 +14,16 @@ export default {
required: false,
default: false,
},
+ toggleHeader: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ handleOffClick: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
inject: ['canUpdate'],
data() {
@@ -21,13 +31,25 @@ export default {
edit: false,
};
},
+ computed: {
+ showHeader() {
+ if (!this.toggleHeader) {
+ return true;
+ }
+
+ return !this.edit;
+ },
+ },
destroyed() {
window.removeEventListener('click', this.collapseWhenOffClick);
},
methods: {
collapseWhenOffClick({ target }) {
if (!this.$el.contains(target)) {
- this.collapse();
+ this.$emit('off-click');
+ if (this.handleOffClick) {
+ this.collapse();
+ }
}
},
expand() {
@@ -63,21 +85,26 @@ export default {
<template>
<div>
- <div class="gl-display-flex gl-justify-content-space-between gl-mb-3">
+ <header
+ v-show="showHeader"
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-flex-start gl-mb-3"
+ >
<span class="gl-vertical-align-middle">
- <span data-testid="title">{{ title }}</span>
+ <slot name="title">
+ <span data-testid="title">{{ title }}</span>
+ </slot>
<gl-loading-icon v-if="loading" inline class="gl-ml-2" />
</span>
<gl-button
v-if="canUpdate"
variant="link"
- class="gl-text-gray-900! js-sidebar-dropdown-toggle"
+ class="gl-text-gray-900! gl-ml-5 js-sidebar-dropdown-toggle"
data-testid="edit-button"
@click="toggle"
>
{{ __('Edit') }}
</gl-button>
- </div>
+ </header>
<div v-show="!edit" class="gl-text-gray-500" data-testid="collapsed-content">
<slot name="collapsed">{{ __('None') }}</slot>
</div>
diff --git a/app/assets/javascripts/boards/components/sidebar/board_sidebar_issue_title.vue b/app/assets/javascripts/boards/components/sidebar/board_sidebar_issue_title.vue
new file mode 100644
index 00000000000..d0e641daf5c
--- /dev/null
+++ b/app/assets/javascripts/boards/components/sidebar/board_sidebar_issue_title.vue
@@ -0,0 +1,171 @@
+<script>
+import { mapGetters, mapActions } from 'vuex';
+import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
+import BoardEditableItem from '~/boards/components/sidebar/board_editable_item.vue';
+import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
+import { joinPaths } from '~/lib/utils/url_utility';
+import createFlash from '~/flash';
+import { __ } from '~/locale';
+
+export default {
+ components: {
+ GlForm,
+ GlAlert,
+ GlButton,
+ GlFormGroup,
+ GlFormInput,
+ BoardEditableItem,
+ },
+ directives: {
+ autofocusonshow,
+ },
+ data() {
+ return {
+ title: '',
+ loading: false,
+ showChangesAlert: false,
+ };
+ },
+ computed: {
+ ...mapGetters({ issue: 'activeIssue' }),
+ pendingChangesStorageKey() {
+ return this.getPendingChangesKey(this.issue);
+ },
+ projectPath() {
+ const referencePath = this.issue.referencePath || '';
+ return referencePath.slice(0, referencePath.indexOf('#'));
+ },
+ validationState() {
+ return Boolean(this.title);
+ },
+ },
+ watch: {
+ issue: {
+ handler(updatedIssue, formerIssue) {
+ if (formerIssue?.title !== this.title) {
+ localStorage.setItem(this.getPendingChangesKey(formerIssue), this.title);
+ }
+
+ this.title = updatedIssue.title;
+ this.setPendingState();
+ },
+ immediate: true,
+ },
+ },
+ methods: {
+ ...mapActions(['setActiveIssueTitle']),
+ getPendingChangesKey(issue) {
+ if (!issue) {
+ return '';
+ }
+
+ return joinPaths(
+ window.location.pathname.slice(1),
+ String(issue.id),
+ 'issue-title-pending-changes',
+ );
+ },
+ async setPendingState() {
+ const pendingChanges = localStorage.getItem(this.pendingChangesStorageKey);
+
+ if (pendingChanges) {
+ this.title = pendingChanges;
+ this.showChangesAlert = true;
+ await this.$nextTick();
+ this.$refs.sidebarItem.expand();
+ } else {
+ this.showChangesAlert = false;
+ }
+ },
+ cancel() {
+ this.title = this.issue.title;
+ this.$refs.sidebarItem.collapse();
+ this.showChangesAlert = false;
+ localStorage.removeItem(this.pendingChangesStorageKey);
+ },
+ async setTitle() {
+ this.$refs.sidebarItem.collapse();
+
+ if (!this.title || this.title === this.issue.title) {
+ return;
+ }
+
+ try {
+ this.loading = true;
+ await this.setActiveIssueTitle({ title: this.title, projectPath: this.projectPath });
+ localStorage.removeItem(this.pendingChangesStorageKey);
+ this.showChangesAlert = false;
+ } catch (e) {
+ this.title = this.issue.title;
+ createFlash({ message: this.$options.i18n.updateTitleError });
+ } finally {
+ this.loading = false;
+ }
+ },
+ handleOffClick() {
+ if (this.title !== this.issue.title) {
+ this.showChangesAlert = true;
+ localStorage.setItem(this.pendingChangesStorageKey, this.title);
+ } else {
+ this.$refs.sidebarItem.collapse();
+ }
+ },
+ },
+ i18n: {
+ issueTitlePlaceholder: __('Issue title'),
+ submitButton: __('Save changes'),
+ cancelButton: __('Cancel'),
+ updateTitleError: __('An error occurred when updating the issue title'),
+ invalidFeedback: __('An issue title is required'),
+ reviewYourChanges: __('Changes to the title have not been saved'),
+ },
+};
+</script>
+
+<template>
+ <board-editable-item
+ ref="sidebarItem"
+ toggle-header
+ :loading="loading"
+ :handle-off-click="false"
+ @off-click="handleOffClick"
+ >
+ <template #title>
+ <span class="gl-font-weight-bold" data-testid="issue-title">{{ issue.title }}</span>
+ </template>
+ <template #collapsed>
+ <span class="gl-text-gray-800">{{ issue.referencePath }}</span>
+ </template>
+ <template>
+ <gl-alert v-if="showChangesAlert" variant="warning" class="gl-mb-5" :dismissible="false">
+ {{ $options.i18n.reviewYourChanges }}
+ </gl-alert>
+ <gl-form @submit.prevent="setTitle">
+ <gl-form-group :invalid-feedback="$options.i18n.invalidFeedback" :state="validationState">
+ <gl-form-input
+ v-model="title"
+ v-autofocusonshow
+ :placeholder="$options.i18n.issueTitlePlaceholder"
+ :state="validationState"
+ />
+ </gl-form-group>
+
+ <div class="gl-display-flex gl-w-full gl-justify-content-space-between gl-mt-5">
+ <gl-button
+ variant="success"
+ size="small"
+ data-testid="submit-button"
+ :disabled="!title"
+ @click="setTitle"
+ >
+ {{ $options.i18n.submitButton }}
+ </gl-button>
+
+ <gl-button size="small" data-testid="cancel-button" @click="cancel">
+ {{ $options.i18n.cancelButton }}
+ </gl-button>
+ </div>
+ </gl-form>
+ </template>
+ </board-editable-item>
+</template>
diff --git a/app/assets/javascripts/boards/graphql/issue_set_title.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_set_title.mutation.graphql
new file mode 100644
index 00000000000..62e6c1352a6
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/issue_set_title.mutation.graphql
@@ -0,0 +1,8 @@
+mutation issueSetTitle($input: UpdateIssueInput!) {
+ updateIssue(input: $input) {
+ issue {
+ title
+ }
+ errors
+ }
+}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 5783e87ba0a..e64c82f0342 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -27,6 +27,7 @@ import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql
import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
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';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -472,6 +473,30 @@ export default {
});
},
+ setActiveIssueTitle: async ({ commit, getters }, input) => {
+ const { activeIssue } = getters;
+ const { data } = await gqlClient.mutate({
+ mutation: issueSetTitleMutation,
+ variables: {
+ input: {
+ iid: String(activeIssue.iid),
+ projectPath: input.projectPath,
+ title: input.title,
+ },
+ },
+ });
+
+ if (data.updateIssue?.errors?.length > 0) {
+ throw new Error(data.updateIssue.errors);
+ }
+
+ commit(types.UPDATE_ISSUE_BY_ID, {
+ issueId: activeIssue.id,
+ prop: 'title',
+ value: data.updateIssue.issue.title,
+ });
+ },
+
fetchBacklog: () => {
notImplemented();
},