diff --git a/example/e2e/annual-gross-salary.spec.ts b/example/e2e/annual-gross-salary.spec.ts index 4e806832c..e83d57b3a 100644 --- a/example/e2e/annual-gross-salary.spec.ts +++ b/example/e2e/annual-gross-salary.spec.ts @@ -22,7 +22,7 @@ test.describe('annual gross salary', () => { // Using getByText for static text + regex for dynamic part const headerAmount = page.getByText( - /Employee annual gross salary: kr\d+\.\d\d/, + /Employee annual gross salary: SEK\s\d+\.\d\d/, ); await expect(headerAmount).toBeVisible(); }); diff --git a/example/e2e/hiring-budget.spec.ts b/example/e2e/hiring-budget.spec.ts index fe2c3944f..124975941 100644 --- a/example/e2e/hiring-budget.spec.ts +++ b/example/e2e/hiring-budget.spec.ts @@ -19,7 +19,7 @@ test.describe('hiring budget', () => { }); const headerAmount = page.getByText( - /Employee annual gross salary: kr\d{1,3}(,\d{3})*\.\d{2}/, + /Employee annual gross salary: SEK\s\d{1,3}(,\d{3})*\.\d{2}/, ); await expect(headerAmount).toBeVisible(); diff --git a/src/flows/ContractorOnboarding/api.ts b/src/flows/ContractorOnboarding/api.ts index 7c33b5813..a84476902 100644 --- a/src/flows/ContractorOnboarding/api.ts +++ b/src/flows/ContractorOnboarding/api.ts @@ -287,7 +287,7 @@ export const useContractorSubscriptionSchemaField = ( if (opts.price.amount) { formattedPrice = formatCurrency( opts.price.amount, - opts.currency.symbol, + opts.currency.code, ); } const product = opts.product; diff --git a/src/flows/CostCalculator/EstimationResults/EstimationResults.tsx b/src/flows/CostCalculator/EstimationResults/EstimationResults.tsx index 4e430e39b..fd20105a1 100644 --- a/src/flows/CostCalculator/EstimationResults/EstimationResults.tsx +++ b/src/flows/CostCalculator/EstimationResults/EstimationResults.tsx @@ -614,7 +614,7 @@ export const EstimationResults = ({ const formattedSalary = formatCurrency( estimation.regional_currency_costs.annual_gross_salary, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ); return ( @@ -642,16 +642,16 @@ export const EstimationResults = ({ ? [ formatCurrency( estimation.regional_currency_costs.monthly_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), formatCurrency( estimation.employer_currency_costs.monthly_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), ] : formatCurrency( estimation.regional_currency_costs.monthly_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ) } > @@ -661,11 +661,11 @@ export const EstimationResults = ({ label: 'Gross monthly salary', regionalAmount: formatCurrency( estimation.regional_currency_costs.monthly_gross_salary, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs.monthly_gross_salary, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: zendeskArticles.extraPayments.toString(), tooltip: @@ -676,12 +676,12 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .monthly_contributions_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs .monthly_contributions_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), children: estimation.employer_currency_costs.monthly_contributions_breakdown?.map( @@ -691,11 +691,11 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .monthly_contributions_breakdown?.[index]?.amount, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( item.amount, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: item.zendesk_article_id || undefined, tooltip: item.description || undefined, @@ -707,11 +707,11 @@ export const EstimationResults = ({ label: 'Benefits', regionalAmount: formatCurrency( estimation.regional_currency_costs.monthly_benefits_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs.monthly_benefits_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), children: estimation.employer_currency_costs.monthly_benefits_breakdown?.map( @@ -720,11 +720,11 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .monthly_benefits_breakdown?.[index]?.amount, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( item.amount, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: item.zendesk_article_id || undefined, tooltip: item.description || undefined, @@ -738,12 +738,12 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .monthly_management_fee, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs .monthly_management_fee, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), tooltip: 'Discounts may be available based on your commitment and team size. Speak to your account or customer success manager to learn more.', @@ -763,16 +763,16 @@ export const EstimationResults = ({ ? [ formatCurrency( estimation.regional_currency_costs.annual_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), formatCurrency( estimation.employer_currency_costs.annual_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), ] : formatCurrency( estimation.regional_currency_costs.annual_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ) } > @@ -783,22 +783,22 @@ export const EstimationResults = ({ dataSelector: 'annual-gross-salary', regionalAmount: formatCurrency( estimation.regional_currency_costs.annual_gross_salary, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs.annual_gross_salary, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), }, { label: 'Mandatory employer costs', regionalAmount: formatCurrency( estimation.regional_currency_costs.annual_contributions_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs.annual_contributions_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), children: estimation.employer_currency_costs.annual_contributions_breakdown?.map( @@ -807,11 +807,11 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .annual_contributions_breakdown?.[index]?.amount, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( item.amount, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: item.zendesk_article_id || undefined, tooltip: item.description || undefined, @@ -822,11 +822,11 @@ export const EstimationResults = ({ label: 'Benefits', regionalAmount: formatCurrency( estimation.regional_currency_costs.annual_benefits_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs.annual_benefits_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), children: estimation.employer_currency_costs.annual_benefits_breakdown?.map( @@ -835,11 +835,11 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .annual_benefits_breakdown?.[index]?.amount, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( item.amount, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: item.zendesk_article_id || undefined, tooltip: item.description || undefined, @@ -851,12 +851,12 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .extra_statutory_payments_total, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs .extra_statutory_payments_total, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), children: estimation.employer_currency_costs.extra_statutory_payments_breakdown?.map( @@ -865,11 +865,11 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .extra_statutory_payments_breakdown?.[index]?.amount, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( item.amount, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), zendeskId: item.zendesk_article_id || undefined, tooltip: item.description || undefined, @@ -883,12 +883,12 @@ export const EstimationResults = ({ regionalAmount: formatCurrency( estimation.regional_currency_costs .annual_management_fee, - estimation.regional_currency_costs.currency.symbol, + estimation.regional_currency_costs.currency.code, ), employerAmount: formatCurrency( estimation.employer_currency_costs .annual_management_fee, - estimation.employer_currency_costs.currency.symbol, + estimation.employer_currency_costs.currency.code, ), tooltip: 'Discounts may be available based on your commitment and team size. Speak to your account or customer success manager to learn more.', diff --git a/src/flows/CostCalculator/SummaryResults/SummaryResults.tsx b/src/flows/CostCalculator/SummaryResults/SummaryResults.tsx index f23b5e9a2..5197f46b0 100644 --- a/src/flows/CostCalculator/SummaryResults/SummaryResults.tsx +++ b/src/flows/CostCalculator/SummaryResults/SummaryResults.tsx @@ -49,8 +49,8 @@ const useSummaryResults = (estimations: CostCalculatorEstimation[]) => { const groupedCostsPerCountry = Object.values(costsPerCountry).map( ({ country, monthlyTotal, annualTotal }) => ({ country, - monthlyCost: formatCurrency(monthlyTotal, currency.symbol), - annualCost: formatCurrency(annualTotal, currency.symbol), + monthlyCost: formatCurrency(monthlyTotal, currency.code), + annualCost: formatCurrency(annualTotal, currency.code), }), ); @@ -59,13 +59,13 @@ const useSummaryResults = (estimations: CostCalculatorEstimation[]) => { estimations.reduce((acc, estimation) => { return acc + estimation.employer_currency_costs.monthly_total; }, 0), - currency.symbol, + currency.code, ), annualTotal: formatCurrency( estimations.reduce((acc, estimation) => { return acc + estimation.employer_currency_costs.annual_total; }, 0), - currency.symbol, + currency.code, ), }; return { currency, costsPerCountry: groupedCostsPerCountry, employeesCost }; diff --git a/src/flows/CostCalculator/tests/EstimationResults.test.tsx b/src/flows/CostCalculator/tests/EstimationResults.test.tsx index f2db04b7d..f58d557f1 100644 --- a/src/flows/CostCalculator/tests/EstimationResults.test.tsx +++ b/src/flows/CostCalculator/tests/EstimationResults.test.tsx @@ -159,7 +159,7 @@ describe('EstimationResults', () => { // Check if annual gross salary is rendered const formattedSalary = formatCurrency( mockEstimation.regional_currency_costs.annual_gross_salary, - mockEstimation.regional_currency_costs.currency.symbol, + mockEstimation.regional_currency_costs.currency.code, ); const salaryContainer = screen.getByTestId( @@ -209,11 +209,11 @@ describe('EstimationResults', () => { // Check monthly total amounts const monthlyRegionalTotal = formatCurrency( mockEstimation.regional_currency_costs.monthly_total, - mockEstimation.regional_currency_costs.currency.symbol, + mockEstimation.regional_currency_costs.currency.code, ); const monthlyEmployerTotal = formatCurrency( mockEstimation.employer_currency_costs.monthly_total, - mockEstimation.employer_currency_costs.currency.symbol, + mockEstimation.employer_currency_costs.currency.code, ); expect(screen.getByText(monthlyRegionalTotal)).toBeInTheDocument(); @@ -284,12 +284,12 @@ describe('EstimationResults', () => { const expectedRegionalAmount = formatCurrency( mockEstimation.regional_currency_costs.monthly_benefits_breakdown[0] .amount, - mockEstimation.regional_currency_costs.currency.symbol, + mockEstimation.regional_currency_costs.currency.code, ); const expectedEmployerAmount = formatCurrency( mockEstimation.employer_currency_costs.monthly_benefits_breakdown[0] .amount, - mockEstimation.employer_currency_costs.currency.symbol, + mockEstimation.employer_currency_costs.currency.code, ); // Find the amounts in the document @@ -299,11 +299,11 @@ describe('EstimationResults', () => { // Also verify the total core benefits const totalRegionalAmount = formatCurrency( mockEstimation.regional_currency_costs.monthly_benefits_total, - mockEstimation.regional_currency_costs.currency.symbol, + mockEstimation.regional_currency_costs.currency.code, ); const totalEmployerAmount = formatCurrency( mockEstimation.employer_currency_costs.monthly_benefits_total, - mockEstimation.employer_currency_costs.currency.symbol, + mockEstimation.employer_currency_costs.currency.code, ); expect(screen.getByText(totalRegionalAmount)).toBeInTheDocument(); diff --git a/src/lib/__tests__/utils.test.ts b/src/lib/__tests__/utils.test.ts index 82e59f33b..2a4c9c8ff 100644 --- a/src/lib/__tests__/utils.test.ts +++ b/src/lib/__tests__/utils.test.ts @@ -1,5 +1,6 @@ import { JSFFields } from '@/src/types/remoteFlows'; import { + formatCurrency, prettifyFormValues, sanitizeHtml, sanitizeHtmlWithImageErrorHandling, @@ -350,4 +351,27 @@ describe('utils lib', () => { }); }); }); + + describe('formatCurrency', () => { + it('should return "-" for undefined or null amount', () => { + expect(formatCurrency(null)).toBe('-'); + expect(formatCurrency(undefined)).toBe('-'); + }); + + it('should format amount in EUR by default', () => { + expect(formatCurrency(5000)).toBe('€50.00'); + }); + + it('should format amount in specified currency', () => { + expect(formatCurrency(10000, 'USD')).toBe('$100.00'); + }); + + it('should format amount with currency code', () => { + expect(formatCurrency(10000, 'ARS')).toBe('ARS\u00A0100.00'); + }); + + it('should handle zero amount', () => { + expect(formatCurrency(0, 'USD')).toBe('$0.00'); + }); + }); }); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 432f18005..e51aa63e3 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -14,7 +14,7 @@ export function cn(...inputs: ClassValue[]) { export function formatCurrency( amount: number | undefined | null, - symbol = '€', + currencyCode = 'EUR', ): string { if (amount == null) { return '-'; @@ -22,10 +22,11 @@ export function formatCurrency( const value = amount / 100; - return `${symbol}${value.toLocaleString('en-US', { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })}`; + return new Intl.NumberFormat('en', { + style: 'currency', + currency: currencyCode, + currencyDisplay: 'symbol', + }).format(value); } type YupError = Pick & {