Commit 703e2c32 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '287978_02-geo-beta-loading-empty-state' into 'master'

Geo Node Status 2.0 - Loading and Empty States

See merge request gitlab-org/gitlab!54996
parents 21e1da36 fc235235
<script>
import { GlLink, GlButton } from '@gitlab/ui';
import { GlLink, GlButton, GlLoadingIcon } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { GEO_INFO_URL } from '../constants';
import GeoNodes from './geo_nodes.vue';
import GeoNodesEmptyState from './geo_nodes_empty_state.vue';
export default {
name: 'GeoNodesBetaApp',
components: {
GlLink,
GlButton,
GlLoadingIcon,
GeoNodes,
GeoNodesEmptyState,
},
props: {
newNodeUrl: {
type: String,
required: true,
},
geoNodesEmptyStateSvg: {
type: String,
required: true,
},
},
computed: {
...mapState(['nodes']),
...mapState(['nodes', 'isLoading']),
noNodes() {
return !this.nodes || this.nodes.length === 0;
},
},
created() {
this.fetchNodes();
......@@ -47,6 +57,7 @@ export default {
}}</gl-link>
</div>
<gl-button
v-if="!noNodes"
class="gl-w-full gl-md-w-auto gl-ml-auto gl-mr-5 gl-mt-5 gl-md-mt-0"
variant="confirm"
:href="newNodeUrl"
......@@ -54,6 +65,10 @@ export default {
>{{ s__('Geo|Add site') }}
</gl-button>
</div>
<geo-nodes v-for="node in nodes" :key="node.id" :node="node" />
<gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-5" />
<div v-if="!isLoading">
<geo-nodes v-for="node in nodes" :key="node.id" :node="node" />
<geo-nodes-empty-state v-if="noNodes" :svg-path="geoNodesEmptyStateSvg" />
</div>
</section>
</template>
<script>
import { GlEmptyState } from '@gitlab/ui';
import { GEO_FEATURE_URL } from '../constants';
export default {
name: 'GeoNodesEmptyState',
components: {
GlEmptyState,
},
props: {
svgPath: {
type: String,
required: true,
},
},
GEO_FEATURE_URL,
};
</script>
<template>
<gl-empty-state
:title="s__('Geo|Discover GitLab Geo')"
:svg-path="svgPath"
:description="
s__(
'Geo|Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos.',
)
"
:primary-button-link="$options.GEO_FEATURE_URL"
:primary-button-text="s__('Geo|Learn more about Geo')"
/>
</template>
import { helpPagePath } from '~/helpers/help_page_helper';
export const GEO_INFO_URL = helpPagePath('administration/geo/index.md');
export const GEO_FEATURE_URL = 'https://about.gitlab.com/features/gitlab-geo/';
......@@ -13,7 +13,7 @@ export const initGeoNodesBeta = () => {
return false;
}
const { primaryVersion, primaryRevision, newNodeUrl } = el.dataset;
const { primaryVersion, primaryRevision, newNodeUrl, geoNodesEmptyStateSvg } = el.dataset;
let { replicableTypes } = el.dataset;
replicableTypes = convertObjectPropsToCamelCase(JSON.parse(replicableTypes), { deep: true });
......@@ -25,6 +25,7 @@ export const initGeoNodesBeta = () => {
return createElement(GeoNodesBetaApp, {
props: {
newNodeUrl,
geoNodesEmptyStateSvg,
},
});
},
......
......@@ -33,7 +33,8 @@ module EE
node_edit_allowed: ::Gitlab::Geo.license_allows?.to_s,
geo_troubleshooting_help_path: help_page_path('administration/geo/replication/troubleshooting.md'),
replicable_types: replicable_types.to_json,
new_node_url: new_admin_geo_node_path
new_node_url: new_admin_geo_node_path,
geo_nodes_empty_state_svg: image_path("illustrations/empty-state/geo-empty.svg")
}
end
......
import { GlLink, GlButton } from '@gitlab/ui';
import { GlLink, GlButton, GlLoadingIcon } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import GeoNodesBetaApp from 'ee/geo_nodes_beta/components/app.vue';
import GeoNodes from 'ee/geo_nodes_beta/components/geo_nodes.vue';
import GeoNodesEmptyState from 'ee/geo_nodes_beta/components/geo_nodes_empty_state.vue';
import { GEO_INFO_URL } from 'ee/geo_nodes_beta/constants';
import {
MOCK_PRIMARY_VERSION,
MOCK_REPLICABLE_TYPES,
MOCK_NODES,
MOCK_NEW_NODE_URL,
MOCK_EMPTY_STATE_SVG,
} from '../mock_data';
const localVue = createLocalVue();
......@@ -23,6 +25,7 @@ describe('GeoNodesBetaApp', () => {
const defaultProps = {
newNodeUrl: MOCK_NEW_NODE_URL,
geoNodesEmptyStateSvg: MOCK_EMPTY_STATE_SVG,
};
const createComponent = (initialState, props) => {
......@@ -54,35 +57,67 @@ describe('GeoNodesBetaApp', () => {
const findGeoNodesBetaContainer = () => wrapper.find('section');
const findGeoLearnMoreLink = () => wrapper.find(GlLink);
const findGeoAddSiteButton = () => wrapper.find(GlButton);
const findGlLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findGeoEmptyState = () => wrapper.find(GeoNodesEmptyState);
const findGeoNodes = () => wrapper.findAll(GeoNodes);
describe('template', () => {
beforeEach(() => {
createComponent();
});
it('renders the Geo Nodes Beta Container always', () => {
expect(findGeoNodesBetaContainer().exists()).toBe(true);
});
it('renders the Learn more link correctly', () => {
expect(findGeoLearnMoreLink().exists()).toBe(true);
expect(findGeoLearnMoreLink().attributes('href')).toBe(GEO_INFO_URL);
describe('always', () => {
beforeEach(() => {
createComponent();
});
it('renders the Geo Nodes Beta Container', () => {
expect(findGeoNodesBetaContainer().exists()).toBe(true);
});
it('renders the Learn more link correctly', () => {
expect(findGeoLearnMoreLink().exists()).toBe(true);
expect(findGeoLearnMoreLink().attributes('href')).toBe(GEO_INFO_URL);
});
});
it('renders the Add site button correctly', () => {
expect(findGeoAddSiteButton().exists()).toBe(true);
expect(findGeoAddSiteButton().attributes('href')).toBe(MOCK_NEW_NODE_URL);
});
});
describe.each`
isLoading | nodes | showLoadingIcon | showNodes | showEmptyState | showAddButton
${true} | ${[]} | ${true} | ${false} | ${false} | ${false}
${true} | ${MOCK_NODES} | ${true} | ${false} | ${false} | ${true}
${false} | ${[]} | ${false} | ${false} | ${true} | ${false}
${false} | ${MOCK_NODES} | ${false} | ${true} | ${false} | ${true}
`(
`conditionally`,
({ isLoading, nodes, showLoadingIcon, showNodes, showEmptyState, showAddButton }) => {
beforeEach(() => {
createComponent({ isLoading, nodes });
});
describe(`when isLoading is ${isLoading} & nodes length ${nodes.length}`, () => {
it(`does ${!showLoadingIcon ? 'not ' : ''}render GlLoadingIcon`, () => {
expect(findGlLoadingIcon().exists()).toBe(showLoadingIcon);
});
it(`does ${!showNodes ? 'not ' : ''}render GeoNodes`, () => {
expect(findGeoNodes().exists()).toBe(showNodes);
});
it(`does ${!showEmptyState ? 'not ' : ''}render EmptyState`, () => {
expect(findGeoEmptyState().exists()).toBe(showEmptyState);
});
it(`does ${!showAddButton ? 'not ' : ''}render AddSiteButton`, () => {
expect(findGeoAddSiteButton().exists()).toBe(showAddButton);
});
});
},
);
describe('Geo Nodes', () => {
beforeEach(() => {
createComponent({ nodes: MOCK_NODES });
});
describe('with Geo Nodes', () => {
beforeEach(() => {
createComponent({ nodes: MOCK_NODES });
});
it('renders a Geo Node component for each node', () => {
expect(findGeoNodes()).toHaveLength(MOCK_NODES.length);
it('renders a Geo Node component for each node', () => {
expect(findGeoNodes()).toHaveLength(MOCK_NODES.length);
});
});
});
......
import { GlEmptyState } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import GeoNodesEmptyState from 'ee/geo_nodes_beta/components/geo_nodes_empty_state.vue';
import { GEO_FEATURE_URL } from 'ee/geo_nodes_beta/constants';
import { MOCK_PRIMARY_VERSION, MOCK_REPLICABLE_TYPES, MOCK_EMPTY_STATE_SVG } from '../mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('GeoNodesEmptyState', () => {
let wrapper;
const defaultProps = {
svgPath: MOCK_EMPTY_STATE_SVG,
};
const createComponent = (initialState, props) => {
const store = new Vuex.Store({
state: {
primaryVersion: MOCK_PRIMARY_VERSION.version,
primaryRevision: MOCK_PRIMARY_VERSION.revision,
replicableTypes: MOCK_REPLICABLE_TYPES,
...initialState,
},
});
wrapper = shallowMount(GeoNodesEmptyState, {
localVue,
store,
propsData: {
...defaultProps,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findGeoEmptyState = () => wrapper.find(GlEmptyState);
describe('template', () => {
beforeEach(() => {
createComponent();
});
it('renders the Geo Empty State always', () => {
expect(findGeoEmptyState().exists()).toBe(true);
});
it('adds the correct SVG', () => {
expect(findGeoEmptyState().attributes('svgpath')).toBe(MOCK_EMPTY_STATE_SVG);
});
it('links the correct help link', () => {
expect(findGeoEmptyState().attributes('primarybuttontext')).toBe('Learn more about Geo');
expect(findGeoEmptyState().attributes('primarybuttonlink')).toBe(GEO_FEATURE_URL);
});
});
});
export const MOCK_NEW_NODE_URL = 'http://localhost:3000/admin/geo/nodes/new';
export const MOCK_EMPTY_STATE_SVG = 'illustrations/empty-state/geo-empty.svg';
export const MOCK_PRIMARY_VERSION = {
version: '10.4.0-pre',
revision: 'b93c51849b',
......
......@@ -13574,6 +13574,9 @@ msgstr ""
msgid "Geo|Could not remove tracking entry for an existing upload."
msgstr ""
msgid "Geo|Discover GitLab Geo"
msgstr ""
msgid "Geo|Failed"
msgstr ""
......@@ -13610,6 +13613,12 @@ msgstr ""
msgid "Geo|Last time verified"
msgstr ""
msgid "Geo|Learn more about Geo"
msgstr ""
msgid "Geo|Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
msgstr ""
msgid "Geo|Never"
msgstr ""
......
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