Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow "name" Property on Bar Object to Take In Function Type or Provide Avenue to Set Name Value Elsewhere #4138

Open
1 task
Fantasy-Zone opened this issue Feb 5, 2024 · 15 comments
Labels
enhancement Enhancement to a current API feature request Issues that are feature requests question Issue that is a question about recharts rather than a bug or a feature request

Comments

@Fantasy-Zone
Copy link

  • I have searched the issues of this repository and believe that this is not a duplicate.

What problem does this feature solve?

I'm working on a user story to create a barchart based on dynamic data. Both the names and values for both the individual bars and groups of bars are driven by dynamic data and can be sorted in a variety of ways. This wouldn't a problem normally, however one of the sorting orders that we're providing is "by value" in which the bars will appear in different orders from group to group. I've been able to get the bar colors and values to render in the correct ordering, however because the name property is static (i.e. it must be a string/number or based on the dataKey value itself) I am unable to actually set that properly. Which obviously is causing anything legend related to fall apart.

I would like to be able to pass a function into the names property that receives the same parameters as both the dataKey and shape properties so that I can also change that dynamically as well.

What does the proposed API look like?

How it would be used:

chartData.barTypes.map( ( _item, index ) => {
   <Bar
      ...
      name={ ( props ) => props.barGroups[index].name }
      ...
   </Bar>
}

What it would look like:

I haven't looked through the API enough to know a specific fix but it would be similar to how DataKey works with getValueByDataKey. Wherein it would either assign itself to the name property if a string/number is given, the result of the function call if the value is a function, or the dataKey otherwise.

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

Would you be able to give a mockup of this or something that helps describe what you're trying to do? name is the name of a series and therefore should be relatively static once set. How does sorting require the name of something to be changed? Would it not still be the same series?

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

Edit simple-bar-chart-custom-legend

You can use a custom Legend to accomplish this for now. Not as convenient as a function, but I'm still trying to understand the use-case

@ckifer ckifer added the pending response Pending response from the issue requester label Feb 5, 2024
@Fantasy-Zone
Copy link
Author

Fantasy-Zone commented Feb 5, 2024

I can give an example of the data object we're working with...

[
    {
        "name": "Austin, TX",
        "values": [
             {
                "value": 10,
                "name": "0 - 17"
             },
             {
                "value": 12,
                "name": "29 - 40"
             },
             {
                "value": 15,
                "name": "18 - 29"
             },
             {
                "value": 22,
                "name": "40 - 51"
             }
        ]
    },
    {
        "name": "San Francisco, CA",
        "values": [
             {
                "value": 9,
                "name": "18 - 29"
             },
             {
                "value": 10,
                "name": "0 - 17"
             },
             {
                "value": 22,
                "name": "40 - 51"
             },
             {
                "value": 25,
                "name": "29 - 40"
             }
        ]
    },
    {
        "name": "New York, NY",
        "values": [
             {
                "value": 7,
                "name": "29 - 40"
             },
             {
                "value": 15,
                "name": "18 - 29"
             },
             {
                "value": 22,
                "name": "40 - 51"
             },
             {
                "value": 30,
                "name": "0 - 17"
             }
        ]
    }
]

So I have data that sort of looks like this. Although the names aren't always age and state related or even contain the same sets of states and are driven by both user selection and database values. Also if you can tell the ordering is different in each state as it's sorted by value. So the first bar group has the first bar as the group of children from ages 0-17 while for the second it's people from ages 18-29. If I was just dealing with one ordering I could do it like you have in your example (and actually have done it that way), it just doesn't work for ordering each group individually by value if that makes sense.

@ckifer ckifer removed the pending response Pending response from the issue requester label Feb 5, 2024
@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

Would you be able to put an example into codesandbox with the data above?

@Fantasy-Zone
Copy link
Author

It's a little annoying as I'm using my personal account (and thus am on my personal machine) as I can't interact with repos outside of my work organization but let me see what I can do.

@Fantasy-Zone
Copy link
Author

I'm not 100% familiar with codesandbox but here is my actual code. Minus obviously the colors that I'm trying to read from the theme and everything else that I have wired up. Just so that you can see what I'm working with while I try to figure codesandbox out/work on a sample.

<ResponsiveContainer height="100%" width="100%" debounce={300}>
  <BarChart
    margin={{
      top: 5,
      right: 117,
      left: 117,
      bottom: 30,    
    }}
    style={{ display: "inline-block" }}
    data={chartData.barChart.data}
  >
    <CartesianGrid strokeDasharray="0" vertical={false} />

    <XAxis dataKey="name" axisLine={false} height={35} />

    <YAxis width={35} axisLine={false} />

    <Legend
      formatter={(value) => {
        return <span style={{ color: theme.colors.gray7 }}>{value}</span>;
      }}
    />

    <Tooltip
      cursor={false}
      labelFormatter={(_value, payload) => {
        // I have no idea why it happens but this is getting called twice
        // in which the first time it gets passed the legend name. Fix for issue.

        if (!payload?.value) {
          return <span />;
        }

        return `${payload.name}: ${payload.value}`;
      }}
    />

    {Array.from(chartData.barChart.legend, ([name, value]) => ({
      name,

      value,
    })).map((_entry, index) => {
      return (
        <Bar
          dataKey={(props) => {
            return props.valuesArray[index]?.formattedValue;
          }}
          shape={(props) => {
            const name = props.valuesArray[index].name;

            const legendIndex = chartData.barChart.legend.get(name);

            return (
              <Rectangle
                {...props}
                fill={theme.colors[CHART_COLOR_NAMES[legendIndex]]}
                className="recharts-bar-rectangle"
              />
            );
          }}
          barSize={24}
          name={(props) => props.valuesArray[index]?.name}
        />
      );
    })}
  </BarChart>
</ResponsiveContainer>;

Also had a weird issue where I was getting the "state names" and ages both printed in the legend but I figured out a workaround by passing in that function into the label formatter (which is why that's there in the first place. Sorry about the formatting... I emailed it to myself and stuff got lost in that process.

@Fantasy-Zone
Copy link
Author

Oh and for formatted value I just found that it was easier to deal with that than dealing with other avenues as my values on the tooltips need to be formatted with commas, dollar signs, percentages, certain precision, etc depending on the DB configuration.

@Fantasy-Zone
Copy link
Author

Oh and the name thing is what I was trying to do when I was hoping against hope that name had some sort of ability to take in functions. Obv doesn't work.

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

what is in chartData.barChart.legend?

@Fantasy-Zone
Copy link
Author

Fantasy-Zone commented Feb 5, 2024

Sort of a similar structure to what you have in your mySeries object. Only I'm really only using it to keep track of how many items would appear in the legend at the moment and as a map to my array of colors, CHART_COLOR_NAMES. I'm not using it all for ordering anymore.

But for example it'd be a map of:
[
{ key: "0-17", value: 0, },
{ key: "18-29", value: 1, },
{ key: "29-40", value: 2, },
]

Probably could've done it more efficiently but you're seeing my coding after I got a little frustrated and was trying a million things to get it working for sorting by value.

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

I can't get the sandbox to work correctly but here's what I did get:

Edit simple-bar-chart-custom-legend (forked)

Feel free to edit that to where your current implementation is, but a change like this probably won't get addressed very quickly.

Irrespective of what this actually looks like in the sandbox, the solution as it stands would be to use a custom Legend like in the earlier codesanbox example I posted above.

The approach you are taking in rendering your data isn't the standard recharts so you're kind of in uncharted waters.

@ckifer ckifer added enhancement Enhancement to a current API feature request Issues that are feature requests question Issue that is a question about recharts rather than a bug or a feature request labels Feb 5, 2024
@Fantasy-Zone
Copy link
Author

Fantasy-Zone commented Feb 5, 2024

Like rough estimate wise how long would you think it would take for it to even get someone to take a look? I was pushing my PO to hold off on this functionality for our next release and hopefully address it within the next month or two. Are we possibly talking like longer like up to 6mo-1yr? Obv not expecting something 100% signed in blood official.

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

If you'd like to contribute the change, it could be out as soon as next release. Otherwise it may never get done (so 6 months+) unless a ton of others also need the same functionality.

Recharts has no dedicated developers, everyone who works on the project does so out of their spare time which greatly increases the time to get changes out. Right now, the maintainers are focused on refactoring so it becomes easier for others to jump in and make changes. Feature/bug work may be minimal for quite some time.

@Fantasy-Zone
Copy link
Author

Ah cool. I'll see what I can do. Honestly wasn't familiar with how the project was maintained.

And thanks for taking the time to help and chat through this issue!

@ckifer
Copy link
Member

ckifer commented Feb 5, 2024

For more info and a link to slack if you'd like to chat more see #3407

And np!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to a current API feature request Issues that are feature requests question Issue that is a question about recharts rather than a bug or a feature request
Projects
None yet
Development

No branches or pull requests

2 participants