Commit 2fcca0f5 authored by Miranda Fluharty's avatar Miranda Fluharty Committed by Scott Hampton

UX improvements for inline code quality

Apply gl-stroke-white class to code quality gutter icons
Clicking on a code quality gutter icon opens a modal with more details
parent 5c83b952
...@@ -288,6 +288,7 @@ export default { ...@@ -288,6 +288,7 @@ export default {
<div class="diff-td line-codequality left-side" :class="[...parallelViewLeftLineType]"> <div class="diff-td line-codequality left-side" :class="[...parallelViewLeftLineType]">
<code-quality-gutter-icon <code-quality-gutter-icon
v-if="showCodequalityLeft" v-if="showCodequalityLeft"
:file-path="filePath"
:codequality="line.left.codequality" :codequality="line.left.codequality"
/> />
</div> </div>
...@@ -403,6 +404,7 @@ export default { ...@@ -403,6 +404,7 @@ export default {
> >
<code-quality-gutter-icon <code-quality-gutter-icon
v-if="showCodequalityRight" v-if="showCodequalityRight"
:file-path="filePath"
:codequality="line.right.codequality" :codequality="line.right.codequality"
/> />
</div> </div>
......
<script> <script>
import { GlTooltipDirective, GlIcon } from '@gitlab/ui'; import { GlTooltipDirective, GlIcon, GlModalDirective, GlModal } from '@gitlab/ui';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue';
import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants'; import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants';
const i18n = {
tooltip: s__('CodeQuality|Code quality: %{severity} - %{description}'),
};
export default { export default {
components: { components: {
GlIcon, GlIcon,
GlModal,
CodequalityIssueBody,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
GlModal: GlModalDirective,
},
modalCloseButton: {
text: __('Close'),
attributes: [{ variant: 'info' }],
},
i18n: {
modalTitle: s__('CodeQuality|New code quality degradations on this line'),
}, },
props: { props: {
filePath: {
type: String,
required: true,
},
codequality: { codequality: {
type: Array, type: Array,
required: false, required: false,
...@@ -17,8 +38,11 @@ export default { ...@@ -17,8 +38,11 @@ export default {
}, },
}, },
computed: { computed: {
description() { tooltipText() {
return this.codequality[0].description; return sprintf(i18n.tooltip, {
severity: capitalizeFirstCharacter(this.severity),
description: this.codequality[0].description,
});
}, },
severity() { severity() {
return this.codequality[0].severity; return this.codequality[0].severity;
...@@ -29,12 +53,44 @@ export default { ...@@ -29,12 +53,44 @@ export default {
severityIcon() { severityIcon() {
return SEVERITY_ICONS[this.severity] || SEVERITY_ICONS.unknown; return SEVERITY_ICONS[this.severity] || SEVERITY_ICONS.unknown;
}, },
line() {
return this.codequality[0].line;
},
degradations() {
return this.codequality.map((degradation) => {
return {
name: degradation.description,
severity: degradation.severity,
path: this.filePath,
line: this.line,
};
});
},
}, },
}; };
</script> </script>
<template> <template>
<div v-gl-tooltip.hover :title="description"> <div>
<gl-icon :size="12" :name="severityIcon" :class="severityClass" /> <gl-icon
v-gl-modal="`codequality-${filePath}:${line}`"
v-gl-tooltip.hover
:title="tooltipText"
:size="12"
:name="severityIcon"
:class="severityClass"
class="gl-hover-cursor-pointer codequality-severity-icon"
/>
<gl-modal
:modal-id="`codequality-${filePath}:${line}`"
:title="$options.i18n.modalTitle"
:action-primary="$options.modalCloseButton"
>
<codequality-issue-body
v-for="(degradation, index) in degradations"
:key="index"
:issue="degradation"
/>
</gl-modal>
</div> </div>
</template> </template>
...@@ -11,4 +11,8 @@ ...@@ -11,4 +11,8 @@
grid-template-columns: 50px 50px 8px 20px 1fr; grid-template-columns: 50px 50px 8px 20px 1fr;
} }
} }
.codequality-severity-icon {
filter: drop-shadow(0 1px 0.5px #fff) drop-shadow(1px 0 0.5px #fff) drop-shadow(0 -1px 0.5px #fff) drop-shadow(-1px 0 0.5px #fff);
}
} }
...@@ -4,6 +4,7 @@ import Vue from 'vue'; ...@@ -4,6 +4,7 @@ import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import CodeQualityGutterIcon from 'ee/diffs/components/code_quality_gutter_icon.vue'; import CodeQualityGutterIcon from 'ee/diffs/components/code_quality_gutter_icon.vue';
import createDiffsStore from 'jest/diffs/create_diffs_store'; import createDiffsStore from 'jest/diffs/create_diffs_store';
import CodequalityIssueBody from '~/reports/codequality_report/components/codequality_issue_body.vue';
import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants'; import { SEVERITY_CLASSES, SEVERITY_ICONS } from '~/reports/codequality_report/constants';
Vue.use(Vuex); Vue.use(Vuex);
...@@ -15,6 +16,23 @@ describe('EE CodeQualityGutterIcon', () => { ...@@ -15,6 +16,23 @@ describe('EE CodeQualityGutterIcon', () => {
const findIcon = () => wrapper.findComponent(GlIcon); const findIcon = () => wrapper.findComponent(GlIcon);
const defaultProps = {
filePath: 'index.js',
codequality: [
{
severity: 'major',
description:
'Function `aVeryLongFunction` has 52 lines of code (exceeds 25 allowed). Consider refactoring.',
line: 3,
},
{
severity: 'minor',
description: 'Arrow function has too many statements (52). Maximum allowed is 30.',
line: 3,
},
],
};
const createComponent = (props = {}, extendStore = () => {}) => { const createComponent = (props = {}, extendStore = () => {}) => {
store = createDiffsStore(); store = createDiffsStore();
store.state.diffs.codequalityDiff = codequalityDiff; store.state.diffs.codequalityDiff = codequalityDiff;
...@@ -22,7 +40,7 @@ describe('EE CodeQualityGutterIcon', () => { ...@@ -22,7 +40,7 @@ describe('EE CodeQualityGutterIcon', () => {
extendStore(store); extendStore(store);
wrapper = shallowMount(CodeQualityGutterIcon, { wrapper = shallowMount(CodeQualityGutterIcon, {
propsData: { ...props }, propsData: { ...defaultProps, ...props },
store, store,
}); });
}; };
...@@ -43,10 +61,51 @@ describe('EE CodeQualityGutterIcon', () => { ...@@ -43,10 +61,51 @@ describe('EE CodeQualityGutterIcon', () => {
createComponent({ codequality: [{ severity }] }); createComponent({ codequality: [{ severity }] });
expect(findIcon().exists()).toBe(true); expect(findIcon().exists()).toBe(true);
expect(findIcon().attributes()).toEqual({ expect(findIcon().attributes()).toMatchObject({
class: SEVERITY_CLASSES[severity], class: `gl-hover-cursor-pointer codequality-severity-icon ${SEVERITY_CLASSES[severity]}`,
name: SEVERITY_ICONS[severity], name: SEVERITY_ICONS[severity],
size: '12', size: '12',
}); });
}); });
describe('code quality modal', () => {
beforeEach(() => {
createComponent();
});
it('opens a code quality modal on click', () => {
const modalId = 'codequality-index.js:3';
const rootEmit = jest.spyOn(wrapper.vm.$root, '$emit');
wrapper.findComponent(GlIcon).trigger('click');
expect(rootEmit.mock.calls[0]).toContainEqual(modalId);
});
it('passes the issue data into the issue components correctly', () => {
const issueProps = wrapper
.findAllComponents(CodequalityIssueBody)
.wrappers.map((w) => w.props());
expect(issueProps).toEqual([
{
issue: {
path: defaultProps.filePath,
severity: defaultProps.codequality[0].severity,
name: defaultProps.codequality[0].description,
line: defaultProps.codequality[0].line,
},
status: 'neutral',
},
{
issue: {
path: defaultProps.filePath,
severity: defaultProps.codequality[1].severity,
name: defaultProps.codequality[1].description,
line: defaultProps.codequality[1].line,
},
status: 'neutral',
},
]);
});
});
}); });
...@@ -7948,9 +7948,15 @@ msgstr "" ...@@ -7948,9 +7948,15 @@ msgstr ""
msgid "CodeQuality|Code quality" msgid "CodeQuality|Code quality"
msgstr "" msgstr ""
msgid "CodeQuality|Code quality: %{severity} - %{description}"
msgstr ""
msgid "CodeQuality|New code quality degradations in this file" msgid "CodeQuality|New code quality degradations in this file"
msgstr "" msgstr ""
msgid "CodeQuality|New code quality degradations on this line"
msgstr ""
msgid "CodeQuality|Some changes in this file degrade the code quality." msgid "CodeQuality|Some changes in this file degrade the code quality."
msgstr "" 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