@takaro/lib-components 0.4.8 → 0.4.10

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 (57) hide show
  1. package/package.json +1 -1
  2. package/src/components/actions/Button/__snapshots__/Button.test.tsx.snap +1 -1
  3. package/src/components/actions/IconButton/__snapshots__/IconButton.test.tsx.snap +1 -1
  4. package/src/components/charts/AreaChart/AreaChart.stories.tsx +11 -7
  5. package/src/components/charts/AreaChart/index.tsx +114 -63
  6. package/src/components/charts/BarChart/BarChart.stories.tsx +33 -10
  7. package/src/components/charts/BarChart/index.tsx +280 -147
  8. package/src/components/charts/EmptyChart.tsx +45 -0
  9. package/src/components/charts/GeoMercator/GeoMercator.stories.tsx +15 -9
  10. package/src/components/charts/GeoMercator/index.tsx +15 -172
  11. package/src/components/charts/Heatmap/Heatmap.stories.tsx +167 -33
  12. package/src/components/charts/Heatmap/index.tsx +427 -193
  13. package/src/components/charts/LineChart/LineChart.stories.tsx +77 -3
  14. package/src/components/charts/LineChart/index.tsx +200 -79
  15. package/src/components/charts/PieChart/PieChart.stories.tsx +128 -20
  16. package/src/components/charts/PieChart/index.tsx +353 -59
  17. package/src/components/charts/PointHighlight.tsx +2 -2
  18. package/src/components/charts/RadarChart/RadarChart.stories.tsx +14 -5
  19. package/src/components/charts/RadarChart/index.tsx +94 -45
  20. package/src/components/charts/RadialBarChart/RadialBarChart.stories.tsx +26 -1
  21. package/src/components/charts/RadialBarChart/index.tsx +100 -34
  22. package/src/components/charts/RadialLineChart/RadialLineChart.stories.tsx +19 -2
  23. package/src/components/charts/RadialLineChart/index.tsx +116 -26
  24. package/src/components/charts/index.tsx +0 -26
  25. package/src/components/charts/util.ts +50 -12
  26. package/src/components/data/CountryList/index.tsx +146 -0
  27. package/src/components/data/Stats/Sparkline.tsx +48 -0
  28. package/src/components/data/Stats/Stat.tsx +15 -4
  29. package/src/components/data/Stats/context.tsx +1 -1
  30. package/src/components/data/Stats/index.tsx +8 -3
  31. package/src/components/data/index.ts +3 -0
  32. package/src/components/feedback/IconTooltip/index.tsx +9 -6
  33. package/src/components/feedback/ProgressBar/ProgressBar.stories.tsx +13 -14
  34. package/src/components/feedback/ProgressBar/index.tsx +1 -1
  35. package/src/components/inputs/DurationField/__tests__/Generic.test.tsx +12 -0
  36. package/src/components/visual/Card/CardTitle.tsx +7 -1
  37. package/src/components/visual/Card/index.tsx +0 -4
  38. package/src/helpers/formatNumber.ts +6 -0
  39. package/src/helpers/index.ts +1 -0
  40. package/src/components/charts/echarts/EChartsArea.stories.tsx +0 -139
  41. package/src/components/charts/echarts/EChartsArea.tsx +0 -139
  42. package/src/components/charts/echarts/EChartsBar.stories.tsx +0 -141
  43. package/src/components/charts/echarts/EChartsBar.tsx +0 -133
  44. package/src/components/charts/echarts/EChartsBase.tsx +0 -264
  45. package/src/components/charts/echarts/EChartsFunnel.stories.tsx +0 -164
  46. package/src/components/charts/echarts/EChartsFunnel.tsx +0 -114
  47. package/src/components/charts/echarts/EChartsHeatmap.stories.tsx +0 -168
  48. package/src/components/charts/echarts/EChartsHeatmap.tsx +0 -141
  49. package/src/components/charts/echarts/EChartsLine.stories.tsx +0 -132
  50. package/src/components/charts/echarts/EChartsLine.tsx +0 -111
  51. package/src/components/charts/echarts/EChartsPie.stories.tsx +0 -131
  52. package/src/components/charts/echarts/EChartsPie.tsx +0 -124
  53. package/src/components/charts/echarts/EChartsRadialBar.stories.tsx +0 -124
  54. package/src/components/charts/echarts/EChartsRadialBar.tsx +0 -118
  55. package/src/components/charts/echarts/EChartsScatter.stories.tsx +0 -166
  56. package/src/components/charts/echarts/EChartsScatter.tsx +0 -135
  57. package/src/components/charts/echarts/index.ts +0 -26
