@oanda/labs-instruments-price-chart-widget 1.0.13 → 1.0.14

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 (98) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/codegen.ts +2 -5
  3. package/dist/main/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.js +6 -2
  4. package/dist/main/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.js.map +1 -1
  5. package/dist/main/InstrumentsPriceChartWidget/Main.js +32 -16
  6. package/dist/main/InstrumentsPriceChartWidget/Main.js.map +1 -1
  7. package/dist/main/InstrumentsPriceChartWidget/components/Chart.js +4 -2
  8. package/dist/main/InstrumentsPriceChartWidget/components/Chart.js.map +1 -1
  9. package/dist/main/InstrumentsPriceChartWidget/components/TimeZoneLabel.js +29 -0
  10. package/dist/main/InstrumentsPriceChartWidget/components/TimeZoneLabel.js.map +1 -0
  11. package/dist/main/InstrumentsPriceChartWidget/components/formatters.js +50 -11
  12. package/dist/main/InstrumentsPriceChartWidget/components/formatters.js.map +1 -1
  13. package/dist/main/InstrumentsPriceChartWidget/components/getOption.js +13 -13
  14. package/dist/main/InstrumentsPriceChartWidget/components/getOption.js.map +1 -1
  15. package/dist/main/InstrumentsPriceChartWidget/components/types.js.map +1 -1
  16. package/dist/main/InstrumentsPriceChartWidget/config.js +14 -7
  17. package/dist/main/InstrumentsPriceChartWidget/config.js.map +1 -1
  18. package/dist/main/InstrumentsPriceChartWidget/render.js +10 -2
  19. package/dist/main/InstrumentsPriceChartWidget/render.js.map +1 -1
  20. package/dist/main/InstrumentsPriceChartWidget/types.js.map +1 -1
  21. package/dist/main/InstrumentsPriceChartWidget/utils.js +12 -0
  22. package/dist/main/InstrumentsPriceChartWidget/utils.js.map +1 -0
  23. package/dist/main/gql/{mock/getPriceCandles.js → priceCandles.js} +9 -8
  24. package/dist/main/gql/priceCandles.js.map +1 -0
  25. package/dist/main/gql/types/gql.js +1 -1
  26. package/dist/main/gql/types/gql.js.map +1 -1
  27. package/dist/main/gql/types/graphql.js +53 -24
  28. package/dist/main/gql/types/graphql.js.map +1 -1
  29. package/dist/main/translations/sources/en.json +2 -1
  30. package/dist/module/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.js +6 -2
  31. package/dist/module/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.js.map +1 -1
  32. package/dist/module/InstrumentsPriceChartWidget/Main.js +33 -17
  33. package/dist/module/InstrumentsPriceChartWidget/Main.js.map +1 -1
  34. package/dist/module/InstrumentsPriceChartWidget/components/Chart.js +4 -2
  35. package/dist/module/InstrumentsPriceChartWidget/components/Chart.js.map +1 -1
  36. package/dist/module/InstrumentsPriceChartWidget/components/TimeZoneLabel.js +21 -0
  37. package/dist/module/InstrumentsPriceChartWidget/components/TimeZoneLabel.js.map +1 -0
  38. package/dist/module/InstrumentsPriceChartWidget/components/formatters.js +47 -9
  39. package/dist/module/InstrumentsPriceChartWidget/components/formatters.js.map +1 -1
  40. package/dist/module/InstrumentsPriceChartWidget/components/getOption.js +14 -14
  41. package/dist/module/InstrumentsPriceChartWidget/components/getOption.js.map +1 -1
  42. package/dist/module/InstrumentsPriceChartWidget/components/types.js.map +1 -1
  43. package/dist/module/InstrumentsPriceChartWidget/config.js +14 -7
  44. package/dist/module/InstrumentsPriceChartWidget/config.js.map +1 -1
  45. package/dist/module/InstrumentsPriceChartWidget/render.js +11 -3
  46. package/dist/module/InstrumentsPriceChartWidget/render.js.map +1 -1
  47. package/dist/module/InstrumentsPriceChartWidget/types.js.map +1 -1
  48. package/dist/module/InstrumentsPriceChartWidget/utils.js +4 -0
  49. package/dist/module/InstrumentsPriceChartWidget/utils.js.map +1 -0
  50. package/dist/module/gql/{mock/getPriceCandles.js → priceCandles.js} +9 -8
  51. package/dist/module/gql/priceCandles.js.map +1 -0
  52. package/dist/module/gql/types/gql.js +1 -1
  53. package/dist/module/gql/types/gql.js.map +1 -1
  54. package/dist/module/gql/types/graphql.js +52 -23
  55. package/dist/module/gql/types/graphql.js.map +1 -1
  56. package/dist/module/translations/sources/en.json +2 -1
  57. package/dist/types/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.d.ts +1 -1
  58. package/dist/types/InstrumentsPriceChartWidget/Main.d.ts +1 -1
  59. package/dist/types/InstrumentsPriceChartWidget/components/Chart.d.ts +1 -1
  60. package/dist/types/InstrumentsPriceChartWidget/components/TimeZoneLabel.d.ts +2 -0
  61. package/dist/types/InstrumentsPriceChartWidget/components/formatters.d.ts +5 -1
  62. package/dist/types/InstrumentsPriceChartWidget/components/types.d.ts +6 -4
  63. package/dist/types/InstrumentsPriceChartWidget/config.d.ts +5 -3
  64. package/dist/types/InstrumentsPriceChartWidget/types.d.ts +5 -1
  65. package/dist/types/InstrumentsPriceChartWidget/utils.d.ts +3 -0
  66. package/dist/types/gql/priceCandles.d.ts +2 -0
  67. package/dist/types/gql/types/gql.d.ts +4 -3
  68. package/dist/types/gql/types/graphql.d.ts +31 -27
  69. package/package.json +3 -3
  70. package/src/InstrumentsPriceChartWidget/InstrumentsPriceChartWidget.tsx +7 -1
  71. package/src/InstrumentsPriceChartWidget/Main.tsx +45 -19
  72. package/src/InstrumentsPriceChartWidget/components/Chart.tsx +2 -1
  73. package/src/InstrumentsPriceChartWidget/components/TimeZoneLabel.tsx +24 -0
  74. package/src/InstrumentsPriceChartWidget/components/formatters.ts +72 -13
  75. package/src/InstrumentsPriceChartWidget/components/getOption.ts +20 -21
  76. package/src/InstrumentsPriceChartWidget/components/types.ts +6 -4
  77. package/src/InstrumentsPriceChartWidget/config.ts +16 -7
  78. package/src/InstrumentsPriceChartWidget/render.tsx +12 -2
  79. package/src/InstrumentsPriceChartWidget/types.ts +5 -1
  80. package/src/InstrumentsPriceChartWidget/utils.ts +16 -0
  81. package/src/gql/{mock/getPriceCandles.ts → priceCandles.ts} +8 -7
  82. package/src/gql/types/gql.ts +4 -4
  83. package/src/gql/types/graphql.ts +62 -37
  84. package/src/translations/sources/en.json +2 -1
  85. package/test/Main.test.tsx +6 -2
  86. package/test/mocks/chartMock.ts +201 -37
  87. package/dist/main/InstrumentsPriceChartWidget/getPriceCandlesMock.js +0 -83
  88. package/dist/main/InstrumentsPriceChartWidget/getPriceCandlesMock.js.map +0 -1
  89. package/dist/main/gql/mock/getPriceCandles.js.map +0 -1
  90. package/dist/main/gql/mock/schema.graphqls +0 -62
  91. package/dist/module/InstrumentsPriceChartWidget/getPriceCandlesMock.js +0 -76
  92. package/dist/module/InstrumentsPriceChartWidget/getPriceCandlesMock.js.map +0 -1
  93. package/dist/module/gql/mock/getPriceCandles.js.map +0 -1
  94. package/dist/module/gql/mock/schema.graphqls +0 -62
  95. package/dist/types/InstrumentsPriceChartWidget/getPriceCandlesMock.d.ts +0 -9
  96. package/dist/types/gql/mock/getPriceCandles.d.ts +0 -2
  97. package/src/InstrumentsPriceChartWidget/getPriceCandlesMock.tsx +0 -75
  98. package/src/gql/mock/schema.graphqls +0 -62
