Skip to content

feat/bug(aria/accordion): custom trigger click #33810

Description

@mauriziocescon

Hey!

I'm not sure if this is intended behaviour, but I think I may have hit an edge case with AccordionTrigger when it's applied via hostDirectives rather than directly on an element. In that setup, clicks on nested children inside the trigger don't seem to toggle the panel (clicking its direct text works fine), whereas using ngAccordionTrigger directly works in all cases.

From what I can tell, it may be related to _findTriggerPattern resolving the target via .closest('[ngAccordionTrigger]') — since host-directive usage doesn't stamp that attribute onto the host element, the lookup appears to fail for nested targets.

Adding an explicit host: { ngAccordionTrigger: '' } seems to fix the problem, but I haven't found any reference to such practice. I believe it would be nice to make it working out of the box.

Cheers!

@Component({
  selector: '[myTrigger]',
  hostDirectives: [
    {
      directive: AccordionTrigger,
      inputs: ['panel', 'disabled', 'expanded'],
      outputs: ['expandedChange'],
    },
  ],
  // uncomment to make it working
  // host: { ngAccordionTrigger: '' },
  template: ` <ng-content /> `,
  styles: `
    :host {
      font-size: 16px;
    }
  `,
})
export class MyTrigger {}

@Component({
  selector: 'CustomAccordion',
  imports: [
    NgTemplateOutlet,
    AccordionGroup,
    AccordionTrigger,
    AccordionPanel,
    AccordionContent,
  ],
  template: `
    <div ngAccordionGroup [multiExpandable]="false">
      @if (trigger()) {
        <ng-container
          [ngTemplateOutlet]="trigger()"
          [ngTemplateOutletContext]="{
            panel: panel,
            expanded: expanded(),
            expandedChange: () => expanded.update(v=> !v),
            disabled: disabled(),
          }"
          [ngTemplateOutletInjector]="'outlet'" />
      } @else {
        <button 
          ngAccordionTrigger 
          [panel]="panel" 
          [(expanded)]="expanded">
          Click works! ✅
        <span>Text inside span: working ✅</span>
        </button>
      }
      <div ngAccordionPanel #panel="ngAccordionPanel">
        <ng-template ngAccordionContent>
          Content  
        </ng-template>
      </div>
    </div>
  `,
})
export class CustomAccordion {
  readonly disabled = model(false);
  readonly expanded = model(false);
  readonly trigger = input<TemplateRef<unknown> | undefined>(undefined);
}

@Component({
  selector: 'Consumer',
  imports: [MyTrigger, CustomAccordion],
  template: `
    <CustomAccordion />
    <hr />
    <CustomAccordion [trigger]="customTrigger" />

    <ng-template
      #customTrigger
      let-panel="panel"
      let-disabled="disabled"
      let-expanded="expanded"
      let-expandedChange="expandedChange">

      <button
        myTrigger
        class="trigger"
        [panel]="panel"
        [disabled]="disabled"
        [expanded]="expanded"
        (expandedChange)="expandedChange()">
        Click works! ✅
        <span>Text inside span: not working ❌</span>
      </button>
    </ng-template>
  `,
  styles: `
    .trigger {
      color: red;
    }
  `,
})
export class Consumer {}

https://stackblitz.com/edit/stackblitz-starters-gpx1tbtc?file=src%2Fmain.ts

Use Case

Custom trigger

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P3An issue that is relevant to core functions, but does not impede progress. Important, but not urgentarea: aria/accordion

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions