esnet / react-timeseries-charts

Declarative and modular timeseries charting components for React
http://software.es.net/react-timeseries-charts
Other
868 stars 283 forks source link

Ability to add a suffix to axis ticks #127

Open yrokhlin opened 7 years ago

yrokhlin commented 7 years ago

Is there any possible way to add a suffix to the ticks on a yAxis component?

I noticed in some examples, the suffix seems to change, but I can't quiet pinpoint how it's done. Basically, what do you do when d3.format rules alone are not enough? For example adding a (kb/mb/gb) to the ticks in the yAxis?

I know the examples on the site generally show something like "Traffic Measured in (bps)" -- but once that label is set, it can't be changed (I've tried) -- so keeping that dynamic is a little difficult. I am about to resort to just making the label invisible and absolutely positioning my own, which seems like a terrible idea.

pjm17971 commented 7 years ago

Yeah, I think this has been asked about before and is part of why there's new axis code in the works. The examples rely on d3.format's SI functionality, e.g. d3.format(".2s")(42e6); // SI-prefix with two significant digits, "42M".

lquinn2015 commented 7 years ago

I have been looking at this today. I have a question about the esnet source when/where exactly do you add on the Y axis to graphs. Taking the example from here https://bl.ocks.org/d3noob/0e276dc70bb9184727ee47d6dd06e915

At some point drawling the graph they have to do

 svg.append("g")
      .attr("class", "axis")
      .attr("transform", "translate(0," + height + ")")
      .call(d3.axisBottom(x)
              .tickFormat(d3.timeFormat("%Y-%m-%d")))
      .selectAll("text")    
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", ".15em")
        .attr("transform", "rotate(-65)");

  // Add the Y Axis
  svg.append("g")
      .attr("class", "axis")
      .call(d3.axisLeft(y));

You could do a hardcoding for certain values say like for every 10's place on the y you want show a location/some other value. You could use this stackoverflow idea https://stackoverflow.com/questions/37491691/just-change-tick-labels They use following work around .

var data =[
  {sepalLength: "0", sepalWidth: 3.5, name: "AT"},
  {sepalLength: "1", sepalWidth: 3.0, name: "AS"},
  {sepalLength: "2", sepalWidth: 2.5, name: "AM"},
  {sepalLength: "3", sepalWidth: 2.0, name: "CS"},
  {sepalLength: "4", sepalWidth: 1.5, name: "CT"},
  {sepalLength: "5", sepalWidth: 1.0, name: "CX"}
]

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left")
    .tickFormat(function(d, i) {
      var inData = data.filter(function (v) { return +v.sepalLength === d })
      return inData.length ? inData[0].name : d
    });

This could work if you could supply the sample data array through an optional property. I could look more into this source but I am not really sure when you make/format the yAxis

pjm17971 commented 7 years ago

The ChartContainer basically divides the layout up into ChartRows and the TimeAxis, then each row will add as many YAxis components as needed. Each axis is added to a list here and then further down actually put in the render here.

It's a little complicated because the ChartRow injects props based on if its on the left or right, and what sizes it gets.

Once the YAxis is involved its pretty self contained, though complicated by the fact that this is the last place in this library where d3 is doing dom manipulation outside of react (hence the react-axis project, which would also be a good place to contribute). Anyway, basically here is where you'll find all the d3 code: https://github.com/esnet/react-timeseries-charts/blob/master/src/components/YAxis.js#L171 and specifically, formatting ends up here: https://github.com/esnet/react-timeseries-charts/blob/master/src/components/YAxis.js#L172

You could probably detect that what's being passed in here is a function rather than a string, and then call that to customize.

lquinn2015 commented 7 years ago

I just did the fix for this issue its quite simple actually. This is a quick picture of my results i am displaying the location places based there y height so I can supply an array as property to change fixed, discrete y values. You can easily edit my code to display suffixes by change my comparing statements to just d + " "+v.name. This is more of a hard coded solution but its quick and simple I include the lines i changed below. image

In your component add this to you YAxis html tag

customLabels = {yourLabels}

In the YAxis code files change the following (line numbers included)

//line 65
var cLabeles = [
    {
      sLen: 0,
      sWidth: 3.14,
      name: "none",
    },
    {
      sLen: 1,
      sWidth: 1.74,
      name: "none",
    },
];

//138
      this.renderAxis(this.props.align, this.props.scale, +this.props.width, this.props.absolute, this.props.format, this.props.customLabels);
//149
      var customLabels = nextProps.customLabels;
//152
     this.updateAxis(align, scale, width, absolute, type, fmt, customLabels);
//163
    value: function updateAxis(align, scale, width, absolute, type, fmt, customLabels) {
//181 + 191 + 226 change the return yformat(d) with...(<b> Note: </b> You may also have change this //for the absolutevalue side too)
            var inData = customLabels.filter(function (v) {return v.sWidth === d});
            return inData.length ? inData[0].name : yformat(d);
//293 add this to defaultProps
customLabels: cLabeles
//275
customLabels: _react2.default.PropTypes.array
pjm17971 commented 7 years ago

I think this would be better if that code (the filter, label list etc) was out in user land, i.e. allow the fmt being added to the YAxis already to be a string or a function. Enable that in propTypes. Then in the YAxis code use the format as it is now if its a string (using the d3 formatter yFormat), otherwise call fmt as a function to get the actual value you want to be displayed (pass it the d, return the string in the axis generation). You'll have to deal with the abs and type stuff, so there's multiple places to change. But this enables any modification to the formatting, not just label substitution.

lquinn2015 commented 7 years ago

Your probably right I am not really use to the fine details of JavaScript I learned it about a week ago. One draw back I did notice to my solution is that it really depends on your axis giving you the tick marks you need ie if those 10s or whatever are not there it will produced unexpected results.