@@ -1,46 +1,72 @@
1
+ import { useQuery } from '@apollo/client';
1
2
  import {
3
+ ChartError,
2
4
  Spinner,
3
5
  SpinnerSize,
4
6
  TimeUnitSwitch,
5
7
  } from '@oanda/labs-widget-common';
6
8
  import React, { useState } from 'react';
7
9
 
10
+ import { priceCandles } from '../gql/priceCandles';
11
+ import {
12
+ type PriceCandlesQuery,
13
+ type PriceCandlesQueryVariables,
14
+ TimeSpan,
15
+ } from '../gql/types/graphql';
8
16
  import { Chart } from './components/Chart';
17
+ import { TimeZoneLabel } from './components/TimeZoneLabel';
9
18
  import { timeUnitConfig } from './config';
10
- import { getPriceCandlesMock } from './getPriceCandlesMock';
11
- import { type MainProps, TimeSpanSubset } from './types';
19
+ import { type MainProps } from './types';
20
+ import { getCandles, getGranularityForTimeSpan } from './utils';
12
21
 
13
- const Main = ({ division }: MainProps) => {
14
- const [selectedTimeUnit, setSelectedTimeUnit] = useState(
15
- TimeSpanSubset.Day_1
16
- );
22
+ const Main = ({ division, instrument, dataSource }: MainProps) => {
23
+ const [selectedTimeUnit, setSelectedTimeUnit] = useState(TimeSpan.Day_1);
17
24
 
18
- const { loading, data, error } = getPriceCandlesMock({ division });
25
+ const { loading, data, error } = useQuery<
26
+ PriceCandlesQuery,
27
+ PriceCandlesQueryVariables
28
+ >(priceCandles, {
29
+ variables: {
30
+ dataSource,
31
+ division,
32
+ instrument,
33
+ granularity: getGranularityForTimeSpan(selectedTimeUnit),
34
+ timeSpan: selectedTimeUnit,
35
+ },
36
+ fetchPolicy: 'cache-and-network',
37
+ });
19
38
 
20
- const showError =
21
- data?.getPriceCandles.length === 0 || !data?.getPriceCandles || !!error;
39
+ const candles = getCandles(data);
40
+ const showError = candles?.length === 0 || !candles || !!error;
22
41
 
23
42
  return (
24
43
  <div data-testid="instruments-price-chart-wrapper">
44
+ <div className="lw-flex lw-pt-2">
45
+ <TimeUnitSwitch<TimeSpan>
46
+ callback={setSelectedTimeUnit}
47
+ options={timeUnitConfig}
48
+ selected={selectedTimeUnit}
49
+ />
50
+ </div>
51
+
25
52
  {loading && (
26
- <div className="lw-mb-[50px] lw-flex lw-h-[425px] lw-w-full lw-items-center lw-border lw-border-solid lw-border-border-primary">
53
+ <div className="lw-flex lw-h-[425px] lw-w-full lw-items-center lw-border lw-border-solid lw-border-border-primary">
27
54
  <Spinner size={SpinnerSize.lg} />
28
55
  </div>
29
56
  )}
30
57
 
58
+ {!loading && showError && (
59
+ <div className="lw-flex lw-h-[425px] lw-w-full lw-items-center lw-border lw-border-solid lw-border-border-primary">
60
+ <ChartError />
61
+ </div>
62
+ )}
63
+
31
64
  {!loading && !showError && (
32
65
  <div data-testid="instruments-price-chart-widget">
33
- <div className="lw-flex lw-pt-2">
34
- <TimeUnitSwitch<TimeSpanSubset>
35
- callback={setSelectedTimeUnit}
36
- options={timeUnitConfig}
37
- selected={selectedTimeUnit}
38
- />
39
- </div>
40
-
41
- <Chart values={data?.getPriceCandles} />
66
+ <Chart timeSpan={selectedTimeUnit} values={candles} />
42
67
  </div>
43
68
  )}
69
+ <TimeZoneLabel />
44
70
  </div>
45
71
  );
46
72
  };
@@ -33,7 +33,7 @@ echarts.use([
33
33
  echarts.registerTheme('dark_theme', getChartTheme(Theme.Dark));
34
34
  echarts.registerTheme('light_theme', getChartTheme(Theme.Light));
35
35
 
36
- export const Chart = ({ values }: ChartProps) => {
36
+ export const Chart = ({ values, timeSpan }: ChartProps) => {
37
37
  const { isDark } = useLayoutProvider();
38
38
  const { lang } = useLocale();
39
39
 
@@ -46,6 +46,7 @@ export const Chart = ({ values }: ChartProps) => {
46
46
  values,
47
47
  isDark,
48
48
  lang,
49
+ timeSpan,
49
50
  })}
50
51
  />
51
52
  );
@@ -0,0 +1,24 @@
1
+ import { useLocale } from '@oanda/mono-i18n';
2
+ import React from 'react';
3
+
4
+ const getBrowserOffsetLabel = (): string => {
5
+ const parts = new Intl.DateTimeFormat(undefined, {
6
+ timeZoneName: 'longOffset',
7
+ }).formatToParts(new Date());
8
+
9
+ const tzPart = parts.find((p) => p.type === 'timeZoneName');
10
+ return tzPart ? tzPart.value : '';
11
+ };
12
+
13
+ export const TimeZoneLabel = () => {
14
+ const { lang } = useLocale();
15
+
16
+ return (
17
+ <span
18
+ className="lw-ml-1 lw-font-sans lw-text-xs lw-font-bold"
19
+ data-testid="timezone-label"
20
+ >{`${lang('timezone_display', {
21
+ timezone: getBrowserOffsetLabel(),
22
+ })}`}</span>
23
+ );
24
+ };
@@ -1,21 +1,80 @@
1
- import {
2
- chartDateTimeFormat,
3
- getChartDateAndTime,
4
- } from '@oanda/labs-widget-common';
5
-
1
+ import { TimeSpan } from '../../gql/types/graphql';
6
2
  import type { TooltipFormatterParams } from './types';
7
3
 
8
- export const dailyFormatter = (timestamp: string) => {
9
- const date = new Date(timestamp);
10
- return date.toLocaleTimeString(undefined, {
11
- hour: 'numeric',
12
- hour12: true,
13
- });
4
+ export const showSplitLine = (index: number, total: number): boolean => {
5
+ const interval = Math.max(1, Math.floor(total / 6));
6
+ return index % interval === 0;
7
+ };
8
+
9
+ const showChartIntervalLabel = (
10
+ timeSpan: TimeSpan,
11
+ values: { point: string }[],
12
+ index: number
13
+ ): boolean => {
14
+ const date = new Date(values[index].point);
15
+ const labelInterval = Math.max(1, Math.floor(values.length / 6));
16
+
17
+ if (
18
+ timeSpan === TimeSpan.Month_6 ||
19
+ timeSpan === TimeSpan.Year_1 ||
20
+ timeSpan === TimeSpan.Year_5
21
+ ) {
22
+ const prev = index > 0 ? new Date(values[index - 1].point) : null;
23
+ const monthChanged =
24
+ !prev ||
25
+ date.getMonth() !== prev.getMonth() ||
26
+ date.getFullYear() !== prev.getFullYear();
27
+
28
+ return monthChanged;
29
+ }
30
+
31
+ return index % labelInterval === 0;
14
32
  };
15
33
 
34
+ export const labelFormatter =
35
+ (timeSpan: TimeSpan, values: { point: string }[]) =>
36
+ (_: string, index: number): string => {
37
+ const date = new Date(values[index].point);
38
+
39
+ if (!showChartIntervalLabel(timeSpan, values, index)) return '';
40
+
41
+ switch (timeSpan) {
42
+ case TimeSpan.Day_1:
43
+ const hour = date.getHours();
44
+ return `${String(hour).padStart(2, '0')}:00`;
45
+
46
+ case TimeSpan.Week_1:
47
+ case TimeSpan.Month_1:
48
+ return date.toLocaleDateString(undefined, {
49
+ day: '2-digit',
50
+ month: 'short',
51
+ });
52
+
53
+ case TimeSpan.Month_6:
54
+ case TimeSpan.Year_1:
55
+ case TimeSpan.Year_5:
56
+ return date.toLocaleDateString(undefined, {
57
+ month: 'short',
58
+ });
59
+
60
+ default:
61
+ return '';
62
+ }
63
+ };
64
+
65
+ const chartDateTimeFormat = (iso: string) =>
66
+ new Date(iso).toLocaleString(undefined, {
67
+ year: 'numeric',
68
+ month: '2-digit',
69
+ day: '2-digit',
70
+ hour: '2-digit',
71
+ minute: '2-digit',
72
+ hour12: false,
73
+ });
74
+
16
75
  export const tooltipFormatter = ({ lang, data }: TooltipFormatterParams) => {
17
- const { open, high, close, low, time } = data;
18
- const date = chartDateTimeFormat(getChartDateAndTime(time));
76
+ const { open, high, close, low, point } = data;
77
+ const date = chartDateTimeFormat(point);
19
78
 
20
79
  return `
21
80
  <div style="display:flex; flex-direction:column;">
@@ -11,15 +11,16 @@ import {
11
11
  X_LABEL_SIZE,
12
12
  Y_LABEL_SIZE_DESKTOP,
13
13
  } from './constants';
14
- import { dailyFormatter, tooltipFormatter } from './formatters';
14
+ import { labelFormatter, showSplitLine, tooltipFormatter } from './formatters';
15
15
  import type { ChartValue, GetOptionType } from './types';
16
16
 
17
- export const getOption: GetOptionType = ({ values, isDark, lang }) => {
18
- const priceValues = values.map((d) => d.candle.high);
19
-
20
- const timestamps = values.map((d) => dailyFormatter(d.time));
21
-
22
- const labelInterval = Math.max(1, Math.floor(timestamps.length / 6));
17
+ export const getOption: GetOptionType = ({
18
+ values,
19
+ isDark,
20
+ lang,
21
+ timeSpan,
22
+ }) => {
23
+ const priceValues = values.map((d) => d.high);
23
24
 
24
25
  const gridLines = getGridLines({
25
26
  isDark,
@@ -31,6 +32,7 @@ export const getOption: GetOptionType = ({ values, isDark, lang }) => {
31
32
  });
32
33
 
33
34
  return {
35
+ animation: false,
34
36
  tooltip: {
35
37
  trigger: 'axis',
36
38
  axisPointer: {
@@ -39,7 +41,7 @@ export const getOption: GetOptionType = ({ values, isDark, lang }) => {
39
41
  formatter: (params) => {
40
42
  const p = Array.isArray(params) ? params[0] : params;
41
43
  const data = p.data as {
42
- time: string;
44
+ point: string;
43
45
  open: number;
44
46
  high: number;
45
47
  low: number;
@@ -52,19 +54,16 @@ export const getOption: GetOptionType = ({ values, isDark, lang }) => {
52
54
  xAxis: {
53
55
  type: 'category',
54
56
  boundaryGap: false,
55
- data: timestamps,
56
- axisTick: {
57
- show: false,
58
- },
57
+ data: values.map((d) => d.point),
58
+ axisTick: { show: false },
59
59
  axisLabel: {
60
- margin: 10,
61
- interval: (index: number) => index % labelInterval === 0,
62
- formatter: (value: string, index: number) =>
63
- index % labelInterval === 0 ? value : '',
60
+ padding: [0, 0, 0, 30],
61
+ interval: 'auto',
62
+ formatter: labelFormatter(timeSpan, values),
64
63
  },
65
64
  splitLine: {
66
65
  show: true,
67
- interval: (index: number) => index % labelInterval === 0,
66
+ interval: (index: number) => showSplitLine(index, values.length),
68
67
  },
69
68
  },
70
69
  yAxis: {
@@ -78,7 +77,7 @@ export const getOption: GetOptionType = ({ values, isDark, lang }) => {
78
77
  axisLine: { show: false },
79
78
  axisTick: { show: false },
80
79
  axisLabel: {
81
- margin: 8,
80
+ margin: 10,
82
81
  formatter: (value: number) => {
83
82
  const dataMin = Math.floor(Math.min(...priceValues) * 100) / 100;
84
83
  const dataMax = Math.ceil(Math.max(...priceValues) * 100) / 100;
@@ -97,9 +96,9 @@ export const getOption: GetOptionType = ({ values, isDark, lang }) => {
97
96
  name: 'price-close',
98
97
  type: 'line',
99
98
  data: values.map((d) => ({
100
- value: d.candle.high,
101
- ...d.candle,
102
- time: d.time,
99
+ value: d.high,
100
+ ...d,
101
+ time: d.point,
103
102
  })),
104
103
  smooth: true,
105
104
  symbol: 'none',
@@ -1,15 +1,17 @@
1
1
  import type { EChartsOption } from 'echarts';
2
2
 
3
- import type { CandlesData } from '../../gql/types/graphql';
3
+ import type { Candle, TimeSpan } from '../../gql/types/graphql';
4
4
 
5
5
  export interface ChartProps {
6
- values: CandlesData[];
6
+ values: Candle[];
7
+ timeSpan: TimeSpan;
7
8
  }
8
9
 
9
10
  export interface GetOptionProps {
10
- values: CandlesData[];
11
+ values: Candle[];
11
12
  isDark: boolean;
12
13
  lang: (label: string) => string;
14
+ timeSpan: TimeSpan;
13
15
  }
14
16
 
15
17
  export type GetOptionType = (props: GetOptionProps) => EChartsOption;
@@ -20,7 +22,7 @@ export interface ChartValue {
20
22
  }
21
23
 
22
24
  export interface TooltipData {
23
- time: string;
25
+ point: string;
24
26
  open: number;
25
27
  high: number;
26
28
  low: number;
@@ -1,8 +1,9 @@
1
- import { TimeSpanSubset } from './types';
1
+ import { Granularity, TimeSpan } from '../gql/types/graphql';
2
+ import type { TimeSpanSubset } from './types';
2
3
 
3
4
  const timeUnitConfig = [
4
5
  {
5
- value: TimeSpanSubset.Day_1,
6
+ value: TimeSpan.Day_1,
6
7
  label: '1d',
7
8
  tooltipLabel: {
8
9
  translationKey: 'day',
@@ -10,7 +11,7 @@ const timeUnitConfig = [
10
11
  },
11
12
  },
12
13
  {
13
- value: TimeSpanSubset.Week_1,
14
+ value: TimeSpan.Week_1,
14
15
  label: '1w',
15
16
  tooltipLabel: {
16
17
  translationKey: 'week',
@@ -18,7 +19,7 @@ const timeUnitConfig = [
18
19
  },
19
20
  },
20
21
  {
21
- value: TimeSpanSubset.Month_1,
22
+ value: TimeSpan.Month_1,
22
23
  label: '1m',
23
24
  tooltipLabel: {
24
25
  translationKey: 'month',
@@ -27,7 +28,7 @@ const timeUnitConfig = [
27
28
  },
28
29
 
29
30
  {
30
- value: TimeSpanSubset.Month_6,
31
+ value: TimeSpan.Month_6,
31
32
  label: '6m',
32
33
  tooltipLabel: {
33
34
  translationKey: 'month',
@@ -35,7 +36,7 @@ const timeUnitConfig = [
35
36
  },
36
37
  },
37
38
  {
38
- value: TimeSpanSubset.Year_1,
39
+ value: TimeSpan.Year_1,
39
40
  label: '1y',
40
41
  tooltipLabel: {
41
42
  translationKey: 'year',
@@ -44,4 +45,12 @@ const timeUnitConfig = [
44
45
  },
45
46
  ];
46
47
 
47
- export { timeUnitConfig };
48
+ const granularityForTimeSpan: Record<TimeSpanSubset, Granularity> = {
49
+ [TimeSpan.Day_1]: Granularity.H1,
50
+ [TimeSpan.Week_1]: Granularity.H4,
51
+ [TimeSpan.Month_1]: Granularity.D1,
52
+ [TimeSpan.Month_6]: Granularity.D1,
53
+ [TimeSpan.Year_1]: Granularity.D1,
54
+ };
55
+
56
+ export { granularityForTimeSpan, timeUnitConfig };
@@ -3,7 +3,7 @@ import { validateLocale, validateToolParams } from '@oanda/labs-widget-common';
3
3
  import React from 'react';
4
4
  import { createRoot } from 'react-dom/client';
5
5
 
6
- import { Division } from '../gql/types/graphql';
6
+ import { Division, InstrumentDataSource } from '../gql/types/graphql';
7
7
  import { InstrumentsPriceChartWidget } from './InstrumentsPriceChartWidget';
8
8
 
9
9
  const { graphqlUrl } = window.widgetsConfig || {};
@@ -16,13 +16,16 @@ dataInstrumentsPriceChartParamsElements.forEach((element) => {
16
16
  const root = createRoot(element);
17
17
  const params = element.getAttribute('data-instruments-price-chart-params');
18
18
  const mode = element.getAttribute('data-mode');
19
- const { division, locale } = JSON.parse(params as string);
19
+ const { division, locale, instrument, dataSource } = JSON.parse(
20
+ params as string
21
+ );
20
22
 
21
23
  const isParamError = validateToolParams(
22
24
  {
23
25
  division,
24
26
  locale,
25
27
  graphqlUrl,
28
+ dataSource,
26
29
  },
27
30
  [
28
31
  {
@@ -37,13 +40,20 @@ dataInstrumentsPriceChartParamsElements.forEach((element) => {
37
40
  {
38
41
  name: 'graphqlUrl',
39
42
  },
43
+ {
44
+ name: 'dataSource',
45
+ valueCheck: (value: InstrumentDataSource) =>
46
+ Object.values(InstrumentDataSource).includes(value),
47
+ },
40
48
  ]
41
49
  );
42
50
 
43
51
  root.render(
44
52
  <InstrumentsPriceChartWidget
53
+ dataSource={dataSource}
45
54
  division={division}
46
55
  graphqlUrl={graphqlUrl}
56
+ instrument={instrument}
47
57
  isParamError={isParamError}
48
58
  locale={locale}
49
59
  theme={mode as Theme}
@@ -1,14 +1,18 @@
1
1
  import type { WidgetConfig } from '@oanda/labs-widget-common';
2
2
 
3
- import type { Division } from '../gql/types/graphql';
3
+ import type { Division, InstrumentDataSource } from '../gql/types/graphql';
4
4
  import { TimeSpan } from '../gql/types/graphql';
5
5
 
6
6
  export interface InstrumentsPriceChartConfig extends WidgetConfig {
7
7
  division: Division;
8
+ instrument: string;
9
+ dataSource: InstrumentDataSource;
8
10
  }
9
11
 
10
12
  export interface MainProps {
11
13
  division: Division;
14
+ instrument: string;
15
+ dataSource: InstrumentDataSource;
12
16
  }
13
17
 
14
18
  export enum TimeSpanSubset {
@@ -0,0 +1,16 @@
1
+ import type {
2
+ Candle,
3
+ Granularity,
4
+ PriceCandlesQuery,
5
+ TimeSpan,
6
+ } from '../gql/types/graphql';
7
+ import { granularityForTimeSpan } from './config';
8
+ import type { TimeSpanSubset } from './types';
9
+
10
+ export const getGranularityForTimeSpan = (timeSpan: TimeSpan): Granularity =>
11
+ granularityForTimeSpan[timeSpan as unknown as TimeSpanSubset];
12
+
13
+ export const getCandles = (
14
+ data: PriceCandlesQuery | undefined
15
+ ): Candle[] | undefined =>
16
+ data?.priceCandles.candle.filter((c): c is Candle => c !== null);
@@ -1,21 +1,22 @@
1
1
  import { gql } from '@apollo/client';
2
2
 
3
- const getPriceCandles = gql`
4
- query GetPriceCandles(
5
- $division: Division
3
+ const priceCandles = gql`
4
+ query PriceCandles(
5
+ $division: Division!
6
+ $dataSource: InstrumentDataSource!
6
7
  $instrument: String!
7
8
  $granularity: Granularity!
8
9
  $timeSpan: TimeSpan!
9
10
  ) {
10
- getPriceCandles(
11
+ priceCandles(
11
12
  division: $division
13
+ dataSource: $dataSource
12
14
  instrument: $instrument
13
15
  granularity: $granularity
14
16
  timeSpan: $timeSpan
15
17
  ) {
16
- time
17
- unixTime
18
18
  candle {
19
+ point
19
20
  high
20
21
  low
21
22
  open
@@ -26,4 +27,4 @@ const getPriceCandles = gql`
26
27
  }
27
28
  `;
28
29
 
29
- export { getPriceCandles };
30
+ export { priceCandles };
@@ -13,8 +13,8 @@ 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 GetPriceCandles(\n $division: Division\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n getPriceCandles(\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n time\n unixTime\n candle {\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n':
17
- types.GetPriceCandlesDocument,
16
+ '\n query PriceCandles(\n $division: Division!\n $dataSource: InstrumentDataSource!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n division: $division\n dataSource: $dataSource\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n':
17
+ types.PriceCandlesDocument,
18
18
  '\n query validateInstruments($instruments: [String]!, $division: Division) {\n mapInstrumentNames(instruments: $instruments, division: $division) {\n name\n displayName\n }\n }\n':
19
19
  types.ValidateInstrumentsDocument,
20
20
  };
@@ -37,8 +37,8 @@ export function graphql(source: string): unknown;
37
37
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
38
38
  */
39
39
  export function graphql(
40
- source: '\n query GetPriceCandles(\n $division: Division\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n getPriceCandles(\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n time\n unixTime\n candle {\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n'
41
- ): (typeof documents)['\n query GetPriceCandles(\n $division: Division\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n getPriceCandles(\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n time\n unixTime\n candle {\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n'];
40
+ source: '\n query PriceCandles(\n $division: Division!\n $dataSource: InstrumentDataSource!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n division: $division\n dataSource: $dataSource\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n'
41
+ ): (typeof documents)['\n query PriceCandles(\n $division: Division!\n $dataSource: InstrumentDataSource!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n division: $division\n dataSource: $dataSource\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n timeSpan\n }\n }\n'];
42
42
  /**
43
43
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
44
44
  */