Commit 4169eef2 authored by Tom Quirk's avatar Tom Quirk Committed by Natalia Tepluhina

Apollo foundational work for design todos

This commit adds the necessary mutations,
client mutations, Vue components, and
additional markup for Design management
Todo creation and deletion.

As of this commit, the work is behind
the design_management_todo_button feature flag.
parent 0a8ed206
...@@ -8,7 +8,7 @@ import { extractDiscussions, extractParticipants } from '../utils/design_managem ...@@ -8,7 +8,7 @@ import { extractDiscussions, extractParticipants } from '../utils/design_managem
import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../constants'; import { ACTIVE_DISCUSSION_SOURCE_TYPES } from '../constants';
import DesignDiscussion from './design_notes/design_discussion.vue'; import DesignDiscussion from './design_notes/design_discussion.vue';
import Participants from '~/sidebar/components/participants/participants.vue'; import Participants from '~/sidebar/components/participants/participants.vue';
import TodoButton from '~/vue_shared/components/todo_button.vue'; import DesignTodoButton from './design_todo_button.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default { export default {
...@@ -18,7 +18,7 @@ export default { ...@@ -18,7 +18,7 @@ export default {
GlCollapse, GlCollapse,
GlButton, GlButton,
GlPopover, GlPopover,
TodoButton, DesignTodoButton,
}, },
mixins: [glFeatureFlagsMixin()], mixins: [glFeatureFlagsMixin()],
props: { props: {
...@@ -41,6 +41,14 @@ export default { ...@@ -41,6 +41,14 @@ export default {
discussionWithOpenForm: '', discussionWithOpenForm: '',
}; };
}, },
inject: {
projectPath: {
default: '',
},
issueIid: {
default: '',
},
},
computed: { computed: {
discussions() { discussions() {
return extractDiscussions(this.design.discussions); return extractDiscussions(this.design.discussions);
...@@ -119,7 +127,7 @@ export default { ...@@ -119,7 +127,7 @@ export default {
class="gl-py-4 gl-mb-4 gl-display-flex gl-justify-content-space-between gl-align-items-center gl-border-b-1 gl-border-b-solid gl-border-b-gray-100" class="gl-py-4 gl-mb-4 gl-display-flex gl-justify-content-space-between gl-align-items-center gl-border-b-1 gl-border-b-solid gl-border-b-gray-100"
> >
<span>{{ __('To-Do') }}</span> <span>{{ __('To-Do') }}</span>
<todo-button issuable-type="design" :issuable-id="design.iid" /> <design-todo-button :design="design" @error="$emit('todoError', $event)" />
</div> </div>
<h2 class="gl-font-weight-bold gl-mt-0"> <h2 class="gl-font-weight-bold gl-mt-0">
{{ issue.title }} {{ issue.title }}
......
<script>
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import getDesignQuery from '../graphql/queries/get_design.query.graphql';
import createDesignTodoMutation from '../graphql/mutations/create_design_todo.mutation.graphql';
import TodoButton from '~/vue_shared/components/todo_button.vue';
import allVersionsMixin from '../mixins/all_versions';
import { updateStoreAfterDeleteDesignTodo } from '../utils/cache_update';
import { findIssueId } from '../utils/design_management_utils';
import { CREATE_DESIGN_TODO_ERROR, DELETE_DESIGN_TODO_ERROR } from '../utils/error_messages';
export default {
components: {
TodoButton,
},
mixins: [allVersionsMixin],
props: {
design: {
type: Object,
required: true,
},
},
inject: {
projectPath: {
default: '',
},
issueIid: {
default: '',
},
},
data() {
return {
todoLoading: false,
};
},
computed: {
designVariables() {
return {
fullPath: this.projectPath,
iid: this.issueIid,
filenames: [this.$route.params.id],
atVersion: this.designsVersion,
};
},
designTodoVariables() {
return {
projectPath: this.projectPath,
issueId: findIssueId(this.design.issue.id),
issueIid: this.issueIid,
filenames: [this.$route.params.id],
atVersion: this.designsVersion,
};
},
pendingTodo() {
// TODO data structure pending BE MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40555#note_405732940
return this.design.currentUserTodos?.nodes[0];
},
hasPendingTodo() {
return Boolean(this.pendingTodo);
},
},
methods: {
createTodo() {
this.todoLoading = true;
return this.$apollo
.mutate({
mutation: createDesignTodoMutation,
variables: this.designTodoVariables,
update: (store, { data: { createDesignTodo } }) => {
// because this is a @client mutation,
// we control what is in errors, and therefore
// we are certain that there is at most 1 item in the array
const createDesignTodoError = (createDesignTodo.errors || [])[0];
if (createDesignTodoError) {
this.$emit('error', Error(createDesignTodoError.message));
}
},
})
.catch(err => {
this.$emit('error', Error(CREATE_DESIGN_TODO_ERROR));
throw err;
})
.finally(() => {
this.todoLoading = false;
});
},
deleteTodo() {
if (!this.hasPendingTodo) return Promise.reject();
const { id } = this.pendingTodo;
const { designVariables } = this;
this.todoLoading = true;
return this.$apollo
.mutate({
mutation: todoMarkDoneMutation,
variables: {
id,
},
update(
store,
{
data: { todoMarkDone },
},
) {
const todoMarkDoneFirstError = (todoMarkDone.errors || [])[0];
if (todoMarkDoneFirstError) {
this.$emit('error', Error(todoMarkDoneFirstError));
} else {
updateStoreAfterDeleteDesignTodo(
store,
todoMarkDone,
getDesignQuery,
designVariables,
);
}
},
})
.catch(err => {
this.$emit('error', Error(DELETE_DESIGN_TODO_ERROR));
throw err;
})
.finally(() => {
this.todoLoading = false;
});
},
toggleTodo() {
if (this.hasPendingTodo) {
return this.deleteTodo();
}
return this.createTodo();
},
},
};
</script>
<template>
<todo-button
issuable-type="design"
:issuable-id="design.iid"
:is-todo="hasPendingTodo"
:loading="todoLoading"
@click.stop.prevent="toggleTodo"
/>
</template>
...@@ -3,9 +3,14 @@ import VueApollo from 'vue-apollo'; ...@@ -3,9 +3,14 @@ import VueApollo from 'vue-apollo';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import produce from 'immer'; import produce from 'immer';
import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
import axios from '~/lib/utils/axios_utils';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql'; import activeDiscussionQuery from './graphql/queries/active_discussion.query.graphql';
import getDesignQuery from './graphql/queries/get_design.query.graphql';
import typeDefs from './graphql/typedefs.graphql'; import typeDefs from './graphql/typedefs.graphql';
import { extractTodoIdFromDeletePath, createPendingTodo } from './utils/design_management_utils';
import { CREATE_DESIGN_TODO_EXISTS_ERROR } from './utils/error_messages';
import { addPendingTodoToStore } from './utils/cache_update';
Vue.use(VueApollo); Vue.use(VueApollo);
...@@ -25,6 +30,37 @@ const resolvers = { ...@@ -25,6 +30,37 @@ const resolvers = {
cache.writeQuery({ query: activeDiscussionQuery, data }); cache.writeQuery({ query: activeDiscussionQuery, data });
}, },
createDesignTodo: (_, { projectPath, issueId, issueIid, filenames, atVersion }, { cache }) => {
return axios
.post(`/${projectPath}/todos`, {
issue_id: issueId,
issuable_id: issueIid,
issuable_type: 'design',
})
.then(({ data }) => {
const { delete_path } = data;
const todoId = extractTodoIdFromDeletePath(delete_path);
if (!todoId) {
return {
errors: [
{
message: CREATE_DESIGN_TODO_EXISTS_ERROR,
},
],
};
}
const pendingTodo = createPendingTodo(todoId);
addPendingTodoToStore(cache, pendingTodo, getDesignQuery, {
fullPath: projectPath,
iid: issueIid,
filenames,
atVersion,
});
return pendingTodo;
});
},
}, },
}; };
......
mutation createDesignTodo(
$projectPath: String!
$issueId: String!
$issueIid: String!
$filenames: [String]!
$atVersion: String
) {
createDesignTodo(
projectPath: $projectPath
issueId: $issueId
issueIid: $issueIid
filenames: $filenames
atVersion: $atVersion
) @client
}
...@@ -10,6 +10,7 @@ query getDesign($fullPath: ID!, $iid: String!, $atVersion: ID, $filenames: [Stri ...@@ -10,6 +10,7 @@ query getDesign($fullPath: ID!, $iid: String!, $atVersion: ID, $filenames: [Stri
nodes { nodes {
...DesignItem ...DesignItem
issue { issue {
id
title title
webPath webPath
webUrl webUrl
......
...@@ -33,6 +33,7 @@ import { ...@@ -33,6 +33,7 @@ import {
DESIGN_NOT_FOUND_ERROR, DESIGN_NOT_FOUND_ERROR,
DESIGN_VERSION_NOT_EXIST_ERROR, DESIGN_VERSION_NOT_EXIST_ERROR,
UPDATE_NOTE_ERROR, UPDATE_NOTE_ERROR,
TOGGLE_TODO_ERROR,
designDeletionError, designDeletionError,
} from '../../utils/error_messages'; } from '../../utils/error_messages';
import { trackDesignDetailView } from '../../utils/tracking'; import { trackDesignDetailView } from '../../utils/tracking';
...@@ -226,7 +227,7 @@ export default { ...@@ -226,7 +227,7 @@ export default {
}, },
onError(message, e) { onError(message, e) {
this.errorMessage = message; this.errorMessage = message;
throw e; if (e) throw e;
}, },
onCreateImageDiffNoteError(e) { onCreateImageDiffNoteError(e) {
this.onError(ADD_IMAGE_DIFF_NOTE_ERROR, e); this.onError(ADD_IMAGE_DIFF_NOTE_ERROR, e);
...@@ -246,6 +247,9 @@ export default { ...@@ -246,6 +247,9 @@ export default {
onResolveDiscussionError(e) { onResolveDiscussionError(e) {
this.onError(UPDATE_IMAGE_DIFF_NOTE_ERROR, e); this.onError(UPDATE_IMAGE_DIFF_NOTE_ERROR, e);
}, },
onTodoError(e) {
this.onError(e?.message || TOGGLE_TODO_ERROR, e);
},
openCommentForm(annotationCoordinates) { openCommentForm(annotationCoordinates) {
this.annotationCoordinates = annotationCoordinates; this.annotationCoordinates = annotationCoordinates;
if (this.$refs.newDiscussionForm) { if (this.$refs.newDiscussionForm) {
...@@ -349,6 +353,7 @@ export default { ...@@ -349,6 +353,7 @@ export default {
@updateNoteError="onUpdateNoteError" @updateNoteError="onUpdateNoteError"
@resolveDiscussionError="onResolveDiscussionError" @resolveDiscussionError="onResolveDiscussionError"
@toggleResolvedComments="toggleResolvedComments" @toggleResolvedComments="toggleResolvedComments"
@todoError="onTodoError"
> >
<template #replyForm> <template #replyForm>
<apollo-mutation <apollo-mutation
......
...@@ -7,6 +7,7 @@ import { extractCurrentDiscussion, extractDesign, extractDesigns } from './desig ...@@ -7,6 +7,7 @@ import { extractCurrentDiscussion, extractDesign, extractDesigns } from './desig
import { import {
ADD_IMAGE_DIFF_NOTE_ERROR, ADD_IMAGE_DIFF_NOTE_ERROR,
UPDATE_IMAGE_DIFF_NOTE_ERROR, UPDATE_IMAGE_DIFF_NOTE_ERROR,
DELETE_DESIGN_TODO_ERROR,
designDeletionError, designDeletionError,
} from './error_messages'; } from './error_messages';
...@@ -188,6 +189,30 @@ const moveDesignInStore = (store, designManagementMove, query) => { ...@@ -188,6 +189,30 @@ const moveDesignInStore = (store, designManagementMove, query) => {
}); });
}; };
export const addPendingTodoToStore = (store, pendingTodo, query, queryVariables) => {
const data = store.readQuery({
query,
variables: queryVariables,
});
// TODO produce new version of data that includes the new pendingTodo.
// This is only possible after BE MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40555
store.writeQuery({ query, variables: queryVariables, data });
};
export const deletePendingTodoFromStore = (store, pendingTodo, query, queryVariables) => {
const data = store.readQuery({
query,
variables: queryVariables,
});
// TODO produce new version of data without the pendingTodo.
// This is only possible after BE MR: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40555
store.writeQuery({ query, variables: queryVariables, data });
};
const onError = (data, message) => { const onError = (data, message) => {
createFlash(message); createFlash(message);
throw new Error(data.errors); throw new Error(data.errors);
...@@ -243,3 +268,11 @@ export const updateDesignsOnStoreAfterReorder = (store, data, query) => { ...@@ -243,3 +268,11 @@ export const updateDesignsOnStoreAfterReorder = (store, data, query) => {
moveDesignInStore(store, data, query); moveDesignInStore(store, data, query);
} }
}; };
export const updateStoreAfterDeleteDesignTodo = (store, data, query, queryVariables) => {
if (hasErrors(data)) {
onError(data, DELETE_DESIGN_TODO_ERROR);
} else {
deletePendingTodoFromStore(store, data, query, queryVariables);
}
};
...@@ -30,6 +30,8 @@ export const findVersionId = id => (id.match('::Version/(.+$)') || [])[1]; ...@@ -30,6 +30,8 @@ export const findVersionId = id => (id.match('::Version/(.+$)') || [])[1];
export const findNoteId = id => (id.match('DiffNote/(.+$)') || [])[1]; export const findNoteId = id => (id.match('DiffNote/(.+$)') || [])[1];
export const findIssueId = id => (id.match('Issue/(.+$)') || [])[1];
export const extractDesigns = data => data.project.issue.designCollection.designs.nodes; export const extractDesigns = data => data.project.issue.designCollection.designs.nodes;
export const extractDesign = data => (extractDesigns(data) || [])[0]; export const extractDesign = data => (extractDesigns(data) || [])[0];
...@@ -146,3 +148,22 @@ const normalizeAuthor = author => ({ ...@@ -146,3 +148,22 @@ const normalizeAuthor = author => ({
export const extractParticipants = users => users.map(node => normalizeAuthor(node)); export const extractParticipants = users => users.map(node => normalizeAuthor(node));
export const getPageLayoutElement = () => document.querySelector('.layout-page'); export const getPageLayoutElement = () => document.querySelector('.layout-page');
/**
* Extract the ID of the To-Do for a given 'delete' path
* Example of todoDeletePath: /delete/1234
* @param {String} todoDeletePath delete_path from REST API response
*/
export const extractTodoIdFromDeletePath = todoDeletePath =>
(todoDeletePath.match('todos/([0-9]+$)') || [])[1];
const createTodoGid = todoId => {
return `gid://gitlab/Todo/${todoId}`;
};
export const createPendingTodo = todoId => {
return {
__typename: 'Todo', // eslint-disable-line @gitlab/require-i18n-strings
id: createTodoGid(todoId),
};
};
...@@ -44,6 +44,14 @@ export const MOVE_DESIGN_ERROR = __( ...@@ -44,6 +44,14 @@ export const MOVE_DESIGN_ERROR = __(
'Something went wrong when reordering designs. Please try again', 'Something went wrong when reordering designs. Please try again',
); );
export const CREATE_DESIGN_TODO_ERROR = __('Failed to create To-Do for the design.');
export const CREATE_DESIGN_TODO_EXISTS_ERROR = __('There is already a To-Do for this design.');
export const DELETE_DESIGN_TODO_ERROR = __('Failed to remove To-Do for the design.');
export const TOGGLE_TODO_ERROR = __('Failed to toggle To-Do for the design.');
const MAX_SKIPPED_FILES_LISTINGS = 5; const MAX_SKIPPED_FILES_LISTINGS = 5;
const oneDesignSkippedMessage = filename => const oneDesignSkippedMessage = filename =>
......
...@@ -10414,6 +10414,9 @@ msgstr "" ...@@ -10414,6 +10414,9 @@ msgstr ""
msgid "Failed to create Merge Request. Please try again." msgid "Failed to create Merge Request. Please try again."
msgstr "" msgstr ""
msgid "Failed to create To-Do for the design."
msgstr ""
msgid "Failed to create a branch for this issue. Please try again." msgid "Failed to create a branch for this issue. Please try again."
msgstr "" msgstr ""
...@@ -10504,6 +10507,9 @@ msgstr "" ...@@ -10504,6 +10507,9 @@ msgstr ""
msgid "Failed to publish issue on status page." msgid "Failed to publish issue on status page."
msgstr "" msgstr ""
msgid "Failed to remove To-Do for the design."
msgstr ""
msgid "Failed to remove a Zoom meeting" msgid "Failed to remove a Zoom meeting"
msgstr "" msgstr ""
...@@ -10546,6 +10552,9 @@ msgstr "" ...@@ -10546,6 +10552,9 @@ msgstr ""
msgid "Failed to signing using smartcard authentication" msgid "Failed to signing using smartcard authentication"
msgstr "" msgstr ""
msgid "Failed to toggle To-Do for the design."
msgstr ""
msgid "Failed to update branch!" msgid "Failed to update branch!"
msgstr "" msgstr ""
...@@ -25117,6 +25126,9 @@ msgstr "" ...@@ -25117,6 +25126,9 @@ msgstr ""
msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project." msgid "There is a limit of %{ci_project_subscriptions_limit} subscriptions from or to a project."
msgstr "" msgstr ""
msgid "There is already a To-Do for this design."
msgstr ""
msgid "There is already a repository with that name on disk" msgid "There is already a repository with that name on disk"
msgstr "" msgstr ""
......
...@@ -6,7 +6,7 @@ import Participants from '~/sidebar/components/participants/participants.vue'; ...@@ -6,7 +6,7 @@ import Participants from '~/sidebar/components/participants/participants.vue';
import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue'; import DesignDiscussion from '~/design_management/components/design_notes/design_discussion.vue';
import design from '../mock_data/design'; import design from '../mock_data/design';
import updateActiveDiscussionMutation from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql'; import updateActiveDiscussionMutation from '~/design_management/graphql/mutations/update_active_discussion.mutation.graphql';
import TodoButton from '~/vue_shared/components/todo_button.vue'; import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
const scrollIntoViewMock = jest.fn(); const scrollIntoViewMock = jest.fn();
HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; HTMLElement.prototype.scrollIntoView = scrollIntoViewMock;
...@@ -248,7 +248,7 @@ describe('Design management design sidebar component', () => { ...@@ -248,7 +248,7 @@ describe('Design management design sidebar component', () => {
it('does not render To-Do button by default', () => { it('does not render To-Do button by default', () => {
createComponent(); createComponent();
expect(wrapper.find(TodoButton).exists()).toBe(false); expect(wrapper.find(DesignTodoButton).exists()).toBe(false);
}); });
describe('when `design_management_todo_button` feature flag is enabled', () => { describe('when `design_management_todo_button` feature flag is enabled', () => {
...@@ -260,8 +260,8 @@ describe('Design management design sidebar component', () => { ...@@ -260,8 +260,8 @@ describe('Design management design sidebar component', () => {
expect(wrapper.classes()).toContain('gl-pt-0'); expect(wrapper.classes()).toContain('gl-pt-0');
}); });
it('renders todo_button component', () => { it('renders To-Do button', () => {
expect(wrapper.find(TodoButton).exists()).toBe(true); expect(wrapper.find(DesignTodoButton).exists()).toBe(true);
}); });
}); });
}); });
import { shallowMount, mount } from '@vue/test-utils';
import TodoButton from '~/vue_shared/components/todo_button.vue';
import DesignTodoButton from '~/design_management/components/design_todo_button.vue';
import createDesignTodoMutation from '~/design_management/graphql/mutations/create_design_todo.mutation.graphql';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import mockDesign from '../mock_data/design';
const mockDesignWithPendingTodos = {
...mockDesign,
currentUserTodos: {
nodes: [
{
id: 'todo-id',
},
],
},
};
const mutate = jest.fn().mockResolvedValue();
describe('Design management design todo button', () => {
let wrapper;
function createComponent(props = {}, { mountFn = shallowMount } = {}) {
wrapper = mountFn(DesignTodoButton, {
propsData: {
design: mockDesign,
...props,
},
provide: {
projectPath: 'project-path',
issueIid: '10',
},
mocks: {
$route: {
params: {
id: 'my-design.jpg',
},
query: {},
},
$apollo: {
mutate,
},
},
});
}
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders TodoButton component', () => {
expect(wrapper.find(TodoButton).exists()).toBe(true);
});
describe('when design has a pending todo', () => {
beforeEach(() => {
createComponent({ design: mockDesignWithPendingTodos }, { mountFn: mount });
});
it('renders correct button text', () => {
expect(wrapper.text()).toBe('Mark as done');
});
describe('when clicked', () => {
beforeEach(() => {
createComponent({ design: mockDesignWithPendingTodos }, { mountFn: mount });
wrapper.trigger('click');
return wrapper.vm.$nextTick();
});
it('calls `$apollo.mutate` with the `todoMarkDone` mutation and variables containing `id`', async () => {
const todoMarkDoneMutationVariables = {
mutation: todoMarkDoneMutation,
update: expect.anything(),
variables: {
id: 'todo-id',
},
};
expect(mutate).toHaveBeenCalledTimes(1);
expect(mutate).toHaveBeenCalledWith(todoMarkDoneMutationVariables);
});
});
});
describe('when design has no pending todos', () => {
beforeEach(() => {
createComponent({}, { mountFn: mount });
});
it('renders correct button text', () => {
expect(wrapper.text()).toBe('Add a To-Do');
});
describe('when clicked', () => {
beforeEach(() => {
createComponent({}, { mountFn: mount });
wrapper.trigger('click');
return wrapper.vm.$nextTick();
});
it('calls `$apollo.mutate` with the `createDesignTodoMutation` mutation and variables containing `issuable_id`, `issue_id`, & `projectPath`', async () => {
const createDesignTodoMutationVariables = {
mutation: createDesignTodoMutation,
update: expect.anything(),
variables: {
atVersion: null,
filenames: ['my-design.jpg'],
issueId: '1',
issueIid: '10',
projectPath: 'project-path',
},
};
expect(mutate).toHaveBeenCalledTimes(1);
expect(mutate).toHaveBeenCalledWith(createDesignTodoMutationVariables);
});
});
});
});
export default { export default {
id: 'design-id', id: 'gid::/gitlab/Design/1',
filename: 'test.jpg', filename: 'test.jpg',
fullPath: 'full-design-path', fullPath: 'full-design-path',
image: 'test.jpg', image: 'test.jpg',
...@@ -8,6 +8,7 @@ export default { ...@@ -8,6 +8,7 @@ export default {
name: 'test', name: 'test',
}, },
issue: { issue: {
id: 'gid::/gitlab/Issue/1',
title: 'My precious issue', title: 'My precious issue',
webPath: 'full-issue-path', webPath: 'full-issue-path',
webUrl: 'full-issue-url', webUrl: 'full-issue-url',
......
...@@ -59,11 +59,11 @@ exports[`Design management design index page renders design index 1`] = ` ...@@ -59,11 +59,11 @@ exports[`Design management design index page renders design index 1`] = `
<design-discussion-stub <design-discussion-stub
data-testid="unresolved-discussion" data-testid="unresolved-discussion"
designid="design-id" designid="gid::/gitlab/Design/1"
discussion="[object Object]" discussion="[object Object]"
discussionwithopenform="" discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue" markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
noteableid="design-id" noteableid="gid::/gitlab/Design/1"
/> />
<gl-button-stub <gl-button-stub
...@@ -107,11 +107,11 @@ exports[`Design management design index page renders design index 1`] = ` ...@@ -107,11 +107,11 @@ exports[`Design management design index page renders design index 1`] = `
> >
<design-discussion-stub <design-discussion-stub
data-testid="resolved-discussion" data-testid="resolved-discussion"
designid="design-id" designid="gid::/gitlab/Design/1"
discussion="[object Object]" discussion="[object Object]"
discussionwithopenform="" discussionwithopenform=""
markdownpreviewpath="/project-path/preview_markdown?target_type=Issue" markdownpreviewpath="/project-path/preview_markdown?target_type=Issue"
noteableid="design-id" noteableid="gid::/gitlab/Design/1"
/> />
</gl-collapse-stub> </gl-collapse-stub>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment