y-takey / chartjs-plugin-stacked100

This plugin for Chart.js that makes your bar chart to 100% stacked bar chart.
MIT License
129 stars 29 forks source link

Bars are not being displayed with parsing option #100

Open thiagonzalez opened 9 months ago

thiagonzalez commented 9 months ago

Hello everyone,

I have been working on a 100% stacked bar chart and once I initialized the chartjs-plugin-stacked100 on the plugins object, the bars stopped displaying.

Without the chartjs-plugin-stacked100: image

With the chartjs-plugin-stacked100: image

It is worth pointing out that I'm using chartjs 4.2.1 and chartjs-plugin-stacked100 1.3.0. The dataset structure is:

[
...

{
  label: "sweater",
  data: [
    {
      channel_name: "facebook",
      product_category: "sweater",
      purchase_amount: 169816.56
    },
    {
      channel_name: "google ads",
      product_category: "sweater",
      purchase_amount: 216422.88
    },
    {
      channel_name: "instagram",
      product_category: "sweater",
      purchase_amount: 190589.4
    },
    {
      channel_name: "tiktok",
      product_category: "sweater",
      purchase_amount: 195401.69
    }
  ],
  parsing: {
    xAxisKey: "purchase_amount",
    yAxisKey: "channel_name"
  }
}

...
]

You can find the setup running on the link below: https://codesandbox.io/s/happy-fermat-lyxmmj?file=/src/script.js

Please, let me know if you have any clue of what is going on. Thank you!

y-takey commented 9 months ago

Hello @thiagonzalez :)

Thank you for reporting the issue.

As you've correctly pointed out, the plugin currently does not support the parsing option. I intend to address it when I have the time. If you require a solution urgently, please consider using the default 'x' and 'y' keys for the axis key.

Thank you for your understanding, and I appreciate your patience while I work on resolving this issue.

y-takey commented 9 months ago

I have addressed the issue, and I'm pleased to announce that chartjs-plugin-stacked100 version 1.5 now supports the parsing option. Please update the plugin and give it a try. Thank you for your patience, and if you encounter any further issues or have additional questions, feel free to reach out.

thiagonzalez commented 9 months ago

hey @y-takey, Thank you so much for your answer explaining the problem and also fixing it! You were so fast! Everything worked just fine in here. The only problem I still have is #87, because I'm using NextJS with a few pages rendering on the server-side.

What I could notice is that Webpack is generating some code that is breaking it. To just have my feature here done, I copied the whole content of your plugin and everything worked great.

Maybe it's a good thing to migrate from Webpack to Rollup, just like others plugins do.

Again, you rock! thank you so much

y-takey commented 9 months ago

Thank you for trying it out so quickly, and I'm glad to hear that the issue is resolved. :)

Regarding the matter with #87, I appreciate your suggestion to migrate to Rollup. However, I currently lack the knowledge and time for such a migration. Instead, I will investigate if it's possible to make the necessary configuration changes to support rendering on the server-side while keeping Webpack.

thiagonzalez commented 3 months ago

hey @y-takey, I hope you are having a great week!

I know you already closed this issue, but I came up with a different issue, but very related to this one.

I have the following config to initialize a chartJS instance:

{
    "type": "bar",
    "data": {
        "labels": [
            "User 1",
            "User 2",
            "User 3",
            "User 4",
            "User 5",
            "User 6",
            "User 7",
            "User 8",
            "User 9",
            "User 10"
        ],
        "datasets": [
            {
                "label": "Blocked",
                "data": [
                    {
                        "count": 1,
                        "assignee": "User 1",
                        "status": "Blocked"
                    }
                ],
                "parsing": {
                    "xAxisKey": "assignee",
                    "yAxisKey": "count"
                }
            },
            {
                "label": "To Do",
                "data": [
                    {
                        "count": 4,
                        "assignee": "User 1",
                        "status": "To Do"
                    },
                    {
                        "count": 2,
                        "assignee": "User 2",
                        "status": "To Do"
                    },
                    {
                        "count": 4,
                        "assignee": "User 3",
                        "status": "To Do"
                    },
                    {
                        "count": 1,
                        "assignee": "User 4",
                        "status": "To Do"
                    },
                    {
                        "count": 8,
                        "assignee": "User 5",
                        "status": "To Do"
                    },
                    {
                        "count": 2,
                        "assignee": "User 6",
                        "status": "To Do"
                    },
                    {
                        "count": 1,
                        "assignee": "User 7",
                        "status": "To Do"
                    },
                    {
                        "count": 2,
                        "assignee": "User 9",
                        "status": "To Do"
                    },
                    {
                        "count": 1,
                        "assignee": "User 10",
                        "status": "To Do"
                    }
                ],
                "parsing": {
                    "xAxisKey": "assignee",
                    "yAxisKey": "count"
                }
            },
            {
                "label": "Done",
                "data": [
                    {
                        "count": 2,
                        "assignee": "User 3",
                        "status": "Done"
                    },
                    {
                        "count": 1,
                        "assignee": "User 6",
                        "status": "Done"
                    }
                ],
                "parsing": {
                    "xAxisKey": "assignee",
                    "yAxisKey": "count"
                }
            },
            {
                "label": "In Progress",
                "data": [
                    {
                        "count": 2,
                        "assignee": "User 3",
                        "status": "In Progress"
                    },
                    {
                        "count": 1,
                        "assignee": "User 7",
                        "status": "In Progress"
                    },
                    {
                        "count": 1,
                        "assignee": "User 8",
                        "status": "In Progress"
                    }
                ],
                "parsing": {
                    "xAxisKey": "assignee",
                    "yAxisKey": "count"
                }
            }
        ]
    },
    "options": {
        "scales": {
            "x": {
                "title": {
                    "display": true,
                    "text": "ASSIGNEE"
                },
                "stacked": true
            },
            "y": {
                "title": {
                    "display": true,
                    "text": "COUNT"
                },
                "stacked": true,
                "max": 100,
                "ticks": {}
            }
        },
        "plugins": {
            "stacked100": {
                "enable": true
            }
        }
    },
    "plugins": []
}

