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