@oanda/labs-order-book-widget 1.0.70 → 1.0.72
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 +548 -0
- package/dist/main/OrderBookWidget/ChartWithData.js +62 -0
- package/dist/main/OrderBookWidget/ChartWithData.js.map +1 -0
- package/dist/main/OrderBookWidget/Main.js +46 -0
- package/dist/main/OrderBookWidget/Main.js.map +1 -0
- package/dist/main/OrderBookWidget/OrderBookWidget.js +2 -2
- package/dist/main/OrderBookWidget/OrderBookWidget.js.map +1 -1
- package/dist/main/OrderBookWidget/components/Chart/Chart.js +16 -7
- package/dist/main/OrderBookWidget/components/Chart/Chart.js.map +1 -1
- package/dist/main/OrderBookWidget/components/Chart/constants.js +5 -3
- package/dist/main/OrderBookWidget/components/Chart/constants.js.map +1 -1
- package/dist/main/OrderBookWidget/components/Chart/formatters.js +10 -4
- package/dist/main/OrderBookWidget/components/Chart/formatters.js.map +1 -1
- package/dist/main/OrderBookWidget/components/Chart/getOption.js +81 -36
- package/dist/main/OrderBookWidget/components/Chart/getOption.js.map +1 -1
- package/dist/main/OrderBookWidget/components/Chart/types.js.map +1 -1
- package/dist/main/OrderBookWidget/config.js +69 -1
- package/dist/main/OrderBookWidget/config.js.map +1 -1
- package/dist/main/OrderBookWidget/types.js +20 -0
- package/dist/main/OrderBookWidget/types.js.map +1 -1
- package/dist/main/translations/sources/en.json +3 -0
- package/dist/main/translations/sources/zh_TW.json +3 -0
- package/dist/module/OrderBookWidget/ChartWithData.js +55 -0
- package/dist/module/OrderBookWidget/ChartWithData.js.map +1 -0
- package/dist/module/OrderBookWidget/Main.js +38 -0
- package/dist/module/OrderBookWidget/Main.js.map +1 -0
- package/dist/module/OrderBookWidget/OrderBookWidget.js +2 -2
- package/dist/module/OrderBookWidget/OrderBookWidget.js.map +1 -1
- package/dist/module/OrderBookWidget/components/Chart/Chart.js +17 -8
- package/dist/module/OrderBookWidget/components/Chart/Chart.js.map +1 -1
- package/dist/module/OrderBookWidget/components/Chart/constants.js +4 -2
- package/dist/module/OrderBookWidget/components/Chart/constants.js.map +1 -1
- package/dist/module/OrderBookWidget/components/Chart/formatters.js +10 -4
- package/dist/module/OrderBookWidget/components/Chart/formatters.js.map +1 -1
- package/dist/module/OrderBookWidget/components/Chart/getOption.js +80 -35
- package/dist/module/OrderBookWidget/components/Chart/getOption.js.map +1 -1
- package/dist/module/OrderBookWidget/components/Chart/types.js.map +1 -1
- package/dist/module/OrderBookWidget/config.js +69 -1
- package/dist/module/OrderBookWidget/config.js.map +1 -1
- package/dist/module/OrderBookWidget/types.js +19 -1
- package/dist/module/OrderBookWidget/types.js.map +1 -1
- package/dist/module/translations/sources/en.json +3 -0
- package/dist/module/translations/sources/zh_TW.json +3 -0
- package/dist/types/OrderBookWidget/ChartWithData.d.ts +4 -0
- package/dist/types/OrderBookWidget/Main.d.ts +4 -0
- package/dist/types/OrderBookWidget/components/Chart/Chart.d.ts +1 -1
- package/dist/types/OrderBookWidget/components/Chart/constants.d.ts +4 -2
- package/dist/types/OrderBookWidget/components/Chart/getOption.d.ts +16 -1
- package/dist/types/OrderBookWidget/components/Chart/types.d.ts +11 -1
- package/dist/types/OrderBookWidget/config.d.ts +7 -1
- package/dist/types/OrderBookWidget/types.d.ts +26 -3
- package/package.json +3 -3
- package/src/OrderBookWidget/ChartWithData.tsx +78 -0
- package/src/OrderBookWidget/Main.tsx +40 -0
- package/src/OrderBookWidget/OrderBookWidget.tsx +2 -2
- package/src/OrderBookWidget/components/Chart/Chart.tsx +12 -6
- package/src/OrderBookWidget/components/Chart/constants.ts +4 -2
- package/src/OrderBookWidget/components/Chart/formatters.ts +6 -4
- package/src/OrderBookWidget/components/Chart/getOption.ts +93 -39
- package/src/OrderBookWidget/components/Chart/types.ts +11 -3
- package/src/OrderBookWidget/config.ts +70 -1
- package/src/OrderBookWidget/types.ts +28 -3
- package/src/translations/sources/en.json +3 -0
- package/src/translations/sources/zh_TW.json +3 -0
- package/test/Main.test.tsx +129 -0
- package/test/chartOptions.test.ts +20 -0
- package/dist/main/OrderBookWidget/Widget.js +0 -61
- package/dist/main/OrderBookWidget/Widget.js.map +0 -1
- package/dist/module/OrderBookWidget/Widget.js +0 -53
- package/dist/module/OrderBookWidget/Widget.js.map +0 -1
- package/dist/types/OrderBookWidget/Widget.d.ts +0 -4
- package/src/OrderBookWidget/Widget.tsx +0 -62
- package/test/Widget.test.tsx +0 -71
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
import { colorPalette, getGridLines, getZoomControls } from '@oanda/labs-widget-common';
|
|
2
2
|
import {
|
|
3
|
-
INITIAL_BARS,
|
|
3
|
+
INITIAL_BARS,
|
|
4
|
+
CHART_WIDTH,
|
|
5
|
+
CHART_HEIGHT_DESKTOP,
|
|
6
|
+
CHART_HEIGHT_MOBILE,
|
|
7
|
+
X_LABEL_SIZE,
|
|
8
|
+
Y_LABEL_SIZE_DESKTOP,
|
|
9
|
+
Y_LABEL_SIZE_MOBILE,
|
|
10
|
+
ZOOM_CONTROL_HEIGHT,
|
|
4
11
|
} from './constants';
|
|
5
12
|
import { GetOptionType, GetResponsiveOptionsProps } from './types';
|
|
6
13
|
import { tooltipFormatter } from './formatters';
|
|
7
14
|
|
|
8
|
-
export const
|
|
15
|
+
export const getResponsiveOption = (
|
|
16
|
+
{
|
|
17
|
+
isDark, isOrderBook, isDesktop, lang,
|
|
18
|
+
}
|
|
19
|
+
: GetResponsiveOptionsProps,
|
|
20
|
+
) => {
|
|
9
21
|
const desktopGridLines = getGridLines({
|
|
10
22
|
isDark,
|
|
11
23
|
chartWidth: CHART_WIDTH,
|
|
12
|
-
chartHeight:
|
|
13
|
-
xLabelsSize: X_LABEL_SIZE,
|
|
14
|
-
yLabelSize: Y_LABEL_SIZE_DESKTOP,
|
|
24
|
+
chartHeight: isDesktop ? CHART_HEIGHT_DESKTOP : CHART_HEIGHT_MOBILE,
|
|
25
|
+
xLabelsSize: isDesktop ? X_LABEL_SIZE : X_LABEL_SIZE + ZOOM_CONTROL_HEIGHT,
|
|
26
|
+
yLabelSize: isDesktop ? Y_LABEL_SIZE_DESKTOP : Y_LABEL_SIZE_MOBILE,
|
|
27
|
+
bottomLeftBox: isDesktop,
|
|
28
|
+
marginBottom: isDesktop ? 0 : ZOOM_CONTROL_HEIGHT,
|
|
15
29
|
});
|
|
16
30
|
|
|
17
31
|
return {
|
|
@@ -20,8 +34,25 @@ export const getDesktopOption = ({ isDark, isOrderBook }: GetResponsiveOptionsPr
|
|
|
20
34
|
name: 'main-grid',
|
|
21
35
|
top: '48px',
|
|
22
36
|
left: '0px',
|
|
23
|
-
right: `${Y_LABEL_SIZE_DESKTOP}px`,
|
|
24
|
-
bottom: `${X_LABEL_SIZE}px`,
|
|
37
|
+
right: `${isDesktop ? Y_LABEL_SIZE_DESKTOP : Y_LABEL_SIZE_MOBILE}px`,
|
|
38
|
+
bottom: `${isDesktop ? X_LABEL_SIZE : X_LABEL_SIZE + ZOOM_CONTROL_HEIGHT}px`,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
yAxis: {
|
|
42
|
+
axisLabel: {
|
|
43
|
+
margin: isDesktop ? 10 : 0,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
series: [
|
|
47
|
+
{
|
|
48
|
+
type: 'custom',
|
|
49
|
+
name: 'current-price',
|
|
50
|
+
id: 'current-price',
|
|
51
|
+
markLine: {
|
|
52
|
+
label: {
|
|
53
|
+
padding: isDesktop ? [5, 15, 5, 15] : [5, 12, 5, 5],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
25
56
|
},
|
|
26
57
|
],
|
|
27
58
|
graphic: [
|
|
@@ -58,14 +89,14 @@ export const getDesktopOption = ({ isDark, isOrderBook }: GetResponsiveOptionsPr
|
|
|
58
89
|
fill: isDark ? colorPalette.white : colorPalette.black,
|
|
59
90
|
width: 70,
|
|
60
91
|
height: 30,
|
|
61
|
-
text: isOrderBook ? '
|
|
92
|
+
text: lang(isOrderBook ? 'sell' : 'short'),
|
|
62
93
|
},
|
|
63
94
|
},
|
|
64
95
|
],
|
|
65
96
|
},
|
|
66
97
|
{
|
|
67
98
|
type: 'group',
|
|
68
|
-
right: '
|
|
99
|
+
right: `${(isDesktop ? Y_LABEL_SIZE_DESKTOP : Y_LABEL_SIZE_MOBILE) + 8}px'`,
|
|
69
100
|
top: '56px',
|
|
70
101
|
silent: true,
|
|
71
102
|
children: [
|
|
@@ -95,7 +126,7 @@ export const getDesktopOption = ({ isDark, isOrderBook }: GetResponsiveOptionsPr
|
|
|
95
126
|
fill: isDark ? colorPalette.white : colorPalette.black,
|
|
96
127
|
width: 70,
|
|
97
128
|
height: 30,
|
|
98
|
-
text: isOrderBook ? '
|
|
129
|
+
text: lang(isOrderBook ? 'buy' : 'long'),
|
|
99
130
|
},
|
|
100
131
|
},
|
|
101
132
|
],
|
|
@@ -105,11 +136,12 @@ export const getDesktopOption = ({ isDark, isOrderBook }: GetResponsiveOptionsPr
|
|
|
105
136
|
};
|
|
106
137
|
|
|
107
138
|
export const getOption: GetOptionType = ({
|
|
108
|
-
data, precision, isDark, isOrderBook,
|
|
139
|
+
data, precision, isDark, isOrderBook, isDesktop, lang,
|
|
109
140
|
}) => {
|
|
110
141
|
const buckets = data.orderPositionBooks[0]?.buckets || [];
|
|
111
142
|
const bucketWidth = data.orderPositionBooks[0]?.bucketWidth!;
|
|
112
143
|
const price = data.orderPositionBooks[0]?.price!;
|
|
144
|
+
const bucketPrecision = bucketWidth.toString().split('.')[1].length || 0;
|
|
113
145
|
|
|
114
146
|
const dataset = buckets.map((item) => ([
|
|
115
147
|
item!.price,
|
|
@@ -133,7 +165,7 @@ export const getOption: GetOptionType = ({
|
|
|
133
165
|
isDark ? colorPalette.orange : colorPalette.raspberryDark,
|
|
134
166
|
],
|
|
135
167
|
title: {
|
|
136
|
-
text: isOrderBook ? '
|
|
168
|
+
text: lang(isOrderBook ? 'open_orders' : 'open_positions').toUpperCase(),
|
|
137
169
|
padding: 20,
|
|
138
170
|
textStyle: {
|
|
139
171
|
fontSize: 14,
|
|
@@ -161,12 +193,23 @@ export const getOption: GetOptionType = ({
|
|
|
161
193
|
axisPointer: {
|
|
162
194
|
axis: 'y',
|
|
163
195
|
},
|
|
164
|
-
formatter: (val) => tooltipFormatter(
|
|
165
|
-
(val as { data: number[] }[])[0].data,
|
|
166
|
-
precision,
|
|
196
|
+
formatter: (val) => tooltipFormatter({
|
|
197
|
+
data: (val as { data: number[] }[])[0].data,
|
|
198
|
+
precision: bucketPrecision,
|
|
167
199
|
isOrderBook,
|
|
168
|
-
|
|
200
|
+
lang,
|
|
201
|
+
}),
|
|
202
|
+
extraCssText: 'z-index: 1',
|
|
169
203
|
},
|
|
204
|
+
grid: [
|
|
205
|
+
{
|
|
206
|
+
name: 'main-grid',
|
|
207
|
+
top: '48px',
|
|
208
|
+
left: '0px',
|
|
209
|
+
right: `${isDesktop ? Y_LABEL_SIZE_DESKTOP : Y_LABEL_SIZE_MOBILE}px`,
|
|
210
|
+
bottom: `${isDesktop ? X_LABEL_SIZE : X_LABEL_SIZE + ZOOM_CONTROL_HEIGHT}px`,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
170
213
|
xAxis: {
|
|
171
214
|
type: 'value',
|
|
172
215
|
min: range * -1.05,
|
|
@@ -186,10 +229,11 @@ export const getOption: GetOptionType = ({
|
|
|
186
229
|
axisLine: { show: false },
|
|
187
230
|
axisTick: { show: false },
|
|
188
231
|
axisLabel: {
|
|
232
|
+
margin: isDesktop ? 10 : 0,
|
|
189
233
|
showMaxLabel: false,
|
|
190
234
|
showMinLabel: false,
|
|
191
235
|
padding: [0, 0, 0, 10],
|
|
192
|
-
formatter: (value) => value.toFixed(
|
|
236
|
+
formatter: (value) => value.toFixed(bucketPrecision),
|
|
193
237
|
},
|
|
194
238
|
},
|
|
195
239
|
dataset: {
|
|
@@ -198,6 +242,8 @@ export const getOption: GetOptionType = ({
|
|
|
198
242
|
series: [
|
|
199
243
|
{
|
|
200
244
|
type: 'custom',
|
|
245
|
+
name: 'sell-short',
|
|
246
|
+
id: 'sell-short',
|
|
201
247
|
clip: true,
|
|
202
248
|
encode: {
|
|
203
249
|
x: 1,
|
|
@@ -234,34 +280,14 @@ export const getOption: GetOptionType = ({
|
|
|
234
280
|
},
|
|
235
281
|
{
|
|
236
282
|
type: 'custom',
|
|
283
|
+
name: 'buy-long',
|
|
284
|
+
id: 'buy-long',
|
|
237
285
|
clip: true,
|
|
238
286
|
encode: {
|
|
239
287
|
x: 1,
|
|
240
288
|
y: 0,
|
|
241
289
|
tooltip: 2,
|
|
242
290
|
},
|
|
243
|
-
markLine: {
|
|
244
|
-
animation: false,
|
|
245
|
-
silent: true,
|
|
246
|
-
precision: 4,
|
|
247
|
-
symbol: ['none', 'triangle'],
|
|
248
|
-
symbolRotate: 90,
|
|
249
|
-
symbolSize: [20, 10],
|
|
250
|
-
lineStyle: {
|
|
251
|
-
color: isDark ? colorPalette.orange : colorPalette.bottleGreenDark,
|
|
252
|
-
width: 1,
|
|
253
|
-
},
|
|
254
|
-
label: {
|
|
255
|
-
padding: [5, 15, 5, 15],
|
|
256
|
-
color: isDark ? colorPalette.black : colorPalette.white,
|
|
257
|
-
backgroundColor: isDark ? colorPalette.orange : colorPalette.bottleGreenDark,
|
|
258
|
-
},
|
|
259
|
-
data: [
|
|
260
|
-
{
|
|
261
|
-
yAxis: price,
|
|
262
|
-
},
|
|
263
|
-
],
|
|
264
|
-
},
|
|
265
291
|
renderItem: (params, api) => {
|
|
266
292
|
const yValue = api.value(0);
|
|
267
293
|
const xStart = api.coord([api.value(2), yValue]);
|
|
@@ -290,6 +316,34 @@ export const getOption: GetOptionType = ({
|
|
|
290
316
|
};
|
|
291
317
|
},
|
|
292
318
|
},
|
|
319
|
+
{
|
|
320
|
+
type: 'custom',
|
|
321
|
+
name: 'current-price',
|
|
322
|
+
id: 'current-price',
|
|
323
|
+
markLine: {
|
|
324
|
+
animation: false,
|
|
325
|
+
silent: true,
|
|
326
|
+
precision,
|
|
327
|
+
symbol: ['none', 'triangle'],
|
|
328
|
+
symbolRotate: 90,
|
|
329
|
+
symbolSize: [20, 10],
|
|
330
|
+
lineStyle: {
|
|
331
|
+
color: isDark ? colorPalette.orange : colorPalette.bottleGreenDark,
|
|
332
|
+
width: 1,
|
|
333
|
+
},
|
|
334
|
+
label: {
|
|
335
|
+
padding: isDesktop ? [5, 15, 5, 15] : [5, 12, 5, 5],
|
|
336
|
+
color: isDark ? colorPalette.black : colorPalette.white,
|
|
337
|
+
backgroundColor: isDark ? colorPalette.orange : colorPalette.bottleGreenDark,
|
|
338
|
+
},
|
|
339
|
+
data: [
|
|
340
|
+
{
|
|
341
|
+
yAxis: price,
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
renderItem: () => null,
|
|
346
|
+
},
|
|
293
347
|
],
|
|
294
348
|
}
|
|
295
349
|
);
|
|
@@ -4,11 +4,14 @@ import { GetOrderPositionBooksQuery } from '../../../gql/types/graphql';
|
|
|
4
4
|
export interface ChartProps {
|
|
5
5
|
data: GetOrderPositionBooksQuery;
|
|
6
6
|
isOrderBook: boolean;
|
|
7
|
+
precision: number;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export interface GetResponsiveOptionsProps {
|
|
11
|
+
isDesktop: boolean;
|
|
10
12
|
isDark: boolean;
|
|
11
13
|
isOrderBook: boolean;
|
|
14
|
+
lang: (label: string) => string;
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
export interface GetOptionProps {
|
|
@@ -16,6 +19,8 @@ export interface GetOptionProps {
|
|
|
16
19
|
precision: number;
|
|
17
20
|
isDark: boolean;
|
|
18
21
|
isOrderBook: boolean;
|
|
22
|
+
isDesktop: boolean;
|
|
23
|
+
lang: (label: string) => string;
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
export type GetOptionType = (
|
|
@@ -23,7 +28,10 @@ export type GetOptionType = (
|
|
|
23
28
|
) => EChartsOption;
|
|
24
29
|
|
|
25
30
|
export type TooltipFormatterType = (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
props : {
|
|
32
|
+
data: number[],
|
|
33
|
+
precision: number,
|
|
34
|
+
isOrderBook: boolean,
|
|
35
|
+
lang: (label: string) => string;
|
|
36
|
+
}
|
|
29
37
|
) => string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BookType } from '../gql/types/graphql';
|
|
2
|
+
import { InstrumentId } from './types';
|
|
2
3
|
|
|
3
4
|
const navigationConfig = [{
|
|
4
5
|
id: BookType.Order,
|
|
@@ -8,4 +9,72 @@ const navigationConfig = [{
|
|
|
8
9
|
label: 'position_book',
|
|
9
10
|
}];
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
const instrumentSelectConfig = [{
|
|
13
|
+
id: InstrumentId.EUR_AUD,
|
|
14
|
+
label: 'EUR/AUD',
|
|
15
|
+
}, {
|
|
16
|
+
id: InstrumentId.EUR_GBP,
|
|
17
|
+
label: 'EUR/GBP',
|
|
18
|
+
}, {
|
|
19
|
+
id: InstrumentId.EUR_JPY,
|
|
20
|
+
label: 'EUR/JPY',
|
|
21
|
+
}, {
|
|
22
|
+
id: InstrumentId.EUR_USD,
|
|
23
|
+
label: 'EUR/USD',
|
|
24
|
+
}, {
|
|
25
|
+
id: InstrumentId.EUR_CHF,
|
|
26
|
+
label: 'EUR/CHF',
|
|
27
|
+
}, {
|
|
28
|
+
id: InstrumentId.USD_CHF,
|
|
29
|
+
label: 'USD/CHF',
|
|
30
|
+
}, {
|
|
31
|
+
id: InstrumentId.USD_JPY,
|
|
32
|
+
label: 'USD/JPY',
|
|
33
|
+
}, {
|
|
34
|
+
id: InstrumentId.USD_CAD,
|
|
35
|
+
label: 'USD/CAD',
|
|
36
|
+
}, {
|
|
37
|
+
id: InstrumentId.GBP_USD,
|
|
38
|
+
label: 'GBP/USD',
|
|
39
|
+
}, {
|
|
40
|
+
id: InstrumentId.GBP_JPY,
|
|
41
|
+
label: 'GBP/JPY',
|
|
42
|
+
}, {
|
|
43
|
+
id: InstrumentId.GBP_CHF,
|
|
44
|
+
label: 'GBP/CHF',
|
|
45
|
+
}, {
|
|
46
|
+
id: InstrumentId.AUD_JPY,
|
|
47
|
+
label: 'AUD/JPY',
|
|
48
|
+
}, {
|
|
49
|
+
id: InstrumentId.AUD_USD,
|
|
50
|
+
label: 'AUD/USD',
|
|
51
|
+
}, {
|
|
52
|
+
id: InstrumentId.NZD_USD,
|
|
53
|
+
label: 'NZD/USD',
|
|
54
|
+
}, {
|
|
55
|
+
id: InstrumentId.XAU_USD,
|
|
56
|
+
label: 'XAU/USD',
|
|
57
|
+
}, {
|
|
58
|
+
id: InstrumentId.XAG_USD,
|
|
59
|
+
label: 'XAG/USD',
|
|
60
|
+
}];
|
|
61
|
+
|
|
62
|
+
const instrumentPrecisionConfig: Record<InstrumentId, number> = {
|
|
63
|
+
[InstrumentId.EUR_AUD]: 5,
|
|
64
|
+
[InstrumentId.EUR_GBP]: 5,
|
|
65
|
+
[InstrumentId.EUR_JPY]: 3,
|
|
66
|
+
[InstrumentId.EUR_USD]: 5,
|
|
67
|
+
[InstrumentId.EUR_CHF]: 5,
|
|
68
|
+
[InstrumentId.USD_CHF]: 5,
|
|
69
|
+
[InstrumentId.USD_JPY]: 3,
|
|
70
|
+
[InstrumentId.USD_CAD]: 5,
|
|
71
|
+
[InstrumentId.GBP_USD]: 5,
|
|
72
|
+
[InstrumentId.GBP_JPY]: 3,
|
|
73
|
+
[InstrumentId.GBP_CHF]: 5,
|
|
74
|
+
[InstrumentId.AUD_JPY]: 3,
|
|
75
|
+
[InstrumentId.AUD_USD]: 5,
|
|
76
|
+
[InstrumentId.NZD_USD]: 5,
|
|
77
|
+
[InstrumentId.XAU_USD]: 3,
|
|
78
|
+
[InstrumentId.XAG_USD]: 5,
|
|
79
|
+
};
|
|
80
|
+
export { navigationConfig, instrumentSelectConfig, instrumentPrecisionConfig };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Theme } from '@oanda/labs-widget-common';
|
|
2
2
|
import { Locale } from '@oanda/mono-i18n';
|
|
3
|
+
import { BookType } from '../gql/types/graphql';
|
|
3
4
|
|
|
4
5
|
export interface OrderBookWidgetConfig {
|
|
5
6
|
graphqlUrl: string;
|
|
6
|
-
instrument:
|
|
7
|
+
instrument: InstrumentId;
|
|
7
8
|
locale: Locale;
|
|
8
9
|
theme?: Theme;
|
|
9
10
|
}
|
|
@@ -12,6 +13,30 @@ export interface OrderBookWrapperConfig extends OrderBookWidgetConfig {
|
|
|
12
13
|
renderElementId: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export interface
|
|
16
|
-
instrument
|
|
16
|
+
export interface MainProps {
|
|
17
|
+
instrument?: InstrumentId;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ChartWithDataProps {
|
|
21
|
+
instrument: InstrumentId;
|
|
22
|
+
bookType: BookType;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export enum InstrumentId {
|
|
26
|
+
EUR_AUD = 'EUR_AUD',
|
|
27
|
+
EUR_GBP = 'EUR_GBP',
|
|
28
|
+
EUR_JPY = 'EUR_JPY',
|
|
29
|
+
EUR_USD = 'EUR_USD',
|
|
30
|
+
EUR_CHF = 'EUR_CHF',
|
|
31
|
+
USD_CHF = 'USD_CHF',
|
|
32
|
+
USD_JPY = 'USD_JPY',
|
|
33
|
+
USD_CAD = 'USD_CAD',
|
|
34
|
+
GBP_USD = 'GBP_USD',
|
|
35
|
+
GBP_JPY = 'GBP_JPY',
|
|
36
|
+
GBP_CHF = 'GBP_CHF',
|
|
37
|
+
AUD_JPY = 'AUD_JPY',
|
|
38
|
+
AUD_USD = 'AUD_USD',
|
|
39
|
+
NZD_USD = 'NZD_USD',
|
|
40
|
+
XAU_USD = 'XAU_USD',
|
|
41
|
+
XAG_USD = 'XAG_USD',
|
|
17
42
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"buy": "Buy",
|
|
2
3
|
"data_unavailable": "Data unavailable",
|
|
3
4
|
"long": "Long",
|
|
4
5
|
"long_positions": "Long Positions",
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
"position_book": "Position Book",
|
|
10
11
|
"price": "Price",
|
|
11
12
|
"reset_zoom": "Reset Zoom",
|
|
13
|
+
"select_instrument": "Select instrument",
|
|
14
|
+
"sell": "Sell",
|
|
12
15
|
"sell_orders": "Sell Orders",
|
|
13
16
|
"short": "Short",
|
|
14
17
|
"short_positions": "Short Positions",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
+
"buy": "買進",
|
|
2
3
|
"data_unavailable": "沒有數據",
|
|
3
4
|
"long": "長",
|
|
4
5
|
"long_positions": "長倉",
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
"position_book": "倉位手冊",
|
|
10
11
|
"price": "價格",
|
|
11
12
|
"reset_zoom": "重置比例",
|
|
13
|
+
"select_instrument": "選擇證券",
|
|
14
|
+
"sell": "賣出",
|
|
12
15
|
"sell_orders": "賣單",
|
|
13
16
|
"short": "短",
|
|
14
17
|
"short_positions": "短倉",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { render } from '@testing-library/react';
|
|
6
|
+
import { MockedProvider } from '@apollo/client/testing';
|
|
7
|
+
import { Main } from '../src/OrderBookWidget/Main';
|
|
8
|
+
import { getOrderPositionBooks } from '../src/gql/getOrderPositionBooks';
|
|
9
|
+
import { BookType } from '../src/gql/types/graphql';
|
|
10
|
+
import { InstrumentId } from '../src';
|
|
11
|
+
|
|
12
|
+
jest.mock('usehooks-ts', () => ({
|
|
13
|
+
useMediaQuery: jest.fn(),
|
|
14
|
+
useOnClickOutside: jest.fn(),
|
|
15
|
+
useElementSize: jest.fn().mockImplementation(() => [null, {
|
|
16
|
+
width: 100,
|
|
17
|
+
}]),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
const mocks = [
|
|
21
|
+
{
|
|
22
|
+
request: {
|
|
23
|
+
query: getOrderPositionBooks,
|
|
24
|
+
variables: {
|
|
25
|
+
instrument: InstrumentId.EUR_AUD,
|
|
26
|
+
bookType: BookType.Order,
|
|
27
|
+
recentHours: 1,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
result: {
|
|
31
|
+
data: {
|
|
32
|
+
orderPositionBooks: [
|
|
33
|
+
{
|
|
34
|
+
bucketWidth: 0.0005,
|
|
35
|
+
price: 0.8,
|
|
36
|
+
buckets: [
|
|
37
|
+
{
|
|
38
|
+
price: 0.02,
|
|
39
|
+
longCountPercent: 0.0582,
|
|
40
|
+
shortCountPercent: 0.0,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
price: 0.7,
|
|
44
|
+
longCountPercent: 0.0582,
|
|
45
|
+
shortCountPercent: 0.0,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
price: 0.9925,
|
|
49
|
+
longCountPercent: 0.0582,
|
|
50
|
+
shortCountPercent: 0.0,
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
price: 1.0,
|
|
54
|
+
longCountPercent: 0.1163,
|
|
55
|
+
shortCountPercent: 0.0,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
request: {
|
|
65
|
+
query: getOrderPositionBooks,
|
|
66
|
+
variables: {
|
|
67
|
+
instrument: InstrumentId.EUR_AUD,
|
|
68
|
+
bookType: BookType.Order,
|
|
69
|
+
recentHours: 1,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
result: {
|
|
73
|
+
data: {
|
|
74
|
+
orderPositionBooks: [
|
|
75
|
+
{
|
|
76
|
+
bucketWidth: 0.0005,
|
|
77
|
+
price: 0.8,
|
|
78
|
+
buckets: [
|
|
79
|
+
{
|
|
80
|
+
price: 0.02,
|
|
81
|
+
longCountPercent: 0.0582,
|
|
82
|
+
shortCountPercent: 0.0,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
price: 0.7,
|
|
86
|
+
longCountPercent: 0.0582,
|
|
87
|
+
shortCountPercent: 0.0,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
price: 0.9925,
|
|
91
|
+
longCountPercent: 0.0582,
|
|
92
|
+
shortCountPercent: 0.0,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
price: 1.0,
|
|
96
|
+
longCountPercent: 0.1163,
|
|
97
|
+
shortCountPercent: 0.0,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
describe('Main component', () => {
|
|
108
|
+
it('should render tool component', async () => {
|
|
109
|
+
const { findByTestId, getByTestId } = render(
|
|
110
|
+
<MockedProvider mocks={mocks}>
|
|
111
|
+
<Main />
|
|
112
|
+
</MockedProvider>,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
expect(await findByTestId('order-book-widget')).toBeInTheDocument();
|
|
116
|
+
expect(getByTestId('spinner')).toBeInTheDocument();
|
|
117
|
+
expect((await findByTestId('select'))).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
it('should render widget component', async () => {
|
|
120
|
+
const { findByTestId, queryAllByTestId } = render(
|
|
121
|
+
<MockedProvider mocks={mocks}>
|
|
122
|
+
<Main instrument={InstrumentId.EUR_AUD} />
|
|
123
|
+
</MockedProvider>,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
expect(await findByTestId('order-book-widget')).toBeInTheDocument();
|
|
127
|
+
expect((await queryAllByTestId('select')).length).toBe(0);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { tooltipFormatter } from '../src/OrderBookWidget/components/Chart/formatters';
|
|
2
|
+
|
|
3
|
+
describe('tooltipFormatter', () => {
|
|
4
|
+
it('should display correct label on order book data', () => {
|
|
5
|
+
expect(tooltipFormatter({
|
|
6
|
+
data: [1.235, 0.023, -0.012],
|
|
7
|
+
precision: 5,
|
|
8
|
+
lang: (label) => label,
|
|
9
|
+
isOrderBook: true,
|
|
10
|
+
})).toEqual('price: 1.23500<br />buy: 0.023%<br />sell: 0.012%');
|
|
11
|
+
});
|
|
12
|
+
it('should display correct label on position book data', () => {
|
|
13
|
+
expect(tooltipFormatter({
|
|
14
|
+
data: [1.235, 0.023, -0.012],
|
|
15
|
+
precision: 5,
|
|
16
|
+
lang: (label) => label,
|
|
17
|
+
isOrderBook: false,
|
|
18
|
+
})).toEqual('price: 1.23500<br />long_positions: 0.023%<br />short_positions: 0.012%');
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.Widget = void 0;
|
|
7
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
-
var _client = require("@apollo/client");
|
|
9
|
-
var _labsWidgetCommon = require("@oanda/labs-widget-common");
|
|
10
|
-
var _monoI18n = require("@oanda/mono-i18n");
|
|
11
|
-
var _getOrderPositionBooks = require("../gql/getOrderPositionBooks");
|
|
12
|
-
var _graphql = require("../gql/types/graphql");
|
|
13
|
-
var _Chart = require("./components/Chart/Chart");
|
|
14
|
-
var _config = require("./config");
|
|
15
|
-
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
16
|
-
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
17
|
-
const Widget = _ref => {
|
|
18
|
-
let {
|
|
19
|
-
instrument
|
|
20
|
-
} = _ref;
|
|
21
|
-
const [bookType, setBookType] = (0, _react.useState)(_graphql.BookType.Order);
|
|
22
|
-
const {
|
|
23
|
-
lang
|
|
24
|
-
} = (0, _monoI18n.useLocale)();
|
|
25
|
-
const {
|
|
26
|
-
loading,
|
|
27
|
-
data,
|
|
28
|
-
error
|
|
29
|
-
} = (0, _client.useQuery)(_getOrderPositionBooks.getOrderPositionBooks, {
|
|
30
|
-
variables: {
|
|
31
|
-
instrument,
|
|
32
|
-
bookType,
|
|
33
|
-
recentHours: 1
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
const isError = !loading && !data || !!error;
|
|
37
|
-
return _react.default.createElement("div", {
|
|
38
|
-
"data-testid": "order-book-widget",
|
|
39
|
-
className: "lw-p-4 lw-text-sm lw-tracking-normal lw-text-black"
|
|
40
|
-
}, _react.default.createElement(_labsWidgetCommon.Tabs, {
|
|
41
|
-
activeTab: bookType,
|
|
42
|
-
handleClick: e => setBookType(e.currentTarget.value),
|
|
43
|
-
labelCallback: lang,
|
|
44
|
-
items: _config.navigationConfig
|
|
45
|
-
}), _react.default.createElement("div", {
|
|
46
|
-
className: "lw-relative lw-h-[450px] lw-w-full"
|
|
47
|
-
}, isError && _react.default.createElement("div", {
|
|
48
|
-
className: "lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary"
|
|
49
|
-
}, _react.default.createElement(_labsWidgetCommon.ChartError, null)), loading && _react.default.createElement("div", {
|
|
50
|
-
className: "lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary"
|
|
51
|
-
}, _react.default.createElement(_labsWidgetCommon.Spinner, {
|
|
52
|
-
size: _labsWidgetCommon.SpinnerSize.lg
|
|
53
|
-
})), data && _react.default.createElement("div", {
|
|
54
|
-
className: "lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full"
|
|
55
|
-
}, _react.default.createElement(_Chart.Chart, {
|
|
56
|
-
data: data,
|
|
57
|
-
isOrderBook: bookType === _graphql.BookType.Order
|
|
58
|
-
}))));
|
|
59
|
-
};
|
|
60
|
-
exports.Widget = Widget;
|
|
61
|
-
//# sourceMappingURL=Widget.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Widget.js","names":["_react","_interopRequireWildcard","require","_client","_labsWidgetCommon","_monoI18n","_getOrderPositionBooks","_graphql","_Chart","_config","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","prototype","hasOwnProperty","call","i","set","Widget","_ref","instrument","bookType","setBookType","useState","BookType","Order","lang","useLocale","loading","data","error","useQuery","getOrderPositionBooks","variables","recentHours","isError","createElement","className","Tabs","activeTab","handleClick","currentTarget","value","labelCallback","items","navigationConfig","ChartError","Spinner","size","SpinnerSize","lg","Chart","isOrderBook","exports"],"sources":["../../../src/OrderBookWidget/Widget.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { useQuery } from '@apollo/client';\nimport {\n ChartError, Spinner, SpinnerSize, Tabs,\n} from '@oanda/labs-widget-common';\nimport { useLocale } from '@oanda/mono-i18n';\nimport { WidgetProps } from './types';\nimport { getOrderPositionBooks } from '../gql/getOrderPositionBooks';\nimport { GetOrderPositionBooksQuery, GetOrderPositionBooksQueryVariables, BookType } from '../gql/types/graphql';\nimport { Chart } from './components/Chart/Chart';\nimport { navigationConfig } from './config';\n\nconst Widget = ({\n instrument,\n}: WidgetProps) => {\n const [bookType, setBookType] = useState(BookType.Order);\n const { lang } = useLocale();\n\n const { loading, data, error } = useQuery<\n GetOrderPositionBooksQuery,\n GetOrderPositionBooksQueryVariables\n >(getOrderPositionBooks, {\n variables: {\n instrument,\n bookType,\n recentHours: 1,\n },\n });\n\n const isError = (!loading && !data) || !!error;\n\n return (\n <div data-testid=\"order-book-widget\" className=\"lw-p-4 lw-text-sm lw-tracking-normal lw-text-black\">\n <Tabs\n activeTab={bookType}\n handleClick={(e) => setBookType(e.currentTarget.value as BookType)}\n labelCallback={lang}\n items={navigationConfig}\n />\n {/* @todo: chart height */}\n <div className=\"lw-relative lw-h-[450px] lw-w-full\">\n {isError && (\n <div className=\"lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary\">\n <ChartError />\n </div>\n )}\n {loading && (\n <div className=\"lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary\">\n <Spinner size={SpinnerSize.lg} />\n </div>\n )}\n {data && (\n <div className=\"lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full\">\n <Chart data={data} isOrderBook={bookType === BookType.Order} />\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport { Widget };\n"],"mappings":"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"}
|