Commit 3239e63e authored by Vitaly Slobodin's avatar Vitaly Slobodin

Merge branch 'dz/337632-hint-invalid-input-for-addons-purchase' into 'master'

Improve UX for addons purchase

See merge request gitlab-org/gitlab!71582
parents e544abb4 b1b2a440
...@@ -105,6 +105,7 @@ export default { ...@@ -105,6 +105,7 @@ export default {
name="quantity" name="quantity"
type="number" type="number"
:min="1" :min="1"
:state="isValid"
data-qa-selector="quantity" data-qa-selector="quantity"
class="gl-w-15" class="gl-w-15"
/> />
......
...@@ -90,7 +90,7 @@ export default { ...@@ -90,7 +90,7 @@ export default {
<div class="gl-display-flex gl-justify-content-space-between gl-font-weight-bold gl-my-3"> <div class="gl-display-flex gl-justify-content-space-between gl-font-weight-bold gl-my-3">
<div data-testid="selected-plan"> <div data-testid="selected-plan">
{{ selectedPlanText }} {{ selectedPlanText }}
<span v-if="quantity" data-testid="quantity">{{ <span v-if="hasPositiveQuantity" data-testid="quantity">{{
sprintf($options.i18n.quantity, { quantity }) sprintf($options.i18n.quantity, { quantity })
}}</span> }}</span>
</div> </div>
......
...@@ -43,6 +43,7 @@ export const I18N_STORAGE_TOOLTIP_NOTE = s__( ...@@ -43,6 +43,7 @@ export const I18N_STORAGE_TOOLTIP_NOTE = s__(
export const I18N_DETAILS_STEP_TITLE = s__('Checkout|Purchase details'); export const I18N_DETAILS_STEP_TITLE = s__('Checkout|Purchase details');
export const I18N_DETAILS_NEXT_STEP_BUTTON_TEXT = s__('Checkout|Continue to billing'); export const I18N_DETAILS_NEXT_STEP_BUTTON_TEXT = s__('Checkout|Continue to billing');
export const I18N_DETAILS_FORMULA = s__('Checkout|x %{quantity} %{units} per pack ='); export const I18N_DETAILS_FORMULA = s__('Checkout|x %{quantity} %{units} per pack =');
export const I18N_DETAILS_FORMULA_WITH_ALERT = s__('Checkout|x %{quantity} %{units} per pack');
export const I18N_SUMMARY_QUANTITY = s__('Checkout|(x%{quantity})'); export const I18N_SUMMARY_QUANTITY = s__('Checkout|(x%{quantity})');
export const I18N_SUMMARY_DATES = s__('Checkout|%{startDate} - %{endDate}'); export const I18N_SUMMARY_DATES = s__('Checkout|%{startDate} - %{endDate}');
......
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
I18N_CI_MINUTES_PRODUCT_LABEL, I18N_CI_MINUTES_PRODUCT_LABEL,
I18N_CI_MINUTES_PRODUCT_UNIT, I18N_CI_MINUTES_PRODUCT_UNIT,
I18N_DETAILS_FORMULA, I18N_DETAILS_FORMULA,
I18N_DETAILS_FORMULA_WITH_ALERT,
I18N_CI_MINUTES_FORMULA_TOTAL, I18N_CI_MINUTES_FORMULA_TOTAL,
i18nCIMinutesSummaryTitle, i18nCIMinutesSummaryTitle,
I18N_CI_MINUTES_SUMMARY_TOTAL, I18N_CI_MINUTES_SUMMARY_TOTAL,
...@@ -40,6 +41,7 @@ export default { ...@@ -40,6 +41,7 @@ export default {
productLabel: I18N_CI_MINUTES_PRODUCT_LABEL, productLabel: I18N_CI_MINUTES_PRODUCT_LABEL,
productUnit: I18N_CI_MINUTES_PRODUCT_UNIT, productUnit: I18N_CI_MINUTES_PRODUCT_UNIT,
formula: I18N_DETAILS_FORMULA, formula: I18N_DETAILS_FORMULA,
formulaWithAlert: I18N_DETAILS_FORMULA_WITH_ALERT,
formulaTotal: I18N_CI_MINUTES_FORMULA_TOTAL, formulaTotal: I18N_CI_MINUTES_FORMULA_TOTAL,
summaryTitle: i18nCIMinutesSummaryTitle, summaryTitle: i18nCIMinutesSummaryTitle,
summaryTotal: I18N_CI_MINUTES_SUMMARY_TOTAL, summaryTotal: I18N_CI_MINUTES_SUMMARY_TOTAL,
...@@ -54,17 +56,26 @@ export default { ...@@ -54,17 +56,26 @@ export default {
hasError: false, hasError: false,
}; };
}, },
computed: { methods: {
formulaText() { isQuantityValid(quantity) {
return sprintf(this.$options.i18n.formula, { return Number.isFinite(quantity) && quantity > 0;
},
formulaText(quantity) {
const formulaText = this.isQuantityValid(quantity)
? this.$options.i18n.formula
: this.$options.i18n.formulaWithAlert;
return sprintf(formulaText, {
quantity: formatNumber(CI_MINUTES_PER_PACK), quantity: formatNumber(CI_MINUTES_PER_PACK),
units: this.$options.i18n.productUnit, units: this.$options.i18n.productUnit,
}); });
}, },
},
methods: {
formulaTotal(quantity) { formulaTotal(quantity) {
return sprintf(this.$options.i18n.formulaTotal, { totalCiMinutes: formatNumber(quantity) }); const total = sprintf(this.$options.i18n.formulaTotal, {
totalCiMinutes: formatNumber(quantity),
});
return this.isQuantityValid(quantity) ? total : '';
}, },
summaryTitle(quantity) { summaryTitle(quantity) {
return sprintf(this.$options.i18n.summaryTitle(quantity), { quantity }); return sprintf(this.$options.i18n.summaryTitle(quantity), { quantity });
...@@ -121,7 +132,7 @@ export default { ...@@ -121,7 +132,7 @@ export default {
:alert-text="$options.i18n.alertText" :alert-text="$options.i18n.alertText"
> >
<template #formula="{ quantity }"> <template #formula="{ quantity }">
{{ formulaText }} {{ formulaText(quantity) }}
<strong>{{ formulaTotal(quantity) }}</strong> <strong>{{ formulaTotal(quantity) }}</strong>
</template> </template>
<template #summary-label="{ quantity }"> <template #summary-label="{ quantity }">
......
...@@ -13,6 +13,7 @@ import { ...@@ -13,6 +13,7 @@ import {
I18N_STORAGE_PRODUCT_UNIT, I18N_STORAGE_PRODUCT_UNIT,
I18N_DETAILS_FORMULA, I18N_DETAILS_FORMULA,
I18N_STORAGE_FORMULA_TOTAL, I18N_STORAGE_FORMULA_TOTAL,
I18N_DETAILS_FORMULA_WITH_ALERT,
i18nStorageSummaryTitle, i18nStorageSummaryTitle,
I18N_STORAGE_SUMMARY_TOTAL, I18N_STORAGE_SUMMARY_TOTAL,
I18N_STORAGE_TITLE, I18N_STORAGE_TITLE,
...@@ -43,6 +44,7 @@ export default { ...@@ -43,6 +44,7 @@ export default {
productLabel: I18N_STORAGE_PRODUCT_LABEL, productLabel: I18N_STORAGE_PRODUCT_LABEL,
productUnit: I18N_STORAGE_PRODUCT_UNIT, productUnit: I18N_STORAGE_PRODUCT_UNIT,
formula: I18N_DETAILS_FORMULA, formula: I18N_DETAILS_FORMULA,
formulaWithAlert: I18N_DETAILS_FORMULA_WITH_ALERT,
formulaTotal: I18N_STORAGE_FORMULA_TOTAL, formulaTotal: I18N_STORAGE_FORMULA_TOTAL,
summaryTitle: i18nStorageSummaryTitle, summaryTitle: i18nStorageSummaryTitle,
summaryTotal: I18N_STORAGE_SUMMARY_TOTAL, summaryTotal: I18N_STORAGE_SUMMARY_TOTAL,
...@@ -57,17 +59,24 @@ export default { ...@@ -57,17 +59,24 @@ export default {
hasError: false, hasError: false,
}; };
}, },
computed: { methods: {
formulaText() { isQuantityValid(quantity) {
return sprintf(this.$options.i18n.formula, { return Number.isFinite(quantity) && quantity > 0;
},
formulaText(quantity) {
const formulaText = this.isQuantityValid(quantity)
? this.$options.i18n.formula
: this.$options.i18n.formulaWithAlert;
return sprintf(formulaText, {
quantity: formatNumber(STORAGE_PER_PACK), quantity: formatNumber(STORAGE_PER_PACK),
units: this.$options.i18n.productUnit, units: this.$options.i18n.productUnit,
}); });
}, },
},
methods: {
formulaTotal(quantity) { formulaTotal(quantity) {
return sprintf(this.$options.i18n.formulaTotal, { quantity: formatNumber(quantity) }); const total = sprintf(this.$options.i18n.formulaTotal, { quantity: formatNumber(quantity) });
return this.isQuantityValid(quantity) ? total : '';
}, },
summaryTitle(quantity) { summaryTitle(quantity) {
return sprintf(this.$options.i18n.summaryTitle(quantity), { quantity }); return sprintf(this.$options.i18n.summaryTitle(quantity), { quantity });
...@@ -122,7 +131,7 @@ export default { ...@@ -122,7 +131,7 @@ export default {
:quantity-per-pack="$options.STORAGE_PER_PACK" :quantity-per-pack="$options.STORAGE_PER_PACK"
> >
<template #formula="{ quantity }"> <template #formula="{ quantity }">
{{ formulaText }} {{ formulaText(quantity) }}
<strong>{{ formulaTotal(quantity) }}</strong> <strong>{{ formulaTotal(quantity) }}</strong>
</template> </template>
<template #summary-label="{ quantity }"> <template #summary-label="{ quantity }">
......
...@@ -73,7 +73,7 @@ describe('SummaryDetails', () => { ...@@ -73,7 +73,7 @@ describe('SummaryDetails', () => {
describe('when quantity is less or equal to zero', () => { describe('when quantity is less or equal to zero', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ quantity: 0 }); wrapper = createComponent({ quantity: -1 });
}); });
it('does not render quantity', () => { it('does not render quantity', () => {
......
...@@ -125,5 +125,13 @@ describe('App', () => { ...@@ -125,5 +125,13 @@ describe('App', () => {
expect(findSummaryLabel().text()).toBe('2 CI minute packs'); expect(findSummaryLabel().text()).toBe('2 CI minute packs');
expect(findSummaryTotal().text()).toBe('Total minutes: 2,000'); expect(findSummaryTotal().text()).toBe('Total minutes: 2,000');
}); });
it('are not shown if input is invalid', async () => {
const mockApollo = createMockApolloProvider({}, { quantity: -1 });
wrapper = createComponent(mockApollo);
await waitForPromises();
expect(findQuantityText().text()).toMatchInterpolatedText('x 1,000 minutes per pack');
});
}); });
}); });
...@@ -6784,6 +6784,9 @@ msgstr "" ...@@ -6784,6 +6784,9 @@ msgstr ""
msgid "Checkout|minutes" msgid "Checkout|minutes"
msgstr "" msgstr ""
msgid "Checkout|x %{quantity} %{units} per pack"
msgstr ""
msgid "Checkout|x %{quantity} %{units} per pack =" msgid "Checkout|x %{quantity} %{units} per pack ="
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