@oanda/labs-widget-common 1.0.218 → 1.0.220

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 (31) hide show
  1. package/CHANGELOG.md +1732 -0
  2. package/dist/main/chart/BaseChart.js +7 -2
  3. package/dist/main/chart/BaseChart.js.map +1 -1
  4. package/dist/main/components/Disclaimer/Disclaimer.js +30 -28
  5. package/dist/main/components/Disclaimer/Disclaimer.js.map +1 -1
  6. package/dist/main/components/WidgetWrapper/WidgetWrapper.js +7 -2
  7. package/dist/main/components/WidgetWrapper/WidgetWrapper.js.map +1 -1
  8. package/dist/main/tailwind/colors.js +3 -1
  9. package/dist/main/tailwind/colors.js.map +1 -1
  10. package/dist/main/tailwind/preset.js +2 -1
  11. package/dist/main/tailwind/preset.js.map +1 -1
  12. package/dist/module/chart/BaseChart.js +7 -2
  13. package/dist/module/chart/BaseChart.js.map +1 -1
  14. package/dist/module/components/Disclaimer/Disclaimer.js +28 -27
  15. package/dist/module/components/Disclaimer/Disclaimer.js.map +1 -1
  16. package/dist/module/components/WidgetWrapper/WidgetWrapper.js +7 -2
  17. package/dist/module/components/WidgetWrapper/WidgetWrapper.js.map +1 -1
  18. package/dist/module/tailwind/colors.js +3 -1
  19. package/dist/module/tailwind/colors.js.map +1 -1
  20. package/dist/module/tailwind/preset.js +2 -1
  21. package/dist/module/tailwind/preset.js.map +1 -1
  22. package/dist/types/components/Disclaimer/Disclaimer.d.ts +5 -1
  23. package/dist/types/components/WidgetWrapper/WidgetWrapper.d.ts +5 -0
  24. package/dist/types/tailwind/colors.d.ts +4 -0
  25. package/package.json +2 -2
  26. package/src/chart/BaseChart.tsx +21 -12
  27. package/src/components/Disclaimer/Disclaimer.tsx +80 -42
  28. package/src/components/WidgetWrapper/WidgetWrapper.tsx +10 -0
  29. package/src/tailwind/colors.ts +2 -0
  30. package/src/tailwind/preset.ts +1 -0
  31. package/test/chart/BaseChart.test.tsx +12 -6
@@ -22,10 +22,12 @@ export declare const colorPalette: {
22
22
  white: string;
23
23
  black: string;
24
24
  grayLight: string;
25
+ grayLight95: string;
25
26
  gray: string;
26
27
  darkGray: string;
27
28
  brightBlue: string;
28
29
  brightBlue30: string;
30
+ darkBlue: string;
29
31
  };
30
32
  export declare const twColorPallete: {
31
33
  "red-Dark-": string;
@@ -50,9 +52,11 @@ export declare const twColorPallete: {
50
52
  "white-": string;
51
53
  "black-": string;
52
54
  "gray-Light-": string;
55
+ "gray-Light-9-5-": string;
53
56
  "gray-": string;
54
57
  "dark-Gray-": string;
55
58
  "bright-Blue-": string;
56
59
  "bright-Blue-3-0-": string;
60
+ "dark-Blue-": string;
57
61
  };
58
62
  export type ColorCode = ValueOf<typeof colorPalette>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oanda/labs-widget-common",
3
- "version": "1.0.218",
3
+ "version": "1.0.220",
4
4
  "description": "Labs Widget Common",
5
5
  "main": "dist/main/index.js",
6
6
  "module": "dist/module/index.js",
@@ -21,5 +21,5 @@
21
21
  "tailwind-merge": "2.2.2",
22
22
  "usehooks-ts": "3.0.2"
23
23
  },
24
- "gitHead": "7a07df3d3cadc0733696bb4579b520fe54eacee0"
24
+ "gitHead": "84d9bec88af7893f20ccd2d88fcbc28dd30aec22"
25
25
  }
@@ -18,18 +18,27 @@ export const BaseChart = forwardRef<BaseChartRef, BaseChartProps>(
18
18
  }, []);
19
19
 
20
20
  return (
21
- <ReactEChartsCore
22
- // eslint-disable-next-line react/jsx-props-no-spreading
23
- {...props}
24
- ref={ref}
25
- data-testid="charts-container"
26
- showLoading={!ready}
27
- style={{
28
- height: `${chartHeight}px`,
29
- width: '100%',
30
- }}
31
- theme={isDark ? 'dark_theme' : 'light_theme'}
32
- />
21
+ <>
22
+ {ready ? (
23
+ <ReactEChartsCore
24
+ // eslint-disable-next-line react/jsx-props-no-spreading
25
+ {...props}
26
+ ref={ref}
27
+ data-testid="charts-container"
28
+ style={{
29
+ height: `${chartHeight}px`,
30
+ width: '100%',
31
+ }}
32
+ theme={isDark ? 'dark_theme' : 'light_theme'}
33
+ />
34
+ ) : (
35
+ <div
36
+ className="lw-w-full"
37
+ data-testid="charts-placeholder"
38
+ style={{ height: `${chartHeight}px` }}
39
+ />
40
+ )}
41
+ </>
33
42
  );
34
43
  }
35
44
  );
@@ -1,5 +1,5 @@
1
1
  import type { FC, PropsWithChildren } from 'react';
2
- import React from 'react';
2
+ import React, { useState } from 'react';
3
3
 
4
4
  import { OandaLogoDark, OandaLogoLight } from '../../images';
5
5
  import { useLayoutProvider } from '../../providers';
@@ -7,22 +7,12 @@ import { cn } from '../../tailwind';
7
7
  import type { RenderComponentParams } from '../../types';
8
8
  import { Theme } from '../../types';
9
9
 
10
- const disclaimerStyles: Record<
11
- Theme,
12
- { wrapperStyle: string; borderStyle: string }
13
- > = {
14
- dark: {
15
- wrapperStyle: 'lw-h-6 lw-bg-[#262F3C]',
16
- borderStyle: 'lw-border-[#262F3C]',
17
- },
18
- light: {
19
- wrapperStyle: 'lw-shadow-[0_2px_8px_0_rgba(0,33,74,0.25)]',
20
- borderStyle: 'lw-border-[#e6e6e6]',
21
- },
22
- };
23
-
24
10
  type DisclaimerProps = PropsWithChildren &
25
- Omit<RenderComponentParams, 'Component'>;
11
+ Omit<RenderComponentParams, 'Component'> & {
12
+ text?: string;
13
+ isSlim?: boolean;
14
+ infoButtonPosition?: 'left' | 'top';
15
+ };
26
16
 
27
17
  export const Disclaimer: FC<DisclaimerProps> = ({
28
18
  children,
@@ -30,23 +20,28 @@ export const Disclaimer: FC<DisclaimerProps> = ({
30
20
  brandingSpace,
31
21
  fitContent = false,
32
22
  linkArea = 'full',
23
+ text,
24
+ isSlim,
25
+ infoButtonPosition = 'top',
33
26
  }) => {
34
27
  const { theme } = useLayoutProvider();
28
+ const isDark = theme === Theme.Dark;
35
29
 
36
- if (!logoLink) {
30
+ const [isLogoHovered, setIsLogoHovered] = useState(false);
31
+ const [isInfoHovered, setIsInfoHovered] = useState(false);
32
+
33
+ if (!logoLink && !text) {
37
34
  return children;
38
35
  }
39
36
 
40
- const isDark = theme !== 'light';
41
37
  const isClickable = linkArea === 'full';
42
- const { wrapperStyle, borderStyle } = disclaimerStyles[theme ?? Theme.Light];
43
38
 
44
39
  return (
45
40
  <div
46
41
  className={cn(
47
42
  'lw-relative lw-overflow-hidden',
48
43
  isClickable && 'lw-cursor-pointer',
49
- brandingSpace && brandingSpace === 'vertical' ? 'lw-pb-12' : 'lw-pb-2',
44
+ brandingSpace && brandingSpace === 'vertical' ? 'lw-pb-12' : 'lw-pb-0',
50
45
  !!fitContent && 'lw-w-fit'
51
46
  )}
52
47
  data-testid="disclaimer-wrapper"
@@ -54,34 +49,77 @@ export const Disclaimer: FC<DisclaimerProps> = ({
54
49
  onClick={() => isClickable && window.open(logoLink, '_blank')}
55
50
  >
56
51
  {children}
57
- <div
58
- className={cn(
59
- 'lw-absolute lw-z-20 lw-bottom-3 lw-right-1 lw-translate-x-[3.7rem] lw-rounded-l-full lw-transition lw-delay-150 lw-duration-300 lw-ease-in-out hover:lw-translate-x-2 [&:hover+div]:lw-translate-x-[3.7rem]',
60
- wrapperStyle
61
- )}
62
- >
52
+ {text && (
63
53
  <div
64
- className="lw-cursor-pointer"
65
- data-testid="disclaimer-wrapper-logo"
66
- role="presentation"
67
- onClick={(e) => {
68
- e.stopPropagation();
69
- window.open(logoLink, '_blank');
70
- }}
71
- >
72
- {isDark ? (
73
- <OandaLogoDark data-testid="disclaimer-wrapper-logo-dark" />
74
- ) : (
75
- <OandaLogoLight data-testid="disclaimer-wrapper-logo-light" />
54
+ className={cn(
55
+ 'lw-absolute lw-right-0 lw-top-0 lw-z-30 lw-flex lw-h-full lw-items-center lw-bg-border-primary lw-bg-opacity-90 lw-p-3 lw-text-sm lw-text-text-primary lw-delay-150 lw-duration-150 lw-ease-in-out',
56
+ isInfoHovered && 'lw-opacity-100',
57
+ !isInfoHovered && 'lw-opacity-0 lw-pointer-events-none'
76
58
  )}
59
+ onBlur={() => setIsInfoHovered(false)}
60
+ onMouseOut={() => setIsInfoHovered(false)}
61
+ >
62
+ {text}
77
63
  </div>
78
- </div>
64
+ )}
79
65
  <div
80
66
  className={cn(
81
- 'lw-absolute lw-bottom-2.5 lw-right-0 lw-h-7 lw-w-px lw-translate-x-0 lw-transition lw-delay-150 lw-duration-300',
82
- borderStyle
67
+ 'lw-absolute lw-bottom-3 lw-right-0 lw-z-20 lw-flex lw-transition lw-delay-100 lw-duration-300 lw-ease-in-out',
68
+ logoLink &&
69
+ !isLogoHovered &&
70
+ infoButtonPosition === 'left' &&
71
+ 'lw-translate-x-[55px]',
72
+ isSlim && 'lw-bottom-[6px]',
73
+ !isSlim && 'lw-bottom-2',
74
+ infoButtonPosition === 'left' && 'lw-flex-row lw-items-center',
75
+ infoButtonPosition === 'top' && 'lw-flex-col lw-items-end'
83
76
  )}
84
- />
77
+ >
78
+ {text && (
79
+ <div
80
+ className={cn(
81
+ 'lw-flex lw-h-[20px] lw-w-[20px] lw-items-center lw-justify-center lw-rounded-full lw-border lw-border-border-primary lw-pb-0.5 lw-text-sm lw-font-bold lw-text-text-secondary lw-shadow-basic',
82
+ isDark && 'lw-bg-dark-blue',
83
+ !isDark && 'lw-bg-bg-primary',
84
+ infoButtonPosition === 'top' && 'lw-mr-1',
85
+ infoButtonPosition === 'top' && !!logoLink && 'lw-mb-3',
86
+ infoButtonPosition === 'left' && 'lw-mr-2 '
87
+ )}
88
+ onFocus={() => setIsInfoHovered(true)}
89
+ onMouseOver={() => setIsInfoHovered(true)}
90
+ >
91
+ i
92
+ </div>
93
+ )}
94
+ {logoLink && (
95
+ <div
96
+ className={cn(
97
+ 'lw-cursor-pointer lw-rounded-l-full lw-bg-bg-primary lw-shadow-basic',
98
+ infoButtonPosition === 'top' &&
99
+ 'lw-transition lw-delay-100 lw-duration-300 lw-ease-in-out',
100
+ !isLogoHovered &&
101
+ infoButtonPosition === 'top' &&
102
+ 'lw-translate-x-[55px]'
103
+ )}
104
+ data-testid="disclaimer-wrapper-logo"
105
+ role="presentation"
106
+ onBlur={() => setIsLogoHovered(false)}
107
+ onClick={(e) => {
108
+ e.stopPropagation();
109
+ window.open(logoLink, '_blank');
110
+ }}
111
+ onFocus={() => setIsLogoHovered(true)}
112
+ onMouseOut={() => setIsLogoHovered(false)}
113
+ onMouseOver={() => setIsLogoHovered(true)}
114
+ >
115
+ {isDark ? (
116
+ <OandaLogoDark data-testid="disclaimer-wrapper-logo-dark" />
117
+ ) : (
118
+ <OandaLogoLight data-testid="disclaimer-wrapper-logo-light" />
119
+ )}
120
+ </div>
121
+ )}
122
+ </div>
85
123
  </div>
86
124
  );
87
125
  };
@@ -8,6 +8,11 @@ import { WidgetError } from '../Error';
8
8
  export type WidgetWrapperProps = PropsWithChildren &
9
9
  WidgetLink & {
10
10
  isParamError?: boolean | undefined;
11
+ disclaimer?: {
12
+ position?: 'left' | 'top';
13
+ text?: string;
14
+ };
15
+ isSlim?: boolean;
11
16
  } & Pick<WidgetStyling, 'brandingSpace' | 'fitContent'>;
12
17
 
13
18
  export const WidgetWrapper: FC<WidgetWrapperProps> = ({
@@ -17,12 +22,17 @@ export const WidgetWrapper: FC<WidgetWrapperProps> = ({
17
22
  brandingSpace,
18
23
  fitContent,
19
24
  isParamError,
25
+ disclaimer,
26
+ isSlim,
20
27
  }) => (
21
28
  <Disclaimer
22
29
  brandingSpace={brandingSpace}
23
30
  fitContent={fitContent}
31
+ infoButtonPosition={disclaimer?.position}
32
+ isSlim={isSlim}
24
33
  linkArea={linkArea}
25
34
  logoLink={logoLink}
35
+ text={disclaimer?.text}
26
36
  >
27
37
  {isParamError ? <WidgetError /> : children}
28
38
  </Disclaimer>
@@ -24,10 +24,12 @@ export const colorPalette = {
24
24
  white: '#FFFFFF',
25
25
  black: '#000000',
26
26
  grayLight: '#9EA4AC',
27
+ grayLight95: 'rgba(158, 164, 172, 0.95)',
27
28
  gray: '#7B8085',
28
29
  darkGray: '#1C1C1C',
29
30
  brightBlue: 'rgba(51, 88, 255, 0)',
30
31
  brightBlue30: 'rgba(51, 88, 255, 0.3)',
32
+ darkBlue: '#262f3c',
31
33
  };
32
34
 
33
35
  export const twColorPallete = toTwConfigKeys(colorPalette);
@@ -50,6 +50,7 @@ export const sharedPreset: Omit<Config, 'content'> = {
50
50
  boxShadow: {
51
51
  innerBorder: 'inset 0 0 0 1px',
52
52
  innerBorderBgColor: 'inset 0 0 0 2px hsl(var(--twc-bg-primary))',
53
+ basic: '0 2px 8px 0 rgba(0, 33, 74, 0.25)',
53
54
  },
54
55
  },
55
56
  fontFamily: {
@@ -58,22 +58,23 @@ describe('BaseChart component', () => {
58
58
 
59
59
  it('should show loading state initially', () => {
60
60
  render(<BaseChart {...defaultProps} />);
61
- const chart = screen.getByTestId('charts-container');
62
- expect(chart).toHaveAttribute('data-loading', 'true');
61
+ const placeholder = screen.getByTestId('charts-placeholder');
62
+ expect(placeholder).toBeInTheDocument();
63
63
  });
64
64
 
65
65
  it('should render chart after fonts are ready', async () => {
66
66
  render(<BaseChart {...defaultProps} />);
67
67
 
68
- const chart = screen.getByTestId('charts-container');
69
- expect(chart).toHaveAttribute('data-loading', 'true');
68
+ const placeholder = screen.getByTestId('charts-placeholder');
69
+ expect(placeholder).toBeInTheDocument();
70
70
 
71
71
  await act(async () => {
72
72
  resolveFontsReady();
73
73
  await document.fonts.ready;
74
74
  });
75
75
 
76
- expect(chart).toHaveAttribute('data-loading', 'false');
76
+ const chart = screen.getByTestId('charts-container');
77
+ expect(chart).toBeInTheDocument();
77
78
  });
78
79
 
79
80
  it('should apply dark theme when isDark is true', async () => {
@@ -113,9 +114,14 @@ describe('BaseChart component', () => {
113
114
  expect(chart).toHaveStyle({ height: `${height}px` });
114
115
  });
115
116
 
116
- it('should forward ref to BaseChartRef', () => {
117
+ it('should forward ref to BaseChartRef', async () => {
117
118
  const ref = React.createRef<BaseChartRef>();
118
119
  render(<BaseChart {...defaultProps} ref={ref} />);
120
+
121
+ await act(async () => {
122
+ resolveFontsReady();
123
+ await document.fonts.ready;
124
+ });
119
125
  expect(ref.current).toBeTruthy();
120
126
  });
121
127
  });