Commit fd765430 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '350788-add-statistics-seats-card' into 'master'

Add statistics seats card

See merge request gitlab-org/gitlab!80260
parents eee5d278 b7e33581
import { GlCard } from '@gitlab/ui';
import StatisticsSeatsCard from './statistics_seats_card.vue';
export default {
component: StatisticsSeatsCard,
title: 'usage_quotas/components/statistics_seats_card',
};
const Template = (_, { argTypes }) => ({
components: { StatisticsSeatsCard, GlCard },
props: Object.keys(argTypes),
template: `<gl-card class="gl-w-half">
<statistics-seats-card v-bind="$props">
</statistics-seats-card>
</gl-card>`,
});
export const Default = Template.bind({});
/* eslint-disable @gitlab/require-i18n-strings */
Default.args = {
seatsUsed: 160,
seatsOwed: 10,
purchaseButtonLink: 'purchase.com/test',
purchaseButtonText: 'Add seats',
};
<script>
import { GlLink, GlIcon, GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
export default {
name: 'StatisticsSeatsCard',
components: { GlLink, GlIcon, GlButton },
i18n: {
seatsUsedText: __('Max seats used'),
seatsUsedHelpText: __('Learn more about max seats used'),
seatsOwedText: __('Seats owed'),
seatsOwedHelpText: __('Learn more about seats owed'),
},
helpLinks: {
seatsOwedLink: helpPagePath('subscriptions/gitlab_com/index', { anchor: 'seats-owed' }),
seatsUsedLink: helpPagePath('subscriptions/gitlab_com/index', {
anchor: 'view-your-gitlab-saas-subscription',
}),
},
props: {
/**
* Number of seats used
*/
seatsUsed: {
type: Number,
required: false,
default: null,
},
/**
* Number of seats owed
*/
seatsOwed: {
type: Number,
required: false,
default: null,
},
/**
* Link for purchase seats button
*/
purchaseButtonLink: {
type: String,
required: false,
default: null,
},
/**
* Text for the purchase seats button
*/
purchaseButtonText: {
type: String,
required: false,
default: null,
},
},
computed: {
shouldRenderSeatsUsedBlock() {
return this.seatsUsed !== null;
},
shouldRenderSeatsOwedBlock() {
return this.seatsOwed !== null;
},
},
};
</script>
<template>
<div
class="gl-bg-white gl-border-1 gl-border-gray-100 gl-border-solid gl-p-5 gl-rounded-base gl-display-flex"
>
<div class="gl-flex-grow-1">
<p
v-if="shouldRenderSeatsUsedBlock"
class="gl-font-size-h-display gl-font-weight-bold gl-mb-3"
data-testid="seats-used-block"
>
{{ seatsUsed }}
<span class="gl-font-lg">
{{ $options.i18n.seatsUsedText }}
</span>
<gl-link
:href="$options.helpLinks.seatsUsedLink"
:aria-label="$options.i18n.seatsUsedHelpText"
class="gl-ml-2 gl-relative"
>
<gl-icon name="question-o" />
</gl-link>
</p>
<p
v-if="shouldRenderSeatsOwedBlock"
class="gl-font-size-h-display gl-font-weight-bold gl-mb-0"
data-testid="seats-owed-block"
>
{{ seatsOwed }}
<span class="gl-font-lg">
{{ $options.i18n.seatsOwedText }}
</span>
<gl-link
:href="$options.helpLinks.seatsOwedLink"
:aria-label="$options.i18n.seatsOwedHelpText"
class="gl-ml-2 gl-relative"
>
<gl-icon name="question-o" />
</gl-link>
</p>
</div>
<gl-button
v-if="purchaseButtonLink && purchaseButtonText"
:href="purchaseButtonLink"
category="primary"
variant="confirm"
class="gl-ml-3 gl-align-self-start"
data-testid="purchase-button"
>
{{ purchaseButtonText }}
</gl-button>
</div>
</template>
import { GlLink } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import StatisticsSeatsCard from 'ee/usage_quotas/components/statistics_seats_card.vue';
describe('StatisticsSeatsCard', () => {
let wrapper;
const purchaseButtonLink = 'https://gitlab.com/purchase-more-seats';
const purchaseButtonText = 'Add seats';
const defaultProps = {
seatsUsed: 20,
seatsOwed: 5,
purchaseButtonLink,
purchaseButtonText,
};
const createComponent = (props = {}) => {
wrapper = shallowMountExtended(StatisticsSeatsCard, {
propsData: { ...defaultProps, ...props },
});
};
const findSeatsUsedBlock = () => wrapper.findByTestId('seats-used-block');
const findSeatsOwedBlock = () => wrapper.findByTestId('seats-owed-block');
const findPurchaseButton = () => wrapper.findByTestId('purchase-button');
afterEach(() => {
wrapper.destroy();
});
describe('seats used block', () => {
it('renders seats used block if seatsUsed is passed', () => {
createComponent();
const seatsUsedBlock = findSeatsUsedBlock();
expect(seatsUsedBlock.exists()).toBe(true);
expect(seatsUsedBlock.text()).toContain(20);
expect(seatsUsedBlock.findComponent(GlLink).exists()).toBe(true);
});
it('does not render seats used block if seatsUsed is not passed', () => {
createComponent({ seatsUsed: null });
expect(findSeatsUsedBlock().exists()).toBe(false);
});
});
describe('seats owed block', () => {
it('renders seats owed block if seatsOwed is passed', () => {
createComponent();
const seatsOwedBlock = findSeatsOwedBlock();
expect(seatsOwedBlock.exists()).toBe(true);
expect(seatsOwedBlock.text()).toContain(5);
expect(seatsOwedBlock.findComponent(GlLink).exists()).toBe(true);
});
it('does not render seats owed block if seatsOwed is not passed', () => {
createComponent({ seatsOwed: null });
expect(findSeatsOwedBlock().exists()).toBe(false);
});
});
describe('purchase button', () => {
it('renders purchase button if purchase link and purchase text is passed', () => {
createComponent();
const purchaseButton = findPurchaseButton();
expect(purchaseButton.exists()).toBe(true);
expect(purchaseButton.attributes('href')).toBe(purchaseButtonLink);
expect(purchaseButton.text()).toBe(purchaseButtonText);
});
it('does not render purchase button if purchase link is not passed', () => {
createComponent({ purchaseButtonLink: null });
expect(findPurchaseButton().exists()).toBe(false);
});
it('does not render purchase button if purchase text is not passed', () => {
createComponent({ purchaseButtonText: null });
expect(findPurchaseButton().exists()).toBe(false);
});
});
});
...@@ -21802,6 +21802,12 @@ msgstr "" ...@@ -21802,6 +21802,12 @@ msgstr ""
msgid "Learn more about groups." msgid "Learn more about groups."
msgstr "" msgstr ""
msgid "Learn more about max seats used"
msgstr ""
msgid "Learn more about seats owed"
msgstr ""
msgid "Learn more about shards and replicas in the %{configuration_link_start}Advanced Search configuration%{configuration_link_end} documentation. Changes don't take place until you %{recreated_link_start}recreate%{recreated_link_end} the index." msgid "Learn more about shards and replicas in the %{configuration_link_start}Advanced Search configuration%{configuration_link_end} documentation. Changes don't take place until you %{recreated_link_start}recreate%{recreated_link_end} the index."
msgstr "" msgstr ""
...@@ -22714,6 +22720,9 @@ msgstr "" ...@@ -22714,6 +22720,9 @@ msgstr ""
msgid "Max role" msgid "Max role"
msgstr "" msgstr ""
msgid "Max seats used"
msgstr ""
msgid "Max session time" msgid "Max session time"
msgstr "" msgstr ""
...@@ -32576,6 +32585,9 @@ msgstr "" ...@@ -32576,6 +32585,9 @@ msgstr ""
msgid "Seats" msgid "Seats"
msgstr "" msgstr ""
msgid "Seats owed"
msgstr ""
msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)" msgid "Seats usage data as of %{last_enqueue_time} (Updated daily)"
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