Commit 340515b7 authored by Brett Walker's avatar Brett Walker Committed by Natalia Tepluhina

Display issue type icon on issue creation form

parent 46982343
<script>
import { GlFormGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlFormGroup, GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
import { capitalize } from 'lodash';
import { __ } from '~/locale';
import { IssuableTypes } from '../../constants';
......@@ -15,6 +15,7 @@ export default {
IssuableTypes,
components: {
GlFormGroup,
GlIcon,
GlDropdown,
GlDropdownItem,
},
......@@ -72,6 +73,7 @@ export default {
is-check-item
@click="updateIssueType(type.value)"
>
<gl-icon :name="type.icon" />
{{ type.text }}
</gl-dropdown-item>
</gl-dropdown>
......
......@@ -28,8 +28,8 @@ export const STATUS_PAGE_PUBLISHED = __('Published on status page');
export const JOIN_ZOOM_MEETING = __('Join Zoom meeting');
export const IssuableTypes = [
{ value: 'issue', text: __('Issue') },
{ value: 'incident', text: __('Incident') },
{ value: 'issue', text: __('Issue'), icon: 'issue-type-issue' },
{ value: 'incident', text: __('Incident'), icon: 'issue-type-incident' },
];
export const IssueTypePath = 'issues';
......
......@@ -48,6 +48,14 @@ module IssuesHelper
end
end
def work_item_type_icon(issue_type)
if WorkItem::Type.base_types.include?(issue_type)
"issue-type-#{issue_type.to_s.dasherize}"
else
'issue-type-issue'
end
end
def confidential_icon(issue)
sprite_icon('eye-slash', css_class: 'gl-vertical-align-text-bottom') if issue.confidential?
end
......
......@@ -16,14 +16,14 @@
= _("Select type")
%button.dropdown-title-button.dropdown-menu-close.gl-ml-auto{ type: 'button', "aria-label" => _('Close') }
= sprite_icon('close', size: 16, css_class: 'dropdown-menu-close-icon')
.dropdown-content
.dropdown-content{ data: { testid: 'issue-type-select-dropdown' } }
%ul
%li.js-filter-issuable-type
= link_to new_project_issue_path(@project), class: ("is-active" if issuable.issue?) do
= _("Issue")
#{sprite_icon(work_item_type_icon(:issue), css_class: 'gl-icon')} #{_("Issue")}
%li.js-filter-issuable-type{ data: { track: { event: "select_issue_type_incident", label: "select_issue_type_incident_dropdown_option" } } }
= link_to new_project_issue_path(@project, { issuable_template: 'incident', issue: { issue_type: 'incident' } }), class: ("is-active" if issuable.incident?) do
= _("Incident")
#{sprite_icon(work_item_type_icon(:incident), css_class: 'gl-icon')} #{_("Incident")}
#js-type-popover
......
......@@ -6,13 +6,13 @@ RSpec.describe 'New/edit issue', :js do
include ActionView::Helpers::JavaScriptHelper
include FormHelper
let!(:project) { create(:project) }
let!(:user) { create(:user)}
let!(:user2) { create(:user)}
let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) }
let!(:label2) { create(:label, project: project) }
let!(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user)}
let_it_be(:user2) { create(:user)}
let_it_be(:milestone) { create(:milestone, project: project) }
let_it_be(:label) { create(:label, project: project) }
let_it_be(:label2) { create(:label, project: project) }
let_it_be(:issue) { create(:issue, project: project, assignees: [user], milestone: milestone) }
before do
stub_licensed_features(multiple_issue_assignees: false, issue_weights: false)
......@@ -234,6 +234,28 @@ RSpec.describe 'New/edit issue', :js do
expect(page).to have_selector('.atwho-view')
end
describe 'displays issue type options in the dropdown' do
before do
page.within('.issue-form') do
click_button 'Issue'
end
end
it 'correctly displays the Issue type option with an icon', :aggregate_failures do
page.within('[data-testid="issue-type-select-dropdown"]') do
expect(page).to have_selector('[data-testid="issue-type-issue-icon"]')
expect(page).to have_content('Issue')
end
end
it 'correctly displays the Incident type option with an icon', :aggregate_failures do
page.within('[data-testid="issue-type-select-dropdown"]') do
expect(page).to have_selector('[data-testid="issue-type-incident-icon"]')
expect(page).to have_content('Incident')
end
end
end
describe 'milestone' do
let!(:milestone) { create(:milestone, title: '">&lt;img src=x onerror=alert(document.domain)&gt;', project: project) }
......
import { GlFormGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { GlFormGroup, GlDropdown, GlDropdownItem, GlIcon } from '@gitlab/ui';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
......@@ -35,6 +35,9 @@ describe('Issue type field component', () => {
const findTypeFromGroup = () => wrapper.findComponent(GlFormGroup);
const findTypeFromDropDown = () => wrapper.findComponent(GlDropdown);
const findTypeFromDropDownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findTypeFromDropDownItemAt = (at) => findTypeFromDropDownItems().at(at);
const findTypeFromDropDownItemIconAt = (at) =>
findTypeFromDropDownItems().at(at).findComponent(GlIcon);
const createComponent = ({ data } = {}) => {
fakeApollo = createMockApollo([], mockResolvers);
......@@ -60,6 +63,15 @@ describe('Issue type field component', () => {
wrapper.destroy();
});
it.each`
at | text | icon
${0} | ${IssuableTypes[0].text} | ${IssuableTypes[0].icon}
${1} | ${IssuableTypes[1].text} | ${IssuableTypes[1].icon}
`(`renders the issue type $text with an icon in the dropdown`, ({ at, text, icon }) => {
expect(findTypeFromDropDownItemIconAt(at).attributes('name')).toBe(icon);
expect(findTypeFromDropDownItemAt(at).text()).toBe(text);
});
it('renders a form group with the correct label', () => {
expect(findTypeFromGroup().attributes('label')).toBe(i18n.label);
});
......
# frozen_string_literal: true
require "spec_helper"
require 'spec_helper'
RSpec.describe IssuesHelper do
let(:project) { create(:project) }
let(:issue) { create :issue, project: project }
let(:ext_project) { create :redmine_project }
describe '#work_item_type_icon' do
it 'returns icon of all standard base types' do
WorkItem::Type.base_types.each do |type|
expect(work_item_type_icon(type[0])).to eq "issue-type-#{type[0].to_s.dasherize}"
end
end
it 'defaults to issue icon if type is unknown' do
expect(work_item_type_icon('invalid')).to eq 'issue-type-issue'
end
end
describe '#award_user_list' do
it "returns a comma-separated list of the first X users" do
it 'returns a comma-separated list of the first X users' do
user = build_stubbed(:user, name: 'Joe')
awards = Array.new(3, build_stubbed(:award_emoji, user: user))
......@@ -24,7 +36,7 @@ RSpec.describe IssuesHelper do
expect(award_user_list([award], nil)).to eq 'Joe'
end
it "truncates lists" do
it 'truncates lists' do
user = build_stubbed(:user, name: 'Jane')
awards = Array.new(5, build_stubbed(:award_emoji, user: user))
......@@ -32,14 +44,14 @@ RSpec.describe IssuesHelper do
.to eq('Jane, Jane, Jane, and 2 more.')
end
it "displays the current user in front of other users" do
it 'displays the current user in front of other users' do
current_user = build_stubbed(:user)
my_award = build_stubbed(:award_emoji, user: current_user)
award = build_stubbed(:award_emoji, user: build_stubbed(:user, name: 'Jane'))
awards = Array.new(5, award).push(my_award)
expect(award_user_list(awards, current_user, limit: 2))
.to eq("You, Jane, and 4 more.")
.to eq('You, Jane, and 4 more.')
end
end
......@@ -54,19 +66,19 @@ RSpec.describe IssuesHelper do
end
end
it "returns disabled string for unauthenticated user" do
expect(helper.award_state_class(awardable, AwardEmoji.all, nil)).to eq("disabled")
it 'returns disabled string for unauthenticated user' do
expect(helper.award_state_class(awardable, AwardEmoji.all, nil)).to eq('disabled')
end
it "returns disabled for a user that does not have access to the awardable" do
expect(helper.award_state_class(awardable, AwardEmoji.all, build(:user))).to eq("disabled")
it 'returns disabled for a user that does not have access to the awardable' do
expect(helper.award_state_class(awardable, AwardEmoji.all, build(:user))).to eq('disabled')
end
it "returns active string for author" do
expect(helper.award_state_class(awardable, AwardEmoji.all, upvote.user)).to eq("active")
it 'returns active string for author' do
expect(helper.award_state_class(awardable, AwardEmoji.all, upvote.user)).to eq('active')
end
it "is blank for a user that has access to the awardable" do
it 'is blank for a user that has access to the awardable' do
user = build(:user)
expect(helper).to receive(:can?).with(user, :award_emoji, awardable).and_return(true)
......@@ -74,40 +86,40 @@ RSpec.describe IssuesHelper do
end
end
describe "awards_sort" do
it "sorts a hash so thumbsup and thumbsdown are always on top" do
data = { "thumbsdown" => "some value", "lifter" => "some value", "thumbsup" => "some value" }
describe 'awards_sort' do
it 'sorts a hash so thumbsup and thumbsdown are always on top' do
data = { 'thumbsdown' => 'some value', 'lifter' => 'some value', 'thumbsup' => 'some value' }
expect(awards_sort(data).keys).to eq(%w(thumbsup thumbsdown lifter))
end
end
describe "#link_to_discussions_to_resolve" do
describe "passing only a merge request" do
describe '#link_to_discussions_to_resolve' do
describe 'passing only a merge request' do
let(:merge_request) { create(:merge_request) }
it "links just the merge request" do
it 'links just the merge request' do
expected_path = project_merge_request_path(merge_request.project, merge_request)
expect(link_to_discussions_to_resolve(merge_request, nil)).to include(expected_path)
end
it "contains the reference to the merge request" do
it 'contains the reference to the merge request' do
expect(link_to_discussions_to_resolve(merge_request, nil)).to include(merge_request.to_reference)
end
end
describe "when passing a discussion" do
describe 'when passing a discussion' do
let(:diff_note) { create(:diff_note_on_merge_request) }
let(:merge_request) { diff_note.noteable }
let(:discussion) { diff_note.to_discussion }
it "links to the merge request with first note if a single discussion was passed" do
it 'links to the merge request with first note if a single discussion was passed' do
expected_path = Gitlab::UrlBuilder.build(diff_note)
expect(link_to_discussions_to_resolve(merge_request, discussion)).to include(expected_path)
end
it "contains both the reference to the merge request and a mention of the discussion" do
it 'contains both the reference to the merge request and a mention of the discussion' do
expect(link_to_discussions_to_resolve(merge_request, discussion)).to include("#{merge_request.to_reference} (discussion #{diff_note.id})")
end
end
......@@ -235,13 +247,13 @@ RSpec.describe IssuesHelper do
end
describe '#use_startup_call' do
it "returns false when a query param is present" do
it 'returns false when a query param is present' do
allow(controller.request).to receive(:query_parameters).and_return({ foo: 'bar' })
expect(helper.use_startup_call?).to eq(false)
end
it "returns false when user has stored sort preference" do
it 'returns false when user has stored sort preference' do
controller.instance_variable_set(:@sort, 'updated_asc')
expect(helper.use_startup_call?).to eq(false)
......@@ -265,13 +277,13 @@ RSpec.describe IssuesHelper do
it 'returns expected result' do
expected = {
can_create_issue: "true",
can_reopen_issue: "true",
can_report_spam: "false",
can_update_issue: "true",
can_create_issue: 'true',
can_reopen_issue: 'true',
can_report_spam: 'false',
can_update_issue: 'true',
iid: issue.iid,
is_issue_author: "false",
issue_type: "issue",
is_issue_author: 'false',
issue_type: 'issue',
new_issue_path: new_project_issue_path(project),
project_path: project.full_path,
report_abuse_path: new_abuse_report_path(user_id: issue.author.id, ref_url: issue_url(issue)),
......@@ -345,7 +357,7 @@ RSpec.describe IssuesHelper do
end
it 'returns manual ordering class' do
expect(helper.issue_manual_ordering_class).to eq("manual-ordering")
expect(helper.issue_manual_ordering_class).to eq('manual-ordering')
end
context 'when manual sorting disabled' do
......
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