primefaces / primevue

Next Generation Vue UI Component Library
https://primevue.org
MIT License
9.52k stars 1.15k forks source link

Accordion: Doesn't render children except hardcoded AccordionTabs, no other elements and no AccordionTabs that are wrapped in another component #5085

Open factoidforrest opened 7 months ago

factoidforrest commented 7 months ago

Describe the bug

If I define a component with an accordion and in another file define an accordionTab, and try to render the tab in the accordion, it doesnt show up. Am I fundamentally misunderstanding something or is this just broken somehow? The sandbox shows it happening.

Reproducer

https://codesandbox.io/p/devbox/stupefied-cloud-rxhhj3?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clrdxtyfe0007306jzjar1q39%2522%252C%2522sizes%2522%253A%255B70%252C30%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clrdxtyfd0002306j2hm1ii8g%2522%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clrdxtyfd0004306j6abyclwx%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clrdxtyfd0006306jxbd7p5fm%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clrdxtyfd0002306j2hm1ii8g%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clrdxtyfc0001306jqa2ww815%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522id%2522%253A%2522clrdxuhkt0049306jah7hl2a2%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522initialSelections%2522%253A%255B%257B%2522startLineNumber%2522%253A5%252C%2522startColumn%2522%253A1%252C%2522endLineNumber%2522%253A5%252C%2522endColumn%2522%253A1%257D%255D%252C%2522filepath%2522%253A%2522%252Fsrc%252FApp.vue%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522clrdxtyfd0002306j2hm1ii8g%2522%252C%2522activeTabId%2522%253A%2522clrdxuhkt0049306jah7hl2a2%2522%257D%252C%2522clrdxtyfd0006306jxbd7p5fm%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clrdxtyfd0005306j7krswrr9%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522port%2522%253A5173%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522clrdxtyfd0006306jxbd7p5fm%2522%252C%2522activeTabId%2522%253A%2522clrdxtyfd0005306j7krswrr9%2522%257D%252C%2522clrdxtyfd0004306j6abyclwx%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clrdxtyfd0003306jd8196dzg%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%257D%255D%252C%2522id%2522%253A%2522clrdxtyfd0004306j6abyclwx%2522%252C%2522activeTabId%2522%253A%2522clrdxtyfd0003306jd8196dzg%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D

PrimeVue version

3.46.0

Vue version

3.x

Language

TypeScript

Build / Runtime

Vue CLI App

Browser(s)

No response

Steps to reproduce the behavior

You will see the tab not render, in that sandbox there should be two tabs but the list only renders one

Expected behavior

two tabs render

factoidforrest commented 7 months ago

Looking at the code, you can see the offending function pretty clearly:

      isAccordionTab(child) {
            return child.type.name === 'AccordionTab';
        },

and then used here

 tabs() {
            return this.$slots.default().reduce((tabs, child) => {
                if (this.isAccordionTab(child)) {
                    tabs.push(child);
                } else if (child.children && child.children instanceof Array) {
                    child.children.forEach((nestedChild) => {
                        if (this.isAccordionTab(nestedChild)) {
                            tabs.push(nestedChild);
                        }
                    });
                }

                return tabs;
            }, []);
        }

Obviously that works in the most basic of cases but doesn't work when the child is wrapped in another component, I'm not sure why. Also, the effect of just totally failing to render your child elements if they don't match the type you're expecting seems very brittle to me. I wouldn't ever expect a component library to do that.

I was able to hack around this by changing the "name" of my child component to "AccordionTab" which fixes the issue.

factoidforrest commented 7 months ago

I can see that this code was recently changed just three weeks ago, looks like by @mertsincan in https://github.com/primefaces/primevue/commit/40f231b1f5cb19f45de4e49427bb7dbd62ce3103

I assume if I revert to an earlier version it will fix it.

Edit: looks like earlier versions have a similar issue although it's getting beyond my knowledge at this point. Perhaps a maintainer can chime in

mertsincan commented 7 months ago

Hi,

Yes, I think it is the same as https://github.com/primefaces/primevue/issues/4646#issuecomment-1882003926 I'll try it in the future versions. Thanks a lot for your report!

james-cavanagh commented 4 months ago

Hi @mertsincan, is there an update on this issue please?

Additionally, is there a list of components that cannot be wrapped? Such a list would help me make an informed decision about whether PrimeVue is a suitable component library to pursue within my organisation. I hope it is, as, aside from the wrapping issue with several key components, it's fantastic. I just need to ensure its stability before discussing a Pro Support package.

james-cavanagh commented 3 months ago

Hi @mertsincan, is there an update on this issue, please? I have just noticed that I am also unable to wrap the StepperPanel due to a similar issue.

RebeccaStevens commented 2 months ago

I just came across this issue too. Here's a workaround that you can use for now:

import { MyAccordionTab, Accordion } from "#components";

// @ts-ignore
Accordion.methods.isAccordionTab = (child) => 
  child.type === MyAccordionTab;

// @ts-ignore
Accordion.computed.tabs = function tabs() {
  const tabs = [];
  const stack = this.$slots.default();
  do {
    const child = stack.pop();
    if (this.isAccordionTab(child)) {
      tabs.push(child);
    } else if (child.children && Array.isArray(child.children)) {
      stack.push(...child.children.reverse());
    }
  } while (stack.length > 0);

  return tabs;
};