@transferwise/comparison-components 17.1.11 → 17.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -1
- package/dist/es/ComparisonTableHeroContainer-343793c4.js +2 -0
- package/dist/es/{ComparisonTableHeroContainer-1393f690.js.map → ComparisonTableHeroContainer-343793c4.js.map} +1 -1
- package/dist/es/DeliveryEstimation-0067ca5b.js +2 -0
- package/dist/es/{DeliveryEstimation-c096fc7d.js.map → DeliveryEstimation-0067ca5b.js.map} +1 -1
- package/dist/es/{ErrorTemplate-3bdc5c9f.js → ErrorTemplate-6b12db1d.js} +2 -2
- package/dist/es/{ErrorTemplate-3bdc5c9f.js.map → ErrorTemplate-6b12db1d.js.map} +1 -1
- package/dist/es/ExchangeRateIndicator-0071c267.js +2 -0
- package/dist/es/ExchangeRateIndicator-0071c267.js.map +1 -0
- package/dist/es/ProviderComparisonTable-18faf2b2.js +2 -0
- package/dist/es/{ProviderComparisonTable-ff55114a.js.map → ProviderComparisonTable-18faf2b2.js.map} +1 -1
- package/dist/es/ReceiveAmount-ab1ea550.js +2 -0
- package/dist/es/{ReceiveAmount-a51879fe.js.map → ReceiveAmount-ab1ea550.js.map} +1 -1
- package/dist/es/TransferFeeComparisonContainer-32640e8d.js +2 -0
- package/dist/es/TransferFeeComparisonContainer-32640e8d.js.map +1 -0
- package/dist/es/TravelMoney-bbe3d67e.js +2 -0
- package/dist/es/{TravelMoney-c8049afa.js.map → TravelMoney-bbe3d67e.js.map} +1 -1
- package/dist/es/comparison/index.js +1 -1
- package/dist/es/comparison/index.js.map +1 -1
- package/dist/es/comparison-table/index.js +1 -1
- package/dist/es/comparison-table-hero/index.js +1 -1
- package/dist/es/index-8362de5a.js +2 -0
- package/dist/es/{index-5bd6b028.js.map → index-8362de5a.js.map} +1 -1
- package/dist/es/index.js +1 -1
- package/dist/es/index.js.map +1 -1
- package/dist/es/provider-comparison-table/index.js +1 -1
- package/dist/es/provider-comparison-widget/index.js +1 -1
- package/dist/es/provider-comparison-widget/index.js.map +1 -1
- package/dist/es/provider-constants-6f33da72.js +2 -0
- package/dist/es/provider-constants-6f33da72.js.map +1 -0
- package/dist/es/transfer-fee-comparison/index.js +1 -1
- package/dist/es/transformQuotesResponseV2.js +2 -0
- package/dist/es/transformQuotesResponseV2.js.map +1 -0
- package/dist/es/travel-money/index.js +1 -1
- package/dist/umd/comparison-components.css +1 -1
- package/dist/umd/comparison-components.js +6 -6
- package/dist/umd/comparison-components.js.map +1 -1
- package/dist/umd/transfer-fee-comparison/transfer-fee-comparison.css +1 -1
- package/dist/umd/transfer-fee-comparison/transfer-fee-comparison.js +5 -5
- package/dist/umd/transfer-fee-comparison/transfer-fee-comparison.js.map +1 -1
- package/package.json +1 -1
- package/src/index.js +4 -0
- package/src/provider-filters/largest-provider/config.ts +12 -0
- package/src/provider-filters/largest-provider/index.ts +3 -0
- package/src/provider-filters/largest-provider/largest-provider-filter.spec.js +62 -0
- package/src/provider-filters/largest-provider/largest-provider-filter.ts +29 -0
- package/src/resources/clients/quotes/__test__/transformQuotesResponseV2.spec.ts +129 -0
- package/src/resources/clients/quotes/transformQuotesResponseV2.ts +97 -0
- package/src/transfer-fee-comparison/TransferFeeComparison.module.less +29 -0
- package/src/transfer-fee-comparison/TransferFeeComparison.tsx +20 -13
- package/src/transfer-fee-comparison/TransferFeeComparisonContainer.tsx +10 -4
- package/src/transfer-fee-comparison/assets/translations/messages_id.json +1 -1
- package/src/transfer-fee-comparison/domain/LegacyComparisons.ts +24 -0
- package/src/transfer-fee-comparison/domain/Quote.ts +1 -0
- package/src/transfer-fee-comparison/provider-filter-toggle/ProviderFilterToggle.spec.tsx +4 -6
- package/src/transfer-fee-comparison/provider-filter-toggle/ProviderFilterToggle.tsx +40 -31
- package/src/transfer-fee-comparison/provider-filter-toggle/messages_id.json +2 -2
- package/src/transfer-fee-comparison/provider-filter-toggle/provider-filter-toggle.module.less +38 -24
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/TransferFeeComparisonFooter.stories.tsx +1 -0
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/TransferFeeComparisonFooter.tsx +11 -7
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/index.ts +1 -0
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_bg.json +1 -1
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_cs.json +1 -1
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_id.json +2 -2
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_nl.json +1 -1
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_pl.json +2 -2
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_sv.json +1 -1
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_tr.json +2 -2
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/messages_uk.json +1 -1
- package/src/transfer-fee-comparison/transfer-fee-comparison-footer/transfer-fee-comparison-footer.module.less +2 -2
- package/src/transfer-fee-comparison/transfer-fee-comparison-table-row/fee-makeup-popover/messages_id.json +8 -8
- package/src/transfer-fee-comparison/translations.json +22 -22
- package/dist/es/ComparisonTableHeroContainer-1393f690.js +0 -2
- package/dist/es/DeliveryEstimation-c096fc7d.js +0 -2
- package/dist/es/ExchangeRateIndicator-7cefde4a.js +0 -2
- package/dist/es/ExchangeRateIndicator-7cefde4a.js.map +0 -1
- package/dist/es/ProviderComparisonTable-ff55114a.js +0 -2
- package/dist/es/ReceiveAmount-a51879fe.js +0 -2
- package/dist/es/TransferFeeComparisonContainer-462fb7b1.js +0 -2
- package/dist/es/TransferFeeComparisonContainer-462fb7b1.js.map +0 -1
- package/dist/es/TravelMoney-c8049afa.js +0 -2
- package/dist/es/createUrl-73595472.js +0 -2
- package/dist/es/createUrl-73595472.js.map +0 -1
- package/dist/es/index-5bd6b028.js +0 -2
- package/src/transfer-fee-comparison/largest-provider-filter.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transferwise/comparison-components",
|
|
3
|
-
"version": "17.1.
|
|
3
|
+
"version": "17.1.15",
|
|
4
4
|
"description": "Display a comparison table of providers given a route and amount.",
|
|
5
5
|
"homepage": "https://wise.com/gb/compare/",
|
|
6
6
|
"main": "dist/umd/comparison-components.js",
|
package/src/index.js
CHANGED
|
@@ -7,8 +7,10 @@ import { ComparisonTableContainer } from './comparison-table';
|
|
|
7
7
|
import { ComparisonTableHero, ComparisonTableHeroContainer } from './comparison-table-hero';
|
|
8
8
|
import ComparisonTableVersus from './comparison-table-versus';
|
|
9
9
|
import { ProviderComparison } from './provider-comparison-widget';
|
|
10
|
+
import { hasEnoughProviders as hasEnoughLargestProviders } from './provider-filters/largest-provider';
|
|
10
11
|
import { fetchQuotes } from './resources/clients';
|
|
11
12
|
import transformQuotesResponse from './resources/clients/quotes/transformQuotesResponse';
|
|
13
|
+
import transformQuotesResponseV2 from './resources/clients/quotes/transformQuotesResponseV2';
|
|
12
14
|
import {
|
|
13
15
|
isWiseQuote,
|
|
14
16
|
isPartnerQuote,
|
|
@@ -44,6 +46,8 @@ export {
|
|
|
44
46
|
isPartnerQuote,
|
|
45
47
|
isBankQuote,
|
|
46
48
|
isMoneyTransferProviderQuote,
|
|
49
|
+
hasEnoughLargestProviders,
|
|
50
|
+
transformQuotesResponseV2,
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
export default {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import TW_ALIAS from '../../resources/provider-constants';
|
|
2
|
+
const PAYPAL = 'paypal';
|
|
3
|
+
const WESTERN_UNION = 'western-union';
|
|
4
|
+
|
|
5
|
+
export const largestProviderByCountry: Map<string, Array<string>> = new Map([
|
|
6
|
+
['gb', [PAYPAL, WESTERN_UNION, 'hsbc', 'barclays', 'lloyds', 'moneygram', TW_ALIAS]],
|
|
7
|
+
['us', [PAYPAL, WESTERN_UNION, 'chase', 'moneygram', 'wells-fargo', 'xoom', TW_ALIAS]],
|
|
8
|
+
['au', [PAYPAL, WESTERN_UNION, 'commonwealth-bank-of-australia', 'anz', 'westpac', 'national-australia-bank', TW_ALIAS]], //eslint-disable-line prettier/prettier
|
|
9
|
+
['hk', [PAYPAL, WESTERN_UNION, 'hang-seng', 'hsbc-hk', 'ocbc', 'ccb-hk', TW_ALIAS]],
|
|
10
|
+
['nl', [PAYPAL, WESTERN_UNION, 'lacaixa', 'bbva', 'sabadell', TW_ALIAS]],
|
|
11
|
+
['es', [PAYPAL, WESTERN_UNION, 'ing-nl', 'abn-amro-bank', 'rabobank', TW_ALIAS]],
|
|
12
|
+
]);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import TW_ALIAS from '../../resources/provider-constants';
|
|
2
|
+
|
|
3
|
+
import { largestProviderByCountry } from './config';
|
|
4
|
+
import { filter, hasEnoughProviders, supportsCountry } from './largest-provider-filter';
|
|
5
|
+
|
|
6
|
+
describe('largest-provider-filter', () => {
|
|
7
|
+
const quotes = [
|
|
8
|
+
{ provider: { alias: 'lloyds' } },
|
|
9
|
+
{ provider: { alias: TW_ALIAS } },
|
|
10
|
+
{ provider: { alias: 'monzo' } },
|
|
11
|
+
{ provider: { alias: 'barclays' } },
|
|
12
|
+
{ provider: { alias: 'monese' } },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// eslint-disable-next-line unicorn/prefer-spread
|
|
16
|
+
it.each(Array.from(largestProviderByCountry.keys()))(
|
|
17
|
+
'should return true for the country %s',
|
|
18
|
+
(country) => {
|
|
19
|
+
expect(supportsCountry(country)).toStrictEqual(true);
|
|
20
|
+
},
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
it.each(['invalid', 'in', 'sl'])('should return false for the country %s', (country) => {
|
|
24
|
+
expect(supportsCountry(country)).toStrictEqual(false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should filter the quotes based on the largest providers', () => {
|
|
28
|
+
const filteredQuotes = filter(quotes, 'gb');
|
|
29
|
+
|
|
30
|
+
expect(filteredQuotes).toHaveLength(3);
|
|
31
|
+
expect(filteredQuotes).toStrictEqual([
|
|
32
|
+
{ provider: { alias: 'lloyds' } },
|
|
33
|
+
{ provider: { alias: TW_ALIAS } },
|
|
34
|
+
{ provider: { alias: 'barclays' } },
|
|
35
|
+
]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should return the passed in quotes if could not find the supported countries', () => {
|
|
39
|
+
const filteredQuotes = filter(quotes, 'non-supported-country');
|
|
40
|
+
|
|
41
|
+
expect(filteredQuotes).toHaveLength(5);
|
|
42
|
+
expect(filteredQuotes).toStrictEqual(quotes);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('hasEnoughProviders should return true if the country is supported and has the required number of providers', () => {
|
|
46
|
+
const result = hasEnoughProviders(quotes, 'gb', 3);
|
|
47
|
+
|
|
48
|
+
expect(result).toStrictEqual(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('hasEnoughProviders should return false if the country is no supported', () => {
|
|
52
|
+
const result = hasEnoughProviders(quotes, 'in', 3);
|
|
53
|
+
|
|
54
|
+
expect(result).toStrictEqual(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('hasEnoughProviders should return false if available largest providers are less than the required', () => {
|
|
58
|
+
const result = hasEnoughProviders(quotes, 'in', 1);
|
|
59
|
+
|
|
60
|
+
expect(result).toStrictEqual(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Quote from '../../transfer-fee-comparison/domain/Quote';
|
|
2
|
+
|
|
3
|
+
import { largestProviderByCountry } from './config';
|
|
4
|
+
|
|
5
|
+
export const supportsCountry = (country: string): boolean => {
|
|
6
|
+
return largestProviderByCountry.has(country);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const hasEnoughProviders = (
|
|
10
|
+
quotes: Quote[],
|
|
11
|
+
country: string,
|
|
12
|
+
requiredNumberOfProviders = 5,
|
|
13
|
+
): boolean => {
|
|
14
|
+
if (!supportsCountry(country)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const filteredQuotes = filter(quotes, country);
|
|
19
|
+
return filteredQuotes.length >= requiredNumberOfProviders;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const filter = (quotes: Quote[], country: string): Quote[] => {
|
|
23
|
+
if (!supportsCountry(country)) {
|
|
24
|
+
return quotes;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const largestProviders: Array<string> = largestProviderByCountry.get(country) || [];
|
|
28
|
+
return quotes.filter((quote) => largestProviders.includes(quote.provider.alias));
|
|
29
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { FeeType } from '../../../../transfer-fee-comparison/domain/Quote';
|
|
2
|
+
import transformQuotesResponseV2 from '../transformQuotesResponseV2';
|
|
3
|
+
|
|
4
|
+
describe('quotes v3 response transformations to the canonical model V2', () => {
|
|
5
|
+
const inputMidmarketRate = 1;
|
|
6
|
+
|
|
7
|
+
it('given two fees, should get the highest one (no hidden fees)', () => {
|
|
8
|
+
const providerWithMoreThanOneFee = {
|
|
9
|
+
providers: [
|
|
10
|
+
{
|
|
11
|
+
name: 'Monzo',
|
|
12
|
+
alias: 'monzo',
|
|
13
|
+
logo: 'https://dq8dwmysp7hk1.cloudfront.net/logos/monzo.svg',
|
|
14
|
+
partner: true,
|
|
15
|
+
quotes: [
|
|
16
|
+
{
|
|
17
|
+
rate: 1,
|
|
18
|
+
fee: 2,
|
|
19
|
+
dateCollected: new Date('2018-10-23T12:03:27'),
|
|
20
|
+
receivedAmount: 1278.44,
|
|
21
|
+
sendAmount: 1280.44,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
rate: 1,
|
|
25
|
+
fee: 3,
|
|
26
|
+
dateCollected: new Date('2018-10-23T12:03:27'),
|
|
27
|
+
receivedAmount: 1277.44,
|
|
28
|
+
sendAmount: 1280.44,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
sourceCurrency: 'GBP',
|
|
34
|
+
targetCurrency: 'EUR',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const expected = {
|
|
38
|
+
quotes: [
|
|
39
|
+
{
|
|
40
|
+
provider: {
|
|
41
|
+
name: 'Monzo',
|
|
42
|
+
alias: 'monzo',
|
|
43
|
+
logoUrl: 'https://dq8dwmysp7hk1.cloudfront.net/logos/monzo.svg',
|
|
44
|
+
partner: true,
|
|
45
|
+
},
|
|
46
|
+
route: {
|
|
47
|
+
sourceCurrency: 'GBP',
|
|
48
|
+
targetCurrency: 'EUR',
|
|
49
|
+
},
|
|
50
|
+
rate: 1,
|
|
51
|
+
fees: {
|
|
52
|
+
breakdown: [
|
|
53
|
+
{
|
|
54
|
+
type: FeeType.MARKETED,
|
|
55
|
+
amount: { value: 2, currency: 'GBP' },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
total: { value: 2, currency: 'GBP' },
|
|
59
|
+
},
|
|
60
|
+
receiveAmount: { value: 1278.44, currency: 'EUR' },
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
expect(transformQuotesResponseV2(providerWithMoreThanOneFee, inputMidmarketRate)).toStrictEqual(
|
|
66
|
+
expected,
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should show hidden fees when the rate is lower than the midmarket rate', () => {
|
|
71
|
+
const providerWithHiddenFee = {
|
|
72
|
+
providers: [
|
|
73
|
+
{
|
|
74
|
+
name: 'Western Union',
|
|
75
|
+
alias: 'western-union',
|
|
76
|
+
logo: 'https://dq8dwmysp7hk1.cloudfront.net/logos/western-union.svg',
|
|
77
|
+
partner: true,
|
|
78
|
+
quotes: [
|
|
79
|
+
{
|
|
80
|
+
rate: 0.98,
|
|
81
|
+
fee: 2,
|
|
82
|
+
dateCollected: new Date('2018-10-23T12:03:27'),
|
|
83
|
+
receivedAmount: 1252.8712,
|
|
84
|
+
sendAmount: 1280.44,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
sourceCurrency: 'GBP',
|
|
90
|
+
targetCurrency: 'EUR',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const expected = {
|
|
94
|
+
quotes: [
|
|
95
|
+
{
|
|
96
|
+
provider: {
|
|
97
|
+
name: 'Western Union',
|
|
98
|
+
alias: 'western-union',
|
|
99
|
+
logoUrl: 'https://dq8dwmysp7hk1.cloudfront.net/logos/western-union.svg',
|
|
100
|
+
partner: true,
|
|
101
|
+
},
|
|
102
|
+
route: {
|
|
103
|
+
sourceCurrency: 'GBP',
|
|
104
|
+
targetCurrency: 'EUR',
|
|
105
|
+
},
|
|
106
|
+
rate: 0.98,
|
|
107
|
+
fees: {
|
|
108
|
+
breakdown: [
|
|
109
|
+
{
|
|
110
|
+
type: FeeType.MARKETED,
|
|
111
|
+
amount: { value: 2, currency: 'GBP' },
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: FeeType.HIDDEN,
|
|
115
|
+
amount: { value: 25.569, currency: 'GBP' },
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
total: { value: 27.569, currency: 'GBP' },
|
|
119
|
+
},
|
|
120
|
+
receiveAmount: { value: 1252.8712, currency: 'EUR' },
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
expect(transformQuotesResponseV2(providerWithHiddenFee, inputMidmarketRate)).toStrictEqual(
|
|
126
|
+
expected,
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import LegacyComparisons, {
|
|
2
|
+
LegacyQuote,
|
|
3
|
+
} from '../../../transfer-fee-comparison/domain/LegacyComparisons';
|
|
4
|
+
import TransferFeeComparisonQuote, {
|
|
5
|
+
FeeBreakdown,
|
|
6
|
+
FeeType,
|
|
7
|
+
} from '../../../transfer-fee-comparison/domain/Quote';
|
|
8
|
+
|
|
9
|
+
import extractQuote from './extractQuote';
|
|
10
|
+
|
|
11
|
+
export type TransformQuotesResponseType = {
|
|
12
|
+
quotes: TransferFeeComparisonQuote[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function extractHiddenFeeInSourceCurrency(
|
|
16
|
+
sendAmount: number,
|
|
17
|
+
receivedAmount: number,
|
|
18
|
+
marketedFee: number,
|
|
19
|
+
midmarketRate: number,
|
|
20
|
+
) {
|
|
21
|
+
const expectedReceivedAmount = midmarketRate * (sendAmount - marketedFee);
|
|
22
|
+
const hiddenFeePaidOnTargetCurrency =
|
|
23
|
+
receivedAmount < expectedReceivedAmount ? expectedReceivedAmount - receivedAmount : 0;
|
|
24
|
+
|
|
25
|
+
return parseFloat((hiddenFeePaidOnTargetCurrency * (1 / midmarketRate)).toFixed(3));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function extractFees(
|
|
29
|
+
marketedFee: number,
|
|
30
|
+
hiddenFee: number,
|
|
31
|
+
sourceCurrency: string,
|
|
32
|
+
): FeeBreakdown[] {
|
|
33
|
+
const fees: FeeBreakdown[] = [];
|
|
34
|
+
|
|
35
|
+
if (marketedFee > 0) {
|
|
36
|
+
fees.push({
|
|
37
|
+
type: FeeType.MARKETED,
|
|
38
|
+
amount: { value: marketedFee, currency: sourceCurrency },
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (hiddenFee > 0) {
|
|
43
|
+
fees.push({
|
|
44
|
+
type: FeeType.HIDDEN,
|
|
45
|
+
amount: { value: hiddenFee, currency: sourceCurrency },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return fees;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function transformQuotesResponseV2(
|
|
53
|
+
quotesResults: LegacyComparisons,
|
|
54
|
+
midmarketRate: number,
|
|
55
|
+
): TransformQuotesResponseType {
|
|
56
|
+
const quotes = quotesResults.providers.map((p) => {
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
58
|
+
const quote: LegacyQuote = extractQuote(p.quotes, quotesResults.targetCurrency);
|
|
59
|
+
const hiddenFee: number = extractHiddenFeeInSourceCurrency(
|
|
60
|
+
quote.sendAmount,
|
|
61
|
+
quote.receivedAmount,
|
|
62
|
+
quote.fee,
|
|
63
|
+
midmarketRate,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const feesBreakdown: FeeBreakdown[] = extractFees(
|
|
67
|
+
quote.fee,
|
|
68
|
+
hiddenFee,
|
|
69
|
+
quotesResults.sourceCurrency,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
provider: {
|
|
74
|
+
alias: p.alias,
|
|
75
|
+
name: p.name,
|
|
76
|
+
logoUrl: p.logo,
|
|
77
|
+
partner: p.partner,
|
|
78
|
+
},
|
|
79
|
+
route: {
|
|
80
|
+
sourceCurrency: quotesResults.sourceCurrency,
|
|
81
|
+
targetCurrency: quotesResults.targetCurrency,
|
|
82
|
+
},
|
|
83
|
+
rate: quote.rate,
|
|
84
|
+
fees: {
|
|
85
|
+
breakdown: feesBreakdown,
|
|
86
|
+
total: { value: quote.fee + hiddenFee, currency: quotesResults.sourceCurrency },
|
|
87
|
+
},
|
|
88
|
+
receiveAmount: { value: quote.receivedAmount, currency: quotesResults.targetCurrency },
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
quotes,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default transformQuotesResponseV2;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
@component-spacing: 24px;
|
|
2
|
+
|
|
3
|
+
.transfer-fee-comparison__container {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.horizontally-centered() {
|
|
10
|
+
display: flex;
|
|
11
|
+
flex-direction: row;
|
|
12
|
+
justify-content: center;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.transfer-fee-comparison__provider-filter-toggle {
|
|
16
|
+
.horizontally-centered();
|
|
17
|
+
|
|
18
|
+
margin-bottom: @component-spacing;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.transfer-fee-comparison__table {
|
|
22
|
+
.horizontally-centered();
|
|
23
|
+
|
|
24
|
+
margin-bottom: @component-spacing;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.transfer-fee-comparison__footer {
|
|
28
|
+
.horizontally-centered();
|
|
29
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
import styles from './TransferFeeComparison.module.less';
|
|
3
4
|
import ProviderFilteringOption from './domain/ProviderFilteringOption';
|
|
4
5
|
import Quote from './domain/Quote';
|
|
5
6
|
import ProviderFilterToggle from './provider-filter-toggle';
|
|
6
|
-
import TransferFeeComparisonFooter from './transfer-fee-comparison-footer
|
|
7
|
+
import TransferFeeComparisonFooter from './transfer-fee-comparison-footer';
|
|
7
8
|
import TransferFeeComparisonTable from './transfer-fee-comparison-table';
|
|
8
9
|
|
|
9
10
|
export type TransferFeeComparisonProps = {
|
|
@@ -26,21 +27,27 @@ const TransferFeeComparison = ({
|
|
|
26
27
|
urlLocale,
|
|
27
28
|
}: TransferFeeComparisonProps): JSX.Element => {
|
|
28
29
|
return (
|
|
29
|
-
<div>
|
|
30
|
+
<div className={styles.transferFeeComparison__container}>
|
|
30
31
|
{providerFiltering.showToggle ? (
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
<div className={styles.transferFeeComparison__providerFilterToggle}>
|
|
33
|
+
<ProviderFilterToggle
|
|
34
|
+
activeToggle={providerFiltering.selectedOption}
|
|
35
|
+
onToggle={providerFiltering.onProviderFilteringOptionChange}
|
|
36
|
+
locale={locale}
|
|
37
|
+
/>
|
|
38
|
+
</div>
|
|
39
|
+
) : null}
|
|
40
|
+
<div className={styles.transferFeeComparison__table}>
|
|
41
|
+
<TransferFeeComparisonTable
|
|
42
|
+
quotes={quotes}
|
|
43
|
+
midmarketRate={midmarketRate}
|
|
44
|
+
urlLocale={urlLocale}
|
|
34
45
|
locale={locale}
|
|
35
46
|
/>
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
urlLocale={urlLocale}
|
|
41
|
-
locale={locale}
|
|
42
|
-
/>
|
|
43
|
-
<TransferFeeComparisonFooter urlLocale={urlLocale} />
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.transferFeeComparison__footer}>
|
|
49
|
+
<TransferFeeComparisonFooter locale={locale} urlLocale={urlLocale} />
|
|
50
|
+
</div>
|
|
44
51
|
</div>
|
|
45
52
|
);
|
|
46
53
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
|
|
3
|
+
import { filter, hasEnoughProviders } from '../provider-filters/largest-provider';
|
|
4
|
+
|
|
3
5
|
import TransferFeeComparison from './TransferFeeComparison';
|
|
4
6
|
import ProviderFilteringOption from './domain/ProviderFilteringOption';
|
|
5
7
|
import Quote from './domain/Quote';
|
|
6
|
-
import { supportsCountry, filter } from './largest-provider-filter';
|
|
7
8
|
|
|
8
9
|
export type TransferFeeComparisonContainerProps = {
|
|
9
10
|
quotes: Quote[];
|
|
@@ -11,6 +12,7 @@ export type TransferFeeComparisonContainerProps = {
|
|
|
11
12
|
locale: string;
|
|
12
13
|
urlLocale: string;
|
|
13
14
|
enableLargestProviderFiltering: boolean;
|
|
15
|
+
requiredNumberOfLargestProviders?: number;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
// TODO: Needs test
|
|
@@ -20,6 +22,7 @@ const TransferFeeComparisonContainer = ({
|
|
|
20
22
|
locale,
|
|
21
23
|
urlLocale,
|
|
22
24
|
enableLargestProviderFiltering,
|
|
25
|
+
requiredNumberOfLargestProviders = 5,
|
|
23
26
|
}: TransferFeeComparisonContainerProps): JSX.Element => {
|
|
24
27
|
const [providerFilteringOption, setProviderFilteringOption] = useState(
|
|
25
28
|
enableLargestProviderFiltering
|
|
@@ -31,9 +34,12 @@ const TransferFeeComparisonContainer = ({
|
|
|
31
34
|
setProviderFilteringOption(newValue);
|
|
32
35
|
};
|
|
33
36
|
|
|
37
|
+
const filterToggleEnabled =
|
|
38
|
+
enableLargestProviderFiltering &&
|
|
39
|
+
hasEnoughProviders(quotes, urlLocale, requiredNumberOfLargestProviders);
|
|
40
|
+
|
|
34
41
|
const filteredQuotes: Quote[] =
|
|
35
|
-
providerFilteringOption === ProviderFilteringOption.LARGEST_PROVIDERS &&
|
|
36
|
-
supportsCountry(urlLocale)
|
|
42
|
+
providerFilteringOption === ProviderFilteringOption.LARGEST_PROVIDERS && filterToggleEnabled
|
|
37
43
|
? filter(quotes, urlLocale)
|
|
38
44
|
: quotes;
|
|
39
45
|
|
|
@@ -42,7 +48,7 @@ const TransferFeeComparisonContainer = ({
|
|
|
42
48
|
quotes={filteredQuotes}
|
|
43
49
|
midmarketRate={midmarketRate}
|
|
44
50
|
providerFiltering={{
|
|
45
|
-
showToggle:
|
|
51
|
+
showToggle: filterToggleEnabled,
|
|
46
52
|
selectedOption: providerFilteringOption,
|
|
47
53
|
onProviderFilteringOptionChange: onProviderFilteringOptionChange,
|
|
48
54
|
}}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type LegacyComparisons = {
|
|
2
|
+
providers: Provider[];
|
|
3
|
+
sourceCurrency: string;
|
|
4
|
+
targetCurrency: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type Provider = {
|
|
8
|
+
alias: string;
|
|
9
|
+
name: string;
|
|
10
|
+
logo: string;
|
|
11
|
+
partner: boolean;
|
|
12
|
+
quotes: LegacyQuote[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type LegacyQuote = {
|
|
16
|
+
fee: number;
|
|
17
|
+
rate: number;
|
|
18
|
+
receivedAmount: number;
|
|
19
|
+
sendAmount: number;
|
|
20
|
+
dateCollected: Date;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default LegacyComparisons;
|
|
24
|
+
export type { LegacyQuote };
|
|
@@ -34,10 +34,8 @@ describe('<ProviderFilterToggle />', () => {
|
|
|
34
34
|
render(<ProviderFilterToggle activeToggle={activeToggle} onToggle={onToggle} locale="en" />);
|
|
35
35
|
|
|
36
36
|
// expect:
|
|
37
|
-
expect(screen.getByText(activeToggleText)).
|
|
38
|
-
expect(screen.getByText(inactiveToggleText)).
|
|
39
|
-
'providerFilterToggle__activeButton',
|
|
40
|
-
);
|
|
37
|
+
expect(screen.getByText(activeToggleText)).toBeDisabled();
|
|
38
|
+
expect(screen.getByText(inactiveToggleText)).toBeEnabled();
|
|
41
39
|
},
|
|
42
40
|
);
|
|
43
41
|
|
|
@@ -61,8 +59,8 @@ describe('<ProviderFilterToggle />', () => {
|
|
|
61
59
|
);
|
|
62
60
|
|
|
63
61
|
// then::
|
|
64
|
-
expect(screen.getByText('Largest providers')).
|
|
65
|
-
expect(screen.getByText('All providers')).
|
|
62
|
+
expect(screen.getByText('Largest providers')).toBeDisabled();
|
|
63
|
+
expect(screen.getByText('All providers')).toBeEnabled();
|
|
66
64
|
});
|
|
67
65
|
|
|
68
66
|
it.each(TEST_CASES)(
|
|
@@ -3,7 +3,7 @@ import React from 'react';
|
|
|
3
3
|
import { FormattedMessage, IntlProvider } from 'react-intl';
|
|
4
4
|
|
|
5
5
|
import ProviderFilteringOption from '../domain/ProviderFilteringOption';
|
|
6
|
-
import
|
|
6
|
+
import translations from '../translations.json';
|
|
7
7
|
|
|
8
8
|
import styles from './provider-filter-toggle.module.less';
|
|
9
9
|
|
|
@@ -18,43 +18,52 @@ export const ProviderFilterToggle = ({
|
|
|
18
18
|
onToggle,
|
|
19
19
|
locale,
|
|
20
20
|
}: ProviderFilterToggleProps): JSX.Element => {
|
|
21
|
-
const
|
|
22
|
-
const messages: Record<string, string> =
|
|
21
|
+
const typedTranslations: { [language: string]: typeof messages } = translations;
|
|
22
|
+
const messages: Record<string, string> = typedTranslations[locale];
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<IntlProvider messages={messages} locale={locale} defaultLocale="en">
|
|
26
26
|
<div className={styles.providerFilterToggle__toggleContainer}>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
<FormattedMessage id="largest-providers" />
|
|
40
|
-
</Button>
|
|
41
|
-
<Button
|
|
42
|
-
className={[
|
|
43
|
-
styles.providerFilterToggle__rightButton,
|
|
44
|
-
activeToggle === ProviderFilteringOption.ALL_PROVIDERS
|
|
45
|
-
? styles.providerFilterToggle__activeButton
|
|
46
|
-
: '',
|
|
47
|
-
]}
|
|
48
|
-
onClick={() => {
|
|
49
|
-
activeToggle === ProviderFilteringOption.LARGEST_PROVIDERS &&
|
|
50
|
-
onToggle(ProviderFilteringOption.ALL_PROVIDERS);
|
|
51
|
-
}}
|
|
52
|
-
>
|
|
53
|
-
<FormattedMessage id="all-providers" />
|
|
54
|
-
</Button>
|
|
27
|
+
<Toggle
|
|
28
|
+
label={<FormattedMessage id="largest-providers" />}
|
|
29
|
+
value={ProviderFilteringOption.LARGEST_PROVIDERS}
|
|
30
|
+
isActive={activeToggle === ProviderFilteringOption.LARGEST_PROVIDERS}
|
|
31
|
+
onToggle={onToggle}
|
|
32
|
+
/>
|
|
33
|
+
<Toggle
|
|
34
|
+
label={<FormattedMessage id="all-providers" />}
|
|
35
|
+
value={ProviderFilteringOption.ALL_PROVIDERS}
|
|
36
|
+
isActive={activeToggle === ProviderFilteringOption.ALL_PROVIDERS}
|
|
37
|
+
onToggle={onToggle}
|
|
38
|
+
/>
|
|
55
39
|
</div>
|
|
56
40
|
</IntlProvider>
|
|
57
41
|
);
|
|
58
42
|
};
|
|
59
43
|
|
|
44
|
+
type ToggleProps = {
|
|
45
|
+
label: JSX.Element;
|
|
46
|
+
value: ProviderFilteringOption;
|
|
47
|
+
isActive: boolean;
|
|
48
|
+
onToggle: (newValue: ProviderFilteringOption) => void;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const Toggle = ({ label, value, isActive, onToggle }: ToggleProps): JSX.Element => {
|
|
52
|
+
return (
|
|
53
|
+
<Button
|
|
54
|
+
className={`${styles.providerFilterToggle__button} ${
|
|
55
|
+
isActive
|
|
56
|
+
? styles.providerFilterToggle__buttonActive
|
|
57
|
+
: styles.providerFilterToggle__buttonInactive
|
|
58
|
+
}`}
|
|
59
|
+
onClick={() => {
|
|
60
|
+
!isActive && onToggle(value);
|
|
61
|
+
}}
|
|
62
|
+
disabled={isActive}
|
|
63
|
+
>
|
|
64
|
+
{label}
|
|
65
|
+
</Button>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
60
69
|
export default ProviderFilterToggle;
|