Commit f9e98deb authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'usage-quotas-update-storage-tooltip-content' into 'master'

Usage quotas update storage tooltip content

See merge request gitlab-org/gitlab!45913
parents 541aff99 5f225a35
......@@ -169,7 +169,10 @@ export default {
/>
</div>
</div>
<projects-table :projects="namespaceProjects" />
<projects-table
:projects="namespaceProjects"
:additional-purchased-storage-size="namespace.additionalPurchasedStorageSize || 0"
/>
<temporary-storage-increase-modal
v-if="isStorageIncreaseModalVisible"
:limit="formattedNamespaceLimit"
......
......@@ -6,13 +6,27 @@
* lifted this component could replace and be used mainstream.
*/
import { GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { s__, sprintf } from '~/locale';
import ProjectAvatar from '~/vue_shared/components/project_avatar/default.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { formatUsageSize, usageRatioToThresholdLevel } from '../utils';
import { ALERT_THRESHOLD, ERROR_THRESHOLD, WARNING_THRESHOLD } from '../constants';
export default {
i18n: {
warningWithNoPurchasedStorageText: s__(
'UsageQuota|This project is near the free %{actualRepositorySizeLimit} limit and at risk of being locked.',
),
lockedWithNoPurchasedStorageText: s__(
'UsageQuota|This project is locked because it is using %{actualRepositorySizeLimit} of free storage and there is no purchased storage available.',
),
warningWithPurchasedStorageText: s__(
'UsageQuota|This project is at risk of being locked because purchased storage is running low.',
),
lockedWithPurchasedStorageText: s__(
'UsageQuota|This project is locked because it used %{actualRepositorySizeLimit} of free storage and all the purchased storage.',
),
},
components: {
GlIcon,
GlLink,
......@@ -26,6 +40,10 @@ export default {
required: true,
type: Object,
},
additionalPurchasedStorageSize: {
type: Number,
required: true,
},
},
computed: {
projectAvatar() {
......@@ -40,6 +58,9 @@ export default {
name() {
return this.project.nameWithNamespace;
},
hasPurchasedStorage() {
return this.additionalPurchasedStorageSize > 0;
},
storageSize() {
return formatUsageSize(this.project.totalCalculatedUsedStorage);
},
......@@ -53,21 +74,32 @@ export default {
return usageRatioToThresholdLevel(this.excessStorageRatio);
},
status() {
const i18nTextOpts = {
actualRepositorySizeLimit: formatUsageSize(this.project.actualRepositorySizeLimit),
};
if (this.thresholdLevel === ERROR_THRESHOLD) {
const tooltipText = this.hasPurchasedStorage
? this.$options.i18n.lockedWithPurchasedStorageText
: this.$options.i18n.lockedWithNoPurchasedStorageText;
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.'),
tooltipText: sprintf(tooltipText, i18nTextOpts),
};
} else if (
this.thresholdLevel === WARNING_THRESHOLD ||
this.thresholdLevel === ALERT_THRESHOLD
) {
const tooltipText = this.hasPurchasedStorage
? this.$options.i18n.warningWithPurchasedStorageText
: this.$options.i18n.warningWithNoPurchasedStorageText;
return {
bgColor: { 'gl-bg-orange-50': true },
iconClass: 'gl-text-orange-500',
tooltipText: s__('UsageQuota|This project is at risk of being locked.'),
tooltipText: sprintf(tooltipText, i18nTextOpts),
};
}
......
......@@ -14,6 +14,10 @@ export default {
type: Array,
required: true,
},
additionalPurchasedStorageSize: {
type: Number,
required: true,
},
},
computed: {
isAdditionalStorageFlagEnabled() {
......@@ -61,6 +65,7 @@ export default {
v-for="project in projects"
:key="project.id"
:project="project"
:additional-purchased-storage-size="additionalPurchasedStorageSize"
/>
</div>
</template>
......@@ -12,6 +12,7 @@ const createComponent = (propsData = {}) => {
wrapper = shallowMount(ProjectWithExcessStorage, {
propsData: {
project: projects[0],
additionalPurchasedStorageSize: 0,
...propsData,
},
directives: {
......@@ -34,67 +35,113 @@ describe('Storage Counter project component', () => {
wrapper.destroy();
});
it('renders project avatar', () => {
expect(wrapper.find(ProjectAvatar).exists()).toBe(true);
});
it('renders project name', () => {
expect(wrapper.text()).toContain(projects[0].nameWithNamespace);
});
it('renders formatted storage size', () => {
expect(wrapper.text()).toContain(formatUsageSize(projects[0].statistics.storageSize));
});
it('does not render the warning icon if project is not in error state', () => {
expect(findWarningIcon().exists()).toBe(false);
});
it('render row without error state background', () => {
expect(findTableRow().classes('gl-bg-red-50')).toBe(false);
});
describe('renders the row in error state', () => {
beforeEach(() => {
createComponent({ project: projects[2] });
describe('without extra storage purchased', () => {
it('renders project avatar', () => {
expect(wrapper.find(ProjectAvatar).exists()).toBe(true);
});
it('with error state background', () => {
expect(findTableRow().classes('gl-bg-red-50')).toBe(true);
it('renders project name', () => {
expect(wrapper.text()).toContain(projects[0].nameWithNamespace);
});
it('with project link in error state', () => {
expect(findProjectLink().classes('gl-text-red-500!')).toBe(true);
it('renders formatted storage size', () => {
expect(wrapper.text()).toContain(formatUsageSize(projects[0].statistics.storageSize));
});
it('with error icon', () => {
expect(findWarningIcon().exists()).toBe(true);
it('does not render the warning icon if project is not in error state', () => {
expect(findWarningIcon().exists()).toBe(false);
});
it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is locked.');
it('render row without error state background', () => {
expect(findTableRow().classes('gl-bg-red-50')).toBe(false);
});
});
describe('renders the row in warning state', () => {
beforeEach(() => {
createComponent({ project: projects[1] });
});
describe('renders the row in error state', () => {
beforeEach(() => {
createComponent({ project: projects[2] });
});
it('with error state background', () => {
expect(findTableRow().classes('gl-bg-red-50')).toBe(true);
});
it('with project link in error state', () => {
expect(findProjectLink().classes('gl-text-red-500!')).toBe(true);
});
it('with error icon', () => {
expect(findWarningIcon().exists()).toBe(true);
});
it('with warning state background', () => {
expect(findTableRow().classes('gl-bg-orange-50')).toBe(true);
it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe(
'This project is locked because it is using 97.7KiB of free storage and there is no purchased storage available.',
);
});
});
it('with project link in default gray state', () => {
expect(findProjectLink().classes('gl-text-gray-900!')).toBe(true);
describe('renders the row in warning state', () => {
beforeEach(() => {
createComponent({ project: projects[1] });
});
it('with warning 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 near the free 97.7KiB limit and at risk of being locked.',
);
});
});
});
it('with warning icon', () => {
expect(findWarningIcon().exists()).toBe(true);
describe('with extra storage purchased', () => {
describe('if projects is in error state', () => {
beforeEach(() => {
createComponent({
project: projects[2],
additionalPurchasedStorageSize: 100000,
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders purchased storage specific error tooltip ', () => {
expect(getWarningIconTooltipText().title).toBe(
'This project is locked because it used 97.7KiB of free storage and all the purchased storage.',
);
});
});
it('with tooltip', () => {
expect(getWarningIconTooltipText().title).toBe('This project is at risk of being locked.');
describe('if projects is in warning state', () => {
beforeEach(() => {
createComponent({
project: projects[1],
additionalPurchasedStorageSize: 100000,
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders purchased storage specific warning tooltip ', () => {
expect(getWarningIconTooltipText().title).toBe(
'This project is at risk of being locked because purchased storage is running low.',
);
});
});
});
});
......@@ -14,6 +14,7 @@ const createComponent = ({ additionalRepoStorageByNamespace = false } = {}) => {
wrapper = shallowMount(ProjectsTable, {
propsData: {
projects,
additionalPurchasedStorageSize: 0,
},
stubs,
provide: {
......
......@@ -28714,10 +28714,16 @@ msgstr ""
msgid "UsageQuota|This namespace has no projects which use shared runners"
msgstr ""
msgid "UsageQuota|This project is at risk of being locked."
msgid "UsageQuota|This project is at risk of being locked because purchased storage is running low."
msgstr ""
msgid "UsageQuota|This project is locked."
msgid "UsageQuota|This project is locked because it is using %{actualRepositorySizeLimit} of free storage and there is no purchased storage available."
msgstr ""
msgid "UsageQuota|This project is locked because it used %{actualRepositorySizeLimit} of free storage and all the purchased storage."
msgstr ""
msgid "UsageQuota|This project is near the free %{actualRepositorySizeLimit} limit and at risk of being locked."
msgstr ""
msgid "UsageQuota|Total excess storage used"
......
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