Commit 65a01d5e authored by Brandon Labuschagne's avatar Brandon Labuschagne Committed by Jose Ivan Vargas

DevOps Adoption - Link group titles to group

parent a0d10fed
...@@ -7,6 +7,7 @@ import { ...@@ -7,6 +7,7 @@ import {
GlIcon, GlIcon,
GlBadge, GlBadge,
GlProgressBar, GlProgressBar,
GlLink,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
...@@ -22,6 +23,7 @@ import { ...@@ -22,6 +23,7 @@ import {
OVERVIEW_TABLE_SORT_DESC_STORAGE_KEY, OVERVIEW_TABLE_SORT_DESC_STORAGE_KEY,
OVERVIEW_TABLE_NAME_KEY, OVERVIEW_TABLE_NAME_KEY,
} from '../constants'; } from '../constants';
import { getGroupAdoptionPath } from '../utils/helpers';
import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue';
const thClass = ['gl-bg-white!', 'gl-text-gray-400']; const thClass = ['gl-bg-white!', 'gl-text-gray-400'];
...@@ -52,6 +54,7 @@ export default { ...@@ -52,6 +54,7 @@ export default {
GlProgressBar, GlProgressBar,
DevopsAdoptionDeleteModal, DevopsAdoptionDeleteModal,
LocalStorageSync, LocalStorageSync,
GlLink,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -146,6 +149,9 @@ export default { ...@@ -146,6 +149,9 @@ export default {
cellSlotName(key) { cellSlotName(key) {
return `cell(${key})`; return `cell(${key})`;
}, },
getGroupAdoptionPath(fullPath) {
return getGroupAdoptionPath(fullPath);
},
}, },
}; };
</script> </script>
...@@ -168,16 +174,25 @@ export default { ...@@ -168,16 +174,25 @@ export default {
<template #cell(name)="{ item }"> <template #cell(name)="{ item }">
<div data-testid="namespace"> <div data-testid="namespace">
<span v-if="item.group.latestSnapshot" class="gl-font-weight-bold">{{ <template v-if="item.group.latestSnapshot">
<template v-if="isCurrentGroup(item.group)">
<span class="gl-text-gray-500 gl-font-weight-bold">{{
item.group.namespace.fullName item.group.namespace.fullName
}}</span> }}</span>
<gl-badge class="gl-ml-1" variant="info">{{ __('This group') }}</gl-badge>
</template>
<gl-link
v-else
:href="getGroupAdoptionPath(item.group.namespace.fullPath)"
class="gl-text-gray-500 gl-font-weight-bold"
>
{{ item.group.namespace.fullName }}
</gl-link>
</template>
<template v-else> <template v-else>
<span class="gl-text-gray-400">{{ item.group.namespace.fullName }}</span> <span class="gl-text-gray-400">{{ item.group.namespace.fullName }}</span>
<gl-icon name="hourglass" class="gl-text-gray-400" /> <gl-icon name="hourglass" class="gl-text-gray-400" />
</template> </template>
<gl-badge v-if="isCurrentGroup(item.group)" class="gl-ml-1" variant="info">{{
__('This group')
}}</gl-badge>
</div> </div>
</template> </template>
......
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
GlTooltipDirective, GlTooltipDirective,
GlIcon, GlIcon,
GlBadge, GlBadge,
GlLink,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { uniqueId } from 'lodash'; import { uniqueId } from 'lodash';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
...@@ -16,6 +17,7 @@ import { ...@@ -16,6 +17,7 @@ import {
I18N_TABLE_REMOVE_BUTTON_DISABLED, I18N_TABLE_REMOVE_BUTTON_DISABLED,
I18N_GROUP_COL_LABEL, I18N_GROUP_COL_LABEL,
} from '../constants'; } from '../constants';
import { getGroupAdoptionPath } from '../utils/helpers';
import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from './devops_adoption_delete_modal.vue';
import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue'; import DevopsAdoptionTableCellFlag from './devops_adoption_table_cell_flag.vue';
...@@ -55,6 +57,7 @@ export default { ...@@ -55,6 +57,7 @@ export default {
DevopsAdoptionDeleteModal, DevopsAdoptionDeleteModal,
GlIcon, GlIcon,
GlBadge, GlBadge,
GlLink,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -129,6 +132,9 @@ export default { ...@@ -129,6 +132,9 @@ export default {
? this.$options.i18n.removeButtonDisabled ? this.$options.i18n.removeButtonDisabled
: this.$options.i18n.removeButton; : this.$options.i18n.removeButton;
}, },
getGroupAdoptionPath(fullPath) {
return getGroupAdoptionPath(fullPath);
},
}, },
}; };
</script> </script>
...@@ -159,14 +165,25 @@ export default { ...@@ -159,14 +165,25 @@ export default {
<template #cell(name)="{ item }"> <template #cell(name)="{ item }">
<div data-testid="namespace"> <div data-testid="namespace">
<strong v-if="item.latestSnapshot">{{ item.namespace.fullName }}</strong> <template v-if="item.latestSnapshot">
<template v-if="isCurrentGroup(item)">
<span class="gl-text-gray-500 gl-font-weight-bold">{{
item.namespace.fullName
}}</span>
<gl-badge class="gl-ml-1" variant="info">{{ __('This group') }}</gl-badge>
</template>
<gl-link
v-else
:href="getGroupAdoptionPath(item.namespace.fullPath)"
class="gl-text-gray-500 gl-font-weight-bold"
>
{{ item.namespace.fullName }}
</gl-link>
</template>
<template v-else> <template v-else>
<span class="gl-text-gray-400">{{ item.namespace.fullName }}</span> <span class="gl-text-gray-400">{{ item.namespace.fullName }}</span>
<gl-icon name="hourglass" class="gl-text-gray-400" /> <gl-icon name="hourglass" class="gl-text-gray-400" />
</template> </template>
<gl-badge v-if="isCurrentGroup(item)" class="gl-ml-1" variant="info">{{
__('This group')
}}</gl-badge>
</div> </div>
</template> </template>
......
...@@ -5,6 +5,7 @@ export const PER_PAGE = 20; ...@@ -5,6 +5,7 @@ export const PER_PAGE = 20;
export const DEBOUNCE_DELAY = 500; export const DEBOUNCE_DELAY = 500;
export const PROGRESS_BAR_HEIGHT = '8px'; export const PROGRESS_BAR_HEIGHT = '8px';
export const DATE_TIME_FORMAT = 'yyyy-mm-dd HH:MM'; export const DATE_TIME_FORMAT = 'yyyy-mm-dd HH:MM';
export const GROUP_DEVOPS_PATH = '/groups/%{fullPath}/-/analytics/devops_adoption';
export const OVERVIEW_TABLE_NAME_KEY = 'name'; export const OVERVIEW_TABLE_NAME_KEY = 'name';
......
#import "../fragments/latest_snapshot.fragment.graphql" #import "../fragments/latest_snapshot.fragment.graphql"
#import "../fragments/namespace.fragment.graphql"
mutation($namespaceIds: [NamespaceID!]!, $displayNamespaceId: NamespaceID) { mutation($namespaceIds: [NamespaceID!]!, $displayNamespaceId: NamespaceID) {
bulkEnableDevopsAdoptionNamespaces( bulkEnableDevopsAdoptionNamespaces(
...@@ -10,8 +11,7 @@ mutation($namespaceIds: [NamespaceID!]!, $displayNamespaceId: NamespaceID) { ...@@ -10,8 +11,7 @@ mutation($namespaceIds: [NamespaceID!]!, $displayNamespaceId: NamespaceID) {
...LatestSnapshot ...LatestSnapshot
} }
namespace { namespace {
fullName ...Namespace
id
} }
} }
errors errors
......
#import "../fragments/latest_snapshot.fragment.graphql" #import "../fragments/latest_snapshot.fragment.graphql"
#import "../fragments/namespace.fragment.graphql"
query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) { query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) {
devopsAdoptionEnabledNamespaces(displayNamespaceId: $displayNamespaceId) { devopsAdoptionEnabledNamespaces(displayNamespaceId: $displayNamespaceId) {
...@@ -8,8 +9,7 @@ query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) { ...@@ -8,8 +9,7 @@ query devopsAdoptionEnabledNamespaces($displayNamespaceId: NamespaceID) {
...LatestSnapshot ...LatestSnapshot
} }
namespace { namespace {
fullName ...Namespace
id
} }
} }
} }
......
import { sprintf } from '~/locale';
import { GROUP_DEVOPS_PATH } from '../constants';
/** /**
* A helper function which accepts the enabledNamespaces, * A helper function which accepts the enabledNamespaces,
* *
...@@ -37,3 +40,15 @@ export const getAdoptedCountsByCols = (snapshots, cols) => { ...@@ -37,3 +40,15 @@ export const getAdoptedCountsByCols = (snapshots, cols) => {
return [...acc, adoptedCount]; return [...acc, adoptedCount];
}, []); }, []);
}; };
/**
* A helper function which computes the DevOps Adoption feature path
* given a specific group path
*
* @param { String } fullPath the full path for the group
*
* @return { String } the path for the group level DevOps Adoption feature
*/
export const getGroupAdoptionPath = (fullPath) => {
return fullPath ? sprintf(GROUP_DEVOPS_PATH, { fullPath }) : null;
};
import { GlButton, GlIcon, GlBadge, GlProgressBar } from '@gitlab/ui'; import { GlButton, GlIcon, GlBadge, GlProgressBar, GlLink } from '@gitlab/ui';
import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue';
import DevopsAdoptionOverviewTable from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_overview_table.vue'; import DevopsAdoptionOverviewTable from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_overview_table.vue';
import { DEVOPS_ADOPTION_TABLE_CONFIGURATION } from 'ee/analytics/devops_report/devops_adoption/constants'; import { DEVOPS_ADOPTION_TABLE_CONFIGURATION } from 'ee/analytics/devops_report/devops_adoption/constants';
...@@ -78,6 +78,17 @@ describe('DevopsAdoptionOverviewTable', () => { ...@@ -78,6 +78,17 @@ describe('DevopsAdoptionOverviewTable', () => {
); );
}); });
it('includes a link to the group DevOps page', () => {
createComponent();
const link = findColSubComponent(TABLE_TEST_IDS_NAMESPACE, GlLink);
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe(
`/groups/${devopsAdoptionNamespaceData.nodes[0].namespace.fullPath}/-/analytics/devops_adoption`,
);
});
describe('"This group" badge', () => { describe('"This group" badge', () => {
const thisGroupGid = devopsAdoptionNamespaceData.nodes[0].namespace.id; const thisGroupGid = devopsAdoptionNamespaceData.nodes[0].namespace.id;
...@@ -106,6 +117,12 @@ describe('DevopsAdoptionOverviewTable', () => { ...@@ -106,6 +117,12 @@ describe('DevopsAdoptionOverviewTable', () => {
expect(name.classes()).toStrictEqual(['gl-text-gray-400']); expect(name.classes()).toStrictEqual(['gl-text-gray-400']);
}); });
it('does not include a link to the group DevOps page', () => {
const link = findColRowChild(TABLE_TEST_IDS_NAMESPACE, 1, GlLink);
expect(link.exists()).toBe(false);
});
describe('hourglass icon', () => { describe('hourglass icon', () => {
let icon; let icon;
......
import { GlTable, GlButton, GlIcon, GlBadge } from '@gitlab/ui'; import { GlTable, GlButton, GlIcon, GlBadge, GlLink } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue'; import DevopsAdoptionDeleteModal from 'ee/analytics/devops_report/devops_adoption/components/devops_adoption_delete_modal.vue';
...@@ -102,10 +102,6 @@ describe('DevopsAdoptionTable', () => { ...@@ -102,10 +102,6 @@ describe('DevopsAdoptionTable', () => {
describe('table fields', () => { describe('table fields', () => {
describe('enabled namespace name', () => { describe('enabled namespace name', () => {
beforeEach(() => {
createComponent();
});
it('displays the correct name', () => { it('displays the correct name', () => {
createComponent(); createComponent();
...@@ -114,6 +110,17 @@ describe('DevopsAdoptionTable', () => { ...@@ -114,6 +110,17 @@ describe('DevopsAdoptionTable', () => {
); );
}); });
it('includes a link to the group DevOps page', () => {
createComponent();
const link = findColSubComponent(TABLE_TEST_IDS_NAMESPACE, GlLink);
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe(
`/groups/${devopsAdoptionNamespaceData.nodes[0].namespace.fullPath}/-/analytics/devops_adoption`,
);
});
describe('"This group" badge', () => { describe('"This group" badge', () => {
const thisGroupGid = devopsAdoptionNamespaceData.nodes[0].namespace.id; const thisGroupGid = devopsAdoptionNamespaceData.nodes[0].namespace.id;
...@@ -142,6 +149,12 @@ describe('DevopsAdoptionTable', () => { ...@@ -142,6 +149,12 @@ describe('DevopsAdoptionTable', () => {
expect(name.classes()).toStrictEqual(['gl-text-gray-400']); expect(name.classes()).toStrictEqual(['gl-text-gray-400']);
}); });
it('does not include a link to the group DevOps page', () => {
const link = findColRowChild(TABLE_TEST_IDS_NAMESPACE, 1, GlLink);
expect(link.exists()).toBe(false);
});
describe('hourglass icon', () => { describe('hourglass icon', () => {
let icon; let icon;
......
import { import {
shouldPollTableData, shouldPollTableData,
getAdoptedCountsByCols, getAdoptedCountsByCols,
getGroupAdoptionPath,
} from 'ee/analytics/devops_report/devops_adoption/utils/helpers'; } from 'ee/analytics/devops_report/devops_adoption/utils/helpers';
import { DEVOPS_ADOPTION_TABLE_CONFIGURATION } from 'ee/analytics/devops_report/devops_adoption/constants'; import { DEVOPS_ADOPTION_TABLE_CONFIGURATION } from 'ee/analytics/devops_report/devops_adoption/constants';
import { devopsAdoptionNamespaceData, namespaceWithSnapotsData } from '../mock_data'; import { devopsAdoptionNamespaceData, namespaceWithSnapotsData } from '../mock_data';
...@@ -43,3 +44,13 @@ describe('getAdoptedCountsByCols', () => { ...@@ -43,3 +44,13 @@ describe('getAdoptedCountsByCols', () => {
}, },
); );
}); });
describe('getGroupAdoptionPath', () => {
it.each`
fullPath | expected
${'gitlab-org'} | ${'/groups/gitlab-org/-/analytics/devops_adoption'}
${null} | ${null}
`('returns the correct value based on the group full path', ({ fullPath, expected }) => {
expect(getGroupAdoptionPath(fullPath)).toBe(expected);
});
});
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