question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

RFC: Proposal for new `xy-chart` package

See original GitHub issue

Motivation

vx packages are (by design) low-level and modular which maximizes their flexibility, but also requires more work to create even simple charts. My library @data-ui/xy-chart is built on top of vx and was designed to make it easier to create common charts with less work. To solve this use-case within the vx ecosystem, and consolidate these efforts, @data-ui/xy-chart is being deprecated and we plan to port its functionality to a new vx package @vx/xy-chart.

Goals of this RFC

  • align on near- and mid-term features
  • align on an XYChart API
  • align on a general direction for implementation

Features

To have feature parity with @data-ui/xychart, the new package should support the following:

  • Computes and provides x- and y- scales across all data series
  • Handles mouse events for the XYChart across all data series
    • Positions tooltips and provides tooltip data
    • Supports programmatic tooltip control
    • Mouse events can be defined at chart or series level, can be disabled at series level
  • Supports the following *Series which are mostly wrappers around vx
    • Bar, Line, Point, Area, AreaDifference, StackedArea, StackedBar, GroupedBar, BoxPlot, ViolinPlot, Interval
  • Exports CrossHair component for use with Tooltip
  • Exports XAxis and YAxis components from vx
  • Supports horizontal and vertical reference lines
  • Supports x- and y-gridlines
  • Supports brush functionality (pre @vx/brush)
  • Supports styles with LinearGradient, Patterns, and a chart theme via props
  • Wraps individual points in FocusBlur handlers that are a11y accessible / tab-able

We would also like to add additional support for the following:

New near-term features

  • re-written in TypeScript
  • responsive by default
  • supports arbitrary datum shape + x / y data accessors (currently requires { x, y } datum shape)
  • first-class hooks support

New mid-term features

  • optionally render Tooltip in a Portal to fix z-index stacking context problem
  • easy creation of Legends
  • better support for overlays / annotations (points + regions)
  • integration with @vx primitives like brush, zoom, and drag
  • canvas support – vx is currently mostly svg based (this likely requires updates in other vx packages)
  • animation – @data-ui does not support animation. while this may not need to be fully baked in we should expose hooks to animate xy-chart

API

@techniq has done some great research on declarative react chart APIs here. Generally they are composable:

<Chart {...}>
  <Axis {...} /> 
  <Gridlines {...} /> 
  <Legend />
  <DataSeries {...} /> 
  <DataSeries {...} />
</Chart>

However there are some key differences:

data provided at the DataSeries level vs the chart container level

  1. DataSeries level – ✅ favored
() => <Chart><DataSeries data={...} /></Chart>

Pros 👍

  • it’s more “natural” to directly link data to the series that will visually represent it
  • @vx/shape’s (the basis for DataSeries) currently require data, so this is more consistent with separate package APIs
  • Series can use any custom logic they need for computing the x- and y- extent from their data, and the Chart container can simply collect these
    • additionally this allows more advanced functionality like horizontal orientation to be pushed to the Series-level without requiring it to be implemented by all Series and the Chart container doesn’t need to have any knowledge of it

Cons 👎

  • Chart container needs a way to access the data across all series

  1. Chart container level – ❌ disfavored
() => <Chart data={...}><DataSeries /></Chart>

Pros 👍

  • ultimately the Chart needs access to all data in order to provide x- and y- scales; this makes that easy.

Cons 👎

  • Series may require custom logic to compute x- and y- extent from their data (e.g., a bar stack) which the Chart needs to be aware of in this model
  • still requires key accessors at the Series-level for Series data
  • forces all DataSeries to have the same data length (or be filled with empty values)

Mouse and Touch events

Mouse and touch events are handled in several ways

  1. Standard mouse events – ✅ favored react-vis and @data-ui expose mouse and touch events at both the Chart and Series level; these use standard react events such as onClick, onTouchMove, etc.

  2. Custom event syntax – ❌ disfavored Some libraries like Victory have their own custom event system with non-standard syntax and selection language.

react hooks

I’ve not been able to find any react vis libraries which expose hooks. Feels like an opportunity on top of a render / component API 😏

Implementation

We’d like to improve upon the following limitations of @data-ui v1 implementation:

  1. Written in TypeScript @data-ui was written in JavaScript, but vx is now a TypeScript project and typings will be similarly useful for @data-ui.

  2. Use react context over cloning children @data-ui was implemented before the new / more robust 16.3 context API. Therefore chart styles and shared scales are passed via props + React.cloneElement. Combined with hooks, using context should open up a whole new set of API possibilities (see below).

What is kept in context

The function of the XYChart wrapper largely equates to managing shared state across the elements of a chart, which components can leverage as needed. . This includes

  • chart theme + styles
  • xScale that accounts for data range of all chart series and chart width + margin
  • yScale that accounts for data range of all chart series and chart height + margin
  • tooltipData + tooltipCoords, when applicable