As you can see above, not all users have entries on all datasets. You can see a regular bar chart below, removing the stacked100 plugin from the object:

image

But once I use the plugin, this is the result I'm getting:

image

You can notice that User 3's "done" and "in progress" were displayed in User 1's column because User 1 and User 2 don't have these 2 pieces of data.

I could be doing something wrong here on my side, but I would like to hear from you as well.

Thank you :)

y-takey commented 3 months ago

Hey @thiagonzalez , Thank you for reporting the issue and providing the reproduction code. I appreciate it. I've managed to reproduce the problem, so I'll work on fixing it :)

y-takey commented 3 months ago

I have fixed the reported issue and released version 1.5.3 of the plugin! Please give it a try whenever you have the time. 😄

thiagonzalez commented 3 months ago

hey @y-takey , I already told you are the man, right? Thank you so much! I was able to run the updated code here and the columns are now being respected.

On the other hand, I had a new issue. I have a very simple function to customize tooltips and some items from my chart are returning undefined for the field raw, which is the one I use to customize my tooltips.

The strangest part is that only a few items get undefined, but most of them return the expected data.

You will be able to simulate this using the link below: https://codesandbox.io/p/sandbox/bold-frost-lyxmmj?file=%2Fsrc%2Fscript.js

I left a console.log there so you can hover over all items and see the ones with undefined attributes.

PS: I'm using replaceTooltipLabel: false.

Thank you so much

y-takey commented 3 months ago

hey @thiagonzalez , Thank you for checking it out promptly and letting me know it worked well for you.

Regarding the tooltip issue, it seems that the provided CodeSandbox link is broken. (Upon opening the URL, it showed "Sandbox not found.") Based on my speculation about the cause of the problem, you probably need to retrieve the position of the data yourself.

tooltip: {
  callbacks: {
    label: (tooltipItem) => {
      const data = tooltipItem.chart.data;
      const datasetIndex = tooltipItem.datasetIndex;
      const index = tooltipItem.dataIndex;
      const datasetLabel = data.datasets[datasetIndex].label || "";
      const rateValue = data.calculatedData[datasetIndex][index];
      // **Here's the key point**
      const originalData = tooltipItem.dataset.data.find(rec => rec.assignee === tooltipItem.label);

      return `${datasetLabel}: ${rateValue}% (raw ${originalData.count})`;
    },
  },
}

However, using the tooltips option can make things more complex and inconvenient. I'll consider if there's a simpler way to access it in the future.

thiagonzalez commented 3 months ago

Hey @y-takey,

Sorry about the sandbox. For some reason, it became private. Please, try again here: https://codesandbox.io/p/sandbox/bold-frost-lyxmmj?file=%2Fsrc%2Fscript.js

I was making a few tests here in the last few days and something I realized is that now the information on the chart in terms of columns and % for each block is working perfectly, but when I try to use data from the objects inside datasets it gets lost.

If you look at my example on the link, you will see this:

{
  count: 1,
  assignee: "User 1",
  status: "READY TO TEST",
}

The status looks ok, but the assignee is getting lost. Try to hover the bars and see the tooltips. I'm still investigating what is the issue here, but I made a workaround to move forward, removing the assignee from the tooltip.

Let me know if the link is working for you now. Thank you so much once again

y-takey commented 3 months ago

Hey @thiagonzalez , No worries at all, don't sweat it! From looking at the contents of the CodeSandbox, I think fixing the following part should make it work as expected.

# script.js Line 354
const raw = fallbackData || tooltipItem.raw;

Actually, this plugin replaces the data with computed values (of type number[][]). The replaced data looks something like this.

User 1 User 2 ...
Blocked 2 0 ...
In Progress 1 1 ...
... ... ... ...

Therefore, the data passed to the tooltip is offset because it retrieves the original data based on the index of the replaced data. For example, when hovering over the "User1" - "In progress" bar, in the replaced data, the index would be 1, 0. However, using this index to access the original data would yield:

{
    count: 1,
    assignee: "User 2",
    status: "In Progress",
}