Commit 3852d404 authored by Filipa Lacerda's avatar Filipa Lacerda
Browse files

Merge branch 'icons-ci-icons' into 'master'

Convert Icons in CI to SVG Sprite Icons

See merge request gitlab-org/gitlab-ce!14567
......@@ -30,7 +30,7 @@ const utils = {
},
isDropDownParts(target) {
if (!target || target.tagName === 'HTML') return false;
if (!target || !target.hasAttribute || target.tagName === 'HTML') return false;
return target.hasAttribute(DATA_TRIGGER) || target.hasAttribute(DATA_DROPDOWN);
},
};
......
<script>
import getActionIcon from '../../../vue_shared/ci_action_icons';
import tooltip from '../../../vue_shared/directives/tooltip';
import icon from '../../../vue_shared/components/icon.vue';
/**
* Renders either a cancel, retry or play icon pointing to the given path.
......@@ -29,17 +29,18 @@
},
},
components: {
icon,
},
directives: {
tooltip,
},
computed: {
actionIconSvg() {
return getActionIcon(this.actionIcon);
},
cssClass() {
return `js-${gl.text.dasherize(this.actionIcon)}`;
const actionIconDash = gl.text.dasherize(this.actionIcon);
return `${actionIconDash} js-icon-${actionIconDash}`;
},
},
};
......@@ -50,14 +51,9 @@
:data-method="actionMethod"
:title="tooltipText"
:href="link"
class="ci-action-icon-container"
class="ci-action-icon-container ci-action-icon-wrapper"
:class="cssClass"
data-container="body">
<i
class="ci-action-icon-wrapper"
:class="cssClass"
v-html="actionIconSvg"
aria-hidden="true"
/>
<icon :name="actionIcon"/>
</a>
</template>
<script>
import getActionIcon from '../../../vue_shared/ci_action_icons';
import icon from '../../../vue_shared/components/icon.vue';
import tooltip from '../../../vue_shared/directives/tooltip';
/**
......@@ -29,14 +29,12 @@
},
},
directives: {
tooltip,
components: {
icon,
},
computed: {
actionIconSvg() {
return getActionIcon(this.actionIcon);
},
directives: {
tooltip,
},
};
</script>
......@@ -49,7 +47,7 @@
rel="nofollow"
class="ci-action-icon-wrapper js-ci-status-icon"
data-container="body"
v-html="actionIconSvg"
aria-label="Job's action">
<icon :name="actionIcon"/>
</a>
</template>
......@@ -18,7 +18,7 @@
* "group": "success",
* "details_path": "/root/ci-mock/builds/4256",
* "action": {
* "icon": "icon_action_retry",
* "icon": "retry",
* "title": "Retry",
* "path": "/root/ci-mock/builds/4256/retry",
* "method": "post"
......
......@@ -19,7 +19,7 @@
* "group": "success",
* "details_path": "/root/ci-mock/builds/4256",
* "action": {
* "icon": "icon_action_retry",
* "icon": "retry",
* "title": "Retry",
* "path": "/root/ci-mock/builds/4256/retry",
* "method": "post"
......
......@@ -14,7 +14,7 @@
*/
import Flash from '../../flash';
import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
import icon from '../../vue_shared/components/icon.vue';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
......@@ -45,6 +45,7 @@ export default {
components: {
loadingIcon,
icon,
},
updated() {
......@@ -122,8 +123,8 @@ export default {
return `ci-status-icon-${this.stage.status.group}`;
},
svgIcon() {
return borderlessStatusIconEntityMap[this.stage.status.icon];
borderlessIcon() {
return `${this.stage.status.icon}_borderless`;
},
},
};
......@@ -145,9 +146,10 @@ export default {
aria-expanded="false">
<span
v-html="svgIcon"
aria-hidden="true"
:aria-label="stage.title">
<icon
:name="borderlessIcon"/>
</span>
<i
......
import PipelineStage from '../../pipelines/components/stage.vue';
import ciIcon from '../../vue_shared/components/ci_icon.vue';
import { statusIconEntityMap } from '../../vue_shared/ci_status_icons';
import icon from '../../vue_shared/components/icon.vue';
export default {
name: 'MRWidgetPipeline',
......@@ -10,6 +10,7 @@ export default {
components: {
'pipeline-stage': PipelineStage,
ciIcon,
icon,
},
computed: {
hasPipeline() {
......@@ -20,9 +21,6 @@ export default {
return hasCI && !ciStatus;
},
svg() {
return statusIconEntityMap.icon_status_failed;
},
stageText() {
return this.mr.pipeline.details.stages.length > 1 ? 'stages' : 'stage';
},
......@@ -38,8 +36,10 @@ export default {
<template v-if="hasCIError">
<div class="ci-status-icon ci-status-icon-failed ci-error js-ci-error append-right-10">
<span
v-html="svg"
aria-hidden="true"></span>
aria-hidden="true">
<icon
name="status_failed"/>
</span>
</div>
<div class="media-body">
Could not connect to the CI server. Please check your settings and try again
......
import cancelSVG from 'icons/_icon_action_cancel.svg';
import retrySVG from 'icons/_icon_action_retry.svg';
import playSVG from 'icons/_icon_action_play.svg';
import stopSVG from 'icons/_icon_action_stop.svg';
/**
* For the provided action returns the respective SVG
*
* @param {String} action
* @return {SVG|String}
*/
export default function getActionIcon(action) {
const icons = {
icon_action_cancel: cancelSVG,
icon_action_play: playSVG,
icon_action_retry: retrySVG,
icon_action_stop: stopSVG,
};
return icons[action] || '';
}
import BORDERLESS_CANCELED_SVG from 'icons/_icon_status_canceled_borderless.svg';
import BORDERLESS_CREATED_SVG from 'icons/_icon_status_created_borderless.svg';
import BORDERLESS_FAILED_SVG from 'icons/_icon_status_failed_borderless.svg';
import BORDERLESS_MANUAL_SVG from 'icons/_icon_status_manual_borderless.svg';
import BORDERLESS_PENDING_SVG from 'icons/_icon_status_pending_borderless.svg';
import BORDERLESS_RUNNING_SVG from 'icons/_icon_status_running_borderless.svg';
import BORDERLESS_SKIPPED_SVG from 'icons/_icon_status_skipped_borderless.svg';
import BORDERLESS_SUCCESS_SVG from 'icons/_icon_status_success_borderless.svg';
import BORDERLESS_WARNING_SVG from 'icons/_icon_status_warning_borderless.svg';
import CANCELED_SVG from 'icons/_icon_status_canceled.svg';
import CREATED_SVG from 'icons/_icon_status_created.svg';
import FAILED_SVG from 'icons/_icon_status_failed.svg';
import MANUAL_SVG from 'icons/_icon_status_manual.svg';
import PENDING_SVG from 'icons/_icon_status_pending.svg';
import RUNNING_SVG from 'icons/_icon_status_running.svg';
import SKIPPED_SVG from 'icons/_icon_status_skipped.svg';
import SUCCESS_SVG from 'icons/_icon_status_success.svg';
import WARNING_SVG from 'icons/_icon_status_warning.svg';
export const borderlessStatusIconEntityMap = {
icon_status_canceled: BORDERLESS_CANCELED_SVG,
icon_status_created: BORDERLESS_CREATED_SVG,
icon_status_failed: BORDERLESS_FAILED_SVG,
icon_status_manual: BORDERLESS_MANUAL_SVG,
icon_status_pending: BORDERLESS_PENDING_SVG,
icon_status_running: BORDERLESS_RUNNING_SVG,
icon_status_skipped: BORDERLESS_SKIPPED_SVG,
icon_status_success: BORDERLESS_SUCCESS_SVG,
icon_status_warning: BORDERLESS_WARNING_SVG,
};
export const statusIconEntityMap = {
icon_status_canceled: CANCELED_SVG,
icon_status_created: CREATED_SVG,
icon_status_failed: FAILED_SVG,
icon_status_manual: MANUAL_SVG,
icon_status_pending: PENDING_SVG,
icon_status_running: RUNNING_SVG,
icon_status_skipped: SKIPPED_SVG,
icon_status_success: SUCCESS_SVG,
icon_status_warning: WARNING_SVG,
};
......@@ -43,7 +43,6 @@
computed: {
cssClass() {
const className = this.status.group;
return className ? `ci-status ci-${className}` : 'ci-status';
},
},
......
<script>
import { statusIconEntityMap } from '../ci_status_icons';
import icon from '../../vue_shared/components/icon.vue';
/**
* Renders CI icon based on API response shared between all places where it is used.
......@@ -30,11 +30,11 @@
},
},
computed: {
statusIconSvg() {
return statusIconEntityMap[this.status.icon];
},
components: {
icon,
},
computed: {
cssClass() {
const status = this.status.group;
return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
......@@ -44,7 +44,8 @@
</script>
<template>
<span
:class="cssClass"
v-html="statusIconSvg">
:class="cssClass">
<icon
:name="status.icon"/>
</span>
</template>
<script>
/* This is a re-usable vue component for rendering a svg sprite
icon
Sample configuration:
<icon
:img-src="userAvatarSrc"
:img-alt="tooltipText"
:tooltip-text="tooltipText"
tooltip-placement="top"
/>
*/
export default {
props: {
name: {
type: String,
required: true,
},
size: {
type: Number,
required: false,
default: 0,
},
cssClasses: {
type: String,
required: false,
default: '',
},
},
computed: {
spriteHref() {
return `${gon.sprite_icons}#${this.name}`;
},
iconSizeClass() {
return this.size ? `s${this.size}` : '';
},
},
};
</script>
<template>
<svg
:class="[iconSizeClass, cssClasses]">
<use
v-bind="{'xlink:href':spriteHref}"/>
</svg>
</template>
......@@ -333,8 +333,10 @@
svg {
position: relative;
top: 2px;
top: 3px;
margin-right: 3px;
width: 14px;
height: 14px;
}
}
......@@ -348,9 +350,10 @@
svg {
position: relative;
top: 2px;
top: 3px;
margin-right: 3px;
height: 13px;
height: 14px;
width: 14px;
}
a {
......@@ -369,7 +372,7 @@
.build-job {
position: relative;
.fa-arrow-right {
.icon-arrow-right {
position: absolute;
left: 15px;
top: 20px;
......@@ -379,7 +382,7 @@
&.active {
font-weight: $gl-font-weight-bold;
.fa-arrow-right {
.icon-arrow-right {
display: block;
}
}
......@@ -392,8 +395,7 @@
background-color: $row-hover;
}
.fa-refresh {
font-size: 13px;
.icon-retry {
margin-left: 3px;
}
}
......
......@@ -165,8 +165,9 @@
z-index: 300;
}
.ci-action-icon-wrapper {
line-height: 16px;
.ci-action-icon-wrapper svg {
width: 16px;
height: 16px;
}
}
......
......@@ -452,7 +452,7 @@
}
// Action Icons in big pipeline-graph nodes
.ci-action-icon-container .ci-action-icon-wrapper {
.ci-action-icon-container.ci-action-icon-wrapper {
height: 30px;
width: 30px;
background: $white-light;
......@@ -468,8 +468,18 @@
svg {
fill: $gl-text-color-secondary;
position: relative;
left: -1px;
top: -1px;
left: 5px;
top: 2px;
width: 18px;
height: 18px;
}
&.play {
svg {
width: #{$ci-action-icon-size - 8};
height: #{$ci-action-icon-size - 8};
left: 8px;
}
}
&:hover svg {
......@@ -721,17 +731,49 @@ button.mini-pipeline-graph-dropdown-toggle {
svg {
fill: $gl-text-color-secondary;
width: $ci-action-icon-size;
height: $ci-action-icon-size;
left: -6px;
width: #{$ci-action-icon-size - 6};
height: #{$ci-action-icon-size - 6};
left: -3px;
position: relative;
top: -3px;
top: -2px;
}
&:hover svg,
&:focus svg {
fill: $gl-text-color;
}
&.icon-action-retry,
&.icon-action-play {
svg {
width: #{$ci-action-icon-size - 6};
height: #{$ci-action-icon-size - 6};
left: 8px;
}
}
svg.icon-action-stop,
svg.icon-action-cancel {
width: 12px;
height: 12px;
top: 1px;
left: -1px;
}
svg.icon-action-play {
width: 11px;
height: 11px;
top: 1px;
left: 1px;
}
svg.icon-action-retry {
width: 16px;
height: 16px;
top: 0;
left: -3px;
}
}
// link to the build
......
......@@ -63,34 +63,34 @@ def ci_status_for_statuseable(subject)
def ci_icon_for_status(status)
if detailed_status?(status)
return custom_icon(status.icon)
return sprite_icon(status.icon)
end
icon_name =
case status
when 'success'
'icon_status_success'
'status_success'
when 'success_with_warnings'
'icon_status_warning'
'status_warning'
when 'failed'
'icon_status_failed'
'status_failed'
when 'pending'
'icon_status_pending'
'status_pending'
when 'running'
'icon_status_running'
'status_running'
when 'play'
'icon_play'
'play'
when 'created'
'icon_status_created'
'status_created'
when 'skipped'
'icon_status_skipped'
'status_skipped'
when 'manual'
'icon_status_manual'
'status_manual'
else
'icon_status_canceled'
'status_canceled'
end
custom_icon(icon_name)
sprite_icon(icon_name, size: 16)
end
def pipeline_status_cache_key(pipeline_status)
......
......@@ -5,9 +5,9 @@
- if link && status.has_details?
= link_to status.details_path, class: css_classes, title: title do
= custom_icon(status.icon)
= sprite_icon(status.icon)
= status.text
- else
%span{ class: css_classes, title: title }
= custom_icon(status.icon)
= sprite_icon(status.icon)
= status.text
......@@ -7,13 +7,13 @@
- if status.has_details?
= link_to status.details_path, class: 'mini-pipeline-graph-dropdown-item', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= custom_icon(status.icon)
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name
- else
.menu-item.mini-pipeline-graph-dropdown-item{ data: { toggle: 'tooltip', title: tooltip, container: 'body' } }
%span{ class: klass }= custom_icon(status.icon)
%span{ class: klass }= sprite_icon(status.icon)
%span.ci-build-text= subject.name
- if status.has_action?
= link_to status.action_path, class: 'ci-action-icon-wrapper js-ci-action-icon', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
= custom_icon(status.action_icon)
= link_to status.action_path, class: "ci-action-icon-wrapper js-ci-action-icon", method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
= sprite_icon(status.action_icon, css_class: "icon-action-#{status.action_icon}")
......@@ -91,7 +91,7 @@
- builds.select{|build| build.status == build_status}.each do |build|
.build-job{ class: sidebar_build_class(build, @build), data: { stage: build.stage } }
= link_to project_job_path(@project, build) do
= icon('arrow-right')
= sprite_icon('arrow-right', size:16, css_class: 'icon-arrow-right')
%span{ class: "ci-status-icon-#{build.status}" }
= ci_icon_for_status(build.status)
%span
......@@ -100,4 +100,5 @@
- else
= build.id
- if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
%span.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
= sprite_icon('retry', size:16, css_class: 'icon-retry')