summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Ivan Vargas <jvargas@gitlab.com>2018-01-29 15:46:01 -0600
committerJose Ivan Vargas <jvargas@gitlab.com>2018-02-06 17:12:28 -0600
commit70e1d0e84f691d625815c53753bd27849fd61272 (patch)
tree675faa94c808b499e418c351289e4fb78caa88e8
parentd701ffc945fbf61d9f7b519d03c580999ff22d03 (diff)
downloadgitlab-ce-41313-new-design-for-project-label-deletion-confirmation.tar.gz
Changed vue logic to only add one instance41313-new-design-for-project-label-deletion-confirmation
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/pages/labels/components/delete_label_modal.vue49
-rw-r--r--app/assets/javascripts/pages/labels/event_hub.js3
-rw-r--r--app/assets/javascripts/pages/labels/index.js94
-rw-r--r--app/views/shared/_delete_label_modal.html.haml20
-rw-r--r--app/views/shared/_label.html.haml19
-rw-r--r--spec/features/projects/labels/remove_labels_spec.rb26
-rw-r--r--spec/javascripts/pages/labels/components/delete_label_modal_component_spec.js26
8 files changed, 158 insertions, 83 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 999db292d39..492fe68ad0e 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -406,7 +406,9 @@ var Dispatcher;
import('./pages/projects/labels/index')
.then(callDefault)
.catch(fail);
- import('./pages/labels/').then(callDefault).catch(fail);
+ import('./pages/labels/')
+ .then(callDefault)
+ .catch(fail);
break;
case 'projects:network:show':
// Ensure we don't create a particular shortcut handler here. This is
diff --git a/app/assets/javascripts/pages/labels/components/delete_label_modal.vue b/app/assets/javascripts/pages/labels/components/delete_label_modal.vue
index 2808476929b..c6e91ab8c81 100644
--- a/app/assets/javascripts/pages/labels/components/delete_label_modal.vue
+++ b/app/assets/javascripts/pages/labels/components/delete_label_modal.vue
@@ -2,7 +2,9 @@
import axios from '~/lib/utils/axios_utils';
import modal from '~/vue_shared/components/modal.vue';
import { redirectTo } from '~/lib/utils/url_utility';
+ import { s__, sprintf } from '~/locale';
import Flash from '~/flash';
+ import eventHub from '../event_hub';
export default {
components: {
@@ -13,11 +15,11 @@
type: String,
required: true,
},
- labelOpenMergeRequestsCount: {
+ openMergeRequestCount: {
type: Number,
required: true,
},
- labelOpenIssuesCount: {
+ openIssuesCount: {
type: Number,
required: true,
},
@@ -28,26 +30,37 @@
},
computed: {
modalTitle() {
- return `Delete label ‘${this.labelTitle}’?`;
+ return sprintf(s__('Labels|Delete label ‘%{labelTitle}’?'), { labelTitle: this.labelTitle });
},
modalDescription() {
- return `You’re about to permanently delete the label ${this.labelTitle} from this project and remove it from
- ${this.labelOpenIssuesCount}, ${this.labelOpenMergeRequestsCount} issues and merge requests.
- Once deleted, it cannot be undone or recovered.`;
+ return sprintf(s__(`Labels|You’re about to permanently delete the label <strong>%{labelTitle}</strong>
+ from this project and remove it from %{openIssuesCount} issues and %{openMergeRequestCount} merge requests.
+ Once deleted, it cannot be undone or recovered.`), {
+ labelTitle: this.labelTitle,
+ openIssuesCount: this.openIssuesCount,
+ openMergeRequestCount: this.openMergeRequestCount,
+ });
},
primaryButtonText() {
- return 'Delete Label';
+ return s__('Labels|Delete Label');
},
},
methods: {
onSubmit() {
+ eventHub.$emit('deleteLabelModal.requestStarted', this.url);
return axios.delete(this.url)
- .then((resp) => {
- redirectTo(resp.request.responseURL);
- })
- .catch((err) => {
- Flash(err);
- });
+ .then((response) => {
+ eventHub.$emit('deleteLabelModal.requestFinished', { labelUrl: this.url, successful: true });
+
+ redirectTo(response.request.responseURL);
+ })
+ .catch((error) => {
+ eventHub.$emit('deleteLabelModal.requestFinished', { labelUrl: this.url, successful: false });
+
+ Flash(error);
+
+ throw error;
+ });
},
},
};
@@ -59,6 +72,12 @@
:text="modalDescription"
kind="danger"
:primary-button-label="primaryButtonText"
- @submit="onSubmit"
- />
+ @submit="onSubmit">
+
+ <template
+ slot="body"
+ slot-scope="props">
+ <p v-html="props.text"></p>
+ </template>
+ </modal>
</template>
diff --git a/app/assets/javascripts/pages/labels/event_hub.js b/app/assets/javascripts/pages/labels/event_hub.js
new file mode 100644
index 00000000000..0948c2e5352
--- /dev/null
+++ b/app/assets/javascripts/pages/labels/event_hub.js
@@ -0,0 +1,3 @@
+import Vue from 'vue';
+
+export default new Vue();
diff --git a/app/assets/javascripts/pages/labels/index.js b/app/assets/javascripts/pages/labels/index.js
index 41abecd51c9..4e9464662c1 100644
--- a/app/assets/javascripts/pages/labels/index.js
+++ b/app/assets/javascripts/pages/labels/index.js
@@ -1,40 +1,88 @@
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import deleteLabelModal from './components/delete_label_modal.vue';
+import eventHub from './event_hub';
-Vue.use(Translate);
+export default () => {
+ Vue.use(Translate);
+
+ const onRequestFinished = ({ labelUrl, successful }) => {
+ const button = document.querySelector(`.js-delete-project-label[data-url="${labelUrl}"]`);
+
+ if (!successful) {
+ button.removeAttribute('disabled');
+ }
+ };
+
+ const onRequestStarted = (labelUrl) => {
+ const button = document.querySelector(`.js-delete-project-label[data-url="${labelUrl}"]`);
+ button.setAttribute('disabled', '');
+ eventHub.$once('deleteLabelModal.requestFinished', onRequestFinished);
+ };
+
+ const onDeleteButtonClick = (event) => {
+ const button = event.currentTarget;
+ const modalProps = {
+ labelTitle: button.dataset.labelTitle,
+ openMergeRequestCount: parseInt(button.dataset.labelOpenMergeRequestsCount, 10),
+ openIssuesCount: parseInt(button.dataset.labelOpenIssuesCount, 10),
+ url: button.dataset.url,
+ };
+ eventHub.$once('deleteLabelModal.requestStarted', onRequestStarted);
+ eventHub.$emit('deleteLabelModal.props', modalProps);
+ };
+
+ const deleteLabelButtons = document.querySelectorAll('.js-delete-project-label');
+ for (let i = 0; i < deleteLabelButtons.length; i += 1) {
+ const button = deleteLabelButtons[i];
+ button.addEventListener('click', onDeleteButtonClick);
+ }
-function createDeleteLabelModal(props) {
- // eslint-disable-next-line no-new
- new Vue({
+ eventHub.$once('deleteLabelModal.mounted', () => {
+ for (let i = 0; i < deleteLabelButtons.length; i += 1) {
+ const button = deleteLabelButtons[i];
+ button.removeAttribute('disabled');
+ }
+ });
+
+ const labelComponent = new Vue({
el: '#delete-label-modal',
components: {
deleteLabelModal,
},
+ data() {
+ return {
+ modalProps: {
+ labelTitle: '',
+ openMergeRequestCount: -1,
+ openIssuesCount: -1,
+ url: '',
+ },
+ };
+ },
+ mounted() {
+ eventHub.$on('deleteLabelModal.props', this.setModalProps);
+ eventHub.$emit('deleteLabelModal.mounted');
+ },
+ beforeDestroy() {
+ eventHub.$off('deleteLabelModal.props', this.setModalProps);
+ },
+ methods: {
+ setModalProps(modalProps) {
+ this.modalProps = modalProps;
+ },
+ },
render(createElement) {
return createElement('delete-label-modal', {
- props,
+ props: this.modalProps,
});
},
});
-}
-
-function getProps(event) {
- const button = event.currentTarget;
- const props = {
- labelTitle: button.dataset.labelTitle,
- labelOpenIssuesCount: parseInt(button.dataset.labelOpenIssuesCount, 10),
- labelOpenMergeRequestsCount: parseInt(button.dataset.labelOpenMergeRequestsCount, 10),
- url: button.dataset.url,
- };
-
- createDeleteLabelModal(props);
-}
-export default () => {
- const deleteLabelButtons = document.querySelectorAll('.js-delete-project-label');
- for (let i = 0; i < deleteLabelButtons.length; i += 1) {
- const button = deleteLabelButtons[i];
- button.onclick = getProps;
+ const labelModal = document.getElementById('delete-label-modal');
+ let withLabel;
+ if (labelModal != null) {
+ withLabel = labelComponent;
}
+ return withLabel;
};
diff --git a/app/views/shared/_delete_label_modal.html.haml b/app/views/shared/_delete_label_modal.html.haml
deleted file mode 100644
index 01effefc34d..00000000000
--- a/app/views/shared/_delete_label_modal.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-.modal{ id: "modal-delete-label-#{label.id}", tabindex: -1 }
- .modal-dialog
- .modal-content
- .modal-header
- %button.close{ data: {dismiss: 'modal' } } &times;
- %h3.page-title Delete #{render_colored_label(label, tooltip: false)} ?
-
- .modal-body
- %p
- %strong= label.name
- %span will be permanently deleted from #{label.is_a?(ProjectLabel)? label.project.name : label.group.name}. This cannot be undone.
-
- .modal-footer
- %a{ href: '#', data: { dismiss: 'modal' }, class: 'btn btn-default' } Cancel
-
- = link_to 'Delete label',
- destroy_label_path(label),
- title: 'Delete',
- method: :delete,
- class: 'btn btn-remove'
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 41312e8c569..adf008a6fa1 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -58,15 +58,16 @@
= link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do
%span.sr-only Edit
= icon('pencil-square-o')
- %a.js-delete-project-label.btn.btn-transparent.btn-action.remove-row.has-tooltip{ title: _('Delete'),
- data: { url: destroy_label_path(label),
- label_title: label.title,
- label_open_merge_requests_count: label.open_merge_requests_count,
- label_open_issues_count: label.open_issues_count,
- target: '#delete-label-modal',
- container: 'body',
- toggle: "modal" } }
- = icon('trash-o')
+ %button.js-delete-project-label.btn.btn-transparent.btn-action.remove-row.has-tooltip{ title: _('Delete'),
+ data: { url: destroy_label_path(label),
+ label_title: label.title,
+ label_open_merge_requests_count: label.open_merge_requests_count,
+ label_open_issues_count: label.open_issues_count,
+ target: '#delete-label-modal',
+ container: 'body',
+ toggle: 'modal' },
+ disabled: true }
+ = sprite_icon('remove')
- if current_user
.label-subscription.inline
- if can_subscribe_to_label_in_different_levels?(label)
diff --git a/spec/features/projects/labels/remove_labels_spec.rb b/spec/features/projects/labels/remove_labels_spec.rb
index 22f5d906845..95ec3ba775d 100644
--- a/spec/features/projects/labels/remove_labels_spec.rb
+++ b/spec/features/projects/labels/remove_labels_spec.rb
@@ -5,7 +5,7 @@ describe 'Removing labels', :js do
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
let!(:bug) { create(:label, project: project, title: 'bug') }
- let!(:test) { create(:label, project: project, title: 'test') }
+ let!(:test_label) { create(:label, project: project, title: 'test') }
before do
project.add_master(user)
@@ -15,17 +15,23 @@ describe 'Removing labels', :js do
context 'with a label list' do
before do
visit project_labels_path(project)
- wait_for_all_requests
end
- it 'Deletes a label' do
- page.within '.labels' do
- first('.remove-row').click
- sleep 2
- page.execute_script('document.querySelector(".js-primary-button").click()')
- wait_for_requests
- expect(page.all('.remove-row').length).to eq 1
- end
+ it 'Deletes all labels' do
+ delete_label(bug.title)
+ wait_for_requests
+ delete_label(test_label.title)
+ wait_for_requests
+ expect(find('.empty-state.labels')).not_to be_nil
+ end
+ end
+
+ def delete_label(label_title)
+ find('#delete-label-modal.modal', visible: false) # wait for Vue component to be loaded
+ find(".js-delete-project-label[data-label-title=\"#{label_title}\"]" % { label_title: label_title }).click
+
+ page.within '#delete-label-modal' do
+ click_on 'Delete Label'
end
end
end
diff --git a/spec/javascripts/pages/labels/components/delete_label_modal_component_spec.js b/spec/javascripts/pages/labels/components/delete_label_modal_component_spec.js
index 53c2a6d1201..2faf1f8f86a 100644
--- a/spec/javascripts/pages/labels/components/delete_label_modal_component_spec.js
+++ b/spec/javascripts/pages/labels/components/delete_label_modal_component_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import deleteLabelModal from '~/pages/labels/components/delete_label_modal.vue';
+import eventHub from '~/pages/labels/event_hub';
import axios from '~/lib/utils/axios_utils';
import * as urlUtility from '~/lib/utils/url_utility';
import mountComponent from '../../../helpers/vue_mount_component_helper';
@@ -9,9 +10,9 @@ describe('Delete label modal component', () => {
let Component;
const labelMockData = {
labelTitle: 'Test',
- labelOpenMergeRequestsCount: 1,
- labelOpenIssuesCount: 2,
- url: '/dummy/endpoint',
+ openMergeRequestCount: 1,
+ openIssuesCount: 2,
+ url: `${gl.TEST_HOST}/dummy/endpoint`,
};
beforeEach(() => {
@@ -35,16 +36,28 @@ describe('Delete label modal component', () => {
it('modalDescription', () => {
expect(vm.modalDescription).toContain(labelMockData.labelTitle);
- expect(vm.modalDescription).toContain(labelMockData.labelOpenMergeRequestsCount);
- expect(vm.modalDescription).toContain(labelMockData.labelOpenIssuesCount);
+ expect(vm.modalDescription).toContain(labelMockData.openMergeRequestCount);
+ expect(vm.modalDescription).toContain(labelMockData.openIssuesCount);
});
});
describe('When requesting a label delete', () => {
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ ...labelMockData,
+ });
+ spyOn(eventHub, '$emit');
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
it('should redirect when a label is deleted', (done) => {
const responseURL = `${gl.TEST_HOST}/dummy/endpoint`;
spyOn(axios, 'delete').and.callFake((url) => {
expect(url).toBe(labelMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteLabelModal.requestStarted', labelMockData.url);
return Promise.resolve({
request: {
responseURL,
@@ -56,6 +69,7 @@ describe('Delete label modal component', () => {
vm.onSubmit()
.then(() => {
expect(redirectSpy).toHaveBeenCalledWith(responseURL);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: true });
})
.then(done)
.catch(done.fail);
@@ -66,6 +80,7 @@ describe('Delete label modal component', () => {
dummyError.response = { status: 500 };
spyOn(axios, 'delete').and.callFake((url) => {
expect(url).toBe(labelMockData.url);
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteLabelModal.requestStarted', labelMockData.url);
return Promise.reject(dummyError);
});
const redirectSpy = spyOn(urlUtility, 'redirectTo');
@@ -74,6 +89,7 @@ describe('Delete label modal component', () => {
.catch((error) => {
expect(error).toBe(dummyError);
expect(redirectSpy).not.toHaveBeenCalled();
+ expect(eventHub.$emit).toHaveBeenCalledWith('deleteLabelModal.requestFinished', { labelUrl: labelMockData.url, successful: false });
})
.then(done)
.catch(done.fail);