Search code examples
javascriptreactjsd3.jschart.jsrecharts

Get my second y-axis in charts.js to always display the 100% tick


I have a scatter chart using recharts that looks like the following:

1

The right-y-axis represents the percentage and it is displaying the correct values as I intended. However, I'd like to adjust it so that the 100% is always displayed. Is this possible?

To build what I currently have, I basically have two nearly-identical datasets, the difference is that one of them is hidden and I changed the ticks callback to display the percentage based on the maximum y values in the points.

Here's my dataset config:

datasets: [
  {
    label,
    data: data
      ? Array.from({ length: data.x.values.length || 0 }, (_, i) => ({
          x: data.x.values[i],
          y: data.y.values[i],
        }))
      : [],
    backgroundColor: color,
    borderColor: color,
    pointRadius: 1.2,
    fill: false,
  },
  {
    data: data
      ? Array.from({ length: data.x.values.length || 0 }, (_, i) => ({
          x: data.x.values[i],
          y: data.y.values[i],
        }))
      : [],
    backgroundColor: 'transparent',
    borderColor: 'transparent',
    pointRadius: 0,
    fill: false,
    yAxisID: 'y1',
  },
],

And here's my scales config:

scales: {
  x: {
    title: {
      display: true,
      text: data?.x.title || '',
    },
    ticks: {
      callback: formatPlotTicks,
    },
  },
  y: {
    title: {
      display: true,
      text: data?.y.title || '',
    },
    type: 'linear',
    display: true,
    position: 'left',
    ticks: {
      callback: formatPlotTicks,
    },
  },
  y1: {
    type: 'linear',
    display: true,
    position: 'right',
    ticks: {
      callback: (value) => {
        const percentageValue = ((value as number) / maxYValue) * 100;
        if (percentageValue > 100.5) {
          return '';
        }
        return `${percentageValue.toFixed(0)}%`;
      },
    },
  },
},

Solution

  • I was able to solve it by adjusting the options and scales like this:

    Dataset:

    datasets: [
      {
        label,
        data: data
          ? Array.from({ length: data.x.values.length || 0 }, (_, i) => ({
              x: data.x.values[i],
              y: data.y.values[i],
            }))
          : [],
        backgroundColor: color,
        borderColor: color,
        pointRadius: 1.2,
        fill: false,
      },
      {
        data: data
          ? Array.from({ length: 7 }, (_, i) => ({
              x:
                i === 0
                  ? 0
                  : data?.x.values[i * 20 - 1] ||
                    data?.x.values[(data?.x.values.length || 0) - 1],
              y: i === 6 ? Kpercent : i * 20,
            }))
          : [],
        backgroundColor: 'transparent',
        borderColor: 'transparent',
        pointRadius: 1.2,
        fill: false,
        yAxisID: 'y1',
      },
    ],
    

    Options:

    scales: {
      x: {
        title: {
          display: true,
          text: data?.x.title || '',
        },
        ticks: {
          callback: formatPlotTicks,
        },
      },
      y: {
        title: {
          display: true,
          text: data?.y.title || '',
        },
        type: 'linear',
        display: true,
        position: 'left',
        ticks: {
          callback: formatPlotTicks,
        },
      },
      y1: {
        type: 'linear',
        display: true,
        position: 'right',
        min: 0,
        max: Kpercent,
        afterBuildTicks: (scaleInstance) => {
          scaleInstance.ticks.pop();
          scaleInstance.ticks.push({ value: 100 });
        },
        ticks: {
          stepSize: 20,
          callback: (value) => `${value}%`,
        },
        grid: {
          drawOnChartArea: false,
        },
      },
    },