@oanda/labs-instruments-table-widget 1.0.44 → 1.0.46

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +372 -0
  2. package/dist/main/InstrumentsTableWidget/InstrumentsTableWidget.js +4 -3
  3. package/dist/main/InstrumentsTableWidget/InstrumentsTableWidget.js.map +1 -1
  4. package/dist/main/InstrumentsTableWidget/Main.js +25 -21
  5. package/dist/main/InstrumentsTableWidget/Main.js.map +1 -1
  6. package/dist/main/InstrumentsTableWidget/config.js +7 -0
  7. package/dist/main/InstrumentsTableWidget/config.js.map +1 -1
  8. package/dist/main/InstrumentsTableWidget/constant.js +2 -1
  9. package/dist/main/InstrumentsTableWidget/constant.js.map +1 -1
  10. package/dist/main/InstrumentsTableWidget/types.js.map +1 -1
  11. package/dist/main/InstrumentsTableWidget/utils.js +21 -15
  12. package/dist/main/InstrumentsTableWidget/utils.js.map +1 -1
  13. package/dist/main/gql/resolveInstrumentsWithFilters.js +1 -1
  14. package/dist/main/gql/resolveInstrumentsWithFilters.js.map +1 -1
  15. package/dist/main/gql/types/gql.js +1 -1
  16. package/dist/main/gql/types/gql.js.map +1 -1
  17. package/dist/main/gql/types/graphql.js +6 -0
  18. package/dist/main/gql/types/graphql.js.map +1 -1
  19. package/dist/module/InstrumentsTableWidget/InstrumentsTableWidget.js +4 -3
  20. package/dist/module/InstrumentsTableWidget/InstrumentsTableWidget.js.map +1 -1
  21. package/dist/module/InstrumentsTableWidget/Main.js +27 -23
  22. package/dist/module/InstrumentsTableWidget/Main.js.map +1 -1
  23. package/dist/module/InstrumentsTableWidget/config.js +7 -0
  24. package/dist/module/InstrumentsTableWidget/config.js.map +1 -1
  25. package/dist/module/InstrumentsTableWidget/constant.js +2 -1
  26. package/dist/module/InstrumentsTableWidget/constant.js.map +1 -1
  27. package/dist/module/InstrumentsTableWidget/types.js.map +1 -1
  28. package/dist/module/InstrumentsTableWidget/utils.js +21 -15
  29. package/dist/module/InstrumentsTableWidget/utils.js.map +1 -1
  30. package/dist/module/gql/resolveInstrumentsWithFilters.js +1 -1
  31. package/dist/module/gql/resolveInstrumentsWithFilters.js.map +1 -1
  32. package/dist/module/gql/types/gql.js +1 -1
  33. package/dist/module/gql/types/gql.js.map +1 -1
  34. package/dist/module/gql/types/graphql.js +6 -0
  35. package/dist/module/gql/types/graphql.js.map +1 -1
  36. package/dist/types/InstrumentsTableWidget/Main.d.ts +2 -2
  37. package/dist/types/InstrumentsTableWidget/constant.d.ts +2 -1
  38. package/dist/types/InstrumentsTableWidget/types.d.ts +1 -0
  39. package/dist/types/InstrumentsTableWidget/utils.d.ts +7 -5
  40. package/dist/types/gql/types/gql.d.ts +2 -2
  41. package/dist/types/gql/types/graphql.d.ts +1 -0
  42. package/package.json +3 -3
  43. package/src/InstrumentsTableWidget/InstrumentsTableWidget.tsx +3 -6
  44. package/src/InstrumentsTableWidget/Main.tsx +33 -10
  45. package/src/InstrumentsTableWidget/config.ts +7 -0
  46. package/src/InstrumentsTableWidget/constant.ts +2 -0
  47. package/src/InstrumentsTableWidget/types.ts +1 -0
  48. package/src/InstrumentsTableWidget/utils.ts +29 -19
  49. package/src/gql/resolveInstrumentsWithFilters.ts +1 -0
  50. package/src/gql/types/gql.ts +3 -3
  51. package/src/gql/types/graphql.ts +5 -0
  52. package/test/utils.test.ts +255 -30
