Skip to content

Commit 800749d

Browse files
zefirkakorvin89
andauthored
feat: custom react tooltip support in Yagr type widgets (#211)
* feat(Yagr plugin)!: update to v3 (#180) * feat!: updated Yagr version * fix: updated package-lock, added npmrc * fix: switched to npm@8 to have lock version of 2 * chore: fixed jest config * feat: updated yagr to 3.3.0 * fix: bumped yarg patch version * fix: fix rebase artifacts --------- Co-authored-by: Алаев Евгений <alaev89@yandex-team.ru> * fix: build fixes (#190) * chore: rebase main (#193) * chore: update node in actions to 18 (#191) * chore: update node in actions to 18 * feat: upgrade lock version to 3 * fix: lint fixes * chore: fix main-preview action (#192) * feat(Yagr plugin)!: update to v3 (#180) * feat!: updated Yagr version * fix: updated package-lock, added npmrc * fix: switched to npm@8 to have lock version of 2 * chore: fixed jest config * feat: updated yagr to 3.3.0 * fix: bumped yarg patch version * fix: fix rebase artifacts --------- Co-authored-by: Алаев Евгений <alaev89@yandex-team.ru> * fix: build fixes (#190) * chore: lock fixes --------- Co-authored-by: Trdat Mkrtchyan <wombtromb@gmail.com> * fix: bumped yagr to 3.3.2 (#194) * feat!: change moment to date-utils (#198) * feat!: remove moment * fix(Highcharts plugin): fix lang changing * chore: add @gravity-ui/eslint-config/prettier to eslintrc * fix: bump yagr to 3.4.0 (#202) * fix: bumped yagr to 3.3.6 * fix(yagr:) bumped version to 3.4.0 * fix: bumped yagr to 3.5.1 * fix: bump yagr to 3.5.2 * fix: bump yagr to 3.5.3 * fix: bumped yagr to 3.5.4 * fix: bump yagr to 3.5.5 * fix: bump yagr to 3.6.0 * feat: adding custom tooltip for Yagr component * fix: bumped yagr to 3.6.1 * refactor: refactored yagr widget * fix: fixed typings * fix: edited custom tooltip suport --------- Co-authored-by: Алаев Евгений <alaev89@yandex-team.ru>
1 parent a23fbd6 commit 800749d

File tree

9 files changed

+218
-133
lines changed

9 files changed

+218
-133
lines changed

package-lock.json

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
},
1515
"dependencies": {
1616
"@gravity-ui/date-utils": "^1.4.1",
17-
"@gravity-ui/yagr": "^3.4.0",
17+
"@gravity-ui/yagr": "^3.6.1",
1818
"bem-cn-lite": "^4.1.0",
1919
"lodash": "^4.17.21",
2020
"react-split-pane": "^0.1.92"
@@ -72,9 +72,9 @@
7272
},
7373
"peerDependencies": {
7474
"@gravity-ui/uikit": "^5.0.0",
75-
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
7675
"highcharts": "^8.2.2",
77-
"highcharts-react-official": "^3.2.0"
76+
"highcharts-react-official": "^3.2.0",
77+
"react": "^16.0.0 || ^17.0.0 || ^18.0.0"
7878
},
7979
"scripts": {
8080
"test": "jest",

src/plugins/yagr/__stories__/Yagr.stories.tsx

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
import React from 'react';
1+
import React, {useEffect, useState} from 'react';
22
import {Meta, Story} from '@storybook/react';
33
import {Button} from '@gravity-ui/uikit';
44
import {settings} from '../../../libs';
5-
import {YagrPlugin, YagrReactRef} from '../../../plugins';
5+
import {CustomTooltipProps, TooltipHandlerData, YagrPlugin} from '../../../plugins';
66
import {ChartKit} from '../../../components/ChartKit';
77
import type {ChartKitRef} from '../../../types';
88
import {getNewConfig, line10} from './mocks/line10';
99

1010
import '@gravity-ui/yagr/dist/index.css';
11+
import placement from '@gravity-ui/yagr/dist/YagrCore/plugins/tooltip/placement';
12+
import {dateTime} from '@gravity-ui/date-utils';
1113

1214
export default {
1315
title: 'Plugins/Yagr',
@@ -17,8 +19,6 @@ export default {
1719
const LineTemplate: Story<any> = () => {
1820
const [shown, setShown] = React.useState(false);
1921
const chartkitRef = React.useRef<ChartKitRef>();
20-
// Example of usage pluginRef property
21-
const yagrPluginRef = React.useRef<YagrReactRef>(null);
2222

2323
if (!shown) {
2424
settings.set({plugins: [YagrPlugin]});
@@ -27,13 +27,7 @@ const LineTemplate: Story<any> = () => {
2727

2828
return (
2929
<div style={{height: 300, width: '100%'}}>
30-
<ChartKit
31-
ref={chartkitRef}
32-
id="1"
33-
type="yagr"
34-
data={line10}
35-
pluginRef={yagrPluginRef}
36-
/>
30+
<ChartKit ref={chartkitRef} id="1" type="yagr" data={line10} />
3731
</div>
3832
);
3933
};
@@ -63,5 +57,78 @@ const UpdatesTemplate: Story<any> = () => {
6357
</div>
6458
);
6559
};
60+
61+
function Tooltip({yagr}: CustomTooltipProps) {
62+
const [x, setX] = useState<number | null | undefined>(null);
63+
const [visible, setVisible] = useState(false);
64+
const tooltipRef = React.useRef<HTMLDivElement>(null);
65+
66+
useEffect(() => {
67+
if (!yagr) {
68+
return;
69+
}
70+
71+
const onChange = (_: unknown, data: TooltipHandlerData) => {
72+
setX(data.data?.x);
73+
setVisible(data.state.visible);
74+
75+
if (data.state.visible && tooltipRef.current && data.data?.anchor) {
76+
placement(tooltipRef.current, data.data.anchor, 'right', {
77+
xOffset: 24,
78+
yOffset: 24,
79+
});
80+
}
81+
};
82+
83+
yagr.plugins.tooltip?.on('render', onChange);
84+
yagr.plugins.tooltip?.on('show', onChange);
85+
yagr.plugins.tooltip?.on('hide', onChange);
86+
}, [yagr]);
87+
88+
if (!visible) {
89+
return null;
90+
}
91+
92+
return (
93+
<div
94+
style={{
95+
zIndex: 1000,
96+
backgroundColor: 'white',
97+
padding: 8,
98+
pointerEvents: 'none',
99+
}}
100+
ref={tooltipRef}
101+
>
102+
{dateTime({input: x ?? 0}).format('DD MMMM YYYY HH:mm:ss')}
103+
</div>
104+
);
105+
}
106+
107+
const CustomTooltipImpl: Story<any> = () => {
108+
const [shown, setShown] = React.useState(false);
109+
const chartkitRef = React.useRef<ChartKitRef>();
110+
111+
const [state, setState] = React.useState(line10);
112+
113+
if (!shown) {
114+
settings.set({plugins: [YagrPlugin]});
115+
return <Button onClick={() => setShown(true)}>Show chart</Button>;
116+
}
117+
118+
return (
119+
<div style={{height: 300, width: '100%'}}>
120+
<ChartKit
121+
ref={chartkitRef}
122+
id="1"
123+
type="yagr"
124+
data={state}
125+
tooltip={(props: CustomTooltipProps) => <Tooltip {...props} />}
126+
/>
127+
<Button onClick={() => setState(getNewConfig())}>Change data</Button>
128+
</div>
129+
);
130+
};
131+
66132
export const Line = LineTemplate.bind({});
67133
export const Updates = UpdatesTemplate.bind({});
134+
export const CustomTooltip = CustomTooltipImpl.bind({});
Lines changed: 102 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,123 @@
11
import React from 'react';
22
import isEmpty from 'lodash/isEmpty';
3-
import {useForkRef} from '@gravity-ui/uikit';
3+
44
import YagrComponent, {YagrChartProps, YagrReactRef} from '@gravity-ui/yagr/dist/react';
5+
56
import {i18n} from '../../../i18n';
6-
import type {ChartKitWidgetRef, ChartKitProps} from '../../../types';
7+
import type {ChartKitWidgetRef} from '../../../types';
78
import {CHARTKIT_ERROR_CODE, ChartKitError} from '../../../libs';
89
import {useWidgetData} from './useWidgetData';
910
import {checkFocus, detectClickOutside, synchronizeTooltipTablesCellsWidth} from './utils';
11+
import {Yagr, YagrWidgetProps} from '../types';
1012

1113
import './polyfills';
1214

1315
import '@gravity-ui/yagr/dist/index.css';
1416
import './YagrWidget.scss';
1517

16-
type Props = ChartKitProps<'yagr'> & {id: string};
17-
18-
const YagrWidget = React.forwardRef<ChartKitWidgetRef | undefined, Props>((props, forwardedRef) => {
19-
const {
20-
id,
21-
data: {data},
22-
pluginRef,
23-
onLoad,
24-
onRender,
25-
onChartLoad,
26-
} = props;
27-
const yagrRef = React.useRef<YagrReactRef>(null);
28-
const handleRef = useForkRef(pluginRef, yagrRef);
29-
30-
if (!data || isEmpty(data)) {
31-
throw new ChartKitError({
32-
code: CHARTKIT_ERROR_CODE.NO_DATA,
33-
message: i18n('error', 'label_no-data'),
34-
});
35-
}
36-
37-
const {config, debug} = useWidgetData({...props.data, id});
38-
39-
const handleChartLoading: NonNullable<YagrChartProps['onChartLoad']> = React.useCallback(
40-
(chart, {renderTime}) => {
41-
onLoad?.({...data, widget: chart, widgetRendering: renderTime});
42-
onRender?.({renderTime});
43-
},
44-
[onLoad, onRender, data],
45-
);
46-
47-
const onWindowResize = React.useCallback(() => {
48-
if (yagrRef.current) {
49-
const chart = yagrRef.current.yagr();
50-
51-
if (!chart) {
52-
return;
53-
}
54-
55-
chart.reflow();
18+
const YagrWidget = React.forwardRef<ChartKitWidgetRef | undefined, YagrWidgetProps>(
19+
(props, forwardedRef) => {
20+
const {
21+
id,
22+
data: {data},
23+
onLoad,
24+
onRender,
25+
onChartLoad,
26+
tooltip,
27+
} = props;
28+
29+
const yagrRef = React.useRef<YagrReactRef>(null);
30+
const [yagr, setYagr] = React.useState<Yagr>();
31+
32+
if (!data || isEmpty(data)) {
33+
throw new ChartKitError({
34+
code: CHARTKIT_ERROR_CODE.NO_DATA,
35+
message: i18n('error', 'label_no-data'),
36+
});
5637
}
57-
}, []);
58-
59-
React.useImperativeHandle(
60-
forwardedRef,
61-
() => ({
62-
reflow() {
63-
onWindowResize();
64-
},
65-
}),
66-
[onWindowResize],
67-
);
6838

69-
React.useEffect(() => {
70-
const yagr = yagrRef.current?.yagr();
39+
const {config, debug} = useWidgetData(props, id);
7140

72-
if (!yagr) {
73-
return;
74-
}
75-
76-
if (yagr.config?.tooltip?.virtual) {
77-
return;
78-
}
41+
const handleChartLoading: NonNullable<YagrChartProps['onChartLoad']> = React.useCallback(
42+
(chart, {renderTime}) => {
43+
onLoad?.({...data, widget: chart, widgetRendering: renderTime});
44+
onRender?.({renderTime});
45+
setYagr(chart);
46+
},
47+
[onLoad, onRender, data, setYagr],
48+
);
7949

80-
const handlers: Record<string, null | ((event: MouseEvent) => void)> = {
81-
mouseMove: null,
82-
mouseDown: null,
83-
};
84-
85-
yagr.plugins.tooltip?.on('render', (tooltip) => {
86-
synchronizeTooltipTablesCellsWidth(tooltip);
87-
});
88-
89-
yagr.plugins.tooltip?.on('pin', (tooltip, {actions}) => {
90-
handlers.mouseMove = checkFocus({tooltip, yagr});
91-
handlers.mouseDown = detectClickOutside({tooltip, actions, yagr});
92-
document.addEventListener('mousemove', handlers.mouseMove);
93-
document.addEventListener('mousedown', handlers.mouseDown);
94-
});
95-
96-
yagr.plugins.tooltip?.on('unpin', () => {
97-
if (handlers.mouseMove) {
98-
document.removeEventListener('mousemove', handlers.mouseMove);
99-
handlers.mouseMove = null;
50+
const onWindowResize = React.useCallback(() => {
51+
if (yagr) {
52+
yagr.reflow();
10053
}
101-
102-
if (handlers.mouseDown) {
103-
document.removeEventListener('mousedown', handlers.mouseDown);
104-
handlers.mouseDown = null;
54+
}, []);
55+
56+
React.useImperativeHandle(
57+
forwardedRef,
58+
() => ({
59+
reflow() {
60+
onWindowResize();
61+
},
62+
}),
63+
[onWindowResize],
64+
);
65+
66+
React.useEffect(() => {
67+
if (!yagr || yagr.config?.tooltip?.virtual) {
68+
return;
10569
}
106-
});
107-
}, []);
108-
109-
React.useLayoutEffect(() => {
110-
onChartLoad?.({widget: yagrRef.current?.yagr()});
111-
}, [yagrRef, onChartLoad]);
112-
113-
return (
114-
<YagrComponent
115-
ref={handleRef}
116-
id={id}
117-
config={config}
118-
debug={debug}
119-
onChartLoad={handleChartLoading}
120-
/>
121-
);
122-
});
70+
71+
const handlers: Record<string, null | ((event: MouseEvent) => void)> = {
72+
mouseMove: null,
73+
mouseDown: null,
74+
};
75+
76+
yagr.plugins.tooltip?.on('render', (tooltip) => {
77+
synchronizeTooltipTablesCellsWidth(tooltip);
78+
});
79+
80+
yagr.plugins.tooltip?.on('pin', (tooltip, {actions}) => {
81+
handlers.mouseMove = checkFocus({tooltip, yagr});
82+
handlers.mouseDown = detectClickOutside({tooltip, actions, yagr});
83+
document.addEventListener('mousemove', handlers.mouseMove);
84+
document.addEventListener('mousedown', handlers.mouseDown);
85+
});
86+
87+
yagr.plugins.tooltip?.on('unpin', () => {
88+
if (handlers.mouseMove) {
89+
document.removeEventListener('mousemove', handlers.mouseMove);
90+
handlers.mouseMove = null;
91+
}
92+
93+
if (handlers.mouseDown) {
94+
document.removeEventListener('mousedown', handlers.mouseDown);
95+
handlers.mouseDown = null;
96+
}
97+
});
98+
}, [yagr]);
99+
100+
React.useLayoutEffect(() => {
101+
onChartLoad?.({widget: yagr});
102+
}, [yagr, onChartLoad]);
103+
104+
return (
105+
<React.Fragment>
106+
{tooltip &&
107+
yagr &&
108+
tooltip({
109+
yagr,
110+
})}
111+
<YagrComponent
112+
ref={yagrRef}
113+
id={id}
114+
config={config}
115+
debug={debug}
116+
onChartLoad={handleChartLoading}
117+
/>
118+
</React.Fragment>
119+
);
120+
},
121+
);
123122

124123
export default YagrWidget;

0 commit comments

Comments
 (0)