@@ -1,8 +1,8 @@
1
- import { useMemo, useRef, useState } from 'react';
1
+ import { useMemo, useRef, useState, useCallback, useEffect } from 'react';
2
2
 
3
3
  import { Bar } from '@visx/shape';
4
4
  import { Group } from '@visx/group';
5
- import { GridRows } from '@visx/grid';
5
+ import { GridRows, GridColumns } from '@visx/grid';
6
6
  import { scaleBand, scaleLinear } from '@visx/scale';
7
7
  import { max } from '@visx/vendor/d3-array';
8
8
  import { ParentSize } from '@visx/responsive';
@@ -11,45 +11,46 @@ import { AxisBottom, AxisLeft } from '@visx/axis';
11
11
  import { Brush } from '@visx/brush';
12
12
  import { PatternLines } from '@visx/pattern';
13
13
  import { shade } from 'polished';
14
+ import { useTooltip, TooltipWithBounds } from '@visx/tooltip';
15
+ import { localPoint } from '@visx/event';
16
+ import { motion } from 'framer-motion';
14
17
 
15
- import { useGradients } from '../useGradients';
16
18
  import { useTheme } from '../../../hooks';
17
- import { ChartProps, InnerChartProps, Margin } from '../util';
19
+ import { ChartProps, InnerChartProps, BrushConfig, TooltipConfig, getDefaultTooltipStyles } from '../util';
18
20
  import { BrushHandle } from '../BrushHandle';
21
+ import { EmptyChart } from '../EmptyChart';
19
22
 
20
23
  export interface BarChartProps<T> extends ChartProps {
21
24
  data: T[];
22
25
  xAccessor: (d: T) => string;
23
26
  yAccessor: (d: T) => number;
24
- margin?: Margin;
25
- showBrush?: boolean;
26
- brushMargin?: Margin;
27
+ /** Bar width as percentage (0-1). 1.0 = bars touch (no gaps), 0.5 = bars take 50% of space. Default: 0.6 */
28
+ barWidth?: number;
29
+ tooltip?: TooltipConfig<T>;
30
+ brush?: BrushConfig;
27
31
  }
28
32
 
29
- const defaultMargin = { top: 20, left: 50, bottom: 20, right: 20 };
30
- const defaultBrushMargin = { top: 10, bottom: 15, left: 50, right: 20 };
33
+ const defaultMargin = { top: 20, left: 60, bottom: 60, right: 20 };
34
+ const defaultBrushMargin = { top: 10, bottom: 15, left: 60, right: 20 };
31
35
  const defaultShowAxisX = true;
32
36
  const defaultShowAxisY = true;
33
- const defaultShowGrid = true;
34
37
 
