@oanda/labs-crowd-view-widget 1.0.56 → 1.0.58

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 (95) hide show
  1. package/CHANGELOG.md +468 -0
  2. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +1 -6
  3. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +5 -4
  5. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +13 -6
  7. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +4 -4
  9. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +9 -43
  11. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  12. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +19 -4
  13. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  14. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +15 -2
  15. package/dist/main/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  16. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.js +27 -0
  17. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.js.map +1 -0
  18. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +14 -23
  19. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  20. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js +11 -0
  21. package/dist/main/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  22. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.js +22 -0
  23. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.js.map +1 -0
  24. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js +8 -8
  25. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  26. package/dist/main/CrowdViewWidget/components/Chart/getOption.js +4 -4
  27. package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  28. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +11 -7
  29. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  30. package/dist/main/CrowdViewWidget/constants.js +8 -6
  31. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  32. package/dist/main/translations/sources/en.json +2 -1
  33. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +2 -7
  34. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  35. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js +5 -4
  36. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.js.map +1 -1
  37. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js +13 -6
  38. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getGridLines.js.map +1 -1
  39. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js +4 -4
  40. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.js.map +1 -1
  41. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js +9 -42
  42. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.js.map +1 -1
  43. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js +20 -5
  44. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.js.map +1 -1
  45. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js +15 -2
  46. package/dist/module/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.js.map +1 -1
  47. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.js +20 -0
  48. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.js.map +1 -0
  49. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js +14 -23
  50. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.js.map +1 -1
  51. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js +1 -0
  52. package/dist/module/CrowdViewWidget/components/Chart/chartUtils/index.js.map +1 -1
  53. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.js +15 -0
  54. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.js.map +1 -0
  55. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js +1 -1
  56. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/index.js.map +1 -1
  57. package/dist/module/CrowdViewWidget/components/Chart/getOption.js +4 -4
  58. package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
  59. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +13 -9
  60. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  61. package/dist/module/CrowdViewWidget/constants.js +7 -5
  62. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  63. package/dist/module/translations/sources/en.json +2 -1
  64. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.d.ts +1 -2
  65. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.d.ts +2 -1
  66. package/dist/types/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.d.ts +4 -2
  67. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.d.ts +1 -0
  68. package/dist/types/CrowdViewWidget/components/Chart/chartUtils/index.d.ts +1 -0
  69. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.d.ts +8 -0
  70. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/index.d.ts +1 -1
  71. package/dist/types/CrowdViewWidget/constants.d.ts +5 -3
  72. package/package.json +3 -3
  73. package/src/CrowdViewWidget/components/Chart/Chart.tsx +1 -7
  74. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridConfig.ts +5 -4
  75. package/src/CrowdViewWidget/components/Chart/chartOptions/getGridLines.ts +19 -10
  76. package/src/CrowdViewWidget/components/Chart/chartOptions/getLabelsConfig.ts +9 -5
  77. package/src/CrowdViewWidget/components/Chart/chartOptions/getTooltipConfig.ts +38 -79
  78. package/src/CrowdViewWidget/components/Chart/chartOptions/getXAxisConfig.ts +26 -2
  79. package/src/CrowdViewWidget/components/Chart/chartOptions/getYAxisConfig.ts +20 -3
  80. package/src/CrowdViewWidget/components/Chart/chartUtils/formatFullDate.ts +26 -0
  81. package/src/CrowdViewWidget/components/Chart/chartUtils/getTooltipFormatter.ts +50 -35
  82. package/src/CrowdViewWidget/components/Chart/chartUtils/index.ts +1 -0
  83. package/src/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer.ts +25 -0
  84. package/src/CrowdViewWidget/components/Chart/dataUtils/index.ts +1 -1
  85. package/src/CrowdViewWidget/components/Chart/getOption.ts +2 -2
  86. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +17 -16
  87. package/src/CrowdViewWidget/constants.ts +11 -7
  88. package/src/translations/sources/en.json +2 -1
  89. package/test/components/Chart/dataUtils/getBucketWidthMultiplayer.test.ts +33 -0
  90. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +0 -19
  91. package/dist/main/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +0 -1
  92. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js +0 -12
  93. package/dist/module/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.js.map +0 -1
  94. package/dist/types/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.d.ts +0 -2
  95. package/src/CrowdViewWidget/components/Chart/dataUtils/getMultiplayerForTimeSpan.ts +0 -13
