Commit cafcde8c authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '345195-add-a-delete-button-to-the-edit-label-pages' into 'master'

Add `Delete` button to label edit view

See merge request gitlab-org/gitlab!77917
parents f9d71777 07fa8e09
...@@ -56,6 +56,7 @@ export default { ...@@ -56,6 +56,7 @@ export default {
</gl-sprintf> </gl-sprintf>
</template> </template>
<gl-sprintf <gl-sprintf
v-if="subjectName"
:message=" :message="
__( __(
`%{strongStart}${labelName}%{strongEnd} will be permanently deleted from ${subjectName}. This cannot be undone.`, `%{strongStart}${labelName}%{strongEnd} will be permanently deleted from ${subjectName}. This cannot be undone.`,
...@@ -66,6 +67,18 @@ export default { ...@@ -66,6 +67,18 @@ export default {
<strong>{{ content }}</strong> <strong>{{ content }}</strong>
</template> </template>
</gl-sprintf> </gl-sprintf>
<gl-sprintf
v-else
:message="
__(
`%{strongStart}${labelName}%{strongEnd} will be permanently deleted. This cannot be undone.`,
)
"
>
<template #strong="{ content }">
<strong>{{ content }}</strong>
</template>
</gl-sprintf>
<template #modal-footer> <template #modal-footer>
<gl-button category="secondary" @click="closeModal">{{ __('Cancel') }}</gl-button> <gl-button category="secondary" @click="closeModal">{{ __('Cancel') }}</gl-button>
<gl-button <gl-button
......
import Labels from '~/labels/labels'; import Labels from '~/labels/labels';
import { initDeleteLabelModal } from '~/labels';
new Labels(); // eslint-disable-line no-new new Labels(); // eslint-disable-line no-new
initDeleteLabelModal();
import Labels from 'ee_else_ce/labels/labels'; import Labels from 'ee_else_ce/labels/labels';
import { initDeleteLabelModal } from '~/labels';
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new Labels(); new Labels();
initDeleteLabelModal();
import Labels from 'ee_else_ce/labels/labels'; import Labels from 'ee_else_ce/labels/labels';
import { initDeleteLabelModal } from '~/labels';
new Labels(); // eslint-disable-line no-new new Labels(); // eslint-disable-line no-new
initDeleteLabelModal();
...@@ -260,7 +260,7 @@ class Label < ApplicationRecord ...@@ -260,7 +260,7 @@ class Label < ApplicationRecord
attributes attributes
end end
def present(attributes) def present(attributes = {})
super(**attributes.merge(presenter_class: ::LabelPresenter)) super(**attributes.merge(presenter_class: ::LabelPresenter))
end end
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
class LabelPresenter < Gitlab::View::Presenter::Delegated class LabelPresenter < Gitlab::View::Presenter::Delegated
presents ::Label, as: :label presents ::Label, as: :label
delegate :name, :full_name, to: :label_subject, prefix: :subject delegate :name, :full_name, to: :label_subject, prefix: :subject, allow_nil: true
delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `Label#subject`. delegator_override :subject # TODO: Fix `Gitlab::View::Presenter::Delegated#subject` not to override `Label#subject`.
...@@ -10,6 +10,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated ...@@ -10,6 +10,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated
case label case label
when GroupLabel then edit_group_label_path(label.group, label) when GroupLabel then edit_group_label_path(label.group, label)
when ProjectLabel then edit_project_label_path(label.project, label) when ProjectLabel then edit_project_label_path(label.project, label)
else edit_admin_label_path(label)
end end
end end
...@@ -17,6 +18,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated ...@@ -17,6 +18,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated
case label case label
when GroupLabel then group_label_path(label.group, label) when GroupLabel then group_label_path(label.group, label)
when ProjectLabel then project_label_path(label.project, label) when ProjectLabel then project_label_path(label.project, label)
else admin_label_path(label)
end end
end end
...@@ -43,7 +45,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated ...@@ -43,7 +45,7 @@ class LabelPresenter < Gitlab::View::Presenter::Delegated
end end
def label_subject def label_subject
@label_subject ||= label.subject @label_subject ||= label.subject if label.respond_to?(:subject)
end end
private private
......
...@@ -26,9 +26,14 @@ ...@@ -26,9 +26,14 @@
%br %br
= _("Or you can choose one of the suggested colors below") = _("Or you can choose one of the suggested colors below")
= render_suggested_colors = render_suggested_colors
.form-actions .gl-display-flex.gl-justify-content-space-between.gl-p-5.gl-bg-gray-10.gl-border-t-solid.gl-border-t-gray-100.gl-border-t-1
%div
- if @label.persisted? - if @label.persisted?
= f.submit _('Save changes'), class: 'btn gl-button btn-confirm js-save-button' = f.submit _('Save changes'), class: 'btn gl-button btn-confirm js-save-button'
- else - else
= f.submit _('Create label'), class: 'btn gl-button btn-confirm js-save-button qa-label-create-button' = f.submit _('Create label'), class: 'btn gl-button btn-confirm js-save-button qa-label-create-button'
= link_to _('Cancel'), back_path, class: 'btn gl-button btn-default btn-cancel' = link_to _('Cancel'), back_path, class: 'btn gl-button btn-default btn-cancel'
- if @label.persisted?
- presented_label = @label.present
%button.btn.btn-danger.gl-button.btn-danger-secondary.js-delete-label-modal-button{ type: 'button', data: { label_name: presented_label.name, subject_name: presented_label.subject_name, destroy_path: presented_label.destroy_path } }
%span.gl-button-text= _('Delete')
...@@ -7,13 +7,12 @@ type: reference ...@@ -7,13 +7,12 @@ type: reference
# Labels administration **(FREE SELF)** # Labels administration **(FREE SELF)**
In the Admin Area, you can manage labels for the GitLab instance. For more details, see [Labels](../project/labels.md). To manage labels for the GitLab instance, select **Labels** (**{labels}**) from the Admin Area sidebar. For more details on how to manage labels, see [Labels](../project/labels.md).
## Default Labels Labels created in the Admin Area are automatically added to new projects.
Updating or adding labels in the Admin Area does not modify labels in existing projects.
Labels created in the Admin Area become available to each _new_ project. ![Default label set](img/admin_labels_v14_7.png)
![Default label set](img/admin_labels.png)
<!-- ## Troubleshooting <!-- ## Troubleshooting
......
...@@ -90,9 +90,10 @@ label section of the right sidebar of an issue or a merge request: ...@@ -90,9 +90,10 @@ label section of the right sidebar of an issue or a merge request:
color value for a specific color. color value for a specific color.
1. Click **Create**. 1. Click **Create**.
Once created, you can edit a label by clicking the pencil (**{pencil}**), or delete To edit a label after you create it, select (**{pencil}**).
a label by clicking the three dots (**{ellipsis_v}**) next to the **Subscribe** button
and selecting **Delete**. To delete a project label, select (**{ellipsis_v}**) next to the **Subscribe** button
and select **Delete** or select **Delete** when you edit a label.
WARNING: WARNING:
If you delete a label, it is permanently deleted. All references to the label are removed from the system and you cannot undo the deletion. If you delete a label, it is permanently deleted. All references to the label are removed from the system and you cannot undo the deletion.
......
...@@ -42,4 +42,6 @@ FactoryBot.define do ...@@ -42,4 +42,6 @@ FactoryBot.define do
factory :group_label, traits: [:base_label] do factory :group_label, traits: [:base_label] do
group group
end end
factory :admin_label, traits: [:base_label], class: 'Label'
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'admin issues labels' do RSpec.describe 'admin issues labels' do
include Spec::Support::Helpers::ModalHelpers
let!(:bug_label) { Label.create!(title: 'bug', template: true) } let!(:bug_label) { Label.create!(title: 'bug', template: true) }
let!(:feature_label) { Label.create!(title: 'feature', template: true) } let!(:feature_label) { Label.create!(title: 'feature', template: true) }
...@@ -99,5 +101,19 @@ RSpec.describe 'admin issues labels' do ...@@ -99,5 +101,19 @@ RSpec.describe 'admin issues labels' do
expect(page).to have_content('fix') expect(page).to have_content('fix')
end end
end end
it 'allows user to delete label', :js do
visit edit_admin_label_path(bug_label)
click_button 'Delete'
within_modal do
expect(page).to have_content("#{bug_label.title} will be permanently deleted. This cannot be undone.")
click_link 'Delete label'
end
expect(page).to have_content('Label was removed')
end
end end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe 'Edit group label' do RSpec.describe 'Edit group label' do
include Spec::Support::Helpers::ModalHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:label) { create(:group_label, group: group) } let(:label) { create(:group_label, group: group) }
...@@ -20,4 +22,16 @@ RSpec.describe 'Edit group label' do ...@@ -20,4 +22,16 @@ RSpec.describe 'Edit group label' do
expect(current_path).to eq(root_path) expect(current_path).to eq(root_path)
expect(label.reload.title).to eq('new label name') expect(label.reload.title).to eq('new label name')
end end
it 'allows user to delete label', :js do
click_button 'Delete'
within_modal do
expect(page).to have_content("#{label.title} will be permanently deleted from #{group.name}. This cannot be undone.")
click_link 'Delete label'
end
expect(page).to have_content("#{label.title} deleted permanently")
end
end end
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require "spec_helper" require "spec_helper"
RSpec.describe "User edits labels" do RSpec.describe "User edits labels" do
include Spec::Support::Helpers::ModalHelpers
let_it_be(:project) { create(:project_empty_repo, :public) } let_it_be(:project) { create(:project_empty_repo, :public) }
let_it_be(:label) { create(:label, project: project) } let_it_be(:label) { create(:label, project: project) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
...@@ -24,4 +26,16 @@ RSpec.describe "User edits labels" do ...@@ -24,4 +26,16 @@ RSpec.describe "User edits labels" do
expect(page).to have_content(new_title).and have_no_content(label.title) expect(page).to have_content(new_title).and have_no_content(label.title)
end end
end end
it 'allows user to delete label', :js do
click_button 'Delete'
within_modal do
expect(page).to have_content("#{label.title} will be permanently deleted from #{project.name}. This cannot be undone.")
click_link 'Delete label'
end
expect(page).to have_content('Label was removed')
end
end end
...@@ -13,6 +13,10 @@ describe('DeleteLabelModal', () => { ...@@ -13,6 +13,10 @@ describe('DeleteLabelModal', () => {
subjectName: 'GitLab Org', subjectName: 'GitLab Org',
destroyPath: `${TEST_HOST}/2`, destroyPath: `${TEST_HOST}/2`,
}, },
{
labelName: 'admin label',
destroyPath: `${TEST_HOST}/3`,
},
]; ];
beforeEach(() => { beforeEach(() => {
...@@ -22,8 +26,12 @@ describe('DeleteLabelModal', () => { ...@@ -22,8 +26,12 @@ describe('DeleteLabelModal', () => {
const button = document.createElement('button'); const button = document.createElement('button');
button.setAttribute('class', 'js-delete-label-modal-button'); button.setAttribute('class', 'js-delete-label-modal-button');
button.setAttribute('data-label-name', x.labelName); button.setAttribute('data-label-name', x.labelName);
button.setAttribute('data-subject-name', x.subjectName);
button.setAttribute('data-destroy-path', x.destroyPath); button.setAttribute('data-destroy-path', x.destroyPath);
if (x.subjectName) {
button.setAttribute('data-subject-name', x.subjectName);
}
button.innerHTML = 'Action'; button.innerHTML = 'Action';
buttonContainer.appendChild(button); buttonContainer.appendChild(button);
}); });
...@@ -62,6 +70,7 @@ describe('DeleteLabelModal', () => { ...@@ -62,6 +70,7 @@ describe('DeleteLabelModal', () => {
index index
${0} ${0}
${1} ${1}
${2}
`(`when multiple buttons exist`, ({ index }) => { `(`when multiple buttons exist`, ({ index }) => {
beforeEach(() => { beforeEach(() => {
initDeleteLabelModal(); initDeleteLabelModal();
...@@ -69,14 +78,22 @@ describe('DeleteLabelModal', () => { ...@@ -69,14 +78,22 @@ describe('DeleteLabelModal', () => {
}); });
it('correct props are passed to gl-modal', () => { it('correct props are passed to gl-modal', () => {
expect(findModal().querySelector('.modal-title').innerHTML).toContain( const button = buttons[index];
buttons[index].labelName,
expect(findModal().querySelector('.modal-title').innerHTML).toContain(button.labelName);
if (button.subjectName) {
expect(findModal().querySelector('.modal-body').textContent).toContain(
`${button.labelName} will be permanently deleted from ${button.subjectName}. This cannot be undone.`,
); );
expect(findModal().querySelector('.modal-body').innerHTML).toContain( } else {
buttons[index].subjectName, expect(findModal().querySelector('.modal-body').textContent).toContain(
`${button.labelName} will be permanently deleted. This cannot be undone.`,
); );
}
expect(findModal().querySelector('.modal-footer .btn-danger').href).toContain( expect(findModal().querySelector('.modal-footer .btn-danger').href).toContain(
buttons[index].destroyPath, button.destroyPath,
); );
}); });
}); });
......
...@@ -10,6 +10,7 @@ RSpec.describe LabelPresenter do ...@@ -10,6 +10,7 @@ RSpec.describe LabelPresenter do
let(:label) { build_stubbed(:label, project: project).present(issuable_subject: project) } let(:label) { build_stubbed(:label, project: project).present(issuable_subject: project) }
let(:group_label) { build_stubbed(:group_label, group: group).present(issuable_subject: project) } let(:group_label) { build_stubbed(:group_label, group: group).present(issuable_subject: project) }
let(:admin_label) { build_stubbed(:admin_label).present(issuable_subject: nil) }
describe '#edit_path' do describe '#edit_path' do
context 'with group label' do context 'with group label' do
...@@ -23,6 +24,12 @@ RSpec.describe LabelPresenter do ...@@ -23,6 +24,12 @@ RSpec.describe LabelPresenter do
it { is_expected.to eq(edit_project_label_path(project, label)) } it { is_expected.to eq(edit_project_label_path(project, label)) }
end end
context 'with an admin label' do
subject { admin_label.edit_path }
it { is_expected.to eq(edit_admin_label_path(admin_label)) }
end
end end
describe '#destroy_path' do describe '#destroy_path' do
...@@ -37,6 +44,12 @@ RSpec.describe LabelPresenter do ...@@ -37,6 +44,12 @@ RSpec.describe LabelPresenter do
it { is_expected.to eq(project_label_path(project, label)) } it { is_expected.to eq(project_label_path(project, label)) }
end end
context 'with an admin label' do
subject { admin_label.destroy_path }
it { is_expected.to eq(admin_label_path(admin_label)) }
end
end end
describe '#filter_path' do describe '#filter_path' do
...@@ -91,6 +104,12 @@ RSpec.describe LabelPresenter do ...@@ -91,6 +104,12 @@ RSpec.describe LabelPresenter do
it { is_expected.to eq(label.project.name) } it { is_expected.to eq(label.project.name) }
end end
context 'with an admin label' do
subject { admin_label.subject_name }
it { is_expected.to be_nil }
end
end end
describe '#subject_full_name' do describe '#subject_full_name' do
...@@ -105,5 +124,11 @@ RSpec.describe LabelPresenter do ...@@ -105,5 +124,11 @@ RSpec.describe LabelPresenter do
it { is_expected.to eq(label.project.full_name) } it { is_expected.to eq(label.project.full_name) }
end end
context 'with an admin label' do
subject { admin_label.subject_full_name }
it { is_expected.to be_nil }
end
end end
end end
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