35
38
  export const BarChart = <T,>({
36
39
  data,
37
40
  xAccessor,
38
41
  yAccessor,
39
- margin = defaultMargin,
40
- showGrid = defaultShowGrid,
41
- showBrush = false,
42
- brushMargin = defaultBrushMargin,
43
42
  name,
44
- showAxisX = defaultShowAxisX,
45
- showAxisY = defaultShowAxisY,
46
- axisXLabel,
47
- axisYLabel,
43
+ grid = 'none',
44
+ axis,
45
+ barWidth = 0.6,
46
+ tooltip,
47
+ brush,
48
+ animate = true,
49
+ margin = defaultMargin,
48
50
  }: BarChartProps<T>) => {
49
- // TODO: handle empty data
50
- if (!data || data.length === 0) return null;
51
-
52
- // TODO: handle loading state
51
+ if (!data || data.length === 0) {
52
+ return <EmptyChart />;
53
+ }
53
54
 
54
55
  return (
55
56
  <>
@@ -62,14 +63,13 @@ export const BarChart = <T,>({
62
63
  data={data}
63
64
  width={parent.width}
64
65
  height={parent.height}
65
- brushMargin={brushMargin}
66
- showBrush={showBrush}
67
- showGrid={showGrid}
66
+ grid={grid}
67
+ axis={axis}
68
+ barWidth={barWidth}
69
+ tooltip={tooltip}
70
+ brush={brush}
71
+ animate={animate}
68
72
  margin={margin}
69
- axisYLabel={axisYLabel}
70
- axisXLabel={axisXLabel}
71
- showAxisX={showAxisX}
72
- showAxisY={showAxisY}
73
73
  />
74
74
  )}
75
75
  </ParentSize>
@@ -86,28 +86,38 @@ const Chart = <T,>({
86
86
  width,
87
87
  height,
88
88
  margin = defaultMargin,
89
- showGrid = defaultShowGrid,
90
- showBrush = false,
91
- brushMargin = defaultBrushMargin,
89
+ grid = 'none',
90
+ axis,
91
+ barWidth = 0.6,
92
+ tooltip,
93
+ brush,
94
+ animate = true,
92
95
  name,
93
- showAxisX = defaultShowAxisX,
94
- showAxisY = defaultShowAxisY,
95
- axisYLabel,
96
- axisXLabel,
97
96
  }: InnerBarChartProps<T>) => {
98
- const gradients = useGradients(name);
97
+ const showAxisX = axis?.showX ?? defaultShowAxisX;
98
+ const showAxisY = axis?.showY ?? defaultShowAxisY;
99
+ const axisXLabel = axis?.labelX;
100
+ const axisYLabel = axis?.labelY;
101
+ const numTicksY = axis?.numTicksY ?? 5;
102
+
103
+ const tooltipAccessor = tooltip?.accessor;
104
+ const showTooltipEnabled = tooltip?.enabled ?? true;
105
+
106
+ const showBrush = brush?.enabled ?? false;
107
+ const brushMargin = brush?.margin ?? defaultBrushMargin;
99
108
  const [filteredData, setFilteredData] = useState(data);
100
109
 
101
110
  const brushRef = useRef(null);
111
+ const { hideTooltip, showTooltip, tooltipData, tooltipLeft = 0, tooltipTop = 0 } = useTooltip<T>();
112
+ const [hoveredBar, setHoveredBar] = useState<string | null>(null);
102
113
 
103
114
  const axisHeight = 100;
104
- const axisWidth = 20;
105
115
  const theme = useTheme();
106
116
 
107
117
  const PATTERN_ID = `${name}-brush_pattern`;
108
118
 
109
119
  // bounds
110
- const xMax = width;
120
+ const xMax = Math.max(width - margin.left - margin.right, 0);
111
121
  const yMax = height - margin.top - axisHeight; // 100 for axis
112
122
 
113
123
  const chartSeparation = 30;
@@ -119,15 +129,19 @@ const Chart = <T,>({
119
129
  const xBrushMax = Math.max(width - brushMargin.left - brushMargin.right, 0);
120
130
  const yBrushMax = Math.max(bottomChartHeight - brushMargin.top - brushMargin.bottom, 0);
121
131
 
132
+ useEffect(() => {
133
+ setFilteredData(data);
134
+ }, [data]);
135
+
122
136
  const xScale = useMemo(
123
137
  () =>
124
138
  scaleBand<string>({
125
139
  range: [0, xMax],
126
140
  round: true,
127
141
  domain: filteredData.map(xAccessor),
128
- padding: 0.4,
142
+ padding: 1 - barWidth,
129
143
  }),
130
- [xMax, filteredData],
144
+ [xMax, filteredData, barWidth, xAccessor],
131
145
  );
132
146
  const yScale = useMemo(
133
147
  () =>
@@ -145,8 +159,9 @@ const Chart = <T,>({
145
159
  range: [0, xBrushMax],
146
160
  domain: data.map(xAccessor),
147
161
  round: true,
162
+ padding: 1 - barWidth,
148
163
  }),
149
- [xBrushMax],
164
+ [xBrushMax, barWidth, xAccessor, data],
150
165
  );
151
166
 
152
167
  const yBrushScale = useMemo(
@@ -171,117 +186,235 @@ const Chart = <T,>({
171
186
  setFilteredData(filtered);
172
187
  };
173
188
 
174
- return (
175
- <svg width={width} height={height}>
176
- {gradients.background.gradient}
177
- {gradients.chart.gradient}
178
- <rect width={width} height={height} fill={`url(#${gradients.background.id})`} rx={14} />
179
- {showGrid && (
180
- <GridRows
181
- top={margin.top}
182
- left={margin.left + axisWidth}
183
- scale={yScale}
184
- width={xMax}
185
- height={yMax}
186
- stroke={theme.colors.backgroundAccent}
187
- strokeOpacity={1}
188
- />
189
- )}
190
- <Group top={margin.top}>
191
- {filteredData.map((d) => {
192
- const xVal = xAccessor(d);
193
- const barWidth = xScale.bandwidth();
194
- const barHeight = yMax - (yScale(yAccessor(d)) ?? 0);
195
- return (
196
- <Bar
197
- key={`bar-${xVal}`}
198
- x={xScale(xVal)}
199
- y={yMax - barHeight}
200
- width={barWidth}
201
- height={barHeight}
202
- fill={shade(0.5, theme.colors.primary)}
203
- stroke={theme.colors.primary}
204
- />
205
- );
206
- })}
207
- </Group>
208
- {showAxisY && (
209
- <AxisLeft
210
- top={margin.top}
211
- left={margin.left}
212
- tickStroke={theme.colors.textAlt}
213
- tickLabelProps={{
214
- fill: theme.colors.textAlt,
215
- fontSize: theme.fontSize.small,
216
- textAnchor: 'middle',
217
- }}
218
- scale={yScale}
219
- label={axisYLabel}
220
- />
221
- )}
189
+ const handleTooltip = useCallback(
190
+ (event: React.TouchEvent<SVGElement> | React.MouseEvent<SVGElement>) => {
191
+ const { x } = localPoint(event) || { x: 0 };
192
+ // x is relative to the SVG, subtract margin to get chart-relative position
193
+ const xRelativeToChart = x - margin.left;
222
194
 
223
- {showAxisX && (
224
- <AxisBottom
225
- top={yMax + margin.top}
226
- tickStroke={theme.colors.textAlt}
227
- tickLabelProps={{
228
- fill: theme.colors.textAlt,
229
- fontSize: theme.fontSize.small,
230
- textAnchor: 'middle',
231
- }}
232
- scale={xScale}
233
- label={axisXLabel}
234
- />
235
- )}
195
+ // Find the bar that contains this x position
196
+ const xValue = xScale.domain().find((domainValue) => {
197
+ const barX = xScale(domainValue) ?? 0;
198
+ const barWidth = xScale.bandwidth();
199
+ return xRelativeToChart >= barX && xRelativeToChart <= barX + barWidth;
200
+ });
201
+
202
+ if (xValue) {
203
+ const d = filteredData.find((item) => xAccessor(item) === xValue);
204
+ if (d) {
205
+ const tooltipX = (xScale(xValue) ?? 0) + xScale.bandwidth() / 2; // Center of bar
206
+ const tooltipY = yScale(yAccessor(d));
207
+
208
+ setHoveredBar(xValue);
209
+ showTooltip({
210
+ tooltipData: d,
211
+ tooltipLeft: tooltipX,
212
+ tooltipTop: tooltipY,
213
+ });
214
+ }
215
+ }
216
+ },
217
+ [xScale, yScale, filteredData, xAccessor, yAccessor, margin.left, showTooltip],
218
+ );
219
+
220
+ return (
221
+ <div>
222
+ <svg width={width} height={height} role="img" aria-label={`Bar chart: ${name}`}>
223
+ <desc id={`${name}-desc`}>Bar chart showing {filteredData.length} data points</desc>
224
+ {(grid === 'y' || grid === 'xy') && (
225
+ <GridRows
226
+ top={margin.top}
227
+ left={margin.left}
228
+ scale={yScale}
229
+ width={xMax}
230
+ height={yMax}
231
+ stroke={theme.colors.backgroundAccent}
232
+ strokeOpacity={1}
233
+ strokeDasharray="2,2"
234
+ pointerEvents="none"
235
+ />
236
+ )}
237
+ {(grid === 'x' || grid === 'xy') && (
238
+ <GridColumns
239
+ top={margin.top}
240
+ left={margin.left}
241
+ scale={xScale}
242
+ height={yMax}
243
+ stroke={theme.colors.backgroundAccent}
244
+ strokeOpacity={1}
245
+ strokeDasharray="2,2"
246
+ pointerEvents="none"
247
+ />
248
+ )}
249
+ {/* Hover highlight */}
250
+ {hoveredBar && (
251
+ <Group top={margin.top} left={margin.left}>
252
+ {(() => {
253
+ const barX = xScale(hoveredBar) ?? 0;
254
+ const barWidth = xScale.bandwidth();
255
+ const stepWidth = xScale.step();
256
+ const highlightX = barX - (stepWidth - barWidth) / 2;
236
257
 
237
- {showBrush && (
238
- <>
239
- {/* brush chart */}
240
- <Group left={brushMargin.left} top={topChartHeight + topChartBottomMargin + margin.top}>
241
- {data.map((d) => {
242
- const xVal = xAccessor(d);
243
- const barWidth = xBrushScale.bandwidth();
244
- const barHeight = yBrushMax - (yBrushScale(yAccessor(d)) ?? 0);
245
258
  return (
246
- <Bar
247
- key={`bar-${xVal}`}
248
- x={xBrushScale(xVal)}
249
- y={yBrushMax - barHeight}
250
- width={barWidth}
251
- height={barHeight}
252
- fill={`url(#${gradients.chart.id})`}
259
+ <motion.rect
260
+ x={highlightX}
261
+ y={0}
262
+ width={stepWidth}
263
+ height={yMax}
264
+ fill={theme.colors.backgroundAccent}
265
+ opacity={0.15}
266
+ pointerEvents="none"
267
+ initial={{ opacity: 0 }}
268
+ animate={{ opacity: 0.15 }}
269
+ transition={{ duration: 0.15 }}
253
270
  />
254
271
  );
255
- })}
256
- <PatternLines
257
- id={PATTERN_ID}
258
- height={8}
259
- width={8}
260
- stroke={theme.colors.backgroundAccent}
261
- strokeWidth={1}
262
- orientation={['diagonal']}
263
- />
264
- <Brush
265
- xScale={xBrushScale}
266
- yScale={yBrushScale}
267
- width={xBrushMax}
268
- height={yBrushMax}
269
- innerRef={brushRef}
270
- resizeTriggerAreas={['left', 'right']}
271
- brushDirection="horizontal"
272
- margin={brushMargin}
273
- onChange={onBrushChange}
274
- selectedBoxStyle={{
275
- fill: `url(#${PATTERN_ID})`,
276
- stroke: theme.colors.backgroundAccent,
277
- }}
278
- useWindowMoveEvents
279
- handleSize={8}
280
- renderBrushHandle={(props) => <BrushHandle {...props} />}
281
- />
272
+ })()}
282
273
  </Group>
283
- </>
274
+ )}
275
+ <Group top={margin.top} left={margin.left}>
276
+ {filteredData.map((d, index) => {
277
+ const xVal = xAccessor(d);
278
+ const barWidth = xScale.bandwidth();
279
+ const barHeight = yMax - (yScale(yAccessor(d)) ?? 0);
280
+ return (
281
+ <g key={`bar-${xVal}`} transform={`translate(${xScale(xVal)}, ${yMax})`}>
282
+ <motion.rect
283
+ x={0}
284
+ width={barWidth}
285
+ fill={shade(0.5, theme.colors.primary)}
286
+ stroke={theme.colors.primary}
287
+ initial={animate ? { y: 0, height: 0 } : { y: -barHeight, height: barHeight }}
288
+ animate={{ y: -barHeight, height: barHeight }}
289
+ transition={
290
+ animate
291
+ ? {
292
+ duration: 0.6,
293
+ delay: index * 0.05,
294
+ ease: 'easeOut',
295
+ }
296
+ : { duration: 0 }
297
+ }
298
+ />
299
+ </g>
300
+ );
301
+ })}
302
+ </Group>
303
+ {showTooltipEnabled && (
304
+ <Bar
305
+ x={margin.left}
306
+ y={margin.top}
307
+ width={xMax}
308
+ height={yMax}
309
+ fill="transparent"
310
+ onTouchStart={handleTooltip}
311
+ onTouchMove={handleTooltip}
312
+ onMouseMove={handleTooltip}
313
+ onMouseLeave={() => {
314
+ hideTooltip();
315
+ setHoveredBar(null);
316
+ }}
317
+ />
318
+ )}
319
+ {showAxisY && (
320
+ <AxisLeft
321
+ top={margin.top}
322
+ left={margin.left}
323
+ numTicks={numTicksY}
324
+ tickStroke={theme.colors.textAlt}
325
+ tickLabelProps={{
326
+ fill: theme.colors.textAlt,
327
+ fontSize: theme.fontSize.small,
328
+ textAnchor: 'end',
329
+ }}
330
+ scale={yScale}
331
+ label={axisYLabel}
332
+ />
333
+ )}
334
+
335
+ {showAxisX && (
336
+ <AxisBottom
337
+ top={yMax + margin.top}
338
+ left={margin.left}
339
+ tickStroke={theme.colors.textAlt}
340
+ tickLabelProps={{
341
+ fill: theme.colors.textAlt,
342
+ fontSize: theme.fontSize.small,
343
+ textAnchor: 'end',
344
+ angle: -45,
345
+ dy: '0.25em',
346
+ }}
347
+ tickFormat={(value) => {
348
+ const maxLength = 15;
349
+ const str = String(value);
350
+ return str.length > maxLength ? `${str.substring(0, maxLength)}...` : str;
351
+ }}
352
+ scale={xScale}
353
+ label={axisXLabel}
354
+ />
355
+ )}
356
+
357
+ {showBrush && (
358
+ <>
359
+ {/* brush chart */}
360
+ <Group left={brushMargin.left} top={topChartHeight + topChartBottomMargin + margin.top}>
361
+ {data.map((d) => {
362
+ const xVal = xAccessor(d);
363
+ const barWidth = xBrushScale.bandwidth();
364
+ const barHeight = yBrushMax - (yBrushScale(yAccessor(d)) ?? 0);
365
+ return (
366
+ <Bar
367
+ key={`bar-${xVal}`}
368
+ x={xBrushScale(xVal)}
369
+ y={yBrushMax - barHeight}
370
+ width={barWidth}
371
+ height={barHeight}
372
+ fill={shade(0.5, theme.colors.primary)}
373
+ />
374
+ );
375
+ })}
376
+ <PatternLines
377
+ id={PATTERN_ID}
378
+ height={8}
379
+ width={8}
380
+ stroke={theme.colors.backgroundAccent}
381
+ strokeWidth={1}
382
+ orientation={['diagonal']}
383
+ />
384
+ <Brush
385
+ xScale={xBrushScale}
386
+ yScale={yBrushScale}
387
+ width={xBrushMax}
388
+ height={yBrushMax}
389
+ innerRef={brushRef}
390
+ resizeTriggerAreas={['left', 'right']}
391
+ brushDirection="horizontal"
392
+ margin={brushMargin}
393
+ onChange={onBrushChange}
394
+ selectedBoxStyle={{
395
+ fill: `url(#${PATTERN_ID})`,
396
+ stroke: theme.colors.backgroundAccent,
397
+ }}
398
+ useWindowMoveEvents
399
+ handleSize={8}
400
+ renderBrushHandle={(props) => <BrushHandle {...props} />}
401
+ />
402
+ </Group>
403
+ </>
404
+ )}
405
+ </svg>
406
+ {showTooltipEnabled && tooltipData && (
407
+ <div>
408
+ <TooltipWithBounds
409
+ key={`${name}-tooltip`}
410
+ top={tooltipTop + margin.top}
411
+ left={tooltipLeft + margin.left}
412
+ style={getDefaultTooltipStyles(theme)}
413
+ >
414
+ {tooltipAccessor ? tooltipAccessor(tooltipData) : yAccessor(tooltipData)}
415
+ </TooltipWithBounds>
416
+ </div>
284
417
  )}
285
- </svg>
418
+ </div>
286
419
  );
287
420
  };
@@ -0,0 +1,45 @@
1
+ import { FC } from 'react';
2
+ import { styled } from '../../styled';
3
+
4
+ const Container = styled.div`
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ width: 100%;
9
+ height: 100%;
10
+ svg {
11
+ position: absolute;
12
+ width: 100%;
13
+ height: 100%;
14
+ }
15
+ path {
16
+ stroke: ${({ theme }) => theme.colors.primary};
17
+ stroke-width: 1;
18
+ fill: none;
19
+ opacity: 0.2;
20
+ }
21
+
22
+ p {
23
+ font-size: ${({ theme }) => theme.fontSize.medium};
24
+ color: ${({ theme }) => theme.colors.textAlt};
25
+ }
26
+ `;
27
+
28
+ export const EmptyChart: FC = () => {
29
+ return (
30
+ <Container>
31
+ <svg viewBox="0 0 100 100" preserveAspectRatio="none">
32
+ <path
33
+ d="
34
+ M 0,80
35
+ C 10,60 20,90 30,70
36
+ S 50,50 60,60
37
+ S 80,30 90,50
38
+ T 100,40
39
+ "
40
+ ></path>
41
+ </svg>
42
+ <p>No data available</p>
43
+ </Container>
44
+ );
45
+ };
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  import { Meta, StoryFn } from '@storybook/react';
3
3
  import { GeoMercator, GeoMercatorProps } from '.';
4
4
  import { Card } from '../../../components';
5
+ import { CountryList } from '../../data/CountryList';
5
6
  import { styled } from '../../../styled';
6
7
 
7
8
  interface Shape {
@@ -39,6 +40,8 @@ const Wrapper = styled.div`
39
40
  const Inner = styled.div`
40
41
  width: 800px;
41
42
  height: 800px;
43
+ display: flex;
44
+ gap: 1rem;
42
45
  `;
43
46
 
44
47
  export const Default: StoryFn<GeoMercatorProps<Shape>> = (args) => {
@@ -54,15 +57,18 @@ export const Default: StoryFn<GeoMercatorProps<Shape>> = (args) => {
54
57
  <Card.Title label="Map" />
55
58
  <Card.Body>
56
59
  <Inner>
57
- <GeoMercator<Shape>
58
- name="geo-mercator"
59
- xAccessor={getCountry}
60
- yAccessor={getAmount}
61
- tooltipAccessor={tooltipAccessor}
62
- data={alpha3}
63
- showZoomControls={args.showZoomControls}
64
- allowZoomAndDrag={args.allowZoomAndDrag}
65
- />
60
+ <div style={{ position: 'relative', flex: 1 }}>
61
+ <GeoMercator<Shape>
62
+ name="geo-mercator"
63
+ xAccessor={getCountry}
64
+ yAccessor={getAmount}
65
+ tooltipAccessor={tooltipAccessor}
66
+ data={alpha3}
67
+ showZoomControls={args.showZoomControls}
68
+ allowZoomAndDrag={args.allowZoomAndDrag}
69
+ />
70
+ </div>
71
+ <CountryList<Shape> data={alpha3} xAccessor={getCountry} yAccessor={getAmount} />
66
72
  </Inner>
67
73
  </Card.Body>
68
74
  </Card>