Commit 52786f4b authored by Dhiraj Bodicherla's avatar Dhiraj Bodicherla

Updated deploy instances color scheme

The definition for deploy instances in environment
page have been updated long ago but the styling is
stale. This MR updates the style with new color scheme
parent daff9851
---
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