Commit af61f3f9 authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '35570-update-deploy-instances-color-scheme' into 'master'

Updated deploy instances color scheme

See merge request gitlab-org/gitlab!20890
parents fe6ff03a 52786f4b
---
title: Update deploy instances color scheme
merge_request: 20890
author:
type: changed
......@@ -110,16 +110,17 @@ export default {
<div v-if="canRenderDeployBoard" class="deploy-board-information">
<section class="deploy-board-status">
<span v-tooltip :title="instanceIsCompletedText">
<span class="percentage text-center text-plain">{{ deployBoardData.completion }}%</span>
<span ref="percentage" class="text-center text-plain gl-font-size-large"
>{{ deployBoardData.completion }}%</span
>
<span class="text text-center text-secondary">{{ __('Complete') }}</span>
</span>
</section>
<section class="deploy-board-instances">
<p class="deploy-board-instances-text text-secondary">
<span>{{ instanceTitle }}</span>
<span class="total-instances">({{ instanceCount }})</span>
</p>
<span class="deploy-board-instances-text text-secondary">
{{ instanceTitle }} ({{ instanceCount }})
</span>
<div class="deploy-board-instances-container d-flex flex-wrap flex-row">
<template v-for="(instance, i) in deployBoardData.instances">
......
......@@ -6,11 +6,11 @@
* Each instance has a state and a tooltip.
* The state needs to be represented in different colors,
* see more information about this in
* https://gitlab.com/gitlab-org/gitlab/uploads/5fff049fd88336d9ee0c6ef77b1ba7e3/monitoring__deployboard--key.png
* https://gitlab.com/gitlab-org/gitlab/uploads/f1f00df6293d30f241dbeaa876a1e939/Screen_Shot_2019-11-26_at_3.35.43_PM.png
*
* An instance can represent a normal deploy or a canary deploy. In the latter we need to provide
* this information in the tooltip and the colors.
* Mockup is https://gitlab.com/gitlab-org/gitlab/merge_requests/1551#note_26595150
* Mockup is https://gitlab.com/gitlab-org/gitlab/issues/35570
*/
import tooltip from '~/vue_shared/directives/tooltip';
......@@ -24,12 +24,12 @@ export default {
* Represents the status of the pod. Each state is represented with a different
* color.
* It should be one of the following:
* finished || deploying || failed || ready || preparing || waiting
* succeeded || running || failed || pending || unknown
*/
status: {
type: String,
required: true,
default: 'finished',
default: 'succeeded',
},
tooltipText: {
......@@ -58,13 +58,10 @@ export default {
computed: {
cssClass() {
let cssClassName = `deployment-instance-${this.status}`;
if (!this.stable) {
cssClassName = `${cssClassName} deployment-instance-canary`;
}
return cssClassName;
return {
[`deployment-instance-${this.status}`]: true,
'deployment-instance-canary': !this.stable,
};
},
computedLogPath() {
......
......@@ -3,32 +3,68 @@
height: 15px;
margin: 1px;
border: 1px solid;
border-radius: 3px;
border-radius: $border-radius-small;
position: relative;
&-running {
background-color: $green-100;
border-color: $green-400;
&-succeeded {
background-color: $green-600;
border-color: $green-800;
&:hover {
background-color: $green-300;
border-color: $green-500;
background-color: $green-800;
border-color: $green-950;
}
}
&-succeeded {
background-color: $green-50;
border-color: $green-400;
&-running {
background-color: $green-300;
border-color: $green-600;
&:hover {
background-color: $green-500;
border-color: $green-800;
}
}
&-failed,
&-unknown {
background-color: $red-200;
border-color: $red-500;
&-failed {
background-color: $red-600;
border-color: $red-800;
&::before {
content: '';
border: 1px solid $white-light;
background: $white-light;
transform: rotate(45deg);
position: absolute;
border-radius: 1px;
top: -2px;
bottom: -2px;
}
&:hover {
background-color: $red-800;
border-color: $red-950;
}
}
&-pending {
background-color: $gray-300;
border-color: $gray-700;
&:hover {
background-color: $gray-500;
border-color: $gray-900;
}
}
&-unknown {
background-color: $white-light;
border-color: $border-color;
border-color: $gray-700;
&:hover {
background-color: $white-light;
border-color: $gray-900;
}
}
&.deployment-instance-canary {
......@@ -39,6 +75,7 @@
background-color: $orange-300;
border-radius: 50%;
content: '';
z-index: 1;
}
}
}
......@@ -30,14 +30,6 @@
width: 100%;
}
.deploy-board-instances-text {
font-size: 12px;
}
.deploy-board-instances-container {
margin-top: -8px;
}
.deploy-board-actions {
order: 3;
align-self: center;
......
import Vue from 'vue';
import { mount } from '@vue/test-utils';
import DeployBoard from 'ee/environments/components/deploy_board_component.vue';
import { environment } from 'spec/environments/mock_data';
import { deployBoardMockData } from './mock_data';
describe('Deploy Board', () => {
let DeployBoardComponent;
beforeEach(() => {
DeployBoardComponent = Vue.extend(DeployBoard);
});
let wrapper;
const createComponent = (props = {}) =>
mount(Vue.extend(DeployBoard), {
propsData: {
deployBoardData: deployBoardMockData,
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
...props,
},
sync: false,
});
describe('with valid data', () => {
let component;
beforeEach(() => {
component = new DeployBoardComponent({
propsData: {
deployBoardData: deployBoardMockData,
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
},
}).$mount();
beforeEach(done => {
wrapper = createComponent();
wrapper.vm.$nextTick(done);
});
it('should render percentage with completion value provided', () => {
expect(
component.$el.querySelector('.deploy-board-information .percentage').textContent,
).toEqual(`${deployBoardMockData.completion}%`);
expect(wrapper.vm.$refs.percentage.innerText).toEqual(`${deployBoardMockData.completion}%`);
});
it('should render total instance count', () => {
const renderedTotal = component.$el.querySelector('.deploy-board-instances .total-instances');
const renderedTotal = wrapper.find('.deploy-board-instances-text');
const actualTotal = deployBoardMockData.instances.length;
const output = `${actualTotal > 1 ? 'Instances' : 'Instance'} (${actualTotal})`;
expect(renderedTotal.textContent).toEqual(`(${actualTotal})`);
expect(renderedTotal.text()).toEqual(output);
});
it('should render all instances', () => {
const instances = component.$el.querySelectorAll('.deploy-board-instances-container a');
const instances = wrapper.findAll('.deploy-board-instances-container a');
expect(instances.length).toEqual(deployBoardMockData.instances.length);
expect(
instances[2].classList.contains(
`deployment-instance-${deployBoardMockData.instances[2].status}`,
),
instances.at(1).classes(`deployment-instance-${deployBoardMockData.instances[2].status}`),
).toBe(true);
});
it('should render an abort and a rollback button with the provided url', () => {
const buttons = component.$el.querySelectorAll('.deploy-board-actions a');
const buttons = wrapper.findAll('.deploy-board-actions a');
expect(buttons[0].getAttribute('href')).toEqual(deployBoardMockData.rollback_url);
expect(buttons[1].getAttribute('href')).toEqual(deployBoardMockData.abort_url);
expect(buttons.at(0).attributes('href')).toEqual(deployBoardMockData.rollback_url);
expect(buttons.at(1).attributes('href')).toEqual(deployBoardMockData.abort_url);
});
});
describe('with empty state', () => {
let component;
beforeEach(() => {
component = new DeployBoardComponent({
propsData: {
deployBoardData: {},
isLoading: false,
isEmpty: true,
logsPath: environment.log_path,
},
}).$mount();
beforeEach(done => {
wrapper = createComponent({
deployBoardData: {},
isLoading: false,
isEmpty: true,
logsPath: environment.log_path,
});
wrapper.vm.$nextTick(done);
});
it('should render the empty state', () => {
expect(component.$el.querySelector('.deploy-board-empty-state-svg svg')).toBeDefined();
expect(wrapper.find('.deploy-board-empty-state-svg svg')).toBeDefined();
expect(
component.$el.querySelector(
'.deploy-board-empty-state-text .deploy-board-empty-state-title',
).textContent,
wrapper.find('.deploy-board-empty-state-text .deploy-board-empty-state-title').text(),
).toContain('Kubernetes deployment not found');
});
});
describe('with loading state', () => {
let component;
beforeEach(() => {
component = new DeployBoardComponent({
propsData: {
deployBoardData: {},
isLoading: true,
isEmpty: false,
logsPath: environment.log_path,
},
}).$mount();
beforeEach(done => {
wrapper = createComponent({
deployBoardData: {},
isLoading: true,
isEmpty: false,
logsPath: environment.log_path,
});
wrapper.vm.$nextTick(done);
});
it('should render loading spinner', () => {
expect(component.$el.querySelector('.fa-spin')).toBeDefined();
expect(wrapper.find('.fa-spin')).toBeDefined();
});
});
describe('with hasLegacyAppLabel equal true', () => {
let component;
beforeEach(() => {
component = new DeployBoardComponent({
propsData: {
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
hasLegacyAppLabel: true,
deployBoardData: {},
},
}).$mount();
beforeEach(done => {
wrapper = createComponent({
isLoading: false,
isEmpty: false,
logsPath: environment.log_path,
hasLegacyAppLabel: true,
deployBoardData: {},
});
wrapper.vm.$nextTick(done);
});
it('should render legacy label warning message', () => {
const warningMessage = component.$el.querySelector('.bs-callout-warning');
const warningMessage = wrapper.find('.bs-callout-warning');
expect(warningMessage).toBeTruthy();
expect(warningMessage.innerText).toContain(
expect(warningMessage.text()).toContain(
'Matching on the app label has been removed for deploy boards.',
);
});
......
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