Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"devDependencies": {
"typescript": "^5.5.4",
"@typescript-eslint/eslint-plugin": "^8.21.0",
"eslint": "^9.19.0",
Expand All @@ -21,5 +21,6 @@
},
"keywords": [],
"author": "engineering@crayon.ai",
"license": "MIT"
"license": "MIT",
"packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0"
}
3 changes: 3 additions & 0 deletions js/packages/react-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@radix-ui/react-tooltip": "^1.2.7",
"clsx": "^2.1.1",
"date-fns": "^4.1.0",
"html-to-image": "1.11.11",
"lodash-es": "^4.17.21",
"lucide-react": "^0.469.0",
"react-day-picker": "^9.5.1",
Expand All @@ -99,13 +100,15 @@
"@storybook/react-vite": "^8.5.3",
"@storybook/test": "^8.5.3",
"@storybook/theming": "^8.5.3",
"@tailwindcss/postcss": "^4.1.11",
"@types/lodash-es": "^4.17.12",
"@types/node": "^22.12.0",
"@types/node-fetch": "2.6.11",
"@types/react": ">=17.0.0",
"@types/react-dom": ">=17.0.0",
"@types/react-syntax-highlighter": "^15.5.13",
"@typescript-eslint/eslint-plugin": "^8.18.0",
"autoprefixer": "^10.4.21",
"concurrently": "^9.2.0",
"eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0",
Expand Down
49 changes: 47 additions & 2 deletions js/packages/react-ui/src/components/Charts/AreaChart/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { Area, AreaChart as RechartsAreaChart, XAxis, YAxis } from "recharts";
import { useId } from "../../../polyfills";
import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts";
import { ExportContextProvider, useExportContext } from "../ExportContext";
import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext";
import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks";
import {
Expand All @@ -15,6 +16,8 @@ import {
XAxisTick,
YAxisTick,
} from "../shared";
import { ChartExportFooter } from "../shared/ChartExportFooter";
import { ExportButton } from "../shared/ExportButton";
import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip";
import { LegendItem, XAxisTickVariant } from "../types";
import {
Expand All @@ -24,6 +27,7 @@ import {
getWidthOfGroup,
} from "../utils/AreaAndLine/AreaAndLineUtils";
import { PaletteName, useChartPalette } from "../utils/PalletUtils";
import { useExportChart } from "../utils/chartExportUtils";
import {
get2dChartConfig,
getColorForDataKey,
Expand Down Expand Up @@ -54,6 +58,7 @@ export interface AreaChartProps<T extends AreaChartData> {
className?: string;
height?: number;
width?: number;
exportRef?: React.RefObject<HTMLDivElement | null>;
}

const X_AXIS_PADDING = 36;
Expand All @@ -76,6 +81,7 @@ const AreaChartComponent = <T extends AreaChartData>({
className,
height,
width,
exportRef,
}: AreaChartProps<T>) => {
const dataKeys = useMemo(() => {
return getDataKeys(data, categoryKey as string);
Expand Down Expand Up @@ -113,6 +119,11 @@ const AreaChartComponent = <T extends AreaChartData>({
title: "",
values: [],
});
const [isChartHovered, setIsChartHovered] = useState(false);
const exportContext = useExportContext();

const exportChartRef = useRef<HTMLDivElement>(null);
const { exportChart } = useExportChart(exportChartRef, "crayon-area-chart-main-container");

// Use provided width or observed width
const effectiveWidth = useMemo(() => {
Expand Down Expand Up @@ -295,11 +306,43 @@ const AreaChartComponent = <T extends AreaChartData>({
data={sideBarTooltipData}
setData={setSideBarTooltipData}
>
{!exportContext && (
<ExportContextProvider value={{ format: "image" }}>
<AreaChart
categoryKey={categoryKey}
data={data}
theme={theme}
customPalette={customPalette}
variant={variant}
tickVariant={"multiLine"}
grid={grid}
icons={icons}
isAnimationActive={false}
showYAxis={showYAxis}
xAxisLabel={xAxisLabel}
yAxisLabel={yAxisLabel}
legend={legend}
className={className}
height={height}
width={width}
exportRef={exportChartRef}
/>
</ExportContextProvider>
)}
<div
className={clsx("crayon-area-chart-container", className)}
className={clsx(
"crayon-area-chart-container",
{
"crayon-chart-export-container": exportContext,
},
className,
)}
style={{
width: width ? `${width}px` : undefined,
}}
onMouseEnter={() => setIsChartHovered(true)}
onMouseLeave={() => setIsChartHovered(false)}
ref={exportRef}
>
<div className="crayon-area-chart-container-inner" ref={chartContainerRef}>
{/* Y-axis of the chart */}
Expand Down Expand Up @@ -408,10 +451,12 @@ const AreaChartComponent = <T extends AreaChartData>({
yAxisLabel={yAxisLabel}
xAxisLabel={xAxisLabel}
containerWidth={effectiveWidth}
isExpanded={isLegendExpanded}
isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode
setIsExpanded={setIsLegendExpanded}
/>
)}
{isChartHovered && <ExportButton exportChart={exportChart} />}
{exportContext && <ChartExportFooter />}
</div>
</SideBarTooltipProvider>
</LabelTooltipProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@use "../../../cssUtils" as cssUtils;

.crayon-area-chart-container {
position: relative;
}

.crayon-area-chart-container-inner {
display: flex;
width: 100%;
Expand Down
56 changes: 50 additions & 6 deletions js/packages/react-ui/src/components/Charts/BarChart/BarChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useId } from "../../../polyfills";
import { useTheme } from "../../ThemeProvider";
import { ChartConfig, ChartContainer, ChartTooltip } from "../Charts";
import { SideBarChartData, SideBarTooltipProvider } from "../context/SideBarTooltipContext";
import { ExportContextProvider, useExportContext } from "../ExportContext";
import { useMaxLabelHeight, useTransformedKeys, useYAxisLabelWidth } from "../hooks";
import {
cartesianGrid,
Expand All @@ -16,24 +17,25 @@ import {
XAxisTickProps,
YAxisTick,
} from "../shared";

import { ChartExportFooter } from "../shared/ChartExportFooter";
import { ExportButton } from "../shared/ExportButton";
import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip";
import { ScrollButtonsHorizontal } from "../shared/ScrollButtonsHorizontal/ScrollButtonsHorizontal";
import { XAxisTickVariant } from "../types";
import { type LegendItem } from "../types/Legend";
import { useChartPalette, type PaletteName } from "../utils/PalletUtils";

import { LabelTooltipProvider } from "../shared/LabelTooltip/LabelTooltip";
import {
findNearestSnapPosition,
getBarStackInfo,
getRadiusArray,
} from "../utils/BarCharts/BarChartsUtils";
import { useExportChart } from "../utils/chartExportUtils";
import {
get2dChartConfig,
getColorForDataKey,
getDataKeys,
getLegendItems,
} from "../utils/dataUtils";
import { useChartPalette, type PaletteName } from "../utils/PalletUtils";
import { BarChartData, BarChartVariant } from "./types";
import {
BAR_WIDTH,
Expand Down Expand Up @@ -65,6 +67,7 @@ export interface BarChartProps<T extends BarChartData> {
className?: string;
height?: number;
width?: number;
exportRef?: React.RefObject<HTMLElement | null>;
}

const BAR_GAP = 10; // Gap between bars
Expand All @@ -91,8 +94,10 @@ const BarChartComponent = <T extends BarChartData>({
className,
height,
width,
exportRef,
}: BarChartProps<T>) => {
const widthOfGroup = getWidthOfGroup(data, categoryKey as string, variant);
const exportContext = useExportContext();

const maxLabelHeight = useMaxLabelHeight(data, categoryKey as string, tickVariant, widthOfGroup);

Expand Down Expand Up @@ -122,11 +127,14 @@ const BarChartComponent = <T extends BarChartData>({
const [canScrollRight, setCanScrollRight] = useState(false);
const [hoveredCategory, setHoveredCategory] = useState<string | number | null>(null);
const [isLegendExpanded, setIsLegendExpanded] = useState(false);
const [isChartHovered, setIsChartHovered] = useState(false);
const [isSideBarTooltipOpen, setIsSideBarTooltipOpen] = useState(false);
const [sideBarTooltipData, setSideBarTooltipData] = useState<SideBarChartData>({
title: "",
values: [],
});
const exportChartRef = useRef<HTMLDivElement>(null);
const { exportChart } = useExportChart(exportChartRef ?? null, "crayon-bar-chart-main-container");

// Use provided width or observed width
const effectiveWidth = useMemo(() => {
Expand Down Expand Up @@ -407,11 +415,45 @@ const BarChartComponent = <T extends BarChartData>({
data={sideBarTooltipData}
setData={setSideBarTooltipData}
>
{!exportContext && (
<ExportContextProvider value={{ format: "image" }}>
<BarChart
data={data}
categoryKey={categoryKey}
theme={theme}
customPalette={customPalette}
variant={variant}
tickVariant={"multiLine"}
grid={grid}
radius={BAR_RADIUS}
isAnimationActive={false}
showYAxis={showYAxis}
xAxisLabel={xAxisLabel}
yAxisLabel={yAxisLabel}
legend={legend}
className={className}
height={height}
width={width}
exportRef={exportChartRef}
icons={icons}
/>
</ExportContextProvider>
)}

<div
className={clsx("crayon-bar-chart-container", className)}
className={clsx(
"crayon-bar-chart-container",
{
"crayon-chart-export-container": exportContext,
},
className,
)}
style={{
width: width ? `${width}px` : undefined,
}}
onMouseEnter={() => setIsChartHovered(true)}
onMouseLeave={() => setIsChartHovered(false)}
ref={exportRef as React.RefObject<HTMLDivElement>}
>
<div className="crayon-bar-chart-container-inner" ref={chartContainerRef}>
{/* Y-axis of the chart */}
Expand Down Expand Up @@ -497,10 +539,12 @@ const BarChartComponent = <T extends BarChartData>({
yAxisLabel={yAxisLabel}
xAxisLabel={xAxisLabel}
containerWidth={effectiveWidth}
isExpanded={isLegendExpanded}
isExpanded={isLegendExpanded || !!exportContext} // legend should always be expanded in export mode
setIsExpanded={setIsLegendExpanded}
/>
)}
{isChartHovered && <ExportButton exportChart={exportChart} />}
{exportContext && <ChartExportFooter />}
</div>
</SideBarTooltipProvider>
</LabelTooltipProvider>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@use "../../../cssUtils" as cssUtils;

.crayon-bar-chart-container {
position: relative;
}

.crayon-bar-chart-container-inner {
display: flex;
width: 100%;
Expand Down
12 changes: 12 additions & 0 deletions js/packages/react-ui/src/components/Charts/ExportContext/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { createContext, useContext } from "react";

interface ExportContext {
format: "image";
}

export const ExportContext = createContext<ExportContext | null>(null);
export const ExportContextProvider = ExportContext.Provider;

export const useExportContext = () => {
return useContext(ExportContext);
};
Loading