Commit 24a67257 authored by Phil Hughes's avatar Phil Hughes

Adds support for level 1 icons in MR widget extensions

Changes the status icon in the current extension to match
the new design.

Closes https://gitlab.com/gitlab-org/gitlab/-/issues/341042
parent 5eaaf70a
<script> <script>
import { GlButton, GlLoadingIcon, GlIcon, GlLink, GlBadge, GlSafeHtmlDirective } from '@gitlab/ui'; import { GlButton, GlLoadingIcon, GlIcon, GlLink, GlBadge, GlSafeHtmlDirective } from '@gitlab/ui';
import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue'; import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
import StatusIcon from '../mr_widget_status_icon.vue'; import { EXTENSION_ICON_CLASS } from '../../constants';
import StatusIcon from './status_icon.vue';
export const LOADING_STATES = { export const LOADING_STATES = {
collapsedLoading: 'collapsedLoading', collapsedLoading: 'collapsedLoading',
...@@ -45,14 +46,6 @@ export default { ...@@ -45,14 +46,6 @@ export default {
return true; return true;
}, },
statusIconName() { statusIconName() {
if (this.isLoadingSummary) {
return 'loading';
}
if (this.loadingState === LOADING_STATES.collapsedError) {
return 'warning';
}
return this.statusIcon(this.collapsedData); return this.statusIcon(this.collapsedData);
}, },
}, },
...@@ -96,13 +89,18 @@ export default { ...@@ -96,13 +89,18 @@ export default {
}); });
}, },
}, },
EXTENSION_ICON_CLASS,
}; };
</script> </script>
<template> <template>
<section class="media-section mr-widget-border-top"> <section class="media-section mr-widget-border-top">
<div class="media gl-p-5"> <div class="media gl-p-5">
<status-icon :status="statusIconName" class="align-self-center" /> <status-icon
:name="$options.name"
:is-loading="isLoadingSummary"
:icon-name="statusIconName"
/>
<div class="media-body d-flex flex-align-self-center align-items-center"> <div class="media-body d-flex flex-align-self-center align-items-center">
<div class="code-text"> <div class="code-text">
<template v-if="isLoadingSummary"> <template v-if="isLoadingSummary">
......
<script>
import { GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { EXTENSION_ICON_CLASS, EXTENSION_ICONS } from '../../constants';
export default {
components: {
GlLoadingIcon,
GlIcon,
},
props: {
name: {
type: String,
required: true,
},
isLoading: {
type: Boolean,
required: true,
},
iconName: {
type: String,
required: true,
},
},
computed: {
iconAriaLabel() {
const statusLabel = Object.keys(EXTENSION_ICONS).find(
(k) => EXTENSION_ICONS[k] === this.iconName,
);
return `${capitalizeFirstCharacter(statusLabel)} ${this.name}`;
},
},
EXTENSION_ICON_CLASS,
};
</script>
<template>
<div
:class="[$options.EXTENSION_ICON_CLASS[iconName], { 'mr-widget-extension-icon': !isLoading }]"
class="align-self-center gl-rounded-full gl-mr-3 gl-relative gl-p-2"
>
<gl-loading-icon v-if="isLoading" size="md" inline class="gl-display-block" />
<gl-icon
v-else
:name="iconName"
:size="16"
:aria-label="iconAriaLabel"
class="gl-display-block"
/>
</div>
</template>
...@@ -91,4 +91,19 @@ export const stateToTransitionMap = { ...@@ -91,4 +91,19 @@ export const stateToTransitionMap = {
export const stateToComponentMap = { export const stateToComponentMap = {
[states.MERGING]: classStateMap[stateKey.merging], [states.MERGING]: classStateMap[stateKey.merging],
}; };
export const EXTENSION_ICONS = {
failed: 'status-failed',
warning: 'status-alert',
success: 'status-success',
neutral: 'status-neutral',
};
export const EXTENSION_ICON_CLASS = {
[EXTENSION_ICONS.failed]: 'gl-text-red-500',
[EXTENSION_ICONS.warning]: 'gl-text-orange-500',
[EXTENSION_ICONS.success]: 'gl-text-green-500',
[EXTENSION_ICONS.neutral]: 'gl-text-gray-400',
};
export { STATE_MACHINE }; export { STATE_MACHINE };
/* eslint-disable */ /* eslint-disable */
import { EXTENSION_ICONS } from '../constants';
import issuesCollapsedQuery from './issues_collapsed.query.graphql'; import issuesCollapsedQuery from './issues_collapsed.query.graphql';
import issuesQuery from './issues.query.graphql'; import issuesQuery from './issues.query.graphql';
export default { export default {
// Give the extension a name // Give the extension a name
// Make it easier to track in Vue dev tools // Make it easier to track in Vue dev tools
name: 'WidgetIssues', name: 'Issues',
// Add an array of props // Add an array of props
// These then get mapped to values stored in the MR Widget store // These then get mapped to values stored in the MR Widget store
props: ['targetProjectFullPath'], props: ['targetProjectFullPath'],
...@@ -14,12 +15,12 @@ export default { ...@@ -14,12 +15,12 @@ export default {
// Small summary text to be displayed in the collapsed state // Small summary text to be displayed in the collapsed state
// Receives the collapsed data as an argument // Receives the collapsed data as an argument
summary(count) { summary(count) {
return `<strong>${count}</strong> open issue`; return 'Summary text';
}, },
// Status icon to be used next to the summary text // Status icon to be used next to the summary text
// Receives the collapsed data as an argument // Receives the collapsed data as an argument
statusIcon(count) { statusIcon(count) {
return count > 0 ? 'warning' : 'success'; return EXTENSION_ICONS.warning;
}, },
}, },
methods: { methods: {
......
...@@ -1040,3 +1040,17 @@ $tabs-holder-z-index: 250; ...@@ -1040,3 +1040,17 @@ $tabs-holder-z-index: 250;
margin-bottom: 1px; margin-bottom: 1px;
} }
} }
.mr-widget-extension-icon::before {
@include gl-content-empty;
@include gl-absolute;
@include gl-left-0;
@include gl-top-0;
@include gl-opacity-3;
@include gl-border-solid;
@include gl-border-4;
@include gl-rounded-full;
width: 24px;
height: 24px;
}
import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import StatusIcon from '~/vue_merge_request_widget/components/extensions/status_icon.vue';
let wrapper;
function factory(propsData = {}) {
wrapper = shallowMount(StatusIcon, {
propsData,
});
}
describe('MR widget extensions status icon', () => {
afterEach(() => {
wrapper.destroy();
});
it('renders loading icon', () => {
factory({ name: 'test', isLoading: true, iconName: 'status-failed' });
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
it('renders status icon', () => {
factory({ name: 'test', isLoading: false, iconName: 'status-failed' });
expect(wrapper.findComponent(GlIcon).exists()).toBe(true);
expect(wrapper.findComponent(GlIcon).props('name')).toBe('status-failed');
});
it('sets aria-label for status icon', () => {
factory({ name: 'test', isLoading: false, iconName: 'status-failed' });
expect(wrapper.findComponent(GlIcon).props('ariaLabel')).toBe('Failed test');
});
});
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