From 96f273242ac1a91bd851426e676ff4dca2f0d8e3 Mon Sep 17 00:00:00 2001 From: Mike Hobizal Date: Wed, 15 Jan 2025 08:07:12 -0800 Subject: [PATCH] feat(ebay-area-chart): refactor existing area chart to design specs (#2358) --- .changeset/sixty-experts-marry.md | 5 + src/common/charts/shared.ts | 26 +- src/components/ebay-area-chart/README.md | 6 + .../area-chart.stories-ignore.ts | 143 ------ .../ebay-area-chart/area-chart.stories.ts | 229 +++++++++ src/components/ebay-area-chart/component.ts | 456 +++++++++--------- .../ebay-area-chart/examples/data.json | 296 ++++++------ .../ebay-area-chart/examples/default.marko | 3 + src/components/ebay-area-chart/index.marko | 21 +- src/components/ebay-area-chart/style.less | 23 - .../tests/__snapshots__/test.server.js.snap | 73 +++ .../ebay-area-chart/tests/test.server.js | 31 ++ src/components/ebay-area-chart/tooltip.marko | 35 ++ 13 files changed, 778 insertions(+), 569 deletions(-) create mode 100644 .changeset/sixty-experts-marry.md delete mode 100644 src/components/ebay-area-chart/area-chart.stories-ignore.ts create mode 100644 src/components/ebay-area-chart/area-chart.stories.ts create mode 100644 src/components/ebay-area-chart/examples/default.marko delete mode 100644 src/components/ebay-area-chart/style.less create mode 100644 src/components/ebay-area-chart/tests/__snapshots__/test.server.js.snap create mode 100644 src/components/ebay-area-chart/tests/test.server.js create mode 100644 src/components/ebay-area-chart/tooltip.marko diff --git a/.changeset/sixty-experts-marry.md b/.changeset/sixty-experts-marry.md new file mode 100644 index 000000000..dc3b97d2c --- /dev/null +++ b/.changeset/sixty-experts-marry.md @@ -0,0 +1,5 @@ +--- +"@ebay/ebayui-core": minor +--- + +Add area chart diff --git a/src/common/charts/shared.ts b/src/common/charts/shared.ts index 46b7351a0..e4c689c87 100644 --- a/src/common/charts/shared.ts +++ b/src/common/charts/shared.ts @@ -1,8 +1,8 @@ export const chartFontFamily = '"Market Sans", Arial, sans-serif', backgroundColor = "var(--color-background-primary)", gridColor = "var(--color-data-viz-grid)", - labelsColor = "var(--color-data-viz-labels)", - legendColor = "var(--color-data-viz-legend)", + labelsColor = "var(--color-foreground-secondary)", + legendColor = "var(--color-foreground-primary)", legendInactiveColor = "var(--color-data-viz-legend-inactive)", legendHoverColor = "var(--color-data-viz-legend-hover)", tooltipBackgroundColor = "var(--color-background-primary)", @@ -79,6 +79,7 @@ export const chartFontFamily = '"Market Sans", Arial, sans-serif', const color = strokeColorMapping[i % strokeColorMapping.length]; series[i].lineColor = color; series[i].borderColor = color; + series[i].fillOpacity = 1; } }, setDonutColors = function (series: any) { @@ -110,4 +111,25 @@ export const chartFontFamily = '"Market Sans", Arial, sans-serif', }); return colors.map((color: any) => color.lineColor); + }, + setSeriesMarkerStyles = function (series: Highcharts.PlotAreaOptions[]) { + series.forEach((s, i) => { + s.zIndex = series.length - i; + s.marker = { + symbol: "circle", + lineWidth: 1, + fillColor: "black", + lineColor: "white", + states: { + hover: { + animation: { duration: 0 }, + radius: 4, + lineWidth: 2, + }, + normal: { + animation: false, + }, + }, + }; + }); }; diff --git a/src/components/ebay-area-chart/README.md b/src/components/ebay-area-chart/README.md index 779c8170d..396a18cd5 100644 --- a/src/components/ebay-area-chart/README.md +++ b/src/components/ebay-area-chart/README.md @@ -8,3 +8,9 @@ The area chart displays one to five series of data points as an interactive stacked area chart + +## Examples and Documentation + +- [Storybook](https://ebay.github.io/ebayui-core/?path=/docs/charts-ebay-area-chart) +- [Storybook Docs](https://ebay.github.io/ebayui-core/?path=/docs/charts-ebay-area-chart) +- [Code Examples](https://github.com/eBay/ebayui-core/tree/master/src/components/ebay-area-chart/examples) diff --git a/src/components/ebay-area-chart/area-chart.stories-ignore.ts b/src/components/ebay-area-chart/area-chart.stories-ignore.ts deleted file mode 100644 index 4ba576e1c..000000000 --- a/src/components/ebay-area-chart/area-chart.stories-ignore.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { tagToString } from "../../common/storybook/storybook-code-source"; -import { addRenderBodies } from "../../common/storybook/utils"; -import Readme from "./README.md"; -import Component from "./index.marko"; -import sampleSeriesData from "./examples/data.json"; -import { Story } from "@storybook/marko"; -import type { Input } from "./component"; - -const Template: Story = (args) => ({ - input: addRenderBodies(args), -}); - -export default { - title: "charts/ebay-area-chart", - excludeStories: ".*", - component: Component, - parameters: { - docs: { - description: { - component: Readme, - }, - }, - }, - argTypes: { - title: { - type: { name: "string", required: false }, - description: "A title displayed above the graph", - }, - description: { - type: { name: "string", required: true }, - description: "A description of what the chart is displaying", - }, - series: { - type: { name: "object", required: true }, - description: - "The series is an array of one to five arrays of point objects, each point contains an `x`, `y`, and `label`. `x` is an epoch/unix time code, `y` is a numeric value, `label` is what is displayed for the `y` value in the tool tip", - }, - xAxisLabelFormat: { - type: { name: "string", required: false }, - description: - "Used to modify the display of the x-axis labels. Accepts a string like `{value:%Y-%m-%d}`. Refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat for available format keys", - table: { - defaultValue: { - summary: "{value:%b %e}", - }, - }, - }, - xAxisPositioner: { - type: { name: "function", required: false }, - description: - "A custom function that returns an array of epoch/unix time values where x-axis labels will be displayed. You can access `this.dataMin` and `this.dataMax` from the function to help determine positions.", - }, - yAxisLabels: { - type: { name: "array", required: false }, - description: - "An array of labels to use on the y-axis. Use in conjunction with yAxisPositioner. Make sure the length of the yAxisLabels match the length of the positions array returned by the yAxisPositioner function", - }, - yAxisPositioner: { - type: { name: "function", required: false }, - description: - "A custom function that returns an array of numeric values where y-axis labels will be displayed. You can access `this.dataMin` and `this.dataMax` from the function to help determine positions", - }, - class: { - type: { name: "string", require: false }, - description: - "A class name that will be added to the main chart container", - }, - }, -}; - -export const Standard = Template.bind({}); -Standard.args = { - title: "Single series sample area chart", - description: "this chart displays 30 days of sample values", - series: sampleSeriesData.slice(0, 1) as any, -}; -Standard.parameters = { - docs: { - source: { - code: tagToString("bar-chart", Standard.args), - }, - }, -}; - -export const TwoSeries = Template.bind({}); -TwoSeries.args = { - title: "Two series sample area chart", - description: - "this chart displays 30 days of values for sample1 and sample2", - series: sampleSeriesData.slice(0, 2) as any, -}; -TwoSeries.parameters = { - docs: { - source: { - code: tagToString("bar-chart", TwoSeries.args), - }, - }, -}; - -export const ThreeSeries = Template.bind({}); -ThreeSeries.args = { - title: "Three series sample area chart", - description: - "this chart displays 30 days of values for sample1, sample2 and sample3", - series: sampleSeriesData.slice(0, 3) as any, -}; -ThreeSeries.parameters = { - docs: { - source: { - code: tagToString("bar-chart", ThreeSeries.args), - }, - }, -}; - -export const FourSeries = Template.bind({}); -FourSeries.args = { - title: "Four series sample area chart", - description: - "this chart displays 30 days of values for sample1, sample2, sample3, and sample4", - series: sampleSeriesData.slice(0, 4) as any, -}; -FourSeries.parameters = { - docs: { - source: { - code: tagToString("bar-chart", FourSeries.args), - }, - }, -}; - -export const FiveSeries = Template.bind({}); -FiveSeries.args = { - title: "Five series sample area chart", - description: - "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", - series: sampleSeriesData as any, -}; -FiveSeries.parameters = { - docs: { - source: { - code: tagToString("bar-chart", FiveSeries.args), - }, - }, -}; diff --git a/src/components/ebay-area-chart/area-chart.stories.ts b/src/components/ebay-area-chart/area-chart.stories.ts new file mode 100644 index 000000000..918c61e47 --- /dev/null +++ b/src/components/ebay-area-chart/area-chart.stories.ts @@ -0,0 +1,229 @@ +import { tagToString } from "../../common/storybook/storybook-code-source"; +import { addRenderBodies } from "../../common/storybook/utils"; +import Readme from "./README.md"; +import Component from "./index.marko"; +import sampleSeriesData from "./examples/data.json"; +import { Story } from "@storybook/marko"; +import type { Input } from "./component"; + +const Template: Story = (args) => ({ + input: addRenderBodies(args), +}); + +const seriesDataWithoutLabels = sampleSeriesData.map((series) => { + const { data, ...rest } = series; + return { + ...rest, + data: data.map((point) => { + const { label, ...rest } = point; + return rest; + }), + }; +}) + +export default { + title: "charts/ebay-area-chart", + component: Component, + parameters: { + docs: { + description: { + component: Readme, + }, + }, + }, + argTypes: { + title: { + type: { name: "string", required: false }, + description: "A title displayed above the graph", + }, + description: { + type: { name: "string", required: true }, + description: "A description of what the chart is displaying", + }, + series: { + type: { name: "object", required: true }, + description: + "The series is an array of one to five arrays of point objects, each point contains an `x`, `y`, and `label`. `x` is an epoch/unix time code, `y` is a numeric value, `label` is what is displayed for the `y` value in the tool tip", + }, + highchartOptions: { + type: { name: "object", required: false }, + description: + "A highcharts options object that will be merged with the default options", + }, + xLabelFormatter: { + type: { name: "function", required: false }, + description: + "A function that will be used to format the x-axis labels. Provides the value and a date formatter function. By default is formatted as `MMM dd`. Refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat for available format keys", + }, + yLabelFormatter: { + type: { name: "function", required: false }, + description: + "A function that will be used to format the y-axis labels. By default is formatted as USD currency.", + }, + tooltipValueFormatter: { + type: { name: "function", required: false }, + description: + "A function that will be used to format the tooltip series values and total. By default is formatted as USD currency.", + }, + areaType: { + type: { name: "string", required: false }, + description: + "The type of area chart to display. By default is `spline`. Options are `spline` and `area`", + }, + class: { + type: { name: "string", require: false }, + description: + "A class name that will be added to the main chart container", + }, + }, +}; + +export const Standard = Template.bind({}); +Standard.args = { + title: "Single series sample area chart", + description: "this chart displays 30 days of sample values", + series: sampleSeriesData.slice(0, 1) as Highcharts.SeriesAreaOptions[], +}; +Standard.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", Standard.args), + }, + }, +}; + +export const TwoSeries = Template.bind({}); +TwoSeries.args = { + title: "Two series sample area chart", + description: + "this chart displays 30 days of values for sample1 and sample2", + series: sampleSeriesData.slice(0, 2) as Highcharts.SeriesAreaOptions[], +}; +TwoSeries.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", TwoSeries.args), + }, + }, +}; + +export const ThreeSeries = Template.bind({}); +ThreeSeries.args = { + title: "Three series sample area chart", + description: + "this chart displays 30 days of values for sample1, sample2 and sample3", + series: sampleSeriesData.slice(0, 3) as Highcharts.SeriesAreaOptions[], +}; +ThreeSeries.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", ThreeSeries.args), + }, + }, +}; + +export const FourSeries = Template.bind({}); +FourSeries.args = { + title: "Four series sample area chart", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, and sample4", + series: sampleSeriesData.slice(0, 4) as Highcharts.SeriesAreaOptions[], +}; +FourSeries.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", FourSeries.args), + }, + }, +}; + +export const FiveSeries = Template.bind({}); +FiveSeries.args = { + title: "Five series sample area chart", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", + series: sampleSeriesData as Highcharts.SeriesAreaOptions[], +}; +FiveSeries.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", FiveSeries.args), + }, + }, +}; + +export const WithUnitlessYFormat = Template.bind({}); +WithUnitlessYFormat.args = { + title: "Custom y-axis label and tooltip value format", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", + series: seriesDataWithoutLabels as Highcharts.SeriesAreaOptions[], + yLabelFormatter: (value) => `${value}`, + tooltipValueFormatter: (value) => `${value}`, +}; +WithUnitlessYFormat.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", WithUnitlessYFormat.args), + }, + }, +}; + +export const WithXLabelFormat = Template.bind({}); +WithXLabelFormat.args = { + title: "Custom x-axis label format", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", + series: sampleSeriesData as Highcharts.SeriesAreaOptions[], + xLabelFormatter: (value, dateFormat) => { + if (typeof value !== "number") { + return value; + } + return dateFormat("%A", value); + }, + tooltipTitleFormatter(value, dateFormat) { + if (typeof value !== "number") { + return value; + } + return dateFormat("%A", value); + }, +}; + +WithXLabelFormat.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", WithXLabelFormat.args), + }, + }, +}; + +export const NonSpline = Template.bind({}); +NonSpline.args = { + title: "Area chart with non-spline type", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", + series: sampleSeriesData.slice(0, 1) as Highcharts.SeriesAreaOptions[], + areaType: "area", +}; + +NonSpline.parameters = { + docs: { + source: { + code: tagToString("ebay-area-chart", NonSpline.args), + }, + }, +}; + +export const CustomHighchartOptions = Template.bind({}); +CustomHighchartOptions.args = { + title: "Custom highchart options", + description: + "this chart displays 30 days of values for sample1, sample2, sample3, sample4, and sample5", + series: sampleSeriesData.slice(0, 1) as Highcharts.SeriesAreaOptions[], + highchartOptions: { + yAxis: { + ceiling: 45000, + tickAmount: 4, + }, + }, +}; \ No newline at end of file diff --git a/src/components/ebay-area-chart/component.ts b/src/components/ebay-area-chart/component.ts index 18217f339..4106caeda 100644 --- a/src/components/ebay-area-chart/component.ts +++ b/src/components/ebay-area-chart/component.ts @@ -11,41 +11,49 @@ import { tooltipShadows, setSeriesColors, colorMapping, + setSeriesMarkerStyles, } from "../../common/charts/shared"; -import { debounce } from "../../common/event-utils"; import { ebayLegend } from "../../common/charts/legend"; import type { WithNormalizedProps } from "../../global"; +import tooltipTemplate from "./tooltip.marko"; +import type { Input as TooltipInput } from "./tooltip.marko"; import type HighchartsTypes from "highcharts"; declare const Highcharts: typeof HighchartsTypes; +// Extend highcharts series data with a label property +declare module "highcharts" { + export interface Point { + label?: string; + } +} + interface AreaChartInput extends Omit, `on${string}`> { - title: Highcharts.TitleOptions["text"]; - /** - * input.xAxisLabelFormat allows overriding the default short month / day label. - * refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat to customize - **/ - "x-axis-label-format"?: Highcharts.XAxisLabelsOptions["format"]; - "x-axis-positioner"?: Highcharts.XAxisOptions["tickPositioner"]; - "y-axis-labels"?: Highcharts.YAxisLabelsOptions["format"]; - "y-axis-positioner"?: Highcharts.YAxisOptions["tickPositioner"]; + title?: Highcharts.TitleOptions["text"]; description?: Highcharts.SeriesAccessibilityOptionsObject["description"]; + series: Highcharts.SeriesAreaOptions | Highcharts.SeriesAreaOptions[]; + tooltipValueFormatter?: (value: string | number) => string; + tooltipTitleFormatter?: ( + value: string | number, + dateFormat: typeof Highcharts.dateFormat, + ) => string; + xLabelFormatter?: ( + value: string | number, + dateFormat: typeof Highcharts.dateFormat, + ) => string; + yLabelFormatter?: (value: string | number) => string; + areaType?: "areaspline" | "area"; + highchartOptions?: Highcharts.Options; "cdn-highcharts"?: string; "cdn-highcharts-accessibility"?: string; "cdn-highcharts-pattern-fill"?: string; version?: string; - series: Highcharts.SeriesAreaOptions | Highcharts.SeriesAreaOptions[]; - "on-load-error": (err: Error) => void; + "on-load-error"?: (err: Error) => void; } export interface Input extends WithNormalizedProps {} -const pointSize = 1.5; - class AreaChart extends Marko.Component { declare chartRef: Highcharts.Chart; - declare mouseOut: ReturnType; - declare mouseOver: ReturnType; - declare points: Highcharts.Point[]; onInput() { // if chartRef does not exist do not try to run setupCharts as it may be server side and highcharts only works on the client side @@ -68,45 +76,35 @@ class AreaChart extends Marko.Component { handleError(err: Error) { this.emit("load-error", err); } + handleSuccess() { this._initializeHighchartExtensions(); - this._setupEvents(); this._setupCharts(); } + getContainerId() { return `ebay-bar-chart-${this.id}`; } + + /** + * Initialize the highchart extensions + */ _initializeHighchartExtensions() { // add custom legend wrapper function - // eslint-disable-next-line no-undef,new-cap ebayLegend(Highcharts); } - _setupEvents() { - // bind functions to keep scope and setup debounced versions of function calls - this.debounce = debounce.bind(this); - this.handleMouseOver = this.handleMouseOver.bind(this); - this.handleMouseOut = this.handleMouseOut.bind(this); - this.mouseOut = this.debounce(() => this.handleMouseOut(), 80); // 80ms delay for debounce - this.mouseOver = this.debounce( - (e: Event) => this.handleMouseOver(e), - 85, - ); // 85ms delay for debounce so it doesn't colide with mouseOut debounce calls - } + + /** + * Set up the chart with the input data and configuration options. + */ _setupCharts() { // check if a single series was passed in for series and if so add it to a new array const series = Array.isArray(this.input.series) ? this.input.series : [this.input.series]; - // update the zIndex of each series object so they render in the correct order - // and configure the markers that are displayed on hover - series.forEach((s, i) => { - s.zIndex = series.length - i; - s.marker = { - symbol: "circle", - lineWidth: 3, - }; - }); + setSeriesMarkerStyles(series); + setSeriesColors(series); const config: Highcharts.Options = { @@ -114,7 +112,7 @@ class AreaChart extends Marko.Component { chart: this.getChartConfig(), colors: colorMapping, xAxis: this.getXAxisConfig(), - yAxis: this.getYAxisConfig(series), + yAxis: this.getYAxisConfig(), legend: this.getLegendConfig(), tooltip: this.getTooltipConfig(), plotOptions: this.getPlotOptions(), @@ -124,285 +122,265 @@ class AreaChart extends Marko.Component { }, }; // initialize and keep reference to chart - // eslint-disable-next-line no-undef,new-cap - this.chartRef = Highcharts.chart(this.getContainerId(), config); + this.chartRef = Highcharts.chart( + this.getContainerId(), + this._mergeConfigs(config, this.input.highchartOptions ?? {}), + ); + } + + /** + * Default format function for the tooltip titles + */ + _tooltipTitleFormatter( + value: number | string, + dateFormat: typeof Highcharts.dateFormat, + ) { + if (typeof value === "string") { + value = parseFloat(value); + } + return dateFormat("%b %e, %Y", value); } + + /** + * Default format function for the tooltip values + */ + _tooltipValueFormatter(value: number | string) { + if (typeof value === "string") { + value = parseFloat(value); + } + return Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(value); + } + + /** + * Default format function for the yAxis labels + */ + _yLabelFormatter(value: number | string) { + if (typeof value === "string") { + value = parseFloat(value); + } + return Intl.NumberFormat("en-US", { + notation: "compact", + style: "currency", + currency: "USD", + maximumSignificantDigits: 4, + }).format(value); + } + + /** + * Merge the source Highcharts config into the target Highcharts config + * + * Allows for custom overrides of ebay default Highcharts configuration settings + */ + _mergeConfigs( + source: { [key: string]: any }, + target: { [key: string]: any }, + ) { + for (const key in source) { + if (source[key] instanceof Object) + Object.assign( + source[key], + this._mergeConfigs(target[key], source[key]), + ); + } + Object.assign(target || {}, source); + return target; + } + + /** + * Get the title configuration for the chart + */ getTitleConfig(): Highcharts.TitleOptions { return { text: this.input.title, align: "left", useHTML: true, style: { - // styles are set in JS since they are rendered in the SVG fontSize: "18px", fontWeight: "700", }, }; } + + /** + * Get the chart configuration for the chart + */ getChartConfig(): Highcharts.ChartOptions { + const type = this.input.areaType ?? "areaspline"; return { - type: "area", + type, + animation: false, backgroundColor: backgroundColor, style: { - // styles are set in JS since they are rendered in the SVG fontFamily: chartFontFamily, }, }; } + + /** + * Get the xAxis configuration for the chart + */ getXAxisConfig(): Highcharts.XAxisOptions { + const xLabelFormatter = this.input.xLabelFormatter; return { // currently setup to support epoch time values for xAxisLabels. // It is possible to set custom non datetime xAxisLabels but will need changes to this component type: "datetime", labels: { - format: this.input.xAxisLabelFormat - ? this.input.xAxisLabelFormat - : "{value:%b %e}", + formatter: xLabelFormatter + ? function () { + return xLabelFormatter?.( + this.value, + Highcharts.dateFormat, + ); + } + : undefined, + format: "{value:%b %e}", align: "center", style: { - color: labelsColor, // setting label colors + color: labelsColor, }, }, tickWidth: 0, // hide the vertical tick on xAxis labels crosshair: { - zIndex: 3, // make sure the vertical crosshair line on hover shows up on top + color: "rgba(0, 0, 0, 0.2)", + zIndex: 3, }, - tickPositioner: this.input.xAxisPositioner, // optional input to allow configuring the position of xAxis tick marks }; } - getYAxisConfig( - series: Highcharts.SeriesAreaOptions[], - ): Highcharts.YAxisOptions { - const component = this; // component reference used in formatter functions that don't have the same scope - let yLabelsIterator = 0; // used when yAxisLabels array is provided in input - let maxYAxisValue = 0; // use to determine the highest yAxis value - series.forEach((s) => { - maxYAxisValue = s.data!.reduce( - (p: number, c: any) => (c > p ? c : p), - maxYAxisValue, - ) as number; - }); + + /** + * Get the yAxis configuration for the chart + */ + getYAxisConfig(): Highcharts.YAxisOptions { + // Formatter function for the yAxis labels + const yLabelFormatter = + this.input.yLabelFormatter ?? this._yLabelFormatter; + return { - gridLineColor: gridColor, // sets the horizontal grid line colors + gridLineColor: gridColor, opposite: true, // moves yAxis labels to the right side of the chart reversedStacks: false, // makes so series one starts at the bottom of the yAxis, by default this is true labels: { - // if yAxisLabels are not passed in display the standard label - format: this.input.yAxisLabels ?? "${text}", - // if yAxisLabels array is passed in this formatter function is needed to - // return the proper label for each yAxis tick mark - formatter: this.input.yAxisLabels - ? function () { - if (this.isFirst) { - yLabelsIterator = -1; - } - yLabelsIterator = yLabelsIterator + 1; - return ( - component.input.yAxisLabels?.[yLabelsIterator] ?? - "" - ); - } - : undefined, + formatter: function () { + return yLabelFormatter(this.value); + }, style: { - color: labelsColor, // setting label colors + color: labelsColor, }, }, - max: maxYAxisValue, title: { - enabled: false, // hide the axis label next to the axis + enabled: false, } as Highcharts.YAxisTitleOptions, - offset: 0, // set to zero for no offset refer to https://api.highcharts.com/highcharts/yAxis.offset - // passed in function for yAxisPositioner refer to https://api.highcharts.com/highcharts/yAxis.tickPositioner for use - tickPositioner: this.input.yAxisPositioner, + offset: 0, }; } - getLegendConfig() { + + /** + * Get the legend configuration for the chart + */ + getLegendConfig(): Highcharts.LegendOptions { return { - // if only a single series is provided do not display the legend + // If only a single series is provided do not display the legend enabled: Array.isArray(this.input.series) && this.input.series.length > 1, - symbolRadius: 2, // corner radius on legend identifiers svg element - symbolWidth: 12, // setting the width of the legend identifiers svg element - symbolHeight: 12, // setting the height of the legend identifiers svg element + symbolRadius: 2, + symbolWidth: 12, + symbolHeight: 12, itemStyle: { - color: legendColor, // set the color of the text in the legend + color: legendColor, + fontWeight: "normal", }, + align: "left", itemHiddenStyle: { - color: legendInactiveColor, // set legend text color when legend item has been clicked and hidden + color: legendInactiveColor, }, itemHoverStyle: { - color: legendHoverColor, // set legend text color on hover of legend element + color: legendHoverColor, }, }; } - getTooltipConfig() { - const component = this; // component reference used in formatter functions that don't have the same scope + /** + * Get the tooltip configuration for the chart + */ + getTooltipConfig(): Highcharts.TooltipOptions { + const tooltipValueFormatter = + this.input.tooltipValueFormatter ?? this._tooltipValueFormatter; + + const tooltipTitleFormatter = + this.input.tooltipTitleFormatter ?? this._tooltipTitleFormatter; + return { - formatter: function (this: any) { - // refer to https://api.highcharts.com/class-reference/Highcharts.Time#dateFormat for dateFormat variables - // s is used to compile html string of formatted tooltip data - - // TODO need to change this to use a component - // eslint-disable-next-line no-undef,new-cap - let s = `${Highcharts.dateFormat( - "%b %e, %Y", - this.x, - false, - )}
`; // sets the displayed date at the top of the tooltip - if (component.chartRef.series.length > 1) { - // setup html for multi series tooltip - component.chartRef.series.forEach((serie) => { - // cycle through each series - serie.data.forEach((d) => { - // cycle through each series data array to match x value with active hovered xAxis position - if (d.x === this.x) { - // when the x value matches the hovered xAxis position - s += `
${serie.name}${d.name}
`; - } - }); - }); - } else { - // setup html for single series tooltip - // cycle through points of the single series and find the one that matches the active xAxis - component.points.forEach((d) => { - if (d.x === this.x) { - // when the x value matches the hovered xAxis position - s += `
${d.name}
`; - } - }); - } - return s; - } as any, - useHTML: true, // allows defining html to format tooltip content - backgroundColor: tooltipBackgroundColor, // sets tooltip background color - borderWidth: 0, // hide the default border stroke - borderRadius: 10, // set the border radius of the tooltip - outside: true, // used to render the tooltip outside of the main SVG element - shadow: false, // hide the default shadow as it conflicts with designs - shared: true, // shared means that if there are multipe series passed in there will be a single tooltip element per xAxis point + formatter: function (this) { + const date = tooltipTitleFormatter( + this.x ?? 0, + Highcharts.dateFormat, + ); + + // Display formatted total, only if there are more than one points + const total = + this.points && + this.points.length > 1 && + this.points.reduce( + (acc, curr) => acc + (curr.y ?? 0) * 100, + 0, + ) / 100; + + return tooltipTemplate.renderToString({ + date, + total: total || 0, + points: this.points, + valueFormatter: tooltipValueFormatter, + } as TooltipInput); + }, + useHTML: true, + backgroundColor: tooltipBackgroundColor, + borderWidth: 0, + borderRadius: 10, + outside: true, + shadow: false, + shared: true, style: { - filter: tooltipShadows, // sets tooltip shadows + filter: tooltipShadows, fontSize: "12px", }, }; } + /** + * Get the plot options configuration for the chart + */ getPlotOptions(): Highcharts.PlotOptions { + const type = this.input.areaType ?? "areaspline"; return { series: { accessibility: { - description: this.input.description, // set the description that was passed in + description: this.input.description, }, - // config stacking to normal to make sure series stack without overlapping - // refer to https://api.highcharts.com/highcharts/plotOptions.area.stacking stacking: "normal", - point: { - // assign mouse events to point hovers - events: { - mouseOver: this.mouseOver, - mouseOut: this.mouseOut, + states: { + hover: { + halo: { size: 0 }, }, }, + marker: { + enabled: false, + animation: { duration: 0 }, + }, }, - area: { - className: "ebay-area-chart", // add class to area chart to allow targetted styles from style.less file - lineWidth: 1, // set the border line width for each series item. - // states: { // set if we do not want series to fade out on legend hover uncomment this block - // inactive: { - // opacity: 1, - // } - // } + [type]: { + className: "ebay-area-chart", + lineWidth: 1, }, }; } - // debounce used to help improve performance on mouse interactions - debounce void>(func: T, timeout = 100) { - let timer: NodeJS.Timeout; - return (...args: Parameters) => { - clearTimeout(timer); - timer = setTimeout(() => { - func.apply(this, args); - }, timeout); - }; - } - handleMouseOut() { - // this function is debounced to improve performance - this.chartRef.series.forEach((s) => { - s.data.forEach((d) => { - // check if hover is on the xAxis (onTick) for each item, - // and if they have a className remove and disable the marker - if (d.getClassName() !== null) { - d.update( - { - className: undefined, // nullify className if not active - marker: { - enabled: false, // disable marker if not active - }, - }, - false, // disable auto redraw - false, // disable auto animation - ); - } else if (d.getClassName() === null) { - d.update( - { - className: "ebay-area-chart__marker--visible", // set classname - marker: { - enabled: true, // set marker enabled - radius: pointSize, // set the size of marker - lineColor: backgroundColor, // set border color of hover markers - lineWidth: 4, // set border width of hover markers - fillColor: "#000000", // set fill color of markers - }, - }, - false, // disable auto redraw - false, // disable auto animation - ); - } - }); - }); - this.chartRef.redraw(); // trigger redraw after all points have been updated - } - handleMouseOver(e: any) { - // this function is debounced to improve performance - this.chartRef.series.forEach((s) => { - s.data.forEach((d) => { - if (d.x === e.target.x) { - // if active xAxis hover position matches the data point x update the marker to display - d.update( - { - className: "ebay-area-chart__marker--visible", // sets the classname - marker: { - enabled: true, // set marker enabled - radius: pointSize, // set the size of marker - lineColor: backgroundColor, // set border color of hover markers - lineWidth: 4, // set border width of hover markers - fillColor: "#000000", // set fill color of markers - }, - }, - false, // disable auto redraw - false, // disable auto animation - ); - } else if (d.getClassName() !== null) { - d.update( - { - className: undefined, // nullify className if not active - marker: { - enabled: false, // disable marker - }, - }, - false, // disable auto redraw - false, // disable auto animation - ); - } - }); - }); - this.chartRef.redraw(); // trigger redraw after all points have been updated - } onDestroy() { - this.chartRef.destroy(); + this.chartRef?.destroy(); } } diff --git a/src/components/ebay-area-chart/examples/data.json b/src/components/ebay-area-chart/examples/data.json index bb2307cba..392e11c19 100644 --- a/src/components/ebay-area-chart/examples/data.json +++ b/src/components/ebay-area-chart/examples/data.json @@ -9,147 +9,147 @@ { "x": 1643760000000, "y": 3529.13, - "label": "$3529.13" + "label": "$3,529.13" }, { "x": 1643846400000, "y": 7290.99, - "label": "$7290.99" + "label": "$7,290.99" }, { "x": 1643932800000, "y": 7598.54, - "label": "$7598.54" + "label": "$7,598.54" }, { "x": 1644019200000, "y": 6014.85, - "label": "$6014.85" + "label": "$6,014.85" }, { "x": 1644105600000, "y": 7197.59, - "label": "$7197.59" + "label": "$7,197.59" }, { "x": 1644192000000, "y": 7054.97, - "label": "$7054.97" + "label": "$7,054.97" }, { "x": 1644278400000, "y": 8813.92, - "label": "$8813.92" + "label": "$8,813.92" }, { "x": 1644364800000, "y": 11303.55, - "label": "$11303.55" + "label": "$11,303.55" }, { "x": 1644451200000, "y": 11425.54, - "label": "$11425.54" + "label": "$11,425.54" }, { "x": 1644537600000, "y": 14767.84, - "label": "$14767.84" + "label": "$14,767.84" }, { "x": 1644624000000, "y": 14595.77, - "label": "$14595.77" + "label": "$14,595.77" }, { "x": 1644710400000, "y": 14896.37, - "label": "$14896.37" + "label": "$14,896.37" }, { "x": 1644796800000, "y": 18686.42, - "label": "$18686.42" + "label": "$18,686.42" }, { "x": 1644883200000, "y": 19397.06, - "label": "$19397.06" + "label": "$19,397.06" }, { "x": 1644969600000, "y": 17377.38, - "label": "$17377.38" + "label": "$17,377.38" }, { "x": 1645056000000, "y": 19762.2, - "label": "$19762.2" + "label": "$19,762.20" }, { "x": 1645142400000, "y": 22693.32, - "label": "$22693.32" + "label": "$22,693.32" }, { "x": 1645228800000, "y": 25474.69, - "label": "$25474.69" + "label": "$25,474.69" }, { "x": 1645315200000, "y": 25716.96, - "label": "$25716.96" + "label": "$25,716.96" }, { "x": 1645401600000, "y": 28229.64, - "label": "$28229.64" + "label": "$28,229.64" }, { "x": 1645488000000, "y": 30802.49, - "label": "$30802.49" + "label": "$30,802.49" }, { "x": 1645574400000, "y": 30784.09, - "label": "$30784.09" + "label": "$30,784.09" }, { "x": 1645660800000, "y": 31268.12, - "label": "$31268.12" + "label": "$31,268.12" }, { "x": 1645747200000, "y": 33762.26, - "label": "$33762.26" + "label": "$33,762.26" }, { "x": 1645833600000, "y": 35829.81, - "label": "$35829.81" + "label": "$35,829.81" }, { "x": 1645920000000, "y": 35459.05, - "label": "$35459.05" + "label": "$35,459.05" }, { "x": 1646006400000, "y": 38357.93, - "label": "$38357.93" + "label": "$38,357.93" }, { "x": 1646092800000, "y": 41968.57, - "label": "$41968.57" + "label": "$41,968.57" }, { "x": 1646179200000, "y": 43848.26, - "label": "$43848.26" + "label": "$43,848.26" } ], "name": "Value 1" @@ -159,152 +159,152 @@ { "x": 1643673600000, "y": 2191.95, - "label": "$2191.95" + "label": "$2,191.95" }, { "x": 1643760000000, "y": 2401.84, - "label": "$2401.84" + "label": "$2,401.84" }, { "x": 1643846400000, "y": 2212.28, - "label": "$2212.28" + "label": "$2,212.28" }, { "x": 1643932800000, "y": 2608.39, - "label": "$2608.39" + "label": "$2,608.39" }, { "x": 1644019200000, "y": 2000.86, - "label": "$2000.86" + "label": "$2,000.86" }, { "x": 1644105600000, "y": 3931.71, - "label": "$3931.71" + "label": "$3,931.71" }, { "x": 1644192000000, "y": 4094.93, - "label": "$4094.93" + "label": "$4,094.93" }, { "x": 1644278400000, "y": 7666.27, - "label": "$7666.27" + "label": "$7,666.27" }, { "x": 1644364800000, "y": 6413, - "label": "$6413" + "label": "$6,413" }, { "x": 1644451200000, "y": 9924.87, - "label": "$9924.87" + "label": "$9,924.87" }, { "x": 1644537600000, "y": 11485.98, - "label": "$11485.98" + "label": "$11,485.98" }, { "x": 1644624000000, "y": 13416.08, - "label": "$13416.08" + "label": "$13,416.08" }, { "x": 1644710400000, "y": 16068.24, - "label": "$16068.24" + "label": "$16,068.24" }, { "x": 1644796800000, "y": 15855.76, - "label": "$15855.76" + "label": "$15,855.76" }, { "x": 1644883200000, "y": 19313.94, - "label": "$19313.94" + "label": "$19,313.94" }, { "x": 1644969600000, "y": 20328.31, - "label": "$20328.31" + "label": "$20,328.31" }, { "x": 1645056000000, "y": 21482.19, - "label": "$21482.19" + "label": "$21,482.19" }, { "x": 1645142400000, "y": 24732.07, - "label": "$24732.07" + "label": "$24,732.07" }, { "x": 1645228800000, "y": 26000.25, - "label": "$26000.25" + "label": "$26,000.25" }, { "x": 1645315200000, "y": 28788.89, - "label": "$28788.89" + "label": "$28,788.89" }, { "x": 1645401600000, "y": 30438.55, - "label": "$30438.55" + "label": "$30,438.55" }, { "x": 1645488000000, "y": 33025.77, - "label": "$33025.77" + "label": "$33,025.77" }, { "x": 1645574400000, "y": 33232.63, - "label": "$33232.63" + "label": "$33,232.63" }, { "x": 1645660800000, "y": 35412.85, - "label": "$35412.85" + "label": "$35,412.85" }, { "x": 1645747200000, "y": 35418.1, - "label": "$35418.1" + "label": "$35,418.10" }, { "x": 1645833600000, "y": 34787.87, - "label": "$34787.87" + "label": "$34,787.87" }, { "x": 1645920000000, "y": 35379.97, - "label": "$35379.97" + "label": "$35,379.97" }, { "x": 1646006400000, "y": 35058.06, - "label": "$35058.06" + "label": "$35,058.06" }, { "x": 1646092800000, "y": 36941.95, - "label": "$36941.95" + "label": "$36,941.95" }, { "x": 1646179200000, "y": 35433.32, - "label": "$35433.32" + "label": "$35,433.32" } ], "name": "Value 2" @@ -319,147 +319,147 @@ { "x": 1643760000000, "y": 1972.03, - "label": "$1972.03" + "label": "$1,972.03" }, { "x": 1643846400000, "y": 2605.21, - "label": "$2605.21" + "label": "$2,605.21" }, { "x": 1643932800000, "y": 2688.03, - "label": "$2688.03" + "label": "$2,688.03" }, { "x": 1644019200000, "y": 3693.2, - "label": "$3693.2" + "label": "$3,693.02" }, { "x": 1644105600000, "y": 6620.37, - "label": "$6620.37" + "label": "$6,620.37" }, { "x": 1644192000000, "y": 9938.94, - "label": "$9938.94" + "label": "$9,938.94" }, { "x": 1644278400000, "y": 9041.09, - "label": "$9041.09" + "label": "$9,041.09" }, { "x": 1644364800000, "y": 8988.36, - "label": "$8988.36" + "label": "$8,988.36" }, { "x": 1644451200000, "y": 10127.2, - "label": "$10127.2" + "label": "$10,127.20" }, { "x": 1644537600000, "y": 13612.53, - "label": "$13612.53" + "label": "$13,612.53" }, { "x": 1644624000000, "y": 14706.86, - "label": "$14706.86" + "label": "$14,706.86" }, { "x": 1644710400000, "y": 13564.87, - "label": "$13564.87" + "label": "$13,564.87" }, { "x": 1644796800000, "y": 12415.62, - "label": "$12415.62" + "label": "$12,415.62" }, { "x": 1644883200000, "y": 16964.94, - "label": "$16964.94" + "label": "$16,964.94" }, { "x": 1644969600000, "y": 18740.34, - "label": "$18740.34" + "label": "$18,740.34" }, { "x": 1645056000000, "y": 18716.07, - "label": "$18716.07" + "label": "$18,716.07" }, { "x": 1645142400000, "y": 21154.56, - "label": "$21154.56" + "label": "$21,154.56" }, { "x": 1645228800000, "y": 21750.07, - "label": "$21750.07" + "label": "$21,750.07" }, { "x": 1645315200000, "y": 22922.59, - "label": "$22922.59" + "label": "$22,922.59" }, { "x": 1645401600000, "y": 26768.79, - "label": "$26768.79" + "label": "$26,768.79" }, { "x": 1645488000000, "y": 26399.77, - "label": "$26399.77" + "label": "$26,399.77" }, { "x": 1645574400000, "y": 29910.73, - "label": "$29910.73" + "label": "$29,910.73" }, { "x": 1645660800000, "y": 32231.86, - "label": "$32231.86" + "label": "$32,231.86" }, { "x": 1645747200000, "y": 34040.09, - "label": "$34040.09" + "label": "$34,040.09" }, { "x": 1645833600000, "y": 31844.54, - "label": "$31844.54" + "label": "$31,844.54" }, { "x": 1645920000000, "y": 34033.48, - "label": "$34033.48" + "label": "$34,033.48" }, { "x": 1646006400000, "y": 34140.3, - "label": "$34140.3" + "label": "$34,140.30" }, { "x": 1646092800000, "y": 33203.36, - "label": "$33203.36" + "label": "$33,203.36" }, { "x": 1646179200000, "y": 35918.18, - "label": "$35918.18" + "label": "$35,918.18" } ], "name": "Value 3" @@ -469,152 +469,152 @@ { "x": 1643673600000, "y": 3714.81, - "label": "$3714.81" + "label": "$3,714.81" }, { "x": 1643760000000, "y": 3515.59, - "label": "$3515.59" + "label": "$3,515.59" }, { "x": 1643846400000, "y": 5668.89, - "label": "$5668.89" + "label": "$5,668.89" }, { "x": 1643932800000, "y": 7707.67, - "label": "$7707.67" + "label": "$7,707.67" }, { "x": 1644019200000, "y": 7065.82, - "label": "$7065.82" + "label": "$7,065.82" }, { "x": 1644105600000, "y": 10961.2, - "label": "$10961.2" + "label": "$10,961.20" }, { "x": 1644192000000, "y": 10810.1, - "label": "$10810.1" + "label": "$10,810.10" }, { "x": 1644278400000, "y": 9299.88, - "label": "$9299.88" + "label": "$9,299.88" }, { "x": 1644364800000, "y": 12175.1, - "label": "$12175.1" + "label": "$12,175.10" }, { "x": 1644451200000, "y": 15678.8, - "label": "$15678.8" + "label": "$15,678.80" }, { "x": 1644537600000, "y": 13403.74, - "label": "$13403.74" + "label": "$13,403.74" }, { "x": 1644624000000, "y": 14934.49, - "label": "$14934.49" + "label": "$14,934.49" }, { "x": 1644710400000, "y": 18195.71, - "label": "$18195.71" + "label": "$18,195.71" }, { "x": 1644796800000, "y": 21630.27, - "label": "$21630.27" + "label": "$21,630.27" }, { "x": 1644883200000, "y": 19205.48, - "label": "$19205.48" + "label": "$19,205.48" }, { "x": 1644969600000, "y": 18236.22, - "label": "$18236.22" + "label": "$18,236.22" }, { "x": 1645056000000, "y": 20057.18, - "label": "$20057.18" + "label": "$20,057.18" }, { "x": 1645142400000, "y": 21599.02, - "label": "$21599.02" + "label": "$21,599.02" }, { "x": 1645228800000, "y": 22092.15, - "label": "$22092.15" + "label": "$22,092.15" }, { "x": 1645315200000, "y": 22049.95, - "label": "$22049.95" + "label": "$22,049.95" }, { "x": 1645401600000, "y": 25488.9, - "label": "$25488.9" + "label": "$25,488.90" }, { "x": 1645488000000, "y": 23560.32, - "label": "$23560.32" + "label": "$23,560.32" }, { "x": 1645574400000, "y": 23287.49, - "label": "$23287.49" + "label": "$23,287.49" }, { "x": 1645660800000, "y": 23291.42, - "label": "$23291.42" + "label": "$23,291.42" }, { "x": 1645747200000, "y": 22578.91, - "label": "$22578.91" + "label": "$22,578.91" }, { "x": 1645833600000, "y": 23359.51, - "label": "$23359.51" + "label": "$23,359.51" }, { "x": 1645920000000, "y": 26612.42, - "label": "$26612.42" + "label": "$26,612.42" }, { "x": 1646006400000, "y": 27406.05, - "label": "$27406.05" + "label": "$27,406.05" }, { "x": 1646092800000, "y": 26739.38, - "label": "$26739.38" + "label": "$26,739.38" }, { "x": 1646179200000, "y": 31025.67, - "label": "$31025.67" + "label": "$31,025.67" } ], "name": "Value 4" @@ -624,152 +624,152 @@ { "x": 1643673600000, "y": 2634.55, - "label": "$2634.55" + "label": "$2,634.55" }, { "x": 1643760000000, "y": 5550.37, - "label": "$5550.37" + "label": "$5,550.37" }, { "x": 1643846400000, "y": 8362.35, - "label": "$8362.35" + "label": "$8,362.35" }, { "x": 1643932800000, "y": 8892.21, - "label": "$8892.21" + "label": "$8,892.21" }, { "x": 1644019200000, "y": 8832.23, - "label": "$8832.23" + "label": "$8,832.23" }, { "x": 1644105600000, "y": 11513.81, - "label": "$11513.81" + "label": "$11,513.81" }, { "x": 1644192000000, "y": 10620.6, - "label": "$10620.6" + "label": "$10,620.60" }, { "x": 1644278400000, "y": 10006.53, - "label": "$10006.53" + "label": "$10,006.53" }, { "x": 1644364800000, "y": 11226.84, - "label": "$11226.84" + "label": "$11,226.84" }, { "x": 1644451200000, "y": 12663.56, - "label": "$12663.56" + "label": "$12,663.56" }, { "x": 1644537600000, "y": 13521.25, - "label": "$13521.25" + "label": "$13,521.25" }, { "x": 1644624000000, "y": 17157.52, - "label": "$17157.52" + "label": "$17,157.52" }, { "x": 1644710400000, "y": 17379.93, - "label": "$17379.93" + "label": "$17,379.93" }, { "x": 1644796800000, "y": 18145.16, - "label": "$18145.16" + "label": "$18,145.16" }, { "x": 1644883200000, "y": 18417.28, - "label": "$18417.28" + "label": "$18,417.28" }, { "x": 1644969600000, "y": 20505.13, - "label": "$20505.13" + "label": "$20,505.13" }, { "x": 1645056000000, "y": 23222.51, - "label": "$23222.51" + "label": "$23,222.51" }, { "x": 1645142400000, "y": 22161.92, - "label": "$22161.92" + "label": "$22,161.92" }, { "x": 1645228800000, "y": 24232.12, - "label": "$24232.12" + "label": "$24,232.12" }, { "x": 1645315200000, "y": 25948.31, - "label": "$25948.31" + "label": "$25,948.31" }, { "x": 1645401600000, "y": 26334.97, - "label": "$26334.97" + "label": "$26,334.97" }, { "x": 1645488000000, "y": 27880.25, - "label": "$27880.25" + "label": "$27,880.25" }, { "x": 1645574400000, "y": 26813.33, - "label": "$26813.33" + "label": "$26,813.33" }, { "x": 1645660800000, "y": 29132.31, - "label": "$29132.31" + "label": "$29,132.31" }, { "x": 1645747200000, "y": 28896.25, - "label": "$28896.25" + "label": "$28,896.25" }, { "x": 1645833600000, "y": 30834.8, - "label": "$30834.8" + "label": "$30,834.80" }, { "x": 1645920000000, "y": 29546.97, - "label": "$29546.97" + "label": "$29,546.97" }, { "x": 1646006400000, "y": 31693.82, - "label": "$31693.82" + "label": "$31,693.82" }, { "x": 1646092800000, "y": 31394.2, - "label": "$31394.2" + "label": "$31,394.20" }, { "x": 1646179200000, "y": 34090.57, - "label": "$34090.57" + "label": "$34,090.57" } ], "name": "Value 5" diff --git a/src/components/ebay-area-chart/examples/default.marko b/src/components/ebay-area-chart/examples/default.marko new file mode 100644 index 000000000..15bded884 --- /dev/null +++ b/src/components/ebay-area-chart/examples/default.marko @@ -0,0 +1,3 @@ +import data from "./data.json"; + + diff --git a/src/components/ebay-area-chart/index.marko b/src/components/ebay-area-chart/index.marko index 71eafe3bd..5a6add459 100644 --- a/src/components/ebay-area-chart/index.marko +++ b/src/components/ebay-area-chart/index.marko @@ -1,23 +1,16 @@ -import { processHtmlAttributes } from "../../common/html-attributes" - +import { processHtmlAttributes } from "../../common/html-attributes"; $ const { data, class: inputClass, series, title, description, - xAxisLabelFormat, - yAxisLabels, - xAxisPositioner, - yAxisPositioner, + xLabelFormatter, + yLabelFormatter, + highchartOptions, ...restInput -} = input +} = input; -
- +
+
diff --git a/src/components/ebay-area-chart/style.less b/src/components/ebay-area-chart/style.less deleted file mode 100644 index e237ac012..000000000 --- a/src/components/ebay-area-chart/style.less +++ /dev/null @@ -1,23 +0,0 @@ -.ebay-area-chart { - .highcharts-point { - opacity: 0; // overriding the default highcharts opacity to 0 - stroke-width: 2px; // and adjusting the stroke width - } - - .ebay-area-chart__marker--visible { - opacity: 1; - } - - .highcharts-legend-item { - .highcharts-point { - opacity: 1; // displays the legend marker symbols which are hidden by the highcharts-point opacity - stroke-width: 1; // sets the stroke width on the plot point markers - } - } - - @media (prefers-color-scheme: dark) { - .highcharts-halo { - fill-opacity: 0.8; // this inverses the default 0.2 fill opacity in from light mode - } - } -} diff --git a/src/components/ebay-area-chart/tests/__snapshots__/test.server.js.snap b/src/components/ebay-area-chart/tests/__snapshots__/test.server.js.snap new file mode 100644 index 000000000..0f6bbce0e --- /dev/null +++ b/src/components/ebay-area-chart/tests/__snapshots__/test.server.js.snap @@ -0,0 +1,73 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`section-title > renders defaults 1`] = ` +" +  +  + 
 +" +`; + +exports[`section-title > renders with five series 1`] = ` +" +  +  + 
 +" +`; + +exports[`section-title > renders with non-spline param 1`] = ` +" +  +  +  +" +`; + +exports[`section-title > renders with two series 1`] = ` +" +  +  +  +" +`; + +exports[`section-title > renders with x-axis label format 1`] = ` +" +  +  +  +" +`; diff --git a/src/components/ebay-area-chart/tests/test.server.js b/src/components/ebay-area-chart/tests/test.server.js new file mode 100644 index 000000000..ab4bfe7fe --- /dev/null +++ b/src/components/ebay-area-chart/tests/test.server.js @@ -0,0 +1,31 @@ +import { describe, it } from "vitest"; + +import { composeStories } from "@storybook/marko"; +import { snapshotHTML } from "../../../common/test-utils/snapshots"; +import * as stories from "../area-chart.stories"; + +const { Standard, TwoSeries, FiveSeries, WithXLabelFormat, NonSpline } = + composeStories(stories); +const htmlSnap = snapshotHTML(__dirname); + +describe("section-title", () => { + it("renders defaults", async () => { + await htmlSnap(Standard); + }); + + it("renders with two series", async () => { + await htmlSnap(TwoSeries); + }); + + it("renders with five series", async () => { + await htmlSnap(FiveSeries); + }); + + it("renders with x-axis label format", async () => { + await htmlSnap(WithXLabelFormat); + }); + + it("renders with non-spline param", async () => { + await htmlSnap(NonSpline); + }); +}); diff --git a/src/components/ebay-area-chart/tooltip.marko b/src/components/ebay-area-chart/tooltip.marko new file mode 100644 index 000000000..a60fbd27c --- /dev/null +++ b/src/components/ebay-area-chart/tooltip.marko @@ -0,0 +1,35 @@ +import type HighchartsTypes from "highcharts"; +export interface Input { + date: string; + points: HighchartsTypes.TooltipFormatterContextObject[] | undefined; + total: number; + valueFormatter: (value: number | string) => string; +} + +${input.date} + +
+ ${points.point.series.name} + + ${points.point.label} + + + ${input.valueFormatter(points.point.y)} + +
+ + +
+ Total + ${input.valueFormatter(input.total)} +
+ + +style.less { + .ebay-area-chart__tooltip-value { + display: flex; + justify-content: space-between; + gap: var(--spacing-100); + width: 100%; + } +}