Commit 76a454e7 authored by Angelo Gulina's avatar Angelo Gulina

Refactor subscription table

- refactor app
- update / add tests
parent 5af515d6
...@@ -7,7 +7,11 @@ export default { ...@@ -7,7 +7,11 @@ export default {
components: { components: {
SubscriptionTable, SubscriptionTable,
}, },
inject: ['planUpgradeHref', 'planRenewHref', 'namespaceId', 'customerPortalUrl', 'namespaceName'], inject: {
namespaceId: {
default: '',
},
},
created() { created() {
this.setNamespaceId(this.namespaceId); this.setNamespaceId(this.namespaceId);
}, },
...@@ -18,10 +22,5 @@ export default { ...@@ -18,10 +22,5 @@ export default {
</script> </script>
<template> <template>
<subscription-table <subscription-table />
:namespace-name="namespaceName"
:plan-upgrade-href="planUpgradeHref"
:plan-renew-href="planRenewHref"
:customer-portal-url="customerPortalUrl"
/>
</template> </template>
<script> <script>
import { escape } from 'lodash'; import { escape } from 'lodash';
import { mapActions, mapState, mapGetters } from 'vuex'; import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui'; import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import { TABLE_TYPE_DEFAULT, TABLE_TYPE_FREE, TABLE_TYPE_TRIAL } from 'ee/billings/constants'; import { TABLE_TYPE_DEFAULT, TABLE_TYPE_FREE, TABLE_TYPE_TRIAL } from 'ee/billings/constants';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import SubscriptionTableRow from './subscription_table_row.vue'; import SubscriptionTableRow from './subscription_table_row.vue';
import glFeaturesFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
const createButtonProps = (text, href, testId) => ({ text, href, testId }); const createButtonProps = (text, href, testId) => ({ text, href, testId });
export default { export default {
name: 'SubscriptionTable', name: 'SubscriptionTable',
components: { components: {
SubscriptionTableRow, GlButton,
GlLoadingIcon, GlLoadingIcon,
SubscriptionTableRow,
}, },
mixins: [glFeaturesFlagMixin()], mixins: [glFeatureFlagsMixin()],
props: { inject: {
namespaceName: { planUpgradeHref: {
type: String, default: '',
required: true,
}, },
customerPortalUrl: { planRenewHref: {
type: String,
required: false,
default: '', default: '',
}, },
planUpgradeHref: { namespaceId: {
type: String,
required: false,
default: '', default: '',
}, },
planRenewHref: { customerPortalUrl: {
type: String, default: '',
required: false, },
namespaceName: {
default: '', default: '',
}, },
},
inject: {
addSeatsHref: { addSeatsHref: {
default: '', default: '',
}, },
...@@ -65,12 +60,20 @@ export default { ...@@ -65,12 +60,20 @@ export default {
}, },
addSeatsButton() { addSeatsButton() {
return this.canAddSeats return this.canAddSeats
? createButtonProps(s__('SubscriptionTable|Add seats'), this.addSeatsHref, 'add-seats') ? createButtonProps(
s__('SubscriptionTable|Add seats'),
this.addSeatsHref,
'add-seats-button',
)
: null; : null;
}, },
upgradeButton() { upgradeButton() {
return this.canUpgrade return this.canUpgrade
? createButtonProps(s__('SubscriptionTable|Upgrade'), this.upgradeButtonHref) ? createButtonProps(
s__('SubscriptionTable|Upgrade'),
this.upgradeButtonHref,
'upgrade-button',
)
: null; : null;
}, },
upgradeButtonHref() { upgradeButtonHref() {
...@@ -78,12 +81,16 @@ export default { ...@@ -78,12 +81,16 @@ export default {
}, },
renewButton() { renewButton() {
return this.canRenew return this.canRenew
? createButtonProps(s__('SubscriptionTable|Renew'), this.planRenewHref) ? createButtonProps(s__('SubscriptionTable|Renew'), this.planRenewHref, 'renew-button')
: null; : null;
}, },
manageButton() { manageButton() {
return !this.isFreePlan return !this.isFreePlan
? createButtonProps(s__('SubscriptionTable|Manage'), this.customerPortalUrl) ? createButtonProps(
s__('SubscriptionTable|Manage'),
this.customerPortalUrl,
'manage-button',
)
: null; : null;
}, },
buttons() { buttons() {
...@@ -108,6 +115,9 @@ export default { ...@@ -108,6 +115,9 @@ export default {
}, },
methods: { methods: {
...mapActions(['fetchSubscription']), ...mapActions(['fetchSubscription']),
isLast(index) {
return index === this.visibleRows.length - 1;
},
}, },
}; };
</script> </script>
...@@ -118,29 +128,31 @@ export default { ...@@ -118,29 +128,31 @@ export default {
v-if="!isLoadingSubscription && !hasErrorSubscription" v-if="!isLoadingSubscription && !hasErrorSubscription"
class="card gl-mt-3 subscription-table js-subscription-table" class="card gl-mt-3 subscription-table js-subscription-table"
> >
<div class="js-subscription-header card-header"> <div class="card-header" data-testid="subscription-header">
<strong>{{ subscriptionHeader }}</strong> <strong>{{ subscriptionHeader }}</strong>
<div class="controls"> <div class="controls">
<a <gl-button
v-for="(button, index) in buttons" v-for="(button, index) in buttons"
:key="button.text" :key="button.text"
:href="button.href" :href="button.href"
target="_blank"
rel="noopener noreferrer"
class="btn btn-inverted-secondary"
:class="{ 'gl-ml-3': index !== 0 }" :class="{ 'gl-ml-3': index !== 0 }"
:data-testid="button.testId" :data-testid="button.testId"
>{{ button.text }}</a category="secondary"
target="_blank"
variant="info"
>{{ button.text }}</gl-button
> >
</div> </div>
</div> </div>
<div class="card-body flex-grid d-flex flex-column flex-sm-row flex-md-row flex-lg-column"> <div
class="card-body gl-display-flex gl-flex-column gl-sm-flex-direction-row flex-lg-column flex-grid"
>
<subscription-table-row <subscription-table-row
v-for="(row, i) in visibleRows" v-for="(row, i) in visibleRows"
:key="`subscription-rows-${i}`" :key="`subscription-rows-${i}`"
:last="isLast(i)"
:header="row.header" :header="row.header"
:columns="row.columns" :columns="row.columns"
:is-free-plan="isFreePlan"
/> />
</div> </div>
</div> </div>
......
<script> <script>
import { GlIcon, GlButton } from '@gitlab/ui'; import { GlIcon, GlButton } from '@gitlab/ui';
import { mapActions, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { dateInWords } from '~/lib/utils/datetime_utility'; import { dateInWords } from '~/lib/utils/datetime_utility';
import Popover from '~/vue_shared/components/help_popover.vue'; import Popover from '~/vue_shared/components/help_popover.vue';
...@@ -20,7 +20,7 @@ export default { ...@@ -20,7 +20,7 @@ export default {
type: Array, type: Array,
required: true, required: true,
}, },
isFreePlan: { last: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false, default: false,
...@@ -29,6 +29,10 @@ export default { ...@@ -29,6 +29,10 @@ export default {
inject: ['billableSeatsHref'], inject: ['billableSeatsHref'],
computed: { computed: {
...mapState(['hasBillableGroupMembers']), ...mapState(['hasBillableGroupMembers']),
...mapGetters(['isFreePlan']),
rowClasses() {
return !this.last ? 'gl-border-b-gray-100 gl-border-b-1 gl-border-b-solid' : null;
},
}, },
created() { created() {
this.fetchHasBillableGroupMembers(); this.fetchHasBillableGroupMembers();
...@@ -64,7 +68,10 @@ export default { ...@@ -64,7 +68,10 @@ export default {
</script> </script>
<template> <template>
<div class="grid-row d-flex flex-grow-1 flex-column flex-sm-column flex-md-column flex-lg-row"> <div
:class="rowClasses"
class="gl-display-flex gl-flex-grow-1 gl-flex-direction-column gl-lg-flex-direction-row"
>
<div class="grid-cell header-cell" data-testid="header-cell"> <div class="grid-cell header-cell" data-testid="header-cell">
<span class="icon-wrapper"> <span class="icon-wrapper">
<gl-icon v-if="header.icon" class="gl-mr-3" :name="header.icon" /> <gl-icon v-if="header.icon" class="gl-mr-3" :name="header.icon" />
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SubscriptionTable component given a bronze plan with state: isFreePlan=false, upgradable=true, isTrialPlan=false has Upgrade and Renew and Manage buttons 1`] = `
Array [
Object {
"href": "http://test.host/plan/upgrade/bronze",
"text": "Upgrade",
},
Object {
"href": "https://customers.gitlab.com/subscriptions",
"text": "Manage",
},
]
`;
exports[`SubscriptionTable component given a free plan with state: isFreePlan=true, upgradable=true, isTrialPlan=false has Upgrade and Renew and Manage buttons 1`] = `
Array [
Object {
"href": "http://test.host/plan/upgrade/free",
"text": "Upgrade",
},
Object {
"href": "https://customers.gitlab.com/subscriptions",
"text": "Manage",
},
]
`;
exports[`SubscriptionTable component given a gold plan with state: isFreePlan=false, upgradable=false, isTrialPlan=false has Renew and Manage buttons 1`] = `
Array [
Object {
"href": "https://customers.gitlab.com/subscriptions",
"text": "Manage",
},
]
`;
exports[`SubscriptionTable component given a trial-gold plan with state: isFreePlan=false, upgradable=false, isTrialPlan=true has Manage button 1`] = `
Array [
Object {
"href": "https://customers.gitlab.com/subscriptions",
"text": "Manage",
},
]
`;
exports[`SubscriptionTable component when created matches the snapshot 1`] = `
<div>
<gl-loading-icon-stub
class="gl-mt-3 gl-mb-3"
color="dark"
label="Loading subscriptions"
size="lg"
/>
</div>
`;
...@@ -2,7 +2,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; ...@@ -2,7 +2,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import initialStore from 'ee/billings/subscriptions/store'; import initialStore from 'ee/billings/subscriptions/store';
import SubscriptionApp from 'ee/billings/subscriptions/components/app.vue'; import SubscriptionApp from 'ee/billings/subscriptions/components/app.vue';
import SubscriptionTable from 'ee/billings/subscriptions/components/subscription_table.vue';
import * as types from 'ee/billings/subscriptions/store/mutation_types'; import * as types from 'ee/billings/subscriptions/store/mutation_types';
import { mockDataSeats } from 'ee_jest/billings/mock_data'; import { mockDataSeats } from 'ee_jest/billings/mock_data';
...@@ -34,15 +33,9 @@ describe('SubscriptionApp component', () => { ...@@ -34,15 +33,9 @@ describe('SubscriptionApp component', () => {
}); });
}; };
const expectComponentWithProps = (Component, props = {}) => {
const componentWrapper = wrapper.find(Component);
expect(componentWrapper.isVisible()).toBeTruthy();
expect(componentWrapper.props()).toEqual(expect.objectContaining(props));
};
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null;
}); });
describe('on creation', () => { describe('on creation', () => {
...@@ -54,14 +47,5 @@ describe('SubscriptionApp component', () => { ...@@ -54,14 +47,5 @@ describe('SubscriptionApp component', () => {
it('dispatches expected actions on created', () => { it('dispatches expected actions on created', () => {
expect(store.dispatch.mock.calls).toEqual([['setNamespaceId', '42']]); expect(store.dispatch.mock.calls).toEqual([['setNamespaceId', '42']]);
}); });
it('passes the correct props to the subscriptions table', () => {
expectComponentWithProps(SubscriptionTable, {
namespaceName: providedFields.namespaceName,
planUpgradeHref: providedFields.planUpgradeHref,
planRenewHref: providedFields.planRenewHref,
customerPortalUrl: providedFields.customerPortalUrl,
});
});
}); });
}); });
...@@ -137,6 +137,21 @@ describe('subscription table row', () => { ...@@ -137,6 +137,21 @@ describe('subscription table row', () => {
}); });
}); });
describe('with free plan', () => {
const dateColumn = {
id: 'a',
label: 'Column A',
value: 0,
colClass: 'number',
};
it('renders a dash when the value is zero', () => {
createComponent({ props: { columns: [dateColumn] } });
expect(wrapper.find('[data-testid="property-value"]').text()).toBe('-');
});
});
describe('date column', () => { describe('date column', () => {
const dateColumn = { const dateColumn = {
id: 'c', id: 'c',
...@@ -156,7 +171,6 @@ describe('subscription table row', () => { ...@@ -156,7 +171,6 @@ describe('subscription table row', () => {
const outputDate = dateInWords(new Date(d[0], d[1] - 1, d[2])); const outputDate = dateInWords(new Date(d[0], d[1] - 1, d[2]));
expect(currentCol.find('[data-testid="property-label"]').text()).toMatch(dateColumn.label); expect(currentCol.find('[data-testid="property-label"]').text()).toMatch(dateColumn.label);
expect(currentCol.find('[data-testid="property-value"]').text()).toMatch(outputDate); expect(currentCol.find('[data-testid="property-value"]').text()).toMatch(outputDate);
}); });
}); });
......
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