Commit 38f17eab authored by Peter Hegman's avatar Peter Hegman

Merge branch '351649-geo-sites-empty-search' into 'master'

Geo Sites - Empty Search State

See merge request gitlab-org/gitlab!85136
parents 88075a58 4b04bd6f
......@@ -21,6 +21,12 @@ export default {
),
primarySite: s__('Geo|Primary site'),
secondarySite: s__('Geo|Secondary site'),
notConfiguredTitle: s__('Geo|Discover GitLab Geo'),
notConfiguredDescription: 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.',
),
noResultsTitle: s__('Geo|No Geo site found'),
noResultsDescription: s__('Geo|Edit your search and try again.'),
},
components: {
GlLink,
......@@ -37,16 +43,15 @@ export default {
type: String,
required: true,
},
geoNodesEmptyStateSvg: {
type: String,
required: true,
},
},
computed: {
...mapState(['nodes', 'isLoading']),
...mapGetters(['filteredNodes']),
noNodes() {
return !this.nodes || this.nodes.length === 0;
hasNodes() {
return this.nodes && this.nodes.length > 0;
},
hasEmptyState() {
return Object.keys(this.emptyState).length;
},
primaryNodes() {
return this.filteredNodes.filter((n) => n.primary);
......@@ -54,6 +59,26 @@ export default {
secondaryNodes() {
return this.filteredNodes.filter((n) => !n.primary);
},
emptyState() {
// Geo isn't configured
if (!this.hasNodes) {
return {
title: this.$options.i18n.notConfiguredTitle,
description: this.$options.i18n.notConfiguredDescription,
showLearnMoreButton: true,
};
// User has searched and returned nothing
} else if (this.filteredNodes.length === 0) {
return {
title: this.$options.i18n.noResultsTitle,
description: this.$options.i18n.noResultsDescription,
showLearnMoreButton: false,
};
}
// Don't show empty state
return {};
},
},
created() {
this.fetchNodes();
......@@ -91,7 +116,7 @@ export default {
</gl-sprintf>
</div>
<gl-button
v-if="!noNodes"
v-if="hasNodes"
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"
......@@ -102,7 +127,7 @@ export default {
</div>
<gl-loading-icon v-if="isLoading" size="xl" class="gl-mt-5" />
<template v-if="!isLoading">
<div v-if="!noNodes">
<div v-if="hasNodes">
<geo-nodes-filters :total-nodes="nodes.length" />
<h4 v-if="primaryNodes.length" class="gl-font-lg gl-my-5">
{{ $options.i18n.primarySite }}
......@@ -123,7 +148,12 @@ export default {
data-testid="secondary-nodes"
/>
</div>
<geo-nodes-empty-state v-else :svg-path="geoNodesEmptyStateSvg" />
<geo-nodes-empty-state
v-if="hasEmptyState"
:title="emptyState.title"
:description="emptyState.description"
:show-learn-more-button="emptyState.showLearnMoreButton"
/>
</template>
<gl-modal
:modal-id="$options.REMOVE_NODE_MODAL_ID"
......
......@@ -6,31 +6,49 @@ import { GEO_FEATURE_URL } from '../constants';
export default {
name: 'GeoNodesEmptyState',
i18n: {
emptyStateTitle: s__('Geo|Discover GitLab Geo'),
emptyStateDescription: 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.',
),
emptyStateButton: s__('Geo|Learn more about Geo'),
learnMoreButtonText: s__('Geo|Learn more about Geo'),
},
components: {
GlEmptyState,
},
inject: {
geoNodesEmptyStateSvg: {
default: '',
},
},
props: {
svgPath: {
title: {
type: String,
required: true,
},
description: {
type: String,
required: false,
default: '',
},
showLearnMoreButton: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
primaryButtonLink() {
return this.showLearnMoreButton ? GEO_FEATURE_URL : '';
},
primaryButtonText() {
return this.showLearnMoreButton ? this.$options.i18n.learnMoreButtonText : '';
},
},
GEO_FEATURE_URL,
};
</script>
<template>
<gl-empty-state
:title="$options.i18n.emptyStateTitle"
:svg-path="svgPath"
:description="$options.i18n.emptyStateDescription"
:primary-button-link="$options.GEO_FEATURE_URL"
:primary-button-text="$options.i18n.emptyStateButton"
:title="title"
:svg-path="geoNodesEmptyStateSvg"
:description="description"
:primary-button-link="primaryButtonLink"
:primary-button-text="primaryButtonText"
/>
</template>
......@@ -23,11 +23,13 @@ export const initGeoNodes = () => {
return new Vue({
el,
store: createStore({ primaryVersion, primaryRevision, replicableTypes, searchFilter }),
provide: {
geoNodesEmptyStateSvg,
},
render(createElement) {
return createElement(GeoNodesApp, {
props: {
newNodeUrl,
geoNodesEmptyStateSvg,
},
});
},
......
......@@ -6,7 +6,12 @@ import GeoNodes from 'ee/geo_nodes/components/geo_nodes.vue';
import GeoNodesEmptyState from 'ee/geo_nodes/components/geo_nodes_empty_state.vue';
import { GEO_INFO_URL } from 'ee/geo_nodes/constants';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { MOCK_NODES, MOCK_NEW_NODE_URL, MOCK_EMPTY_STATE_SVG } from '../mock_data';
import {
MOCK_NODES,
MOCK_NEW_NODE_URL,
MOCK_NOT_CONFIGURED_EMPTY_STATE,
MOCK_NO_RESULTS_EMPTY_STATE,
} from '../mock_data';
Vue.use(Vuex);
......@@ -21,7 +26,6 @@ describe('GeoNodesApp', () => {
const defaultProps = {
newNodeUrl: MOCK_NEW_NODE_URL,
geoNodesEmptyStateSvg: MOCK_EMPTY_STATE_SVG,
};
const createComponent = (initialState, props, getters) => {
......@@ -150,6 +154,28 @@ describe('GeoNodesApp', () => {
});
});
});
describe('Empty state', () => {
describe.each`
description | nodes | filteredNodes | renderEmptyState | emptyStateProps
${'with no nodes configured'} | ${[]} | ${[]} | ${true} | ${MOCK_NOT_CONFIGURED_EMPTY_STATE}
${'with nodes configured and no user filter'} | ${MOCK_NODES} | ${MOCK_NODES} | ${false} | ${null}
${'with nodes configured and user filters returning results'} | ${MOCK_NODES} | ${[MOCK_NODES[0]]} | ${false} | ${null}
${'with nodes configured and user filters returning no results'} | ${MOCK_NODES} | ${[]} | ${true} | ${MOCK_NO_RESULTS_EMPTY_STATE}
`('$description', ({ nodes, filteredNodes, renderEmptyState, emptyStateProps }) => {
beforeEach(() => {
createComponent({ nodes }, null, { filteredNodes: () => filteredNodes });
});
it(`should ${renderEmptyState ? '' : 'not '}render the Geo Empty State`, () => {
expect(findGeoEmptyState().exists()).toBe(renderEmptyState);
if (renderEmptyState) {
expect(findGeoEmptyState().props()).toStrictEqual(emptyStateProps);
}
});
});
});
});
describe('onCreate', () => {
......
......@@ -8,7 +8,8 @@ describe('GeoNodesEmptyState', () => {
let wrapper;
const defaultProps = {
svgPath: MOCK_EMPTY_STATE_SVG,
title: 'test title',
description: 'test description',
};
const createComponent = (props) => {
......@@ -17,6 +18,9 @@ describe('GeoNodesEmptyState', () => {
...defaultProps,
...props,
},
provide: {
geoNodesEmptyStateSvg: MOCK_EMPTY_STATE_SVG,
},
});
};
......@@ -27,21 +31,47 @@ describe('GeoNodesEmptyState', () => {
const findGeoEmptyState = () => wrapper.findComponent(GlEmptyState);
describe('template', () => {
beforeEach(() => {
createComponent();
});
describe('always', () => {
beforeEach(() => {
createComponent();
});
it('renders the Geo Empty State', () => {
expect(findGeoEmptyState().exists()).toBe(true);
});
it('renders the Geo Empty State always', () => {
expect(findGeoEmptyState().exists()).toBe(true);
it('adds the correct SVG', () => {
expect(findGeoEmptyState().props('svgPath')).toBe(MOCK_EMPTY_STATE_SVG);
});
it('sets the title and description', () => {
expect(findGeoEmptyState().props('title')).toBe(defaultProps.title);
expect(findGeoEmptyState().props('description')).toBe(defaultProps.description);
});
});
it('adds the correct SVG', () => {
expect(findGeoEmptyState().attributes('svgpath')).toBe(MOCK_EMPTY_STATE_SVG);
describe('when showLearnMoreButton is true', () => {
beforeEach(() => {
createComponent({ showLearnMoreButton: true });
});
it('renders the learn more button with the correct link', () => {
expect(findGeoEmptyState().props('primaryButtonText')).toBe(
GeoNodesEmptyState.i18n.learnMoreButtonText,
);
expect(findGeoEmptyState().props('primaryButtonLink')).toBe(GEO_FEATURE_URL);
});
});
it('links the correct help link', () => {
expect(findGeoEmptyState().attributes('primarybuttontext')).toBe('Learn more about Geo');
expect(findGeoEmptyState().attributes('primarybuttonlink')).toBe(GEO_FEATURE_URL);
describe('when showLearnMoreButton is false', () => {
beforeEach(() => {
createComponent({ showLearnMoreButton: false });
});
it('does not render the learn more button', () => {
expect(findGeoEmptyState().props('primaryButtonText')).toBe('');
expect(findGeoEmptyState().props('primaryButtonLink')).toBe('');
});
});
});
});
......@@ -277,3 +277,16 @@ export const MOCK_FILTER_NODES = [
healthStatus: null,
},
];
export const MOCK_NOT_CONFIGURED_EMPTY_STATE = {
title: 'Discover GitLab Geo',
description:
'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.',
showLearnMoreButton: true,
};
export const MOCK_NO_RESULTS_EMPTY_STATE = {
title: 'No Geo site found',
description: 'Edit your search and try again.',
showLearnMoreButton: false,
};
......@@ -16506,6 +16506,9 @@ msgstr ""
msgid "Geo|Edit %{nodeType} site"
msgstr ""
msgid "Geo|Edit your search and try again."
msgstr ""
msgid "Geo|Failed"
msgstr ""
......@@ -16581,6 +16584,9 @@ msgstr ""
msgid "Geo|Next sync scheduled at"
msgstr ""
msgid "Geo|No Geo site found"
msgstr ""
msgid "Geo|No available replication slots"
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