Commit bb340771 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '222359-alert-todo' into 'master'

Alert To Do UI

Closes #222359

See merge request gitlab-org/gitlab!38595
parents d03735af 593b3eab
<script>
import { s__ } from '~/locale';
import Todo from '~/sidebar/components/todo_toggle/todo.vue';
import axios from '~/lib/utils/axios_utils';
import createAlertTodo from '../../graphql/mutations/alert_todo_create.graphql';
import createAlertTodo from '../../graphql/mutations/alert_todo_create.mutation.graphql';
import todoMarkDone from '../../graphql/mutations/alert_todo_mark_done.mutation.graphql';
import alertQuery from '../../graphql/queries/details.query.graphql';
export default {
i18n: {
......@@ -30,14 +31,24 @@ export default {
data() {
return {
isUpdating: false,
isTodo: false,
todo: '',
};
},
computed: {
alertID() {
return parseInt(this.alert.iid, 10);
},
firstToDoId() {
return this.alert?.todos?.nodes[0]?.id;
},
hasPendingTodos() {
return this.alert?.todos?.nodes.length > 0;
},
getAlertQueryVariables() {
return {
fullPath: this.projectPath,
alertId: this.alert.iid,
};
},
},
methods: {
updateToDoCount(add) {
......@@ -51,11 +62,7 @@ export default {
return document.dispatchEvent(headerTodoEvent);
},
toggleTodo() {
if (this.todo) {
return this.markAsDone();
}
addToDo() {
this.isUpdating = true;
return this.$apollo
.mutate({
......@@ -65,24 +72,14 @@ export default {
projectPath: this.projectPath,
},
})
.then(({ data: { alertTodoCreate: { todo = {}, errors = [] } } = {} } = {}) => {
.then(({ data: { errors = [] } }) => {
if (errors[0]) {
return this.$emit(
'alert-error',
`${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${errors[0]}.`,
);
return this.throwError(errors[0]);
}
this.todo = todo.id;
return this.updateToDoCount(true);
})
.catch(() => {
this.$emit(
'alert-error',
`${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${s__(
'AlertManagement|Please try again.',
)}`,
);
this.throwError();
})
.finally(() => {
this.isUpdating = false;
......@@ -90,20 +87,45 @@ export default {
},
markAsDone() {
this.isUpdating = true;
return axios
.delete(`/dashboard/todos/${this.todo.split('/').pop()}`)
.then(() => {
this.todo = '';
return this.$apollo
.mutate({
mutation: todoMarkDone,
variables: {
id: this.firstToDoId,
},
update: this.updateCache,
})
.then(({ data: { errors = [] } }) => {
if (errors[0]) {
return this.throwError(errors[0]);
}
return this.updateToDoCount(false);
})
.catch(() => {
this.$emit('alert-error', this.$options.i18n.UPDATE_ALERT_TODO_ERROR);
this.throwError();
})
.finally(() => {
this.isUpdating = false;
});
},
updateCache(store) {
const data = store.readQuery({
query: alertQuery,
variables: this.getAlertQueryVariables,
});
data.project.alertManagementAlerts.nodes[0].todos.nodes.shift();
store.writeQuery({
query: alertQuery,
variables: this.getAlertQueryVariables,
data,
});
},
throwError(err = '') {
const error = err || s__('AlertManagement|Please try again.');
this.$emit('alert-error', `${this.$options.i18n.UPDATE_ALERT_TODO_ERROR} ${error}`);
},
},
};
</script>
......@@ -114,10 +136,10 @@ export default {
data-testid="alert-todo-button"
:collapsed="sidebarCollapsed"
:issuable-id="alertID"
:is-todo="todo !== ''"
:is-todo="hasPendingTodos"
:is-action-active="isUpdating"
issuable-type="alert"
@toggleTodo="toggleTodo"
@toggleTodo="hasPendingTodos ? markAsDone() : addToDo()"
/>
</div>
</template>
......@@ -11,6 +11,11 @@ fragment AlertDetailItem on AlertManagementAlert {
updatedAt
endedAt
details
todos {
nodes {
id
}
}
notes {
nodes {
...AlertNote
......
mutation($projectPath: ID!, $iid: String!) {
#import "../fragments/detail_item.fragment.graphql"
mutation alertTodoCreate($projectPath: ID!, $iid: String!) {
alertTodoCreate(input: { iid: $iid, projectPath: $projectPath }) {
errors
alert {
iid
}
todo {
id
...AlertDetailItem
}
}
}
mutation todoMarkDone($id: ID!) {
todoMarkDone(input: { id: $id }) {
errors
todo {
id
}
}
}
---
title: Add Mark as done capability to Alert To Do's
merge_request: 38595
author:
type: changed
import { mount } from '@vue/test-utils';
import SidebarTodo from '~/alert_management/components/sidebar/sidebar_todo.vue';
import AlertMarkTodo from '~/alert_management/graphql/mutations/alert_todo_create.graphql';
import AlertMarkTodo from '~/alert_management/graphql/mutations/alert_todo_create.mutation.graphql';
import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0];
......@@ -34,6 +34,8 @@ describe('Alert Details Sidebar To Do', () => {
wrapper.destroy();
});
const findToDoButton = () => wrapper.find('[data-testid="alert-todo-button"]');
describe('updating the alert to do', () => {
const mockUpdatedMutationResult = {
data: {
......@@ -44,25 +46,27 @@ describe('Alert Details Sidebar To Do', () => {
},
};
beforeEach(() => {
mountComponent({
data: { alert: mockAlert },
sidebarCollapsed: false,
loading: false,
describe('adding a todo', () => {
beforeEach(() => {
mountComponent({
data: { alert: mockAlert },
sidebarCollapsed: false,
loading: false,
});
});
});
it('renders a button for adding a To-Do', () => {
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find('[data-testid="alert-todo-button"]').text()).toBe('Add a To-Do');
it('renders a button for adding a To-Do', async () => {
await wrapper.vm.$nextTick();
expect(findToDoButton().text()).toBe('Add a To-Do');
});
});
it('calls `$apollo.mutate` with `AlertMarkTodo` mutation and variables containing `iid`, `todoEvent`, & `projectPath`', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
it('calls `$apollo.mutate` with `AlertMarkTodo` mutation and variables containing `iid`, `todoEvent`, & `projectPath`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findToDoButton().trigger('click');
await wrapper.vm.$nextTick();
return wrapper.vm.$nextTick().then(() => {
wrapper.find('button').trigger('click');
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: AlertMarkTodo,
variables: {
......@@ -72,5 +76,28 @@ describe('Alert Details Sidebar To Do', () => {
});
});
});
describe('removing a todo', () => {
beforeEach(() => {
mountComponent({
data: { alert: { ...mockAlert, todos: { nodes: [{ id: '1234' }] } } },
sidebarCollapsed: false,
loading: false,
});
});
it('renders a Mark As Done button when todo is present', async () => {
await wrapper.vm.$nextTick();
expect(findToDoButton().text()).toBe('Mark as done');
});
it('calls `$apollo.mutate` with `AlertMarkTodoDone` mutation and variables containing `id`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findToDoButton().trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledTimes(1);
});
});
});
});
......@@ -9,7 +9,8 @@
"endedAt": "2020-04-17T23:18:14.996Z",
"status": "TRIGGERED",
"assignees": { "nodes": [] },
"notes": { "nodes": [] }
"notes": { "nodes": [] },
"todos": { "nodes": [] }
},
{
"iid": "1527543",
......@@ -37,7 +38,8 @@
"systemNoteIconName": "user"
}
]
}
},
"todos": { "nodes": [] }
},
{
"iid": "1527544",
......@@ -63,6 +65,7 @@
}
}
]
}
},
"todos": { "nodes": [] }
}
]
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