Skip to content

Commit

Permalink
feat(accordion): add support for custom header tag
Browse files Browse the repository at this point in the history
  • Loading branch information
ValentinNelu authored and quentinderoubaix committed Oct 26, 2023
1 parent 771a76e commit 57fc1aa
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 8 deletions.
73 changes: 69 additions & 4 deletions angular/lib/src/lib/accordion/accordion.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
toSlotContextWidget,
useDirectiveForHost,
} from '@agnos-ui/angular-headless';
import {NgIf} from '@angular/common';
import {NgIf, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from '@angular/common';
import type {AfterContentChecked, AfterViewInit, OnChanges, Signal, SimpleChanges} from '@angular/core';
import {
ChangeDetectionStrategy,
Expand Down Expand Up @@ -60,10 +60,65 @@ export class AccordionItemStructureDirective {
@Component({
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [UseDirective, SlotDirective, NgIf, AccordionHeaderDirective, AccordionBodyDirective],
imports: [
UseDirective,
SlotDirective,
NgIf,
AccordionHeaderDirective,
AccordionBodyDirective,
NgSwitch,
NgSwitchCase,
NgSwitchDefault,
NgTemplateOutlet,
],
template: `
<ng-template #structure let-state="state" let-widget="widget">
<h2 class="accordion-header {{ state.itemHeaderClass }}">
<ng-container [ngSwitch]="state.itemHeadingTag">
<ng-container *ngSwitchCase="'h1'" [ngTemplateOutlet]="h1"></ng-container>
<ng-container *ngSwitchCase="'h2'" [ngTemplateOutlet]="h2"></ng-container>
<ng-container *ngSwitchCase="'h3'" [ngTemplateOutlet]="h3"></ng-container>
<ng-container *ngSwitchCase="'h4'" [ngTemplateOutlet]="h4"></ng-container>
<ng-container *ngSwitchCase="'h5'" [ngTemplateOutlet]="h5"></ng-container>
<ng-container *ngSwitchCase="'h6'" [ngTemplateOutlet]="h6"></ng-container>
<ng-container *ngSwitchDefault [ngTemplateOutlet]="h2"></ng-container>
</ng-container>
<ng-template #h1>
<h1 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h1>
</ng-template>
<ng-template #h2>
<h2 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h2>
</ng-template>
<ng-template #h3>
<h3 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h3>
</ng-template>
<ng-template #h4>
<h4 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h4>
</ng-template>
<ng-template #h5>
<h5 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h5>
</ng-template>
<ng-template #h6>
<h6 class="accordion-header {{ state.itemHeaderClass }}">
<ng-template [ngTemplateOutlet]="button"></ng-template>
</h6>
</ng-template>
<ng-template #button>
<button
type="button"
id="{{ state.itemId }}-toggle"
Expand All @@ -77,7 +132,7 @@ export class AccordionItemStructureDirective {
>
<ng-template [auSlotProps]="{state, widget}" [auSlot]="state.slotItemHeader"></ng-template>
</button>
</h2>
</ng-template>
<div
*ngIf="state.shouldBeInDOM"
[auUse]="widget.directives.collapseDirective"
Expand Down Expand Up @@ -170,6 +225,10 @@ export class AccordionItemComponent implements OnChanges, AfterContentChecked, A
* Classes to add on the accordion-item body DOM element.
*/
@Input('auItemBodyClass') itemBodyClass: string | undefined;
/**
* The html tag to use for the accordion-item-header.
*/
@Input('auItemHeadingTag') itemHeadingTag: string | undefined;
/**
* An event fired when an item is shown.
*/
Expand Down Expand Up @@ -322,6 +381,12 @@ export class AccordionDirective implements OnChanges {
* It is a prop of the accordion-item.
*/
@Input('auItemBodyClass') itemBodyClass: string | undefined;
/**
* The html tag to use for the accordion-item-header.
*
* It is a prop of the accordion-item.
*/
@Input('auItemHeadingTag') itemHeadingTag: string | undefined;

//should not be documented
/**
Expand Down
16 changes: 16 additions & 0 deletions core/lib/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ export interface AccordionProps extends WidgetsCommonPropsAndState {
* It is a prop of the accordion-item.
*/
itemBodyClass: string;
/**
* The html tag to use for the accordion-item-header.
*
* It is a prop of the accordion-item.
*/
itemHeadingTag: string;
}

export interface AccordionState extends WidgetsCommonPropsAndState {
Expand Down Expand Up @@ -322,6 +328,10 @@ export interface AccordionItemCommonPropsAndState {
* Classes to add on the accordion-item body DOM element.
*/
itemBodyClass: string;
/**
* The html tag to use for the accordion-item-header.
*/
itemHeadingTag: string;
}

export interface AccordionItemProps extends AccordionItemCommonPropsAndState {
Expand Down Expand Up @@ -374,6 +384,7 @@ const defaultAccordionConfig: AccordionProps = {
itemVisible: false,
itemAnimation: true,
itemTransition: collapseVerticalTransition,
itemHeadingTag: '',
onItemShown: noop,
onItemHidden: noop,
onItemVisibleChange: noop,
Expand Down Expand Up @@ -405,6 +416,7 @@ const defaultItemConfig: AccordionItemProps = {
itemButtonClass: defaultAccordionConfig.itemButtonClass,
itemCollapseClass: defaultAccordionConfig.itemCollapseClass,
itemBodyClass: defaultAccordionConfig.itemBodyClass,
itemHeadingTag: defaultAccordionConfig.itemHeadingTag,
};
const accordionItemProps = Object.keys(defaultItemConfig) as (keyof AccordionItemProps)[];

Expand Down Expand Up @@ -434,6 +446,7 @@ const configAccordionValidator: ConfigValidator<AccordionProps> = {
itemButtonClass: typeString,
itemCollapseClass: typeString,
itemBodyClass: typeString,
itemHeadingTag: typeString,
};

const configItemValidator: ConfigValidator<AccordionItemProps> = {
Expand All @@ -451,6 +464,7 @@ const configItemValidator: ConfigValidator<AccordionItemProps> = {
itemButtonClass: typeString,
itemCollapseClass: typeString,
itemBodyClass: typeString,
itemHeadingTag: typeString,
};

function createAccordionItem(
Expand Down Expand Up @@ -553,6 +567,7 @@ export function createAccordion(config?: PropsConfig<AccordionProps>): Accordion
itemButtonClass$,
itemCollapseClass$,
itemHeaderClass$,
itemHeadingTag$,
onItemVisibleChange$,
onItemHidden$,
onItemShown$,
Expand All @@ -575,6 +590,7 @@ export function createAccordion(config?: PropsConfig<AccordionProps>): Accordion
itemButtonClass: itemButtonClass$,
itemCollapseClass: itemCollapseClass$,
itemHeaderClass: itemHeaderClass$,
itemHeadingTag: itemHeadingTag$,
onItemVisibleChange: onItemVisibleChange$,
onItemHidden: onItemHidden$,
onItemShown: onItemShown$,
Expand Down
8 changes: 6 additions & 2 deletions react/lib/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ import {createContext, forwardRef, useContext, useEffect, useImperativeHandle} f
const AccordionDIContext: React.Context<Partial<AccordionApi>> = createContext({});
const DefaultSlotStructure = (slotContext: AccordionItemContext) => {
const collapseSetRef = useDirective(slotContext.widget.directives.collapseDirective);
const re = new RegExp('^h[1-6]$');
const Heading: keyof JSX.IntrinsicElements = re.test(slotContext.state.itemHeadingTag)
? (slotContext.state.itemHeadingTag as keyof JSX.IntrinsicElements)
: 'h2';
return (
<>
<h2 className={`accordion-header ${slotContext.state.itemHeaderClass}`}>
<Heading className={`accordion-header ${slotContext.state.itemHeaderClass}`}>
<button
type="button"
id={`${slotContext.state.itemId}-toggle`}
Expand All @@ -28,7 +32,7 @@ const DefaultSlotStructure = (slotContext: AccordionItemContext) => {
>
<Slot slotContent={slotContext.state.slotItemHeader} props={slotContext}></Slot>
</button>
</h2>
</Heading>
{slotContext.state.shouldBeInDOM ? (
<div
className={`accordion-collapse ${slotContext.state.itemCollapseClass}`}
Expand Down
7 changes: 5 additions & 2 deletions svelte/lib/accordion/ItemDefaultStructure.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
export let state: $$Props['state'];
export let widget: $$Props['widget'];
const re = new RegExp('^h[1-6]$');
$: slotContext = {widget, state};
$: collapseId = state.itemId + '-collapse';
$: toggleId = state.itemId + '-toggle';
$: headingTag = re.test(state.itemHeadingTag) ? state.itemHeadingTag : 'h2';
</script>

<h2 class="accordion-header {state.itemHeaderClass}">
<svelte:element this={headingTag} class="accordion-header {state.itemHeaderClass}">
<button
type="button"
id={toggleId}
Expand All @@ -34,7 +37,7 @@
</svelte:component>
</Slot>
</button>
</h2>
</svelte:element>
{#if state.shouldBeInDOM}
<div class="accordion-collapse {state.itemCollapseClass}" use:widget.directives.collapseDirective id={collapseId} aria-labelledby={toggleId}>
<div class="accordion-body {state.itemBodyClass}">
Expand Down

0 comments on commit 57fc1aa

Please sign in to comment.