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