Commit 290ea7d2 authored by Emily Ring's avatar Emily Ring Committed by Jose Ivan Vargas

Add GlSkeletonLoading to clusters_list loading

Add GlSkeletonLoading to clusters_list vue
Added separate loading for nodes and clusters
Update associated tests
parent f0b5bd18
......@@ -6,6 +6,7 @@ import {
GlLink,
GlLoadingIcon,
GlPagination,
GlSkeletonLoading,
GlSprintf,
GlTable,
} from '@gitlab/ui';
......@@ -21,6 +22,7 @@ export default {
GlLink,
GlLoadingIcon,
GlPagination,
GlSkeletonLoading,
GlSprintf,
GlTable,
},
......@@ -28,7 +30,18 @@ export default {
tooltip,
},
computed: {
...mapState(['clusters', 'clustersPerPage', 'loading', 'page', 'providers', 'totalCulsters']),
...mapState([
'clusters',
'clustersPerPage',
'loadingClusters',
'loadingNodes',
'page',
'providers',
'totalCulsters',
]),
contentAlignClasses() {
return 'gl-display-flex gl-align-items-center gl-justify-content-end gl-justify-content-md-start';
},
currentPage: {
get() {
return this.page;
......@@ -180,14 +193,12 @@ export default {
</script>
<template>
<gl-loading-icon v-if="loading" size="md" class="mt-3" />
<gl-loading-icon v-if="loadingClusters" size="md" class="gl-mt-3" />
<section v-else>
<gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
<template #cell(name)="{ item }">
<div
class="gl-display-flex gl-align-items-center gl-justify-content-end gl-justify-content-md-start js-status"
>
<div :class="[contentAlignClasses, 'js-status']">
<img
:src="selectedProvider(item.provider_type).path"
:alt="selectedProvider(item.provider_type).text"
......@@ -214,6 +225,9 @@ export default {
<template #cell(node_size)="{ item }">
<span v-if="item.nodes">{{ item.nodes.length }}</span>
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
<small v-else class="gl-font-sm gl-font-style-italic gl-text-gray-400">{{
__('Unknown')
}}</small>
......@@ -231,6 +245,8 @@ export default {
>
</gl-sprintf>
</span>
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
</template>
<template #cell(total_memory)="{ item }">
......@@ -245,6 +261,8 @@ export default {
>
</gl-sprintf>
</span>
<gl-skeleton-loading v-else-if="loadingNodes" :lines="1" :class="contentAlignClasses" />
</template>
<template #cell(cluster_type)="{value}">
......
......@@ -19,6 +19,8 @@ const allNodesPresent = (clusters, retryCount) => {
export const fetchClusters = ({ state, commit }) => {
let retryCount = 0;
commit(types.SET_LOADING_NODES, true);
const poll = new Poll({
resource: {
fetchClusters: paginatedEndPoint => axios.get(paginatedEndPoint),
......@@ -34,15 +36,19 @@ export const fetchClusters = ({ state, commit }) => {
const paginationInformation = parseIntPagination(normalizedHeaders);
commit(types.SET_CLUSTERS_DATA, { data, paginationInformation });
commit(types.SET_LOADING_STATE, false);
commit(types.SET_LOADING_CLUSTERS, false);
if (allNodesPresent(data.clusters, retryCount)) {
poll.stop();
commit(types.SET_LOADING_NODES, false);
}
}
} catch (error) {
poll.stop();
commit(types.SET_LOADING_CLUSTERS, false);
commit(types.SET_LOADING_NODES, false);
Sentry.withScope(scope => {
scope.setTag('javascript_clusters_list', 'fetchClustersSuccessCallback');
Sentry.captureException(error);
......@@ -52,7 +58,8 @@ export const fetchClusters = ({ state, commit }) => {
errorCallback: response => {
poll.stop();
commit(types.SET_LOADING_STATE, false);
commit(types.SET_LOADING_CLUSTERS, false);
commit(types.SET_LOADING_NODES, false);
flash(__('Clusters|An error occurred while loading clusters'));
Sentry.withScope(scope => {
......
export const SET_CLUSTERS_DATA = 'SET_CLUSTERS_DATA';
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
export const SET_LOADING_CLUSTERS = 'SET_LOADING_CLUSTERS';
export const SET_LOADING_NODES = 'SET_LOADING_NODES';
export const SET_PAGE = 'SET_PAGE';
import * as types from './mutation_types';
export default {
[types.SET_LOADING_STATE](state, value) {
state.loading = value;
[types.SET_LOADING_CLUSTERS](state, value) {
state.loadingClusters = value;
},
[types.SET_LOADING_NODES](state, value) {
state.loadingNodes = value;
},
[types.SET_CLUSTERS_DATA](state, { data, paginationInformation }) {
Object.assign(state, {
......
export default (initialState = {}) => ({
endpoint: initialState.endpoint,
hasAncestorClusters: false,
loading: true,
clusters: [],
clustersPerPage: 0,
loadingClusters: true,
loadingNodes: true,
page: 1,
providers: {
aws: { path: initialState.imgTagsAwsPath, text: initialState.imgTagsAwsText },
......
---
title: Add skeleton loader to cluster list
merge_request: 34090
author:
type: changed
......@@ -4,7 +4,7 @@ import ClusterStore from '~/clusters_list/store';
import MockAdapter from 'axios-mock-adapter';
import { apiData } from '../mock_data';
import { mount } from '@vue/test-utils';
import { GlLoadingIcon, GlTable, GlPagination } from '@gitlab/ui';
import { GlLoadingIcon, GlPagination, GlSkeletonLoading, GlTable } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
describe('Clusters', () => {
......@@ -64,7 +64,7 @@ describe('Clusters', () => {
describe('clusters table', () => {
describe('when data is loading', () => {
beforeEach(() => {
wrapper.vm.$store.state.loading = true;
wrapper.vm.$store.state.loadingClusters = true;
return wrapper.vm.$nextTick();
});
......@@ -131,6 +131,33 @@ describe('Clusters', () => {
});
describe('nodes present', () => {
describe('nodes while loading', () => {
it.each`
nodeSize | lineNumber
${null} | ${0}
${'1'} | ${1}
${'2'} | ${2}
${'1'} | ${3}
${'1'} | ${4}
${null} | ${5}
`('renders node size for each cluster', ({ nodeSize, lineNumber }) => {
const sizes = findTable().findAll('td:nth-child(3)');
const size = sizes.at(lineNumber);
if (nodeSize) {
expect(size.text()).toBe(nodeSize);
} else {
expect(size.find(GlSkeletonLoading).exists()).toBe(true);
}
});
});
describe('nodes finish loading', () => {
beforeEach(() => {
wrapper.vm.$store.state.loadingNodes = false;
return wrapper.vm.$nextTick();
});
it.each`
nodeSize | lineNumber
${'Unknown'} | ${0}
......@@ -144,6 +171,8 @@ describe('Clusters', () => {
const size = sizes.at(lineNumber);
expect(size.text()).toBe(nodeSize);
expect(size.find(GlSkeletonLoading).exists()).toBe(false);
});
});
describe('nodes with unknown quantity', () => {
......
......@@ -48,8 +48,9 @@ describe('Clusters store actions', () => {
{ endpoint: apiData.endpoint },
{},
[
{ type: types.SET_LOADING_NODES, payload: true },
{ type: types.SET_CLUSTERS_DATA, payload: { data: apiData, paginationInformation } },
{ type: types.SET_LOADING_STATE, payload: false },
{ type: types.SET_LOADING_CLUSTERS, payload: false },
],
[],
() => done(),
......@@ -63,7 +64,11 @@ describe('Clusters store actions', () => {
actions.fetchClusters,
{ endpoint: apiData.endpoint },
{},
[{ type: types.SET_LOADING_STATE, payload: false }],
[
{ type: types.SET_LOADING_NODES, payload: true },
{ type: types.SET_LOADING_CLUSTERS, payload: false },
{ type: types.SET_LOADING_NODES, payload: false },
],
[],
() => {
expect(flashError).toHaveBeenCalledWith(expect.stringMatching('error'));
......@@ -100,8 +105,9 @@ describe('Clusters store actions', () => {
{ endpoint: apiData.endpoint },
{},
[
{ type: types.SET_LOADING_NODES, payload: true },
{ type: types.SET_CLUSTERS_DATA, payload: { data: apiData, paginationInformation } },
{ type: types.SET_LOADING_STATE, payload: false },
{ type: types.SET_LOADING_CLUSTERS, payload: false },
],
[],
() => {
......@@ -149,11 +155,14 @@ describe('Clusters store actions', () => {
{ endpoint: apiData.endpoint },
{},
[
{ type: types.SET_LOADING_NODES, payload: true },
{
type: types.SET_CLUSTERS_DATA,
payload: { data: badApiResponse, paginationInformation },
},
{ type: types.SET_LOADING_STATE, payload: false },
{ type: types.SET_LOADING_CLUSTERS, payload: false },
{ type: types.SET_LOADING_CLUSTERS, payload: false },
{ type: types.SET_LOADING_NODES, payload: false },
],
[],
() => {
......
import * as types from '~/clusters_list/store/mutation_types';
import { apiData } from '../mock_data';
import getInitialState from '~/clusters_list/store/state';
import mutations from '~/clusters_list/store/mutations';
describe('Admin statistics panel mutations', () => {
let state;
const paginationInformation = {
nextPage: 1,
page: 1,
perPage: 20,
previousPage: 1,
total: apiData.clusters.length,
totalPages: 1,
};
beforeEach(() => {
state = getInitialState();
});
describe(`${types.SET_CLUSTERS_DATA}`, () => {
it('sets clusters and pagination values', () => {
mutations[types.SET_CLUSTERS_DATA](state, { data: apiData, paginationInformation });
expect(state.clusters).toBe(apiData.clusters);
expect(state.clustersPerPage).toBe(paginationInformation.perPage);
expect(state.hasAncestorClusters).toBe(apiData.has_ancestor_clusters);
expect(state.totalCulsters).toBe(paginationInformation.total);
});
});
describe(`${types.SET_LOADING_CLUSTERS}`, () => {
it('sets value to false', () => {
expect(state.loadingClusters).toBe(true);
mutations[types.SET_LOADING_CLUSTERS](state, false);
expect(state.loadingClusters).toBe(false);
});
});
describe(`${types.SET_LOADING_NODES}`, () => {
it('sets value to false', () => {
expect(state.loadingNodes).toBe(true);
mutations[types.SET_LOADING_NODES](state, false);
expect(state.loadingNodes).toBe(false);
});
});
describe(`${types.SET_PAGE}`, () => {
it('changes page value', () => {
mutations[types.SET_PAGE](state, 123);
expect(state.page).toBe(123);
});
});
});
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