Commit a100552f authored by Dhiraj Bodicherla's avatar Dhiraj Bodicherla

Added warning state in usage quota table

This MR adds warning state in the usage quota
table if a project storage is in warning state.
The changes are behind a feature flag. A tooltip
is also added to the warning icon
parent a30d40df
......@@ -5,7 +5,8 @@
* looks similar to project.vue component so that once the flag is
* lifted this component could replace and be used mainstream.
*/
import { GlLink, GlIcon } from '@gitlab/ui';
import { GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
......@@ -16,6 +17,9 @@ export default {
GlLink,
ProjectAvatar,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
project: {
required: true,
......@@ -41,15 +45,32 @@ export default {
excessStorageSize() {
return numberToHumanSize(this.project.statistics?.excessStorageSize ?? 0);
},
hasError() {
status() {
// The project default limit will be sent by backend.
// This is being added here just for testing purposes.
// This entire component is rendered behind the
// additional_repo_storage_by_namespace feature flag. This
// piece will be removed along with the flag.
// piece will be removed along with the flag and the logic
// will be mostly on the backend.
const PROJECT_DEFAULT_LIMIT = 10000000000;
const projectLimit = this.project.statistics?.projectLimit ?? PROJECT_DEFAULT_LIMIT;
return this.project.statistics.storageSize > projectLimit;
const PROJECT_DEFAULT_WARNING_LIMIT = 9000000000;
if (this.project.statistics.storageSize > PROJECT_DEFAULT_LIMIT) {
return {
bgColor: { 'gl-bg-red-50': true },
iconClass: { 'gl-text-red-500': true },
linkClass: 'gl-text-red-500!',
tooltipText: s__('UsageQuota|This project is locked.'),
};
} else if (this.project.statistics.storageSize > PROJECT_DEFAULT_WARNING_LIMIT) {
return {
bgColor: { 'gl-bg-orange-50': true },
iconClass: 'gl-text-orange-500',
tooltipText: s__('UsageQuota|This project is at risk of being locked.'),
};
}
return {};
},
},
};
......@@ -57,7 +78,7 @@ export default {
<template>
<div
class="gl-responsive-table-row gl-border-solid gl-border-b-1 gl-pt-3 gl-pb-3 gl-border-b-gray-100"
:class="{ 'gl-bg-red-50': hasError }"
:class="status.bgColor"
role="row"
data-testid="projectTableRow"
>
......@@ -75,13 +96,18 @@ export default {
<div>
<project-avatar :project="projectAvatar" :size="32" />
</div>
<div v-if="hasError">
<gl-icon name="status_warning" class="gl-text-red-500 gl-mr-3" />
<div v-if="status.iconClass">
<gl-icon
v-gl-tooltip="{ title: status.tooltipText }"
name="status_warning"
class="gl-mr-3"
:class="status.iconClass"
/>
</div>
<gl-link
:href="project.webUrl"
class="gl-font-weight-bold gl-text-gray-900!"
:class="{ 'gl-text-red-500!': hasError }"
:class="status.linkClass"
>{{ name }}</gl-link
>
</div>
......
import { shallowMount } from '@vue/test-utils';
import { GlLink } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import ProjectWithExcessStorage from 'ee/storage_counter/components/project_with_excess_storage.vue';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { numberToHumanSize } from '~/lib/utils/number_utils';
......@@ -10,15 +11,19 @@ let wrapper;
const createComponent = (propsData = {}) => {
wrapper = shallowMount(ProjectWithExcessStorage, {
propsData: {
project: projects[1],
project: projects[0],
...propsData,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
const findTableRow = () => wrapper.find('[data-testid="projectTableRow"]');
const findWarningIcon = () => wrapper.find({ name: 'status_warning' });
const findProjectLink = () => wrapper.find(GlLink);
const getWarningIconTooltipText = () => getBinding(findWarningIcon().element, 'gl-tooltip').value;
describe('Storage Counter project component', () => {
beforeEach(() => {
......@@ -34,11 +39,11 @@ describe('Storage Counter project component', () => {
});
it('renders project name', () => {
expect(wrapper.text()).toContain(projects[1].nameWithNamespace);
expect(wrapper.text()).toContain(projects[0].nameWithNamespace);
});
it('renders formatted storage size', () => {
expect(wrapper.text()).toContain(numberToHumanSize(projects[1].statistics.storageSize));
expect(wrapper.text()).toContain(numberToHumanSize(projects[0].statistics.storageSize));
});
it('does not render the warning icon if project is not in error state', () => {
......@@ -65,5 +70,31 @@ describe('Storage Counter project component', () => {
it('with error icon', () => {
expect(findWarningIcon().exists()).toBe(true);
});
it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is locked.');
});
});
describe('renders the row in warning state', () => {
beforeEach(() => {
createComponent({ project: projects[1] });
});
it('with error state background', () => {
expect(findTableRow().classes('gl-bg-orange-50')).toBe(true);
});
it('with project link in default gray state', () => {
expect(findProjectLink().classes('gl-text-gray-900!')).toBe(true);
});
it('with warning icon', () => {
expect(findWarningIcon().exists()).toBe(true);
});
it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is at risk of being locked.');
});
});
});
......@@ -24,7 +24,7 @@ export const projects = [
name: 'Html5 Boilerplate',
statistics: {
commitCount: 0,
storageSize: 1293346,
storageSize: 9933460120,
repositorySize: 0,
lfsObjectsSize: 0,
buildArtifactsSize: 1272375,
......
......@@ -27947,6 +27947,12 @@ msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr ""
msgid "UsageQuota|This project is at risk of being locked."
msgstr ""
msgid "UsageQuota|This project is locked."
msgstr ""
msgid "UsageQuota|Unlimited"
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