Commit 88e5f4e9 authored by Tim Zallmann's avatar Tim Zallmann

Merge branch '5146-help-text-for-verification-info' into 'master'

Update item titles and add help text in Geo nodes admin dashboard

Closes #5146

See merge request gitlab-org/gitlab-ee!5306
parents 7032fc65 6d65793c
<script> <script>
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import stackedProgressBar from '~/vue_shared/components/stacked_progress_bar.vue'; import popover from '~/vue_shared/directives/popover';
import Icon from '~/vue_shared/components/icon.vue';
import StackedProgressBar from '~/vue_shared/components/stacked_progress_bar.vue';
import { VALUE_TYPE, CUSTOM_TYPE } from '../constants'; import { VALUE_TYPE, CUSTOM_TYPE } from '../constants';
import geoNodeSyncSettings from './geo_node_sync_settings.vue'; import GeoNodeSyncSettings from './geo_node_sync_settings.vue';
import geoNodeEventStatus from './geo_node_event_status.vue'; import GeoNodeEventStatus from './geo_node_event_status.vue';
export default { export default {
components: { components: {
stackedProgressBar, Icon,
geoNodeSyncSettings, StackedProgressBar,
geoNodeEventStatus, GeoNodeSyncSettings,
GeoNodeEventStatus,
},
directives: {
popover,
}, },
props: { props: {
itemTitle: { itemTitle: {
...@@ -56,8 +62,16 @@ ...@@ -56,8 +62,16 @@
required: false, required: false,
default: false, default: false,
}, },
helpInfo: {
type: [Boolean, Object],
required: false,
default: false,
},
}, },
computed: { computed: {
hasHelpInfo() {
return typeof this.helpInfo === 'object';
},
isValueTypePlain() { isValueTypePlain() {
return this.itemValueType === VALUE_TYPE.PLAIN; return this.itemValueType === VALUE_TYPE.PLAIN;
}, },
...@@ -70,6 +84,26 @@ ...@@ -70,6 +84,26 @@
isCustomTypeSync() { isCustomTypeSync() {
return this.customType === CUSTOM_TYPE.SYNC; return this.customType === CUSTOM_TYPE.SYNC;
}, },
popoverConfig() {
return {
html: true,
trigger: 'click',
placement: 'top',
template: `
<div class="popover geo-node-detail-popover" role="tooltip">
<div class="arrow"></div>
<p class="popover-title"></p>
<div class="popover-content"></div>
</div>
`,
title: this.helpInfo.title,
content: `
<a href="${this.helpInfo.url}">
${this.helpInfo.urlText}
</a>
`,
};
},
}, },
}; };
</script> </script>
...@@ -77,7 +111,16 @@ ...@@ -77,7 +111,16 @@
<template> <template>
<div class="node-detail-item prepend-top-15 prepend-left-10"> <div class="node-detail-item prepend-top-15 prepend-left-10">
<div class="node-detail-title"> <div class="node-detail-title">
{{ itemTitle }} <span>
{{ itemTitle }}
</span>
<icon
v-popover="popoverConfig"
v-if="hasHelpInfo"
css-classes="node-detail-help-text prepend-left-5"
name="question"
:size="12"
/>
</div> </div>
<div <div
v-if="isValueTypePlain" v-if="isValueTypePlain"
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<template> <template>
<div class="prepend-top-15 detail-section-item"> <div class="prepend-top-15 detail-section-item">
<div class="node-detail-title"> <div class="node-detail-title">
{{ s__('GeoNodes|Health status:') }} {{ s__('GeoNodes|Health status') }}
</div> </div>
<div <div
class="node-detail-value node-health-status" class="node-detail-value node-health-status"
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<div class="col-md-8"> <div class="col-md-8">
<div class="detail-section-item node-version"> <div class="detail-section-item node-version">
<div class="node-detail-title"> <div class="node-detail-title">
{{ s__('GeoNodes|GitLab version:') }} {{ s__('GeoNodes|GitLab version') }}
</div> </div>
<div <div
class="node-detail-value node-detail-value-bold" class="node-detail-value node-detail-value-bold"
......
...@@ -56,7 +56,7 @@ ...@@ -56,7 +56,7 @@
class="col-md-6 prepend-left-15 prepend-top-10 section-items-container" class="col-md-6 prepend-left-15 prepend-top-10 section-items-container"
> >
<geo-node-detail-item <geo-node-detail-item
:item-title="s__('GeoNodes|Storage config:')" :item-title="s__('GeoNodes|Storage config')"
:item-value="storageShardsStatus" :item-value="storageShardsStatus"
:item-value-type="$options.valueType.PLAIN" :item-value-type="$options.valueType.PLAIN"
:css-class="storageShardsCssClass" :css-class="storageShardsCssClass"
......
...@@ -23,49 +23,49 @@ ...@@ -23,49 +23,49 @@
showSectionItems: false, showSectionItems: false,
nodeDetailItems: [ nodeDetailItems: [
{ {
itemTitle: s__('GeoNodes|Sync settings:'), itemTitle: s__('GeoNodes|Sync settings'),
itemValue: this.syncSettings(), itemValue: this.syncSettings(),
itemValueType: VALUE_TYPE.CUSTOM, itemValueType: VALUE_TYPE.CUSTOM,
customType: CUSTOM_TYPE.SYNC, customType: CUSTOM_TYPE.SYNC,
}, },
{ {
itemTitle: s__('GeoNodes|Repositories:'), itemTitle: s__('GeoNodes|Repositories'),
itemValue: this.nodeDetails.repositories, itemValue: this.nodeDetails.repositories,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
}, },
{ {
itemTitle: s__('GeoNodes|Wikis:'), itemTitle: s__('GeoNodes|Wikis'),
itemValue: this.nodeDetails.wikis, itemValue: this.nodeDetails.wikis,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
}, },
{ {
itemTitle: s__('GeoNodes|Local LFS objects:'), itemTitle: s__('GeoNodes|Local LFS objects'),
itemValue: this.nodeDetails.lfs, itemValue: this.nodeDetails.lfs,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
}, },
{ {
itemTitle: s__('GeoNodes|Local attachments:'), itemTitle: s__('GeoNodes|Local attachments'),
itemValue: this.nodeDetails.attachments, itemValue: this.nodeDetails.attachments,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
}, },
{ {
itemTitle: s__('GeoNodes|Local job artifacts:'), itemTitle: s__('GeoNodes|Local job artifacts'),
itemValue: this.nodeDetails.jobArtifacts, itemValue: this.nodeDetails.jobArtifacts,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
}, },
{ {
itemTitle: s__('GeoNodes|Data replication lag:'), itemTitle: s__('GeoNodes|Data replication lag'),
itemValue: this.dbReplicationLag(), itemValue: this.dbReplicationLag(),
itemValueType: VALUE_TYPE.PLAIN, itemValueType: VALUE_TYPE.PLAIN,
}, },
{ {
itemTitle: s__('GeoNodes|Last event ID seen from primary:'), itemTitle: s__('GeoNodes|Last event ID seen from primary'),
itemValue: this.lastEventStatus(), itemValue: this.lastEventStatus(),
itemValueType: VALUE_TYPE.CUSTOM, itemValueType: VALUE_TYPE.CUSTOM,
customType: CUSTOM_TYPE.EVENT, customType: CUSTOM_TYPE.EVENT,
}, },
{ {
itemTitle: s__('GeoNodes|Latest event log status:'), itemTitle: s__('GeoNodes|Latest event log status'),
itemValue: this.cursorLastEventStatus(), itemValue: this.cursorLastEventStatus(),
itemValueType: VALUE_TYPE.CUSTOM, itemValueType: VALUE_TYPE.CUSTOM,
customType: CUSTOM_TYPE.EVENT, customType: CUSTOM_TYPE.EVENT,
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { numberToHumanSize } from '~/lib/utils/number_utils'; import { numberToHumanSize } from '~/lib/utils/number_utils';
import { VALUE_TYPE } from '../../constants'; import { VALUE_TYPE, HELP_INFO_URL } from '../../constants';
import GeoNodeDetailItem from '../geo_node_detail_item.vue'; import GeoNodeDetailItem from '../geo_node_detail_item.vue';
import SectionRevealButton from './section_reveal_button.vue'; import SectionRevealButton from './section_reveal_button.vue';
...@@ -49,27 +49,37 @@ ...@@ -49,27 +49,37 @@
if (this.nodeDetails.repositoryVerificationEnabled) { if (this.nodeDetails.repositoryVerificationEnabled) {
primaryNodeDetailItems.push( primaryNodeDetailItems.push(
{ {
itemTitle: s__('GeoNodes|Repository verification progress:'), itemTitle: s__('GeoNodes|Repository checksum progress'),
itemValue: this.nodeDetails.verifiedRepositories, itemValue: this.nodeDetails.verifiedRepositories,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
successLabel: s__('GeoNodes|Checksummed'), successLabel: s__('GeoNodes|Checksummed'),
neutraLabel: s__('GeoNodes|Not checksummed'), neutraLabel: s__('GeoNodes|Not checksummed'),
failureLabel: s__('GeoNodes|Failed'), failureLabel: s__('GeoNodes|Failed'),
helpInfo: {
title: s__('GeoNodes|Repositories checksummed for verification with their counterparts on Secondary nodes'),
url: HELP_INFO_URL,
urlText: s__('GeoNodes|Learn more about Repository checksum progress'),
},
}, },
{ {
itemTitle: s__('GeoNodes|Wikis checksums calculated verifies:'), itemTitle: s__('GeoNodes|Wiki checksum progress'),
itemValue: this.nodeDetails.verifiedWikis, itemValue: this.nodeDetails.verifiedWikis,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
successLabel: s__('GeoNodes|Checksummed'), successLabel: s__('GeoNodes|Checksummed'),
neutraLabel: s__('GeoNodes|Not checksummed'), neutraLabel: s__('GeoNodes|Not checksummed'),
failureLabel: s__('GeoNodes|Failed'), failureLabel: s__('GeoNodes|Failed'),
helpInfo: {
title: s__('GeoNodes|Wikis checksummed for verification with their counterparts on Secondary nodes'),
url: HELP_INFO_URL,
urlText: s__('GeoNodes|Learn more about Wiki checksum progress'),
},
}, },
); );
} }
primaryNodeDetailItems.push( primaryNodeDetailItems.push(
{ {
itemTitle: s__('GeoNodes|Replication slots:'), itemTitle: s__('GeoNodes|Replication slots'),
itemValue: this.nodeDetails.replicationSlots, itemValue: this.nodeDetails.replicationSlots,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
successLabel: s__('GeoNodes|Used slots'), successLabel: s__('GeoNodes|Used slots'),
...@@ -80,7 +90,7 @@ ...@@ -80,7 +90,7 @@
if (this.nodeDetails.replicationSlots.totalCount) { if (this.nodeDetails.replicationSlots.totalCount) {
primaryNodeDetailItems.push( primaryNodeDetailItems.push(
{ {
itemTitle: s__('GeoNodes|Replication slot WAL:'), itemTitle: s__('GeoNodes|Replication slot WAL'),
itemValue: numberToHumanSize(this.nodeDetails.replicationSlotWAL), itemValue: numberToHumanSize(this.nodeDetails.replicationSlotWAL),
itemValueType: VALUE_TYPE.PLAIN, itemValueType: VALUE_TYPE.PLAIN,
cssClass: 'node-detail-value-bold', cssClass: 'node-detail-value-bold',
...@@ -93,20 +103,30 @@ ...@@ -93,20 +103,30 @@
getSecondaryNodeDetailItems() { getSecondaryNodeDetailItems() {
const secondaryNodeDetailItems = [ const secondaryNodeDetailItems = [
{ {
itemTitle: s__('GeoNodes|Repository checksums verified:'), itemTitle: s__('GeoNodes|Repository verification progress'),
itemValue: this.nodeDetails.verifiedRepositories, itemValue: this.nodeDetails.verifiedRepositories,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
successLabel: s__('GeoNodes|Verified'), successLabel: s__('GeoNodes|Verified'),
neutraLabel: s__('GeoNodes|Unverified'), neutraLabel: s__('GeoNodes|Unverified'),
failureLabel: s__('GeoNodes|Failed'), failureLabel: s__('GeoNodes|Failed'),
helpInfo: {
title: s__('GeoNodes|Repositories verified with their counterparts on the Primary node'),
url: HELP_INFO_URL,
urlText: s__('GeoNodes|Learn more about Repository verification'),
},
}, },
{ {
itemTitle: s__('GeoNodes|Wiki checksums verified:'), itemTitle: s__('GeoNodes|Wiki verification progress'),
itemValue: this.nodeDetails.verifiedWikis, itemValue: this.nodeDetails.verifiedWikis,
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
successLabel: s__('GeoNodes|Verified'), successLabel: s__('GeoNodes|Verified'),
neutraLabel: s__('GeoNodes|Unverified'), neutraLabel: s__('GeoNodes|Unverified'),
failureLabel: s__('GeoNodes|Failed'), failureLabel: s__('GeoNodes|Failed'),
helpInfo: {
title: s__('GeoNodes|Wikis verified with their counterparts on the Primary node'),
url: HELP_INFO_URL,
urlText: s__('GeoNodes|Learn more about Wiki verification'),
},
}, },
]; ];
...@@ -145,6 +165,7 @@ ...@@ -145,6 +165,7 @@
:neutral-label="nodeDetailItem.neutraLabel" :neutral-label="nodeDetailItem.neutraLabel"
:failure-label="nodeDetailItem.failureLabel" :failure-label="nodeDetailItem.failureLabel"
:custom-type="nodeDetailItem.customType" :custom-type="nodeDetailItem.customType"
:help-info="nodeDetailItem.helpInfo"
/> />
</div> </div>
</template> </template>
......
...@@ -27,3 +27,5 @@ export const TIME_DIFF = { ...@@ -27,3 +27,5 @@ export const TIME_DIFF = {
FIVE_MINS: 300, FIVE_MINS: 300,
HOUR: 3600, HOUR: 3600,
}; };
export const HELP_INFO_URL = 'https://docs.gitlab.com/ee/administration/geo/disaster_recovery/background_verification.html#repository-verification';
...@@ -101,6 +101,14 @@ ...@@ -101,6 +101,14 @@
.section-items-container { .section-items-container {
.node-detail-title { .node-detail-title {
color: $theme-gray-700; color: $theme-gray-700;
.node-detail-help-text {
color: $blue-600;
}
.tooltip .tooltip-inner {
text-align: left;
}
} }
.node-detail-value { .node-detail-value {
...@@ -125,6 +133,7 @@ ...@@ -125,6 +133,7 @@
} }
} }
.node-detail-title,
.node-health-status, .node-health-status,
.node-sync-settings, .node-sync-settings,
.node-detail-section .btn-show-section { .node-detail-section .btn-show-section {
......
---
title: Update item titles and add help text in Geo nodes admin dashboard
merge_request: 5306
author:
type: changed
...@@ -8,7 +8,7 @@ import { rawMockNodeDetails } from '../mock_data'; ...@@ -8,7 +8,7 @@ import { rawMockNodeDetails } from '../mock_data';
const createComponent = (config) => { const createComponent = (config) => {
const Component = Vue.extend(geoNodeDetailItemComponent); const Component = Vue.extend(geoNodeDetailItemComponent);
const defaultConfig = Object.assign({ const defaultConfig = Object.assign({
itemTitle: 'GitLab version:', itemTitle: 'GitLab version',
cssClass: 'node-version', cssClass: 'node-version',
itemValue: '10.4.0-pre', itemValue: '10.4.0-pre',
successLabel: 'Synced', successLabel: 'Synced',
...@@ -26,7 +26,7 @@ describe('GeoNodeDetailItemComponent', () => { ...@@ -26,7 +26,7 @@ describe('GeoNodeDetailItemComponent', () => {
const vm = createComponent(); const vm = createComponent();
expect(vm.$el.classList.contains('node-detail-item')).toBeTruthy(); expect(vm.$el.classList.contains('node-detail-item')).toBeTruthy();
expect(vm.$el.querySelectorAll('.node-detail-title').length).not.toBe(0); expect(vm.$el.querySelectorAll('.node-detail-title').length).not.toBe(0);
expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('GitLab version:'); expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('GitLab version');
vm.$destroy(); vm.$destroy();
}); });
...@@ -37,6 +37,16 @@ describe('GeoNodeDetailItemComponent', () => { ...@@ -37,6 +37,16 @@ describe('GeoNodeDetailItemComponent', () => {
vm.$destroy(); vm.$destroy();
}); });
it('renders item title help info icon and popover with help info', () => {
const helpInfo = { title: 'Foo title tooltip', url: 'https://docs.gitlab.com', urlText: 'Help' };
const vm = createComponent({ helpInfo });
const helpTextIconEl = vm.$el.querySelector('.node-detail-help-text');
expect(helpTextIconEl).not.toBeNull();
expect(helpTextIconEl.querySelector('use').getAttribute('xlink:href')).toContain('question');
vm.$destroy();
});
it('renders graph item value', () => { it('renders graph item value', () => {
const vm = createComponent({ const vm = createComponent({
itemValueType: VALUE_TYPE.GRAPH, itemValueType: VALUE_TYPE.GRAPH,
......
...@@ -51,7 +51,7 @@ describe('GeoNodeHealthStatusComponent', () => { ...@@ -51,7 +51,7 @@ describe('GeoNodeHealthStatusComponent', () => {
it('renders container elements correctly', () => { it('renders container elements correctly', () => {
const vm = createComponent('Healthy'); const vm = createComponent('Healthy');
expect(vm.$el.classList.contains('detail-section-item')).toBe(true); expect(vm.$el.classList.contains('detail-section-item')).toBe(true);
expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('Health status:'); expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('Health status');
const iconContainerEl = vm.$el.querySelector('.node-detail-value.node-health-status'); const iconContainerEl = vm.$el.querySelector('.node-detail-value.node-health-status');
expect(iconContainerEl).not.toBeNull(); expect(iconContainerEl).not.toBeNull();
......
...@@ -75,7 +75,7 @@ describe('NodeDetailsSectionMain', () => { ...@@ -75,7 +75,7 @@ describe('NodeDetailsSectionMain', () => {
}); });
it('renders node version element', () => { it('renders node version element', () => {
expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('GitLab version:'); expect(vm.$el.querySelector('.node-detail-title').innerText.trim()).toBe('GitLab version');
expect(vm.$el.querySelector('.node-detail-value').innerText.trim()).toBe('10.4.0-pre (b93c51849b)'); expect(vm.$el.querySelector('.node-detail-value').innerText.trim()).toBe('10.4.0-pre (b93c51849b)');
}); });
}); });
......
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