From 4b9be7e2f90ebca5f55faa6b88bb6f43a469f1c7 Mon Sep 17 00:00:00 2001 From: Jim Risen Date: Tue, 19 Dec 2023 11:36:44 +0200 Subject: [PATCH] chore(storybook): add SciChartReact examples with 3D, Pie Charts and Typescript usage --- .../DocExamples/ChartUsageWithTypescript.tsx | 146 ++++++++++++++++++ .../DocExamples/ChartWith3dSurface.tsx | 111 +++++++++++++ .../DocExamples/ChartWithInitFunction.tsx | 2 + .../DocExamples/ChartWithPieSurface.tsx | 63 ++++++++ src/stories/SciChartReact.stories.tsx | 56 ++++++- src/stories/raw-loader.d.ts | 4 + tsconfig.json | 3 +- 7 files changed, 377 insertions(+), 8 deletions(-) create mode 100644 src/stories/DocExamples/ChartUsageWithTypescript.tsx create mode 100644 src/stories/DocExamples/ChartWith3dSurface.tsx create mode 100644 src/stories/DocExamples/ChartWithPieSurface.tsx create mode 100644 src/stories/raw-loader.d.ts diff --git a/src/stories/DocExamples/ChartUsageWithTypescript.tsx b/src/stories/DocExamples/ChartUsageWithTypescript.tsx new file mode 100644 index 0000000..b1de154 --- /dev/null +++ b/src/stories/DocExamples/ChartUsageWithTypescript.tsx @@ -0,0 +1,146 @@ +import { useContext } from "react"; +import { + XyDataSeries, + MountainAnimation, + EAutoRange, + NumberRange, + NumericAxis, + SciChartSurface, + SplineMountainRenderableSeries, + easing +} from "scichart"; +import { SciChartReact, SciChartSurfaceContext, TResolvedReturnType } from "scichart-react"; + +// get the TInitResult type specialization from initialization function using a helper utility type +type TChartInitializationResult = TResolvedReturnType; + +export const ChartUsageWithTypescript = () => ( + + fallback={fallbackComponent} + initChart={initializationFunction} + onInit={(initResult: TChartInitializationResult) => { + // enable updating the chart when it is rendered + initResult.startUpdate(); + }} + onDelete={(initResult: TChartInitializationResult) => { + // do a cleanup after the component unmounts + initResult.stopUpdate(); + }} + style={{ + aspectRatio: 2, + minWidth: "600px", + minHeight: "300px" + }} + > +
+ + +
+ +); + +const StartButton = () => { + const initResult = useContext(SciChartSurfaceContext) as TChartInitializationResult; + const handleClick = () => { + initResult.startUpdate(); + }; + return ; +}; + +const StopButton = () => { + const initResult = useContext(SciChartSurfaceContext) as TChartInitializationResult; + const handleClick = () => { + initResult.stopUpdate(); + }; + return ; +}; + +const fallbackComponent = ( +
+
Loading...
+
+); + +const initializationFunction = async (rootElement: string | HTMLDivElement) => { + const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement); + + const xAxis = new NumericAxis(wasmContext, { + autoRange: EAutoRange.Never, + visibleRange: new NumberRange(0, 9) + }); + const yAxis = new NumericAxis(wasmContext, { + autoRange: EAutoRange.Never, + visibleRange: new NumberRange(0, 12) + }); + + sciChartSurface.xAxes.add(xAxis); + sciChartSurface.yAxes.add(yAxis); + + const mountainSeries = new SplineMountainRenderableSeries(wasmContext, { + dataSeries: new XyDataSeries(wasmContext, { + xValues: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + yValues: [1, 4, 7, 3, 7, 6, 7, 4, 2, 5] + }), + fill: "#3ca832", + stroke: "#eb911c", + strokeThickness: 4, + opacity: 0.4 + }); + + sciChartSurface.renderableSeries.add(mountainSeries); + + // create a temp series for passing animation values + const animationSeries = new XyDataSeries(wasmContext); + // register this so it is deleted along with the main surface + sciChartSurface.addDeletable(animationSeries); + + const updatePoints = () => { + const numberOfPoints = 10; + const xValues = new Array(numberOfPoints); + const yValues = new Array(numberOfPoints); + + // Generate points + for (let j = 0; j < numberOfPoints; ++j) { + xValues[j] = j; + yValues[j] = Math.random() * 10 + 1; + } + + // update points + animationSeries.clear(); + animationSeries.appendRange(xValues, yValues); + + mountainSeries.runAnimation( + new MountainAnimation({ duration: 300, ease: easing.outQuad, dataSeries: animationSeries }) + ); + }; + + let timerId; + const startUpdate = () => { + clearInterval(timerId); + timerId = setInterval(updatePoints, 400); + }; + + const stopUpdate = () => { + clearInterval(timerId); + }; + + return { sciChartSurface, startUpdate, stopUpdate }; +}; diff --git a/src/stories/DocExamples/ChartWith3dSurface.tsx b/src/stories/DocExamples/ChartWith3dSurface.tsx new file mode 100644 index 0000000..7f15ba3 --- /dev/null +++ b/src/stories/DocExamples/ChartWith3dSurface.tsx @@ -0,0 +1,111 @@ +import { + CameraController, + EDrawMeshAs, + GradientColorPalette, + MouseWheelZoomModifier3D, + NumberRange, + NumericAxis3D, + OrbitModifier3D, + ResetCamera3DModifier, + SciChart3DSurface, + SurfaceMeshRenderableSeries3D, + UniformGridDataSeries3D, + Vector3, + zeroArray2D +} from "scichart"; +import { SciChartReact } from "scichart-react"; + +export const ChartWith3dSurface = () => ( + { + const { sciChart3DSurface, wasmContext } = await SciChart3DSurface.create(rootElement); + + // Create and position the camera in the 3D world + sciChart3DSurface.camera = new CameraController(wasmContext, { + position: new Vector3(-200, 150, 200), + target: new Vector3(0, 50, 0) + }); + // Set the worlddimensions, which defines the Axis cube size + sciChart3DSurface.worldDimensions = new Vector3(200, 100, 200); + + // Add an X,Y and Z Axis + sciChart3DSurface.xAxis = new NumericAxis3D(wasmContext, { axisTitle: "X Axis" }); + sciChart3DSurface.yAxis = new NumericAxis3D(wasmContext, { + axisTitle: "Y Axis", + visibleRange: new NumberRange(0, 0.3) + }); + sciChart3DSurface.zAxis = new NumericAxis3D(wasmContext, { axisTitle: "Z Axis" }); + + // Create a 2D array using the helper function zeroArray2D + // and fill this with data + const zSize = 25; + const xSize = 25; + const heightmapArray = zeroArray2D([zSize, xSize]); + for (let z = 0; z < zSize; z++) { + for (let x = 0; x < xSize; x++) { + const xVal = (x / xSize) * 25.0; + const zVal = (z / zSize) * 25.0; + const y = Math.sin(xVal * 0.2) / ((zVal + 1) * 2); + heightmapArray[z][x] = y; + } + } + + // Create a UniformGridDataSeries3D + const dataSeries = new UniformGridDataSeries3D(wasmContext, { + yValues: heightmapArray, + xStep: 1, + zStep: 1, + dataSeriesName: "Uniform Surface Mesh" + }); + + // Create the color map + const colorMap = new GradientColorPalette(wasmContext, { + gradientStops: [ + { offset: 1, color: "pink" }, + { offset: 0.9, color: "orange" }, + { offset: 0.7, color: "red" }, + { offset: 0.5, color: "green" }, + { offset: 0.3, color: "blue" }, + { offset: 0, color: "violet" } + ] + }); + + // Finally, create a SurfaceMeshRenderableSeries3D and add to the chart + const series = new SurfaceMeshRenderableSeries3D(wasmContext, { + dataSeries, + minimum: 0, + maximum: 0.5, + opacity: 0.9, + cellHardnessFactor: 1.0, + shininess: 0, + lightingFactor: 0.0, + highlight: 1.0, + stroke: "blue", + strokeThickness: 2.0, + contourStroke: "blue", + contourInterval: 2, + contourOffset: 0, + contourStrokeThickness: 2, + drawSkirt: false, + drawMeshAs: EDrawMeshAs.SOLID_WIREFRAME, + meshColorPalette: colorMap, + isVisible: true + }); + + sciChart3DSurface.renderableSeries.add(series); + + // Optional: Add some interactivity modifiers + sciChart3DSurface.chartModifiers.add(new MouseWheelZoomModifier3D()); + sciChart3DSurface.chartModifiers.add(new OrbitModifier3D()); + sciChart3DSurface.chartModifiers.add(new ResetCamera3DModifier()); + + // the returned result should contain at least a reference to the created surface as `sciChartSurface` + return { sciChartSurface: sciChart3DSurface }; + }} + style={{ + aspectRatio: 2, + minWidth: "600px", + minHeight: "300px" + }} + /> +); diff --git a/src/stories/DocExamples/ChartWithInitFunction.tsx b/src/stories/DocExamples/ChartWithInitFunction.tsx index 935fb87..000564a 100644 --- a/src/stories/DocExamples/ChartWithInitFunction.tsx +++ b/src/stories/DocExamples/ChartWithInitFunction.tsx @@ -11,6 +11,7 @@ import { SciChartReact } from "scichart-react"; export const ChartWithInitFunction = () => ( { const { sciChartSurface, wasmContext } = await SciChartSurface.create(rootElement); @@ -36,6 +37,7 @@ export const ChartWithInitFunction = () => ( new ZoomExtentsModifier() ); + // the returned result should contain at least a reference to the created surface return { sciChartSurface }; }} style={{ diff --git a/src/stories/DocExamples/ChartWithPieSurface.tsx b/src/stories/DocExamples/ChartWithPieSurface.tsx new file mode 100644 index 0000000..8f194f1 --- /dev/null +++ b/src/stories/DocExamples/ChartWithPieSurface.tsx @@ -0,0 +1,63 @@ +import { EColor, GradientParams, PieSegment, Point, SciChartPieSurface } from "scichart"; +import { SciChartReact } from "scichart-react"; + +export const ChartWithPieSurface = () => ( + { + const sciChartPieSurface = await SciChartPieSurface.create(rootElement); + + const pieSegment1 = new PieSegment({ + color: EColor.Green, + value: 10, + text: "Green", + delta: 10, + colorLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [ + { color: "#1D976C", offset: 0 }, + { color: "#93F9B9", offset: 1 } + ]) + }); + pieSegment1.radiusAdjustment = 1.2; + const pieSegment2 = new PieSegment({ + color: EColor.Red, + value: 20, + text: "Red", + delta: 20, + colorLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [ + { color: "#DD5E89", offset: 0 }, + { color: "#F7BB97", offset: 1 } + ]) + }); + pieSegment2.radiusAdjustment = 0.7; + const pieSegment3 = new PieSegment({ + color: EColor.Blue, + value: 30, + text: "Blue", + delta: 30, + colorLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [ + { color: "#2b2828", offset: 0 }, + { color: "#656565", offset: 1 } + ]) + }); + const pieSegment4 = new PieSegment({ + color: EColor.Yellow, + value: 40, + text: "Yellow", + delta: 40, + colorLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [ + { color: "#F09819", offset: 0 }, + { color: "#EDDE5D", offset: 1 } + ]) + }); + + sciChartPieSurface.pieSegments.add(pieSegment1, pieSegment2, pieSegment3, pieSegment4); + + // the returned result should contain at least a reference to the created surface as `sciChartSurface` + return { sciChartSurface: sciChartPieSurface }; + }} + style={{ + aspectRatio: 2, + minWidth: "600px", + minHeight: "300px" + }} + /> +); diff --git a/src/stories/SciChartReact.stories.tsx b/src/stories/SciChartReact.stories.tsx index 1556b9f..2c601c4 100644 --- a/src/stories/SciChartReact.stories.tsx +++ b/src/stories/SciChartReact.stories.tsx @@ -1,20 +1,29 @@ import type { Meta, StoryObj } from "@storybook/react"; -import PrimaryChartExampleSrcCode from "!!raw-loader!./DocExamples/PrimaryChartExample.tsx"; -import ChartWithConfigSrcCode from "!!raw-loader!./DocExamples/ChartWithConfig.tsx"; -import ChartWithInitFunctionSrcCode from "!!raw-loader!./DocExamples/ChartWithInitFunction.tsx"; -import ChartWithFallbackSrcCode from "!!raw-loader!./DocExamples/ChartWithFallback.tsx"; -import ChartWithInitCallbackSrcCode from "!!raw-loader!./DocExamples/ChartWithInitCallback.tsx"; -import ChartWithNestedComponentsSrcCode from "!!raw-loader!./DocExamples/ChartWithNestedComponents.tsx"; +import PrimaryChartExampleSrcCode from "!!raw-loader!./DocExamples/PrimaryChartExample"; +import ChartWithConfigSrcCode from "!!raw-loader!./DocExamples/ChartWithConfig"; +import ChartWithInitFunctionSrcCode from "!!raw-loader!./DocExamples/ChartWithInitFunction"; +import ChartWithFallbackSrcCode from "!!raw-loader!./DocExamples/ChartWithFallback"; +import ChartWithInitCallbackSrcCode from "!!raw-loader!./DocExamples/ChartWithInitCallback"; +import ChartWithNestedComponentsSrcCode from "!!raw-loader!./DocExamples/ChartWithNestedComponents"; +import ChartUsageWithTypescriptSrcCode from "!!raw-loader!./DocExamples/ChartUsageWithTypescript"; +import ChartWith3dSurfaceSrcCode from "!!raw-loader!./DocExamples/ChartWith3dSurface"; +import ChartWithPieSurfaceSrcCode from "!!raw-loader!./DocExamples/ChartWithPieSurface"; import { PrimaryChartExample as PrimaryChartExampleRenderer } from "./DocExamples/PrimaryChartExample"; import { ChartWithConfig as ChartWithConfigRenderer } from "./DocExamples/ChartWithConfig"; import { ChartWithInitFunction as ChartWithInitFunctionRenderer } from "./DocExamples/ChartWithInitFunction"; import { ChartWithFallback as ChartWithFallbackRenderer } from "./DocExamples/ChartWithFallback"; import { ChartWithInitCallback as ChartWithInitCallbackRenderer } from "./DocExamples/ChartWithInitCallback"; import { ChartWithNestedComponents as ChartWithNestedComponentsRenderer } from "./DocExamples/ChartWithNestedComponents"; -import { SciChartSurface } from "scichart"; +import { ChartUsageWithTypescript as ChartUsageWithTypescriptRenderer } from "./DocExamples/ChartUsageWithTypescript"; +import { ChartWith3dSurface as ChartWith3dSurfaceRenderer } from "./DocExamples/ChartWith3dSurface"; +import { ChartWithPieSurface as ChartWithPieSurfaceRenderer } from "./DocExamples/ChartWithPieSurface"; +import { SciChart3DSurface, SciChartSurface } from "scichart"; import { SciChartReact } from "../SciChart"; import { Title, Subtitle, Description, Primary, Controls, Stories, ArgTypes } from "@storybook/blocks"; + SciChartSurface.useWasmFromCDN(); +SciChart3DSurface.useWasmFromCDN(); + // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export const meta = { title: "Example/SciChartReact", @@ -90,3 +99,36 @@ export const ChartWithNestedComponents: Story = { parameters: { docs: { source: { type: "dynamic", language: "tsx", code: ChartWithNestedComponentsSrcCode } } }, render: ChartWithNestedComponentsRenderer }; + +/** + * SciChartReact defines a generic function-component that accepts type parameters specifying: + * - the returned surface type (`TSurface`) + * - a type of an object returned by the initialization function (`TInitResult`) + * + * The `TInitResult` can be applied to: + * - params of callbacks + * - `SciChartSurfaceContext` value + */ +export const ChartUsageWithTypescript: Story = { + parameters: { docs: { source: { type: "dynamic", language: "tsx", code: ChartUsageWithTypescriptSrcCode } } }, + render: ChartUsageWithTypescriptRenderer +}; + +/** + * Creating a 3D chart with an init function is similar to the 2D chart initialization. + * Currently 3D charts do not support initialization via config. + */ +export const ChartWith3dSurface: Story = { + name: "Chart with 3D Surface", + parameters: { docs: { source: { type: "dynamic", language: "tsx", code: ChartWith3dSurfaceSrcCode } } }, + render: ChartWith3dSurfaceRenderer +}; + +/** + * Creating a Pie chart uses the same approach. + * Pie Charts are supported by config definition. + */ +export const ChartWithPieSurface: Story = { + parameters: { docs: { source: { type: "dynamic", language: "tsx", code: ChartWithPieSurfaceSrcCode } } }, + render: ChartWithPieSurfaceRenderer +}; diff --git a/src/stories/raw-loader.d.ts b/src/stories/raw-loader.d.ts new file mode 100644 index 0000000..7f5087a --- /dev/null +++ b/src/stories/raw-loader.d.ts @@ -0,0 +1,4 @@ +declare module "!!raw-loader!*" { + const contents: string; + export = contents; +} diff --git a/tsconfig.json b/tsconfig.json index f6d3971..ca9a314 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,7 +35,8 @@ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ "typeRoots": [ "./src/types", - "./node_modules/@types" + "./node_modules/@types", + ".src/stories/raw-loader.d.ts" ] /* Specify multiple folders that act like './node_modules/@types'. */, // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */