Commit be3adf55 authored by Simon Knox's avatar Simon Knox

Add button to delete iteration cadences

In a confirmation modal
parent 669b4712
......@@ -7,6 +7,7 @@ import {
GlDropdownItem,
GlIcon,
GlInfiniteScroll,
GlModal,
GlSkeletonLoader,
} from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
......@@ -18,6 +19,14 @@ const pageSize = 20;
const i18n = Object.freeze({
noResults: s__('Iterations|No iterations in cadence.'),
error: __('Error loading iterations'),
deleteCadence: s__('Iterations|Delete cadence'),
modalTitle: s__('Iterations|Delete iteration cadence?'),
modalText: s__(
'Iterations|This will delete the cadence as well as all of the iterations within it.',
),
modalConfirm: s__('Iterations|Delete cadence'),
modalCancel: __('Cancel'),
});
export default {
......@@ -30,6 +39,7 @@ export default {
GlDropdownItem,
GlIcon,
GlInfiniteScroll,
GlModal,
GlSkeletonLoader,
},
apollo: {
......@@ -154,6 +164,12 @@ export default {
},
};
},
showModal() {
this.$refs.modal.show();
},
focusMenu() {
this.$refs.menu.$el.focus();
},
},
};
</script>
......@@ -181,17 +197,31 @@ export default {
>
<gl-dropdown
v-if="canEditCadence"
ref="menu"
icon="ellipsis_v"
category="tertiary"
right
lazy
text-sr-only
no-caret
>
<gl-dropdown-item :to="editCadence">
{{ s__('Iterations|Edit cadence') }}
</gl-dropdown-item>
<gl-dropdown-item data-testid="delete-cadence" @click="showModal">
{{ i18n.deleteCadence }}
</gl-dropdown-item>
</gl-dropdown>
<gl-modal
ref="modal"
:modal-id="`${cadenceId}-delete-modal`"
:title="i18n.modalTitle"
:ok-title="i18n.modalConfirm"
ok-variant="danger"
@hidden="focusMenu"
@ok="$emit('delete-cadence', cadenceId)"
>
{{ i18n.modalText }}
</gl-modal>
</div>
<gl-alert v-if="error" variant="danger" :dismissible="true" @dismiss="error = ''">
......
<script>
import { GlAlert, GlButton, GlLoadingIcon, GlKeysetPagination, GlTab, GlTabs } from '@gitlab/ui';
import produce from 'immer';
import { __, s__ } from '~/locale';
import destroyIterationCadence from '../queries/destroy_cadence.mutation.graphql';
import query from '../queries/iteration_cadences_list.query.graphql';
import IterationCadenceListItem from './iteration_cadence_list_item.vue';
......@@ -96,6 +98,40 @@ export default {
handleTabChange() {
this.pagination = {};
},
deleteCadence(cadenceId) {
this.$apollo
.mutate({
mutation: destroyIterationCadence,
variables: {
id: cadenceId,
},
update: (store, { data: { iterationCadenceDestroy } }) => {
if (iterationCadenceDestroy.errors?.length) {
throw iterationCadenceDestroy.errors[0];
}
const sourceData = store.readQuery({
query,
variables: this.queryVariables,
});
const data = produce(sourceData, (draftData) => {
draftData.group.iterationCadences.nodes = draftData.group.iterationCadences.nodes.filter(
({ id }) => id !== cadenceId,
);
});
store.writeQuery({
query,
variables: this.queryVariables,
data,
});
},
})
.catch((err) => {
this.error = err;
});
},
},
};
</script>
......@@ -122,6 +158,7 @@ export default {
:duration-in-weeks="cadence.durationInWeeks"
:title="cadence.title"
:iteration-state="state"
@delete-cadence="deleteCadence"
/>
</ul>
<p v-else class="nothing-here-block">
......
......@@ -113,7 +113,7 @@ export function initCadenceApp({ namespaceType }) {
previewMarkdownPath,
noIssuesSvgPath,
} = el.dataset;
const router = createRouter(cadencesListPath);
const router = createRouter({ base: cadencesListPath });
return new Vue({
el,
......
mutation destroyIterationCadence($id: IterationsCadenceID!) {
iterationCadenceDestroy(input: { id: $id }) {
errors
}
}
......@@ -29,7 +29,7 @@ const routes = [
},
];
export default function createRouter(base) {
export default function createRouter({ base }) {
const router = new VueRouter({
base,
mode: 'history',
......
import { GlDropdown, GlInfiniteScroll, GlSkeletonLoader } from '@gitlab/ui';
import { GlDropdown, GlInfiniteScroll, GlModal, GlSkeletonLoader } from '@gitlab/ui';
import { createLocalVue, RouterLinkStub } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
......@@ -188,6 +188,46 @@ describe('Iteration cadence list item', () => {
);
});
describe('deleting cadence', () => {
describe('canEditCadence = false', () => {
beforeEach(async () => {
await createComponent({
canEditCadence: false,
});
});
it('hides dropdown and delete button', () => {
expect(wrapper.find(GlDropdown).exists()).toBe(false);
});
});
describe('canEditCadence = true', () => {
beforeEach(async () => {
createComponent({
canEditCadence: true,
});
wrapper.vm.$refs.modal.show = jest.fn();
});
it('shows delete button', () => {
expect(wrapper.find(GlDropdown).exists()).toBe(true);
});
it('opens confirmation modal to delete cadence', () => {
wrapper.findByTestId('delete-cadence').trigger('click');
expect(wrapper.vm.$refs.modal.show).toHaveBeenCalled();
});
it('emits delete-cadence event with cadence ID', () => {
wrapper.find(GlModal).vm.$emit('ok');
expect(wrapper.emitted('delete-cadence')).toEqual([[cadence.id]]);
});
});
});
it('hides dropdown when canEditCadence is false', async () => {
await createComponent({ canEditCadence: false });
......
......@@ -2,20 +2,21 @@ import { GlKeysetPagination, GlLoadingIcon } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import IterationCadenceListItem from 'ee/iterations/components/iteration_cadence_list_item.vue';
import IterationCadencesList from 'ee/iterations/components/iteration_cadences_list.vue';
import destroyIterationCadence from 'ee/iterations/queries/destroy_cadence.mutation.graphql';
import cadencesListQuery from 'ee/iterations/queries/iteration_cadences_list.query.graphql';
import createRouter from 'ee/iterations/router';
import createMockApollo from 'helpers/mock_apollo_helper';
import { TEST_HOST } from 'helpers/test_constants';
import { mountExtended as mount } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
const push = jest.fn();
const $router = {
push,
};
const localVue = createLocalVue();
const baseUrl = '/cadences/';
const router = createRouter(baseUrl);
function createMockApolloProvider(requestHandlers) {
localVue.use(VueApollo);
......@@ -84,15 +85,19 @@ describe('Iteration cadences list', () => {
canCreateCadence,
canEditCadence,
resolverMock = jest.fn().mockResolvedValue(querySuccessResponse),
destroyMutationMock = jest
.fn()
.mockResolvedValue({ data: { iterationCadenceDestroy: { errors: [] } } }),
} = {}) {
apolloProvider = createMockApolloProvider([[cadencesListQuery, resolverMock]]);
apolloProvider = createMockApolloProvider([
[cadencesListQuery, resolverMock],
[destroyIterationCadence, destroyMutationMock],
]);
wrapper = mount(IterationCadencesList, {
localVue,
apolloProvider,
mocks: {
$router,
},
router,
provide: {
groupPath,
cadencesListPath,
......@@ -184,9 +189,9 @@ describe('Iteration cadences list', () => {
resolverMock.mockReset();
});
it('correctly disables pagination buttons', async () => {
expect(findNextPageButton().element.disabled).toBe(false);
expect(findPrevPageButton().element.disabled).toBe(true);
it('correctly disables pagination buttons', () => {
expect(findNextPageButton().element).not.toBeDisabled();
expect(findPrevPageButton().element).toBeDisabled();
});
it('updates query when next page clicked', async () => {
......@@ -215,5 +220,26 @@ describe('Iteration cadences list', () => {
);
});
});
describe('deleting cadence', () => {
it('removes item from list', async () => {
await createComponent({
canEditCadence: true,
});
await waitForPromises();
// 3 cadences * 3 tabs, so 9 in total
expect(wrapper.findAllComponents(IterationCadenceListItem).length).toBe(9);
expect(wrapper.text()).toContain(cadences[0].title);
wrapper.findComponent(IterationCadenceListItem).vm.$emit('delete-cadence', cadences[0].id);
await waitForPromises();
expect(wrapper.findAllComponents(IterationCadenceListItem).length).toBe(6);
expect(wrapper.text()).not.toContain(cadences[0].title);
});
});
});
});
......@@ -18238,6 +18238,12 @@ msgstr ""
msgid "Iterations|Create cadence"
msgstr ""
msgid "Iterations|Delete cadence"
msgstr ""
msgid "Iterations|Delete iteration cadence?"
msgstr ""
msgid "Iterations|Duration"
msgstr ""
......@@ -18295,6 +18301,9 @@ msgstr ""
msgid "Iterations|The start date of your first iteration"
msgstr ""
msgid "Iterations|This will delete the cadence as well as all of the iterations within it."
msgstr ""
msgid "Iterations|Title"
msgstr ""
......
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