Commit 54a49f06 authored by Savas Vedova's avatar Savas Vedova

Merge branch 'ag-273028-subs-details' into 'master'

Subscription Details: Table and Details components

See merge request gitlab-org/gitlab!57717
parents 377c1cff 4dd1fd94
<script>
import { GlCard } from '@gitlab/ui';
import { detailsLabels } from '../constants';
import SubscriptionDetailsTable from './subscription_details_table.vue';
export default {
name: 'SubscriptionDetailsCard',
components: {
SubscriptionDetailsTable,
GlCard,
},
props: {
detailsFields: {
type: Array,
required: true,
},
headerText: {
type: String,
required: false,
default: '',
},
subscription: {
type: Object,
required: true,
},
},
computed: {
details() {
if (!Object.keys(this.subscription).length) {
return [];
}
return this.detailsFields.map((detail) => ({
canCopy: detail === 'id',
label: detailsLabels[detail],
value: this.subscription[detail],
}));
},
},
};
</script>
<template>
<gl-card>
<template v-if="headerText" #header>
<h6 class="gl-m-0">{{ headerText }}</h6>
</template>
<subscription-details-table :details="details" />
<template #footer>
<slot name="footer"></slot>
</template>
</gl-card>
</template>
<script>
import { GlSkeletonLoader } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { copySubscriptionIdButtonText } from '../constants';
export default {
i18n: {
copySubscriptionIdButtonText,
},
name: 'SubscriptionDetailsTable',
components: {
ClipboardButton,
GlSkeletonLoader,
},
props: {
details: {
type: Array,
required: true,
},
},
methods: {
isNotLast(index) {
return index < this.details.length - 1;
},
},
};
</script>
<template>
<div v-if="!details.length">
<gl-skeleton-loader :lines="1" />
<gl-skeleton-loader :lines="1" />
</div>
<ul v-else class="gl-list-style-none gl-m-0 gl-p-0">
<li
v-for="(detail, index) in details"
:key="detail.label"
:class="{ 'gl-mb-3': isNotLast(index) }"
class="gl-display-flex"
>
<p class="gl-font-weight-bold gl-m-0" data-testid="details-label">{{ detail.label }}:</p>
<p class="gl-m-0 gl-ml-4" data-testid="details-content">{{ detail.value }}</p>
<clipboard-button
v-if="detail.canCopy"
:text="detail.value"
:title="$options.i18n.copySubscriptionIdButtonText"
category="tertiary"
class="gl-ml-2"
size="small"
/>
</li>
</ul>
</template>
import { __, s__ } from '~/locale';
export const subscriptionDetailsHeaderText = s__('CloudLicense|Subscription details');
export const licensedToHeaderText = s__('CloudLicense|Licensed to');
export const manageSubscriptionButtonText = s__('CloudLicense|Manage');
export const syncSubscriptionButtonText = s__('CloudLicense|Sync Subscription details');
export const copySubscriptionIdButtonText = __('Copy');
export const detailsLabels = {
address: __('Address'),
id: s__('CloudLicense|ID'),
company: __('Company'),
email: __('Email'),
lastSync: s__('CloudLicense|Last Sync'),
name: __('Name'),
plan: s__('CloudLicense|Plan'),
startsAt: s__('CloudLicense|Started'),
renews: s__('CloudLicense|Renews'),
};
import { GlCard } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionDetailsCard from 'ee/pages/admin/cloud_licenses/components/subscription_details_card.vue';
import SubscriptionDetailsTable from 'ee/pages/admin/cloud_licenses/components/subscription_details_table.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { license } from '../mock_data';
describe('Subscription Details Card', () => {
let wrapper;
const findCard = () => wrapper.findComponent(GlCard);
const findCardHeader = () => findCard().find('.gl-card-header');
const findCardFooter = () => findCard().find('.gl-card-footer');
const findSubscriptionDetailsTable = () => wrapper.findComponent(SubscriptionDetailsTable);
const createComponent = (
{ detailsFields = ['id', 'plan'], headerText, subscription = license.ULTIMATE } = {},
slots,
) => {
wrapper = extendedWrapper(
shallowMount(SubscriptionDetailsCard, {
propsData: {
detailsFields,
headerText,
subscription,
},
stubs: {
GlCard,
},
slots,
}),
);
};
afterEach(() => {
wrapper.destroy();
});
describe('with data', () => {
beforeEach(() => {
createComponent({
headerText: 'Card header title',
});
});
it('displays a title', () => {
expect(findCard().text()).toBe('Card header title');
});
it('displays the details table component', () => {
expect(findSubscriptionDetailsTable().exists()).toBe(true);
});
it('passes the details to the table component', () => {
expect(findSubscriptionDetailsTable().props('details')).toEqual([
{
canCopy: true,
label: 'ID',
value: '1309188',
},
{
canCopy: false,
label: 'Plan',
value: 'Ultimate',
},
]);
});
});
describe('with empty subscription', () => {
it('passes an empty array to the table component', () => {
createComponent({ subscription: {} });
expect(findSubscriptionDetailsTable().props('details')).toEqual([]);
});
});
describe('with no title', () => {
it('does not display a title', () => {
createComponent();
expect(findCardHeader().exists()).toBe(false);
});
});
describe('with footer', () => {
beforeEach(() => {
createComponent(
{},
{
footer: '<div>Footer content</div>',
},
);
});
it('displays the footer', () => {
expect(findCardFooter().exists()).toBe(true);
});
it('displays the footer text', () => {
expect(findCardFooter().text()).toContain('Footer content');
});
});
});
import { GlSkeletonLoader } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionDetailsTable from 'ee/pages/admin/cloud_licenses/components/subscription_details_table.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
const licenseDetails = [
{
label: 'Row label 1',
value: 'row content 1',
},
{
label: 'Row label 2',
value: 'row content 2',
},
];
const hasFontWeightBold = (wrapper) => wrapper.classes('gl-font-weight-bold');
describe('Subscription Details Table', () => {
let wrapper;
const findContentCells = () => wrapper.findAllByTestId('details-content');
const findLabelCells = () => wrapper.findAllByTestId('details-label');
const findLastRow = () => wrapper.findAll('li').wrappers.slice(-1).pop();
const findClipboardButton = () => wrapper.findComponent(ClipboardButton);
const createComponent = (details = licenseDetails) => {
wrapper = extendedWrapper(shallowMount(SubscriptionDetailsTable, { propsData: { details } }));
};
afterEach(() => {
wrapper.destroy();
});
describe('with content', () => {
beforeEach(() => {
createComponent();
});
it('displays the correct number of rows', () => {
expect(findLabelCells()).toHaveLength(2);
expect(findContentCells()).toHaveLength(2);
});
it('displays the correct content for rows', () => {
expect(findLabelCells().at(0).text()).toBe('Row label 1:');
expect(findContentCells().at(0).text()).toBe('row content 1');
});
it('displays the labels in bold', () => {
expect(findLabelCells().wrappers.every(hasFontWeightBold)).toBe(true);
});
it('does not add space to the last row', () => {
expect(findLastRow().classes('gl-mb-3')).toBe(false);
});
it('does not show a clipboard button', () => {
expect(findClipboardButton().exists()).toBe(false);
});
});
describe('with copy-able detail', () => {
beforeEach(() => {
createComponent([
{
value: 'Something to copy',
canCopy: true,
},
]);
});
it('shows a clipboard button', () => {
expect(findClipboardButton().exists()).toBe(true);
});
it('passes the text to the clipboard', () => {
expect(findClipboardButton().props('text')).toBe('Something to copy');
});
});
describe('with no content', () => {
it('displays a loader', () => {
createComponent([]);
expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
});
});
});
export const license = {
ULTIMATE: {
id: '1309188',
plan: 'Ultimate',
lastSync: 'just now - actual date',
startsAt: '22 February',
renews: 'in 11 months',
},
};
export const activateLicenseMutationResponse = { export const activateLicenseMutationResponse = {
FAILURE: [ FAILURE: [
{ {
......
...@@ -2076,6 +2076,9 @@ msgstr "" ...@@ -2076,6 +2076,9 @@ msgstr ""
msgid "Additional text" msgid "Additional text"
msgstr "" msgstr ""
msgid "Address"
msgstr ""
msgid "Adds" msgid "Adds"
msgstr "" msgstr ""
...@@ -6429,12 +6432,39 @@ msgstr "" ...@@ -6429,12 +6432,39 @@ msgstr ""
msgid "CloudLicense|Activate" msgid "CloudLicense|Activate"
msgstr "" msgstr ""
msgid "CloudLicense|ID"
msgstr ""
msgid "CloudLicense|Last Sync"
msgstr ""
msgid "CloudLicense|Licensed to"
msgstr ""
msgid "CloudLicense|Manage"
msgstr ""
msgid "CloudLicense|Paste your activation code" msgid "CloudLicense|Paste your activation code"
msgstr "" msgstr ""
msgid "CloudLicense|Paste your activation code below" msgid "CloudLicense|Paste your activation code below"
msgstr "" msgstr ""
msgid "CloudLicense|Plan"
msgstr ""
msgid "CloudLicense|Renews"
msgstr ""
msgid "CloudLicense|Started"
msgstr ""
msgid "CloudLicense|Subscription details"
msgstr ""
msgid "CloudLicense|Sync Subscription details"
msgstr ""
msgid "CloudLicense|This instance is currently using the %{planName} plan." msgid "CloudLicense|This instance is currently using the %{planName} plan."
msgstr "" msgstr ""
...@@ -7763,6 +7793,9 @@ msgstr "" ...@@ -7763,6 +7793,9 @@ msgstr ""
msgid "Community forum" msgid "Community forum"
msgstr "" msgstr ""
msgid "Company"
msgstr ""
msgid "Company name" msgid "Company name"
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