@@ -1,9 +1,6 @@
1
- import { colorPalette } from '@oanda/labs-widget-common';
2
- import chroma from 'chroma-js';
3
1
  import type { TooltipComponentOption } from 'echarts';
4
2
 
5
3
  import type { BookType } from '../../../../gql/types/graphql';
6
- import { CHART_CONFIG } from '../../../constants';
7
4
  import { getTooltipFormatter } from '../chartUtils';
8
5
  import type { Bucket, TooltipParam } from '../types';
9
6
 
@@ -11,7 +8,6 @@ interface GetTooltipConfigParams {
11
8
  bookType: BookType;
12
9
  bucketWidth: number;
13
10
  buckets: Bucket[][];
14
- displayPrecision: number;
15
11
  labelCallback: (key: string, params?: Record<string, unknown>) => string;
16
12
  selectedPriceRef: { current: number };
17
13
  tooltipLinesColor: string;
@@ -26,7 +22,6 @@ export const getTooltipConfig = ({
26
22
  bookType,
27
23
  bucketWidth,
28
24
  buckets,
29
- displayPrecision,
30
25
  labelCallback,
31
26
  selectedPriceRef,
32
27
  tooltipLinesColor,
@@ -35,83 +30,47 @@ export const getTooltipConfig = ({
35
30
  isDesktop,
36
31
  isDark,
37
32
  locale,
38
- }: GetTooltipConfigParams): TooltipComponentOption => {
39
- const styles = isDesktop
33
+ }: GetTooltipConfigParams): TooltipComponentOption => ({
34
+ backgroundColor: 'transparent',
35
+ shadowColor: 'transparent',
36
+ extraCssText: 'width: 100%; z-index: 5;',
37
+ padding: 4,
38
+ textStyle: isDesktop
40
39
  ? {
41
- backgroundColor: isDark
42
- ? chroma(colorPalette.darkGray).alpha(0.95).hex()
43
- : chroma(colorPalette.darkWhite).alpha(0.95).hex(),
44
- shadowColor: colorPalette.black10,
45
- extraCssText: 'z-index: 5;',
46
- padding: 8,
47
- alwaysShowContent: false,
48
- textStyle: {
49
- fontSize: 11,
50
- },
40
+ fontSize: 12,
51
41
  }
52
42
  : {
53
- backgroundColor: 'transparent',
54
- shadowColor: 'transparent',
55
- extraCssText: 'width: 100%; z-index: 5;',
56
- padding: 4,
57
- alwaysShowContent: true,
58
- textStyle: {
59
- fontSize: 10,
60
- lineHeight: 12,
61
- },
62
- };
63
- return {
64
- ...styles,
65
- trigger: 'axis' as const,
66
- formatter: (params) =>
67
- getTooltipFormatter({
68
- bookType,
69
- bucketWidth,
70
- buckets,
71
- labelCallback,
72
- params: params as unknown as TooltipParam[],
73
- selectedPrice: selectedPriceRef.current,
74
- sentimentLongs,
75
- sentimentShorts,
76
- isDesktop,
77
- locale,
78
- isDark,
79
- }),
80
- hideDelay: 0,
81
- axisPointer: {
82
- type: 'cross' as const,
83
- axis: 'x' as const,
84
- lineStyle: {
85
- color: tooltipLinesColor,
86
- },
87
- crossStyle: {
88
- color: tooltipLinesColor,
89
- },
90
- label: {
91
- padding: 0,
92
- lineHeight: 24,
93
- formatter: (params: {
94
- axisDimension?: string;
95
- axisIndex?: number;
96
- value: unknown;
97
- }) => {
98
- if (params.axisDimension === 'y' && params.axisIndex === 0) {
99
- selectedPriceRef.current = Number(params.value);
100
- return ` ${Number(params.value).toFixed(displayPrecision)} `;
101
- }
102
- return '';
103
- },
43
+ fontSize: 10,
44
+ lineHeight: 12,
104
45
  },
46
+ alwaysShowContent: true,
47
+ trigger: 'axis' as const,
48
+ formatter: (params) =>
49
+ getTooltipFormatter({
50
+ bookType,
51
+ bucketWidth,
52
+ buckets,
53
+ labelCallback,
54
+ params: params as unknown as TooltipParam[],
55
+ selectedPrice: selectedPriceRef.current,
56
+ sentimentLongs,
57
+ sentimentShorts,
58
+ isDesktop,
59
+ locale,
60
+ isDark,
61
+ }),
62
+ hideDelay: 0,
63
+ axisPointer: {
64
+ type: 'cross' as const,
65
+ axis: 'x' as const,
66
+ lineStyle: {
67
+ color: tooltipLinesColor,
105
68
  },
106
- confine: true,
107
- position: (point) => {
108
- if (isDesktop) {
109
- return [
110
- point[0] + CHART_CONFIG.TOOLTIP_OFFSET,
111
- point[1] + CHART_CONFIG.TOOLTIP_OFFSET,
112
- ];
113
- }
114
- return [0, 0];
69
+ crossStyle: {
70
+ color: tooltipLinesColor,
115
71
  },
116
- };
117
- };
72
+ z: 30,
73
+ },
74
+ confine: true,
75
+ position: [0, 0],
76
+ });
@@ -1,6 +1,7 @@
1
1
  import type { XAXisComponentOption } from 'echarts';
2
2
 
3
3
  import {
4
+ formatFullDate,
4
5
  formatXAxisAdditionalLabel,
5
6
  formatXAxisLabel,
6
7
  getLabelData,
@@ -10,12 +11,14 @@ interface GetXAxisConfigParams {
10
11
  dates: string[];
11
12
  isGreaterThanTwoWeeks: boolean;
12
13
  locale: string;
14
+ isDesktop: boolean;
13
15
  }
14
16
 
15
17
  export const getXAxisConfig = ({
16
18
  dates,
17
19
  isGreaterThanTwoWeeks,
18
20
  locale,
21
+ isDesktop,
19
22
  }: GetXAxisConfigParams): XAXisComponentOption[] => {
20
23
  const labelsData = getLabelData({
21
24
  dates,
@@ -38,14 +41,32 @@ export const getXAxisConfig = ({
38
41
  },
39
42
  },
40
43
  axisLabel: {
41
- showMaxLabel: false,
42
44
  padding: [8, 16, 8, 16],
43
45
  margin: 0,
44
46
  formatter: (value: unknown) =>
45
- formatXAxisLabel(value, isGreaterThanTwoWeeks, locale),
47
+ value === dates[dates.length - 1]
48
+ ? ''
49
+ : formatXAxisLabel(value, isGreaterThanTwoWeeks, locale),
46
50
  },
47
51
  boundaryGap: false,
48
52
  z: 2,
53
+ axisPointer: {
54
+ label: {
55
+ lineHeight: 22,
56
+ margin: -22,
57
+ padding: [0, 8, 0, 8],
58
+ formatter: (params: {
59
+ axisDimension?: string;
60
+ axisIndex?: number;
61
+ value: unknown;
62
+ }) => {
63
+ const date = new Date(params.value as string);
64
+
65
+ return isDesktop ? formatFullDate(date, locale) : '';
66
+ },
67
+ },
68
+ z: 100,
69
+ },
49
70
  },
50
71
  {
51
72
  type: 'category',
@@ -67,6 +88,9 @@ export const getXAxisConfig = ({
67
88
  formatter: (value: unknown) =>
68
89
  formatXAxisAdditionalLabel(value, isGreaterThanTwoWeeks, locale),
69
90
  },
91
+ axisPointer: {
92
+ show: false,
93
+ },
70
94
  data: dates,
71
95
  boundaryGap: false,
72
96
  z: 2,
@@ -5,13 +5,13 @@ import { CHART_CONFIG } from '../../../constants';
5
5
  interface GetYAxisConfigParams {
6
6
  bucketWidth: number;
7
7
  displayPrecision: number;
8
- isDesktop: boolean;
8
+ selectedPriceRef: { current: number };
9
9
  }
10
10
 
11
11
  export const getYAxisConfig = ({
12
12
  bucketWidth,
13
13
  displayPrecision,
14
- isDesktop,
14
+ selectedPriceRef,
15
15
  }: GetYAxisConfigParams): YAXisComponentOption[] => [
16
16
  {
17
17
  type: 'value',
@@ -35,6 +35,23 @@ export const getYAxisConfig = ({
35
35
  verticalAlignMaxLabel: 'top',
36
36
  formatter: (value: number) => value.toFixed(displayPrecision),
37
37
  },
38
+ axisPointer: {
39
+ label: {
40
+ padding: [4, 4, 4, 4],
41
+ margin: -1,
42
+ formatter: (params: {
43
+ axisDimension?: string;
44
+ axisIndex?: number;
45
+ value: unknown;
46
+ }) => {
47
+ if (params.axisDimension === 'y' && params.axisIndex === 0) {
48
+ selectedPriceRef.current = Number(params.value);
49
+ return Number(params.value).toFixed(displayPrecision);
50
+ }
51
+ return '';
52
+ },
53
+ },
54
+ },
38
55
  z: 30,
39
56
  },
40
57
  {
@@ -51,7 +68,7 @@ export const getYAxisConfig = ({
51
68
  margin: 4,
52
69
  lineHeight: 20,
53
70
  verticalAlignMinLabel: 'bottom',
54
- verticalAlignMaxLabel: isDesktop ? 'middle' : 'top',
71
+ verticalAlignMaxLabel: 'top',
55
72
  customValues: [
56
73
  CHART_CONFIG.SENTIMENT_MIN,
57
74
  CHART_CONFIG.SENTIMENT_MAX / 5,
@@ -0,0 +1,26 @@
1
+ export const formatFullDate = (date: Date, locale: string): string => {
2
+ const formattedDate = date
3
+ .toLocaleDateString(locale, {
4
+ day: 'numeric',
5
+ month: '2-digit',
6
+ year: '2-digit',
7
+ })
8
+ .replace(/\//g, '.');
9
+
10
+ const formattedTime = date.toLocaleTimeString(locale, {
11
+ hour: 'numeric',
12
+ minute: '2-digit',
13
+ hour12: false,
14
+ });
15
+
16
+ const formatter = new Intl.DateTimeFormat('en-US', {
17
+ timeZoneName: 'shortOffset',
18
+ });
19
+
20
+ const parts = formatter.formatToParts(date);
21
+ const timeZoneOffset = parts.find(
22
+ (part) => part.type === 'timeZoneName'
23
+ )?.value;
24
+
25
+ return `${formattedDate} ${formattedTime} ${timeZoneOffset}`;
26
+ };
@@ -2,18 +2,7 @@ import { colorPalette } from '@oanda/labs-widget-common';
2
2
 
3
3
  import { BookType } from '../../../../gql/types/graphql';
4
4
  import type { Bucket, TooltipParam } from '../types';
5
-
6
- const DATE_FORMAT_OPTIONS: Intl.DateTimeFormatOptions = {
7
- day: 'numeric',
8
- month: 'numeric',
9
- year: 'numeric',
10
-
11
- hour: 'numeric',
12
- minute: '2-digit',
13
- hour12: false,
14
-
15
- timeZoneName: 'shortOffset',
16
- };
5
+ import { formatFullDate } from './formatFullDate';
17
6
 
18
7
  const SENTIMENT_DISPLAY_PRECISION = 2;
19
8
 
@@ -67,7 +56,8 @@ const getSentimentOverbalanceLabel = (
67
56
 
68
57
  const formatCandleData = (
69
58
  candleData: [number, number, number, number, number],
70
- labelCallback: (key: string) => string
59
+ labelCallback: (key: string) => string,
60
+ isDesktop: boolean
71
61
  ): string => {
72
62
  const [, open, close, low, high] = candleData;
73
63
  const candleLabel = labelCallback('candle');
@@ -76,11 +66,19 @@ const formatCandleData = (
76
66
  const lowLabel = labelCallback('low');
77
67
  const highLabel = labelCallback('high');
78
68
 
79
- return `<div><b>${candleLabel}:</b></div>
69
+ return isDesktop
70
+ ? `<div><b>${candleLabel}:</b></div>
71
+ <div style="display: flex; gap: 4px; margin-right: 4px;">
80
72
  <div>${openLabel}: ${open} </div>
81
- <div>${closeLabel}: ${close} </div>
73
+ <div>${highLabel}: ${high} </div>
82
74
  <div>${lowLabel}: ${low} </div>
75
+ <div>${closeLabel}: ${close} </div>
76
+ </div>`
77
+ : `<div><b>${candleLabel}:</b></div>
78
+ <div>${openLabel}: ${open} </div>
83
79
  <div>${highLabel}: ${high} </div>
80
+ <div>${lowLabel}: ${low} </div>
81
+ <div>${closeLabel}: ${close} </div>
84
82
  `;
85
83
  };
86
84
 
@@ -112,8 +110,8 @@ const formatBookData = ({
112
110
 
113
111
  return isDesktop
114
112
  ? `<div><b>${labelCallback(bookLabelKey)}:</b></div>
115
- <div>${labelCallback('price_range')}: ${priceRangeStart} - ${priceRangeEnd} </div>
116
- <div>${labelCallback(sentimentLabel)}: ${sentimentValue}% </div>`
113
+ <span style="margin-right: 4px;">${labelCallback('price_range')}: ${priceRangeStart} - ${priceRangeEnd} </span>
114
+ <span style="margin-right: 4px;">${labelCallback(sentimentLabel)}: ${sentimentValue}% </span>`
117
115
  : `<div><b>${labelCallback(bookLabelKey)}:</b></div>
118
116
  <div>${labelCallback('price_range')}:</div>
119
117
  <div>${priceRangeStart} - ${priceRangeEnd} </div>
@@ -124,9 +122,14 @@ const formatBookData = ({
124
122
  const formatSentimentData = (
125
123
  sentimentLong: number,
126
124
  sentimentShort: number,
127
- labelCallback: (key: string) => string
125
+ labelCallback: (key: string) => string,
126
+ isDesktop: boolean
128
127
  ): string => {
129
- return `<div><b>${labelCallback('sentiment')}:</b></div>
128
+ return isDesktop
129
+ ? `<div><b>${labelCallback('sentiment')}:</b></div>
130
+ <span style="margin-right: 4px;">${labelCallback('long')}: ${sentimentLong.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </span>
131
+ <span style="margin-right: 4px;">${labelCallback('short')}: ${sentimentShort.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </span>`
132
+ : `<div><b>${labelCallback('sentiment')}:</b></div>
130
133
  <div>${labelCallback('long')}: ${sentimentLong.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </div>
131
134
  <div>${labelCallback('short')}: ${sentimentShort.toFixed(SENTIMENT_DISPLAY_PRECISION)}% </div>`;
132
135
  };
@@ -214,11 +217,9 @@ export const getTooltipFormatter = ({
214
217
  return '';
215
218
  }
216
219
 
217
- const timeFormatted = time
218
- .toLocaleString(locale, DATE_FORMAT_OPTIONS)
219
- .replace(/\//g, '.');
220
+ const timeFormatted = formatFullDate(time, locale);
220
221
  const candleSection = showCandles
221
- ? formatCandleData(candleData, labelCallback)
222
+ ? formatCandleData(candleData, labelCallback, isDesktop)
222
223
  : '';
223
224
 
224
225
  const bookSection = matchedBucket
@@ -234,7 +235,12 @@ export const getTooltipFormatter = ({
234
235
 
235
236
  const sentimentSection =
236
237
  showSentiment && sentimentLong && sentimentShort
237
- ? formatSentimentData(sentimentLong, sentimentShort, labelCallback)
238
+ ? formatSentimentData(
239
+ sentimentLong,
240
+ sentimentShort,
241
+ labelCallback,
242
+ isDesktop
243
+ )
238
244
  : '';
239
245
 
240
246
  if (isDesktop) {
@@ -243,7 +249,7 @@ export const getTooltipFormatter = ({
243
249
  candleSection,
244
250
  sentimentSection,
245
251
  bookSection,
246
- hasMatchedBucket: !!matchedBucket,
252
+ isDark,
247
253
  });
248
254
  }
249
255
 
@@ -264,20 +270,29 @@ interface TooltipSections {
264
270
  }
265
271
 
266
272
  const buildDesktopTooltip = ({
267
- timeFormatted,
268
273
  candleSection,
269
274
  sentimentSection,
270
275
  bookSection,
271
- hasMatchedBucket,
272
- }: TooltipSections & { hasMatchedBucket: boolean }): string => {
273
- const padding = TOOLTIP_STYLES.PADDING_BOTTOM;
274
- const sentimentPadding = hasMatchedBucket ? padding : '0px';
276
+ isDark,
277
+ }: TooltipSections & {
278
+ isDark: boolean;
279
+ }): string => {
280
+ const backgroundColor = isDark ? colorPalette.black : colorPalette.white;
281
+ const fullWidth = TOOLTIP_STYLES.FULL_WIDTH;
275
282
 
276
- return `<div style="padding-bottom: ${padding};">${timeFormatted}</div>
277
- <div style="padding-bottom: ${padding};">${candleSection}</div>
278
- <div style="padding-bottom: ${sentimentPadding};">${sentimentSection}</div>
279
- <div>${bookSection}</div>
280
- `;
283
+ return `<div style="width: ${fullWidth}; background-color: ${backgroundColor};">
284
+ <div style="width: ${fullWidth}; display: flex; justify-content: space-between;">
285
+ <div style="width: 40%;">
286
+ ${candleSection}
287
+ </div>
288
+ <div style="width: 22%;">
289
+ ${sentimentSection}
290
+ </div>
291
+ <div style="width: 38%;">
292
+ ${bookSection}
293
+ </div>
294
+ </div>
295
+ </div>`;
281
296
  };
282
297
 
283
298
  const buildMobileTooltip = ({
@@ -1,3 +1,4 @@
1
+ export * from './formatFullDate';
1
2
  export * from './formatXAxisAdditionalLabel';
2
3
  export * from './formatXAxisLabel';
3
4
  export * from './getChartStyles';
@@ -0,0 +1,25 @@
1
+ import {
2
+ DISPLAYED_BUCKETS_COUNT,
3
+ INSTRUMENTS_CONFIG,
4
+ } from '../../../constants';
5
+ import type { InstrumentId } from '../../../types';
6
+
7
+ interface GetBucketWidthMultiplayerParams {
8
+ minPrice: number;
9
+ maxPrice: number;
10
+ instrument: InstrumentId;
11
+ }
12
+
13
+ export const getBucketWidthMultiplayer = ({
14
+ minPrice,
15
+ maxPrice,
16
+ instrument,
17
+ }: GetBucketWidthMultiplayerParams): number => {
18
+ const { defaultBucketWidth } = INSTRUMENTS_CONFIG[instrument];
19
+ const priceRange = maxPrice - minPrice;
20
+
21
+ const calculatedMultiplier =
22
+ priceRange / (defaultBucketWidth * DISPLAYED_BUCKETS_COUNT);
23
+
24
+ return Math.max(1, Math.ceil(calculatedMultiplier));
25
+ };
@@ -1,5 +1,5 @@
1
+ export * from './getBucketWidthMultiplayer';
1
2
  export * from './getInstrumentConfigForDivision';
2
- export * from './getMultiplayerForTimeSpan';
3
3
  export * from './getTimeSpanForGranularity';
4
4
  export * from './processOrderPositionBooks';
5
5
  export * from './processPriceCandles';
@@ -89,7 +89,6 @@ export const getOption: GetOptionType = ({
89
89
  bookType,
90
90
  bucketWidth,
91
91
  buckets,
92
- displayPrecision,
93
92
  labelCallback,
94
93
  selectedPriceRef,
95
94
  tooltipLinesColor: styles.tooltipLinesColor,
@@ -104,11 +103,12 @@ export const getOption: GetOptionType = ({
104
103
  dates,
105
104
  isGreaterThanTwoWeeks,
106
105
  locale,
106
+ isDesktop,
107
107
  }),
108
108
  yAxis: getYAxisConfig({
109
- isDesktop,
110
109
  bucketWidth,
111
110
  displayPrecision,
111
+ selectedPriceRef,
112
112
  }),
113
113
  dataZoom: getDataZoomConfig({ isDesktop }),
114
114
  visualMap: getVisualMapConfig({
@@ -12,10 +12,10 @@ import type {
12
12
  GetSentimentsQuery,
13
13
  GetSentimentsQueryVariables,
14
14
  } from '../../../gql/types/graphql';
15
- import { BookType, DataSource, Division } from '../../../gql/types/graphql';
15
+ import { DataSource } from '../../../gql/types/graphql';
16
16
  import { BUCKET_CONFIG, INSTRUMENTS_CONFIG } from '../../constants';
17
17
  import {
18
- getMultiplayerForTimeSpan,
18
+ getBucketWidthMultiplayer,
19
19
  getTimeSpanForGranularity,
20
20
  processOrderPositionBooks,
21
21
  processPriceCandles,
@@ -30,11 +30,6 @@ export const useCrowdViewData = ({
30
30
  division,
31
31
  granularity,
32
32
  }: UseCrowdViewDataProps): UseCrowdViewDataReturn => {
33
- const dataSource =
34
- division === Division.Ogm || division === Division.Oj
35
- ? DataSource.Mt5
36
- : DataSource.V20;
37
-
38
33
  // Get price candles data
39
34
  const {
40
35
  loading: priceCandlesLoading,
@@ -44,12 +39,9 @@ export const useCrowdViewData = ({
44
39
  getPriceCandles,
45
40
  {
46
41
  variables: {
47
- dataSource,
42
+ dataSource: DataSource.V20,
48
43
  division,
49
- instrument:
50
- dataSource === DataSource.Mt5
51
- ? INSTRUMENTS_CONFIG[instrument].mt5name
52
- : INSTRUMENTS_CONFIG[instrument].v20name,
44
+ instrument: INSTRUMENTS_CONFIG[instrument].v20name,
53
45
  granularity,
54
46
  timeSpan: getTimeSpanForGranularity(granularity),
55
47
  },
@@ -69,6 +61,15 @@ export const useCrowdViewData = ({
69
61
  pipsLocation,
70
62
  } = useMemo(() => processPriceCandles(priceCandlesData), [priceCandlesData]);
71
63
 
64
+ const bucketWidthMultiplayer = useMemo(
65
+ () =>
66
+ getBucketWidthMultiplayer({
67
+ minPrice,
68
+ maxPrice,
69
+ instrument,
70
+ }),
71
+ [minPrice, maxPrice, instrument]
72
+ );
72
73
  // Get order position books data
73
74
  const {
74
75
  loading: orderPositionLoading,
@@ -79,12 +80,12 @@ export const useCrowdViewData = ({
79
80
  {
80
81
  variables: {
81
82
  instrument: INSTRUMENTS_CONFIG[instrument].v20name,
82
- bookType: bookType || BookType.Order,
83
+ bookType,
83
84
  timeSpan: getTimeSpanForGranularity(granularity),
84
85
  granularity,
85
86
  maxBookPrice: maxPrice,
86
87
  minBookPrice: minPrice,
87
- bucketMultiplier: getMultiplayerForTimeSpan(granularity),
88
+ bucketMultiplier: bucketWidthMultiplayer,
88
89
  bucketMargin: BUCKET_CONFIG.PRICE_MARGIN_MULTIPLIER,
89
90
  },
90
91
  fetchPolicy: 'no-cache',
@@ -182,7 +183,7 @@ export const useCrowdViewData = ({
182
183
  bucketWidth:
183
184
  bucketWidth ||
184
185
  INSTRUMENTS_CONFIG[instrument].defaultBucketWidth *
185
- getMultiplayerForTimeSpan(granularity),
186
+ bucketWidthMultiplayer,
186
187
  buckets,
187
188
  displayPrecision: pipsLocation,
188
189
  bookType,
@@ -203,8 +204,8 @@ export const useCrowdViewData = ({
203
204
  sentimentShorts,
204
205
  sentimentLongs,
205
206
  bucketWidth,
206
- granularity,
207
207
  instrument,
208
+ bucketWidthMultiplayer,
208
209
  buckets,
209
210
  pipsLocation,
210
211
  bookType,
@@ -15,6 +15,8 @@ export const TIME_THRESHOLDS = {
15
15
  TWO_WEEKS_MS: 14 * 24 * 60 * 60 * 1000,
16
16
  } as const;
17
17
 
18
+ export const DISPLAYED_BUCKETS_COUNT = 40;
19
+
18
20
  const CHART_CONFIG_STATIC = {
19
21
  MAIN_HEIGHT_DESKTOP: 410,
20
22
  MAIN_HEIGHT_MOBILE: 330,
@@ -32,22 +34,24 @@ const CHART_CONFIG_STATIC = {
32
34
  X_AXIS_DATE_PADDING: ' ',
33
35
  SENTIMENT_MIN: 0,
34
36
  SENTIMENT_MAX: 100,
35
- TOP_MARGIN_DESKTOP: 0,
36
- TOP_MARGIN_MOBILE: 24,
37
- MOBILE_TOOLTIP_HEIGHT: 85,
37
+ TOP_LABEL_SPACE_DESKTOP: 0,
38
+ TOP_LABEL_SPACE_MOBILE: 24,
39
+ TOOLTIP_HEIGHT_MOBILE: 85,
40
+ TOOLTIP_HEIGHT_DESKTOP: 46,
38
41
  TOOLTIP_OFFSET: 8,
39
42
  };
40
43
 
41
44
  const CHART_CONFIG_CALCULATED = {
42
45
  HEIGHT_DESKTOP:
43
46
  CHART_CONFIG_STATIC.MAIN_HEIGHT_DESKTOP +
44
- CHART_CONFIG_STATIC.TOP_MARGIN_DESKTOP +
45
- CHART_CONFIG_STATIC.X_LABEL_SIZE,
47
+ CHART_CONFIG_STATIC.TOP_LABEL_SPACE_DESKTOP +
48
+ CHART_CONFIG_STATIC.X_LABEL_SIZE +
49
+ CHART_CONFIG_STATIC.TOOLTIP_HEIGHT_DESKTOP,
46
50
  HEIGHT_MOBILE:
47
51
  CHART_CONFIG_STATIC.MAIN_HEIGHT_MOBILE +
48
52
  CHART_CONFIG_STATIC.X_LABEL_SIZE +
49
- CHART_CONFIG_STATIC.TOP_MARGIN_MOBILE +
50
- CHART_CONFIG_STATIC.MOBILE_TOOLTIP_HEIGHT,
53
+ CHART_CONFIG_STATIC.TOP_LABEL_SPACE_MOBILE +
54
+ CHART_CONFIG_STATIC.TOOLTIP_HEIGHT_MOBILE,
51
55
  };
52
56
  export const CHART_CONFIG = {
53
57
  ...CHART_CONFIG_STATIC,
@@ -29,6 +29,7 @@
29
29
  "sentiment": "Sentiment",
30
30
  "short": "Short",
31
31
  "short_overbalance": "Short overbalance",
32
- "tap_chart_to_see_more_details": "Tap chart to see more details",
32
+ "tap_chart_to_see_more_details": "Tap the chart to see more details",
33
+ "hover_chart_to_see_more_details": "Hover over the chart to see more details",
33
34
  "trades": "Trades"
34
35
  }
@@ -0,0 +1,33 @@
1
+ import { getBucketWidthMultiplayer } from '../../../../src/CrowdViewWidget/components/Chart/dataUtils/getBucketWidthMultiplayer';
2
+ import { InstrumentId } from '../../../../src/CrowdViewWidget/types';
3
+
4
+ describe('getBucketWidthMultiplayer', () => {
5
+ describe('happy path', () => {
6
+ it('should calculate multiplier correctly for EUR_USD with standard price range', () => {
7
+ const result = getBucketWidthMultiplayer({
8
+ minPrice: 1.05,
9
+ maxPrice: 1.1,
10
+ instrument: InstrumentId.EUR_USD,
11
+ });
12
+ expect(result).toBe(3);
13
+ });
14
+
15
+ it('should calculate multiplier correctly for EUR_JPY with standard price range', () => {
16
+ const result = getBucketWidthMultiplayer({
17
+ minPrice: 140.0,
18
+ maxPrice: 150.0,
19
+ instrument: InstrumentId.EUR_JPY,
20
+ });
21
+ expect(result).toBe(5);
22
+ });
23
+ });
24
+
25
+ it('should return 1 when calculated multiplier is less than 1', () => {
26
+ const result = getBucketWidthMultiplayer({
27
+ minPrice: 1.0,
28
+ maxPrice: 1.01,
29
+ instrument: InstrumentId.EUR_USD,
30
+ });
31
+ expect(result).toBe(1);
32
+ });
33
+ });