In addition to chart width + height, the Chart container must have knowledge of all data (+ annotation) values to compute scales appropriately. Rather than having the Chart introspect props from child DataSeries or Annotations (which can get gnarly) we propose that DataSeries and Annotations register their data, xValues, and yValues in context.

This pushes the logic of data <> x/y extent mapping to DataSeries rather than the Chart, and allows the Chart to leverage these values to compute scales properly. It could look something like

// in e.g., <LineSeries {...props} />
const { key, data, xAccessor, yAccessor } = props;
const { registerData } = useContext(XYChart);

registerData({
  dataKey: key,
  xValues: data.map(d => xAccessor(d)),
  yValues: data.map(d => yAccessor(d)),
  // other interesting things to do at this level
  mouseEvents: false,
  legendItemRenderer,
}); 

Unknowns

I’m unsure if there are major performance implications of using hooks ⚡ 🐌

Proposed API

// all of these items have access to the same Chart `context` which includes
// theme, dataRegistry, xScale, yScale, colorScale, tooltipData, tooltipCoords
const { ChartProvider, XYChart, Legend, Tooltip } = useChart({ theme, scaleConfig, ... }));

() => (
  {/* context provider */}
  <ChartProvider>
   {/** 
     * Chart renders `svg` container and computes `x-` and `y-scale`s using the 
     * data registry context. It is either passed `width`/`height`, or it uses 
     * `@vx/responsive` for auto-sizing
     */}
    <XYChart>
     {/** 
       * DataSeries register their data on mount. When `x-` and `y-scale`s 
       * are computed and available in context they render data. 
       */}
      <LineSeries key={dataRegistryKey} data={...} />
    
     {/** 
       * Axes use `x-` or `y-scale`s from context based on orientation.
       */}
      <Axis orientation="left" />

      {/* Custom axis component could use `scale`s from context */}
      <CustomAxis />
    </XYChart>

    {/** 
      * Tooltip is `html`-based so should be rendered outside the chart `svg`
      * It has access to `tooltipData` and `tooltipCoords` from context.
      */}
    <Tooltip renderInPortal={boolean} />

    {/** 
       * Legend is `html`-based so should be rendered outside the chart `svg`. 
       * It has access to all series via `dataRegistry` from context, 
       * or we could add a legend renderer registry.
       */}
    <Legend  />
  </ChartProvider>
)

The same functionality could be provided in a component API:

import { ChartProvider, XYChart, Legend, Tooltip } from '@vx/xy-chart`

() => (
  <ChartProvider theme={...} {...scaleConfig} >
    <XYChart {...} />
    <Legend {...} />
    <Tooltip {...} />
  </ChartProvider>
)

cc @hshoff @techniq @kristw

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:8
  • Comments:25

github_iconTop GitHub Comments

4reactions
williastercommented, Jun 12, 2020

Okay finally! (sorry there were some problems with other vx packages that I fixed locally, that then wouldn’t work in the sandbox)

Here’s the PR #745 🎉 Have a lot of things working (love the dark mode + animation!), really curious about your feedback on the react-spring approach. I tried this approach and your useAnimatedScale variant but couldn’t actually get that to interpolate/tween values correctly. Can share that exploration if you want.

2reactions
kristwcommented, Jun 2, 2020

Comment-3

Now the XYChart magically computes and sets domain for the entire chart based on all given data. There might be times when developers wish to override the domain computation, such as to add min/max bounds, zeroing, manually specify domain. Could we add one more optional hook to the XYChart props which sets the default value to the current behavior.

type Props = {
  ...,
  computeDomain: ({ dataset, xScale, yScale }) => void;
}

<XYChart 
  dataset={...} 
  xScale={...} 
  yScale={...} 
  computeDomain={({ dataset, xScale, yScale }) => { 
    // computes and sets xScale.domain(...)
    // computes and sets yScale.domain(...)
  }} 
/>
Read more comments on GitHub >

github_iconTop Results From Across the Web

RFC: Proposal for new `xy-chart` package · Issue #734 - GitHub
I would like to propose one change from the original @data-ui/xy-chart library. The original library takes scale config object and pass it to ......
Read more >
Proposal for a staged RFC process - Baby Steps
The new process includes an explicit step to move from the “spitballing” stage (roughly “Pre-RFC” today) to the “designing” stage (roughly ...
Read more >
XYChart (JavaFX 8) - Oracle Help Center
Chart base class for all 2 axis charts. It is responsible for drawing the two axes and the plot content. It contains a...
Read more >
What's New and What's Changed - Support
Many new features and changes introduced in version 7.5 service packs SP1, SP2, and SP3 are included in version 8.00. However, the service...
Read more >
Untitled
Tcp 1379, Laundry sink lowes, Andre muller gaviao, Fishtale new prague, Cutterpede blade ... Fanny pasquier ragobert, Xy chart online, Barbara ulvog.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found