@@ -79,6 +79,13 @@ const normal: HeaderConfigType = {
79
79
  minWidth: '80px',
80
80
  },
81
81
  },
82
+ [DataRecordType.DATA_SOURCE]: {
83
+ displayName: 'Data Source',
84
+ additionalStyles: {
85
+ align: 'right',
86
+ minWidth: '80px',
87
+ },
88
+ },
82
89
  };
83
90
 
84
91
  const longInstruments: HeaderConfigType = {
@@ -1,5 +1,6 @@
1
1
  import { DataRecordType } from '@oanda/labs-widget-common';
2
2
 
3
+ const RAW_PRICE_DIVISION = 'MKTD';
3
4
  const DEFAULT_COUNT = 10;
4
5
 
5
6
  const DEFAULT_TOTAL_COUNT = 50;
@@ -20,4 +21,5 @@ export {
20
21
  DEFAULT_COUNT,
21
22
  DEFAULT_TOTAL_COUNT,
22
23
  INSTRUMENT_TOOLTIP_ID,
24
+ RAW_PRICE_DIVISION,
23
25
  };
@@ -34,4 +34,5 @@ export interface MainProps {
34
34
  recordsPerPage?: number;
35
35
  dataSource: DataSource;
36
36
  isLiveRatesDisabled?: boolean;
37
+ priceType?: PriceType;
37
38
  }
@@ -1,24 +1,34 @@
1
- import type { LiveRatesDataSource } from '@oanda/labs-widget-common';
1
+ import type { DataRecord } from '@oanda/labs-widget-common';
2
2
 
3
- import { DataSource, Division } from '../gql/types/graphql';
4
- import { PriceType } from './types';
3
+ import { DataSource } from '../gql/types/graphql';
5
4
 
6
- const getLiveRatesDivisionCode = (
7
- division: Division,
8
- priceType: PriceType,
9
- dataSource: LiveRatesDataSource
10
- ): Division | string | undefined => {
11
- const divisionMap = {
12
- [PriceType.Raw]: {
13
- [DataSource.Mt5]: Division.Oc,
14
- [DataSource.V20]: 'MKTD',
15
- },
16
- [PriceType.Division]: {
17
- [DataSource.Mt5]: division,
18
- [DataSource.V20]: division,
19
- },
5
+ interface GroupedNames {
6
+ v20instruments: string[];
7
+ mt5instruments: string[];
8
+ }
9
+
10
+ const groupNamesBySource = (items: DataRecord[]) => {
11
+ const initialState: GroupedNames = {
12
+ v20instruments: [],
13
+ mt5instruments: [],
20
14
  };
21
- return divisionMap[priceType]?.[dataSource];
15
+
16
+ return items.reduce((accumulator, currentItem) => {
17
+ if (currentItem.dataSource === DataSource.Mt5) {
18
+ return {
19
+ v20instruments: accumulator.v20instruments,
20
+ mt5instruments: [...accumulator.mt5instruments, currentItem.instrument],
21
+ };
22
+ }
23
+ if (currentItem.dataSource === DataSource.V20) {
24
+ return {
25
+ v20instruments: [...accumulator.v20instruments, currentItem.instrument],
26
+ mt5instruments: accumulator.mt5instruments,
27
+ };
28
+ }
29
+
30
+ return accumulator;
31
+ }, initialState);
22
32
  };
23
33
 
24
- export { getLiveRatesDivisionCode };
34
+ export { groupNamesBySource };
@@ -26,6 +26,7 @@ export const resolveInstrumentsWithFilters = gql`
26
26
  name
27
27
  displayName
28
28
  tradeMode @include(if: $withTradingModes)
29
+ dataSource
29
30
  }
30
31
  totalCount
31
32
  updatedAt @include(if: $withTradingModes)
@@ -13,7 +13,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
13
13
  * Therefore it is highly recommended to use the babel or swc plugin for production.
14
14
  */
15
15
  const documents = {
16
- '\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n':
16
+ '\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n dataSource\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n':
17
17
  types.ResolveInstrumentsWithFiltersDocument,
18
18
  };
19
19
 
@@ -35,8 +35,8 @@ export function graphql(source: string): unknown;
35
35
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
36
36
  */
37
37
  export function graphql(
38
- source: '\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n'
39
- ): (typeof documents)['\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n'];
38
+ source: '\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n dataSource\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n'
39
+ ): (typeof documents)['\n query resolveInstrumentsWithFilters(\n $division: Division!\n $assetClass: [AssetClassName]\n $dataSource: DataSource!\n $instruments: [String]\n $searchPattern: String\n $tradeModes: [TradeMode]\n $count: Int\n $offset: Int\n $withTradingModes: Boolean!\n ) {\n resolveInstrumentsWithFilters(\n division: $division\n assetClass: $assetClass\n dataSource: $dataSource\n instruments: $instruments\n searchPattern: $searchPattern\n count: $count\n offset: $offset\n tradeModes: $tradeModes\n ) {\n instruments {\n name\n displayName\n tradeMode @include(if: $withTradingModes)\n dataSource\n }\n totalCount\n updatedAt @include(if: $withTradingModes)\n }\n }\n'];
40
40
 
41
41
  export function graphql(source: string) {
42
42
  return (documents as any)[source] ?? {};
@@ -551,6 +551,7 @@ export type ResolveInstrumentsWithFiltersQuery = {
551
551
  name: string;
552
552
  displayName: string;
553
553
  tradeMode?: TradeMode;
554
+ dataSource: DataSource;
554
555
  }>;
555
556
  } | null;
556
557
  };
@@ -781,6 +782,10 @@ export const ResolveInstrumentsWithFiltersDocument = {
781
782
  },
782
783
  ],
783
784
  },
785
+ {
786
+ kind: 'Field',
787
+ name: { kind: 'Name', value: 'dataSource' },
788
+ },
784
789
  ],
785
790
  },
786
791
  },
@@ -1,32 +1,257 @@
1
- import { PriceType } from '../src';
2
- import { DataSource, Division } from '../src/gql/types/graphql';
3
- import { getLiveRatesDivisionCode } from '../src/InstrumentsTableWidget/utils';
4
-
5
- describe('getLiveRatesDivisionCode', () => {
6
- it('should return Division.Oc for PriceType.Raw and InstrumentDataSource.Mt5', () => {
7
- const division = Division.Oc;
8
- const priceType = PriceType.Raw;
9
- const dataSource = DataSource.Mt5;
10
- expect(getLiveRatesDivisionCode(division, priceType, dataSource)).toBe(
11
- Division.Oc
12
- );
13
- });
14
-
15
- it('should return "MKTD" for PriceType.Raw and DataSource.V20', () => {
16
- const division = Division.Oc;
17
- const priceType = PriceType.Raw;
18
- const dataSource = DataSource.V20;
19
- expect(getLiveRatesDivisionCode(division, priceType, dataSource)).toBe(
20
- 'MKTD'
21
- );
22
- });
23
-
24
- it('should return the division for PriceType.Division', () => {
25
- const division = Division.Oc;
26
- const priceType = PriceType.Division;
27
- const dataSource = DataSource.Mt5;
28
- expect(getLiveRatesDivisionCode(division, priceType, dataSource)).toBe(
29
- Division.Oc
30
- );
1
+ import { DataSource } from '../src/gql/types/graphql';
2
+ import { groupNamesBySource } from '../src/InstrumentsTableWidget/utils';
3
+
4
+ describe('groupNamesBySource', () => {
5
+ it('should return empty arrays for both sources when given empty input', () => {
6
+ const result = groupNamesBySource([]);
7
+
8
+ expect(result).toEqual({
9
+ v20instruments: [],
10
+ mt5instruments: [],
11
+ });
12
+ });
13
+
14
+ it('should group V20 instruments correctly', () => {
15
+ const items = [
16
+ {
17
+ instrument: 'EUR_USD',
18
+ displayName: 'EUR/USD',
19
+ dataSource: DataSource.V20,
20
+ },
21
+ {
22
+ instrument: 'GBP_USD',
23
+ displayName: 'GBP/USD',
24
+ dataSource: DataSource.V20,
25
+ },
26
+ ];
27
+
28
+ const result = groupNamesBySource(items);
29
+
30
+ expect(result).toEqual({
31
+ v20instruments: ['EUR_USD', 'GBP_USD'],
32
+ mt5instruments: [],
33
+ });
34
+ });
35
+
36
+ it('should group MT5 instruments correctly', () => {
37
+ const items = [
38
+ {
39
+ instrument: 'EUR_USD_CFD',
40
+ displayName: 'EUR/USD CFD',
41
+ dataSource: DataSource.Mt5,
42
+ },
43
+ {
44
+ instrument: 'GBP_USD_CFD',
45
+ displayName: 'GBP/USD CFD',
46
+ dataSource: DataSource.Mt5,
47
+ },
48
+ ];
49
+
50
+ const result = groupNamesBySource(items);
51
+
52
+ expect(result).toEqual({
53
+ v20instruments: [],
54
+ mt5instruments: ['EUR_USD_CFD', 'GBP_USD_CFD'],
55
+ });
56
+ });
57
+
58
+ it('should group mixed V20 and MT5 instruments correctly', () => {
59
+ const items = [
60
+ {
61
+ instrument: 'EUR_USD',
62
+ displayName: 'EUR/USD',
63
+ dataSource: DataSource.V20,
64
+ },
65
+ {
66
+ instrument: 'GBP_USD_CFD',
67
+ displayName: 'GBP/USD CFD',
68
+ dataSource: DataSource.Mt5,
69
+ },
70
+ {
71
+ instrument: 'USD_JPY',
72
+ displayName: 'USD/JPY',
73
+ dataSource: DataSource.V20,
74
+ },
75
+ {
76
+ instrument: 'AUD_USD_CFD',
77
+ displayName: 'AUD/USD CFD',
78
+ dataSource: DataSource.Mt5,
79
+ },
80
+ ];
81
+
82
+ const result = groupNamesBySource(items);
83
+
84
+ expect(result).toEqual({
85
+ v20instruments: ['EUR_USD', 'USD_JPY'],
86
+ mt5instruments: ['GBP_USD_CFD', 'AUD_USD_CFD'],
87
+ });
88
+ });
89
+
90
+ it('should ignore items with unknown data source', () => {
91
+ const items = [
92
+ {
93
+ instrument: 'EUR_USD',
94
+ displayName: 'EUR/USD',
95
+ dataSource: DataSource.V20,
96
+ },
97
+ {
98
+ instrument: 'UNKNOWN_INSTRUMENT',
99
+ displayName: 'Unknown Instrument',
100
+ dataSource: 'UNKNOWN_SOURCE' as any,
101
+ },
102
+ {
103
+ instrument: 'GBP_USD_CFD',
104
+ displayName: 'GBP/USD CFD',
105
+ dataSource: DataSource.Mt5,
106
+ },
107
+ ];
108
+
109
+ const result = groupNamesBySource(items);
110
+
111
+ expect(result).toEqual({
112
+ v20instruments: ['EUR_USD'],
113
+ mt5instruments: ['GBP_USD_CFD'],
114
+ });
115
+ });
116
+
117
+ it('should ignore items with undefined data source', () => {
118
+ const items = [
119
+ {
120
+ instrument: 'EUR_USD',
121
+ displayName: 'EUR/USD',
122
+ dataSource: DataSource.V20,
123
+ },
124
+ {
125
+ instrument: 'UNDEFINED_INSTRUMENT',
126
+ displayName: 'Undefined Instrument',
127
+ dataSource: undefined as any,
128
+ },
129
+ {
130
+ instrument: 'GBP_USD_CFD',
131
+ displayName: 'GBP/USD CFD',
132
+ dataSource: DataSource.Mt5,
133
+ },
134
+ ];
135
+
136
+ const result = groupNamesBySource(items);
137
+
138
+ expect(result).toEqual({
139
+ v20instruments: ['EUR_USD'],
140
+ mt5instruments: ['GBP_USD_CFD'],
141
+ });
142
+ });
143
+
144
+ it('should handle items with missing dataSource property', () => {
145
+ const items = [
146
+ {
147
+ instrument: 'EUR_USD',
148
+ displayName: 'EUR/USD',
149
+ dataSource: DataSource.V20,
150
+ },
151
+ {
152
+ instrument: 'MISSING_SOURCE_INSTRUMENT',
153
+ displayName: 'Missing Source Instrument',
154
+ // dataSource property is missing
155
+ } as any,
156
+ {
157
+ instrument: 'GBP_USD_CFD',
158
+ displayName: 'GBP/USD CFD',
159
+ dataSource: DataSource.Mt5,
160
+ },
161
+ ];
162
+
163
+ const result = groupNamesBySource(items);
164
+
165
+ expect(result).toEqual({
166
+ v20instruments: ['EUR_USD'],
167
+ mt5instruments: ['GBP_USD_CFD'],
168
+ });
169
+ });
170
+
171
+ it('should preserve order of instruments within each source group', () => {
172
+ const items = [
173
+ {
174
+ instrument: 'AUD_USD',
175
+ displayName: 'AUD/USD',
176
+ dataSource: DataSource.V20,
177
+ },
178
+ {
179
+ instrument: 'EUR_USD',
180
+ displayName: 'EUR/USD',
181
+ dataSource: DataSource.V20,
182
+ },
183
+ {
184
+ instrument: 'GBP_USD',
185
+ displayName: 'GBP/USD',
186
+ dataSource: DataSource.V20,
187
+ },
188
+ ];
189
+
190
+ const result = groupNamesBySource(items);
191
+
192
+ expect(result.v20instruments).toEqual(['AUD_USD', 'EUR_USD', 'GBP_USD']);
193
+ });
194
+
195
+ it('should handle large arrays efficiently', () => {
196
+ const v20Instruments = Array.from({ length: 100 }, (_, i) => ({
197
+ instrument: `V20_INSTRUMENT_${i}`,
198
+ displayName: `V20 Instrument ${i}`,
199
+ dataSource: DataSource.V20,
200
+ }));
201
+
202
+ const mt5Instruments = Array.from({ length: 100 }, (_, i) => ({
203
+ instrument: `MT5_INSTRUMENT_${i}`,
204
+ displayName: `MT5 Instrument ${i}`,
205
+ dataSource: DataSource.Mt5,
206
+ }));
207
+
208
+ const items = [...v20Instruments, ...mt5Instruments];
209
+
210
+ const result = groupNamesBySource(items);
211
+
212
+ expect(result.v20instruments).toHaveLength(100);
213
+ expect(result.mt5instruments).toHaveLength(100);
214
+ expect(result.v20instruments[0]).toBe('V20_INSTRUMENT_0');
215
+ expect(result.mt5instruments[0]).toBe('MT5_INSTRUMENT_0');
216
+ });
217
+
218
+ it('should handle items with only V20 data source', () => {
219
+ const items = [
220
+ {
221
+ instrument: 'EUR_USD',
222
+ displayName: 'EUR/USD',
223
+ dataSource: DataSource.V20,
224
+ },
225
+ {
226
+ instrument: 'GBP_USD',
227
+ displayName: 'GBP/USD',
228
+ dataSource: DataSource.V20,
229
+ },
230
+ ];
231
+
232
+ const result = groupNamesBySource(items);
233
+
234
+ expect(result.v20instruments).toEqual(['EUR_USD', 'GBP_USD']);
235
+ expect(result.mt5instruments).toEqual([]);
236
+ });
237
+
238
+ it('should handle items with only MT5 data source', () => {
239
+ const items = [
240
+ {
241
+ instrument: 'EUR_USD_CFD',
242
+ displayName: 'EUR/USD CFD',
243
+ dataSource: DataSource.Mt5,
244
+ },
245
+ {
246
+ instrument: 'GBP_USD_CFD',
247
+ displayName: 'GBP/USD CFD',
248
+ dataSource: DataSource.Mt5,
249
+ },
250
+ ];
251
+
252
+ const result = groupNamesBySource(items);
253
+
254
+ expect(result.v20instruments).toEqual([]);
255
+ expect(result.mt5instruments).toEqual(['EUR_USD_CFD', 'GBP_USD_CFD']);
31
256
  });
32
257
  });