diff options
Diffstat (limited to 'app/assets/javascripts/design_management/components/design_notes')
3 files changed, 75 insertions, 83 deletions
diff --git a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue index 6a20517eed7..845f1aec8cf 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_discussion.vue @@ -1,19 +1,20 @@ <script> import { ApolloMutation } from 'vue-apollo'; -import { GlTooltipDirective, GlIcon, GlLoadingIcon, GlLink } from '@gitlab/ui'; +import { GlTooltipDirective, GlIcon, GlLoadingIcon, GlLink, GlBadge } from '@gitlab/ui'; import { s__ } from '~/locale'; +import createFlash from '~/flash'; import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import allVersionsMixin from '../../mixins/all_versions'; import createNoteMutation from '../../graphql/mutations/create_note.mutation.graphql'; import toggleResolveDiscussionMutation from '../../graphql/mutations/toggle_resolve_discussion.mutation.graphql'; -import getDesignQuery from '../../graphql/queries/get_design.query.graphql'; import activeDiscussionQuery from '../../graphql/queries/active_discussion.query.graphql'; import DesignNote from './design_note.vue'; import DesignReplyForm from './design_reply_form.vue'; -import { updateStoreAfterAddDiscussionComment } from '../../utils/cache_update'; import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../../constants'; import ToggleRepliesWidget from './toggle_replies_widget.vue'; +import { hasErrors } from '../../utils/cache_update'; +import { ADD_DISCUSSION_COMMENT_ERROR } from '../../utils/error_messages'; export default { components: { @@ -26,6 +27,7 @@ export default { GlLink, ToggleRepliesWidget, TimeAgoTooltip, + GlBadge, }, directives: { GlTooltip: GlTooltipDirective, @@ -62,22 +64,20 @@ export default { activeDiscussion: { query: activeDiscussionQuery, result({ data }) { - const discussionId = data.activeDiscussion.id; if (this.discussion.resolved && !this.resolvedDiscussionsExpanded) { return; } - // We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists - // We don't want scrollIntoView to be triggered from the discussion click itself - if ( - discussionId && - data.activeDiscussion.source === ACTIVE_DISCUSSION_SOURCE_TYPES.pin && - discussionId === this.discussion.notes[0].id - ) { - this.$el.scrollIntoView({ - behavior: 'smooth', - inline: 'start', - }); - } + + this.$nextTick(() => { + // We watch any changes to the active discussion from the design pins and scroll to this discussion if it exists. + // We don't want scrollIntoView to be triggered from the discussion click itself. + if (this.$el && this.shouldScrollToDiscussion(data.activeDiscussion)) { + this.$el.scrollIntoView({ + behavior: 'smooth', + inline: 'start', + }); + } + }); }, }, }, @@ -107,8 +107,8 @@ export default { atVersion: this.designsVersion, }; }, - isDiscussionHighlighted() { - return this.discussion.notes[0].id === this.activeDiscussion.id; + isDiscussionActive() { + return this.discussion.notes.some(({ id }) => id === this.activeDiscussion.id); }, resolveCheckboxText() { return this.discussion.resolved @@ -138,21 +138,10 @@ export default { }, }, methods: { - addDiscussionComment( - store, - { - data: { createNote }, - }, - ) { - updateStoreAfterAddDiscussionComment( - store, - createNote, - getDesignQuery, - this.designVariables, - this.discussion.id, - ); - }, - onDone() { + onDone({ data: { createNote } }) { + if (hasErrors(createNote)) { + createFlash({ message: ADD_DISCUSSION_COMMENT_ERROR }); + } this.discussionComment = ''; this.hideForm(); if (this.shouldChangeResolvedStatus) { @@ -160,14 +149,14 @@ export default { } }, onCreateNoteError(err) { - this.$emit('createNoteError', err); + this.$emit('create-note-error', err); }, hideForm() { this.isFormRendered = false; this.discussionComment = ''; }, showForm() { - this.$emit('openForm', this.discussion.id); + this.$emit('open-form', this.discussion.id); this.isFormRendered = true; }, toggleResolvedStatus() { @@ -179,16 +168,24 @@ export default { }) .then(({ data }) => { if (data.errors?.length > 0) { - this.$emit('resolveDiscussionError', data.errors[0]); + this.$emit('resolve-discussion-error', data.errors[0]); } }) .catch(err => { - this.$emit('resolveDiscussionError', err); + this.$emit('resolve-discussion-error', err); }) .finally(() => { this.isResolving = false; }); }, + shouldScrollToDiscussion(activeDiscussion) { + const ALLOWED_ACTIVE_DISCUSSION_SOURCES = [ + ACTIVE_DISCUSSION_SOURCE_TYPES.pin, + ACTIVE_DISCUSSION_SOURCE_TYPES.url, + ]; + const { source } = activeDiscussion; + return ALLOWED_ACTIVE_DISCUSSION_SOURCES.includes(source) && this.isDiscussionActive; + }, }, createNoteMutation, }; @@ -196,13 +193,12 @@ export default { <template> <div class="design-discussion-wrapper"> - <div - class="badge badge-pill gl-display-flex gl-align-items-center gl-justify-content-center" + <gl-badge + class="gl-display-flex gl-align-items-center gl-justify-content-center gl-cursor-pointer" :class="{ resolved: discussion.resolved }" - type="button" > {{ discussion.index }} - </div> + </gl-badge> <ul class="design-discussion bordered-box gl-relative gl-p-0 gl-list-style-none" data-qa-selector="design_discussion_content" @@ -211,8 +207,8 @@ export default { :note="firstNote" :markdown-preview-path="markdownPreviewPath" :is-resolving="isResolving" - :class="{ 'gl-bg-blue-50': isDiscussionHighlighted }" - @error="$emit('updateNoteError', $event)" + :class="{ 'gl-bg-blue-50': isDiscussionActive }" + @error="$emit('update-note-error', $event)" > <template v-if="discussion.resolvable" #resolveDiscussion> <button @@ -220,7 +216,6 @@ export default { :class="{ 'is-active': discussion.resolved }" :title="resolveCheckboxText" :aria-label="resolveCheckboxText" - type="button" class="line-resolve-btn note-action-button gl-mr-3" data-testid="resolve-button" @click.stop="toggleResolvedStatus" @@ -255,8 +250,8 @@ export default { :note="note" :markdown-preview-path="markdownPreviewPath" :is-resolving="isResolving" - :class="{ 'gl-bg-blue-50': isDiscussionHighlighted }" - @error="$emit('updateNoteError', $event)" + :class="{ 'gl-bg-blue-50': isDiscussionActive }" + @error="$emit('update-note-error', $event)" /> <li v-show="isReplyPlaceholderVisible" class="reply-wrapper"> <reply-placeholder @@ -272,7 +267,6 @@ export default { :variables="{ input: mutationPayload, }" - :update="addDiscussionComment" @done="onDone" @error="onCreateNoteError" > @@ -280,8 +274,8 @@ export default { v-model="discussionComment" :is-saving="loading" :markdown-preview-path="markdownPreviewPath" - @submitForm="mutate" - @cancelForm="hideForm" + @submit-form="mutate" + @cancel-form="hideForm" > <template v-if="discussion.resolvable" #resolveCheckbox> <label data-testid="resolve-checkbox"> diff --git a/app/assets/javascripts/design_management/components/design_notes/design_note.vue b/app/assets/javascripts/design_management/components/design_notes/design_note.vue index 172e61920ef..7f4b3b31024 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_note.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_note.vue @@ -1,12 +1,12 @@ <script> import { ApolloMutation } from 'vue-apollo'; -import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; +import { GlTooltipDirective, GlIcon, GlLink, GlSafeHtmlDirective } from '@gitlab/ui'; import updateNoteMutation from '../../graphql/mutations/update_note.mutation.graphql'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import DesignReplyForm from './design_reply_form.vue'; -import { findNoteId } from '../../utils/design_management_utils'; +import { findNoteId, extractDesignNoteId } from '../../utils/design_management_utils'; import { hasErrors } from '../../utils/cache_update'; export default { @@ -17,9 +17,11 @@ export default { DesignReplyForm, ApolloMutation, GlIcon, + GlLink, }, directives: { GlTooltip: GlTooltipDirective, + SafeHtml: GlSafeHtmlDirective, }, props: { note: { @@ -46,7 +48,7 @@ export default { return findNoteId(this.note.id); }, isNoteLinked() { - return this.$route.hash === `#note_${this.noteAnchorId}`; + return extractDesignNoteId(this.$route.hash) === this.noteAnchorId; }, mutationPayload() { return { @@ -58,11 +60,6 @@ export default { return !this.isEditing && this.note.userPermissions.adminNote; }, }, - mounted() { - if (this.isNoteLinked) { - this.$el.scrollIntoView({ behavior: 'smooth', inline: 'start' }); - } - }, methods: { hideForm() { this.isEditing = false; @@ -87,30 +84,30 @@ export default { :img-alt="author.username" :img-size="40" /> - <div class="d-flex justify-content-between"> + <div class="gl-display-flex gl-justify-content-space-between"> <div> - <a + <gl-link v-once :href="author.webUrl" class="js-user-link" :data-user-id="author.id" :data-username="author.username" > - <span class="note-header-author-name bold">{{ author.name }}</span> - <span v-if="author.status_tooltip_html" v-html="author.status_tooltip_html"></span> + <span class="note-header-author-name gl-font-weight-bold">{{ author.name }}</span> + <span v-if="author.status_tooltip_html" v-safe-html="author.status_tooltip_html"></span> <span class="note-headline-light">@{{ author.username }}</span> - </a> + </gl-link> <span class="note-headline-light note-headline-meta"> <span class="system-note-message"> <slot></slot> </span> - <template v-if="note.createdAt"> - <span class="system-note-separator"></span> - <a class="note-timestamp system-note-separator" :href="`#note_${noteAnchorId}`"> - <time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" /> - </a> - </template> + <gl-link + class="note-timestamp system-note-separator gl-display-block gl-mb-2" + :href="`#note_${noteAnchorId}`" + > + <time-ago-tooltip :time="note.createdAt" tooltip-placement="bottom" /> + </gl-link> </span> </div> - <div class="gl-display-flex"> + <div class="gl-display-flex gl-align-items-baseline"> <slot name="resolveDiscussion"></slot> <button v-if="isEditButtonVisible" @@ -126,9 +123,9 @@ export default { </div> <template v-if="!isEditing"> <div + v-safe-html="note.bodyHtml" class="note-text js-note-text md" data-qa-selector="note_content" - v-html="note.bodyHtml" ></div> <slot name="resolvedStatus"></slot> </template> @@ -147,9 +144,9 @@ export default { :is-saving="loading" :markdown-preview-path="markdownPreviewPath" :is-new-comment="false" - class="mt-5" - @submitForm="mutate" - @cancelForm="hideForm" + class="gl-mt-5" + @submit-form="mutate" + @cancel-form="hideForm" /> </apollo-mutation> </timeline-entry-item> diff --git a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue index 969034909f2..3754e1dbbc1 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue @@ -1,5 +1,5 @@ <script> -import { GlDeprecatedButton, GlModal } from '@gitlab/ui'; +import { GlButton, GlModal } from '@gitlab/ui'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import { s__ } from '~/locale'; @@ -7,7 +7,7 @@ export default { name: 'DesignReplyForm', components: { MarkdownField, - GlDeprecatedButton, + GlButton, GlModal, }, props: { @@ -66,13 +66,13 @@ export default { }, methods: { submitForm() { - if (this.hasValue) this.$emit('submitForm'); + if (this.hasValue) this.$emit('submit-form'); }, cancelComment() { if (this.hasValue && this.formText !== this.value) { this.$refs.cancelCommentModal.show(); } else { - this.$emit('cancelForm'); + this.$emit('cancel-form'); } }, focusInput() { @@ -112,20 +112,21 @@ export default { </markdown-field> <slot name="resolveCheckbox"></slot> <div class="note-form-actions gl-display-flex gl-justify-content-space-between"> - <gl-deprecated-button + <gl-button ref="submitButton" :disabled="!hasValue || isSaving" + category="primary" variant="success" type="submit" data-track-event="click_button" data-qa-selector="save_comment_button" - @click="$emit('submitForm')" + @click="$emit('submit-form')" > {{ buttonText }} - </gl-deprecated-button> - <gl-deprecated-button ref="cancelButton" @click="cancelComment">{{ + </gl-button> + <gl-button ref="cancelButton" variant="default" category="primary" @click="cancelComment">{{ __('Cancel') - }}</gl-deprecated-button> + }}</gl-button> </div> <gl-modal ref="cancelCommentModal" @@ -134,7 +135,7 @@ export default { :ok-title="modalSettings.okTitle" :cancel-title="modalSettings.cancelTitle" modal-id="cancel-comment-modal" - @ok="$emit('cancelForm')" + @ok="$emit('cancel-form')" >{{ modalSettings.content }} </gl-modal> </form> |