Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 31 additions & 26 deletions src/aria/accordion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe('AccordionGroup', () => {
const endKey = (target: HTMLElement) => keydown(target, 'End');

interface SetupOptions {
initialValue?: string[];
initialExpandedPanels?: string[];
multiExpandable?: boolean;
disabledGroup?: boolean;
disabledItemValues?: string[];
Expand All @@ -43,8 +43,8 @@ describe('AccordionGroup', () => {
function configureAccordionComponent(opts: SetupOptions = {}) {
const testComponent = fixture.componentInstance as AccordionGroupExample;

if (opts.initialValue !== undefined) {
testComponent.value.set(opts.initialValue);
if (opts.initialExpandedPanels !== undefined) {
testComponent.expandedPanels.set(opts.initialExpandedPanels);
}
if (opts.multiExpandable !== undefined) {
testComponent.multiExpandable.set(opts.multiExpandable);
Expand Down Expand Up @@ -109,7 +109,7 @@ describe('AccordionGroup', () => {
});

it('should have aria-expanded="false" when collapsed', () => {
configureAccordionComponent({initialValue: []});
configureAccordionComponent({initialExpandedPanels: []});
expect(triggerElements[0].getAttribute('aria-expanded')).toBe('false');
expect(triggerElements[1].getAttribute('aria-expanded')).toBe('false');
expect(triggerElements[2].getAttribute('aria-expanded')).toBe('false');
Expand Down Expand Up @@ -154,7 +154,7 @@ describe('AccordionGroup', () => {
});

it('should have "inert" attribute when collapsed', () => {
configureAccordionComponent({initialValue: []});
configureAccordionComponent({initialExpandedPanels: []});
expect(panelElements[0].hasAttribute('inert')).toBeTrue();
expect(panelElements[1].hasAttribute('inert')).toBeTrue();
expect(panelElements[2].hasAttribute('inert')).toBeTrue();
Expand All @@ -168,36 +168,36 @@ describe('AccordionGroup', () => {
configureAccordionComponent({multiExpandable: false});
});

it('should expand panel on trigger click and update value', () => {
it('should expand panel on trigger click and update expanded panels', () => {
click(triggerElements[0]);
expect(isTriggerExpanded(triggerElements[0])).toBeTrue();
expect(panelElements[0].hasAttribute('inert')).toBeFalse();
expect(groupInstance.value()).toEqual(['item-1']);
expect(groupInstance.expandedPanels()).toEqual(['item-1']);
});

it('should collapes panel on trigger click and update value', () => {
it('should collapes panel on trigger click and update expanded panels', () => {
click(triggerElements[0]);
click(triggerElements[0]); // Collapse
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(panelElements[0].hasAttribute('inert')).toBeTrue();
expect(groupInstance.value()).toEqual([]);
expect(groupInstance.expandedPanels()).toEqual([]);
});

it('should expand one and collapse others', () => {
click(triggerElements[0]);
expect(isTriggerExpanded(triggerElements[0])).toBeTrue();
expect(groupInstance.value()).toEqual(['item-1']);
expect(groupInstance.expandedPanels()).toEqual(['item-1']);

click(triggerElements[1]);
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(panelElements[0].hasAttribute('inert')).toBeTrue();
expect(isTriggerExpanded(triggerElements[1])).toBeTrue();
expect(panelElements[1].hasAttribute('inert')).toBeFalse();
expect(groupInstance.value()).toEqual(['item-2']);
expect(groupInstance.expandedPanels()).toEqual(['item-2']);
});

it('should allow setting initial value', () => {
configureAccordionComponent({initialValue: ['item-2'], multiExpandable: false});
configureAccordionComponent({initialExpandedPanels: ['item-2'], multiExpandable: false});
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(isTriggerExpanded(triggerElements[1])).toBeTrue();
expect(isTriggerExpanded(triggerElements[2])).toBeFalse();
Expand All @@ -221,16 +221,21 @@ describe('AccordionGroup', () => {
it('should collapse an item without affecting others', () => {
click(triggerElements[0]);
click(triggerElements[1]);
expect(groupInstance.value()).toEqual(jasmine.arrayWithExactContents(['item-1', 'item-2']));
expect(groupInstance.expandedPanels()).toEqual(
jasmine.arrayWithExactContents(['item-1', 'item-2']),
);

click(triggerElements[0]);
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(isTriggerExpanded(triggerElements[1])).toBeTrue();
expect(groupInstance.value()).toEqual(['item-2']);
expect(groupInstance.expandedPanels()).toEqual(['item-2']);
});

it('should allow setting initial multiple values', () => {
configureAccordionComponent({initialValue: ['item-1', 'item-3'], multiExpandable: true});
configureAccordionComponent({
initialExpandedPanels: ['item-1', 'item-3'],
multiExpandable: true,
});
expect(isTriggerExpanded(triggerElements[0])).toBeTrue();
expect(isTriggerExpanded(triggerElements[1])).toBeFalse();
expect(isTriggerExpanded(triggerElements[2])).toBeTrue();
Expand All @@ -242,15 +247,15 @@ describe('AccordionGroup', () => {
configureAccordionComponent({disabledItemValues: ['item-1']});
click(triggerElements[0]);
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(groupInstance.value()).toEqual([]);
expect(groupInstance.expandedPanels()).toEqual([]);
expect(triggerElements[0].getAttribute('aria-disabled')).toBe('true');
});

it('should not expand any trigger if group is disabled', () => {
configureAccordionComponent({disabledGroup: true});
click(triggerElements[0]);
expect(isTriggerExpanded(triggerElements[0])).toBeFalse();
expect(groupInstance.value()).toEqual([]);
expect(groupInstance.expandedPanels()).toEqual([]);
click(triggerElements[1]);
expect(isTriggerExpanded(triggerElements[1])).toBeFalse();
});
Expand Down Expand Up @@ -382,22 +387,22 @@ describe('AccordionGroup', () => {
template: `
<div
ngAccordionGroup
[(value)]="value"
[(expandedPanels)]="expandedPanels"
[multiExpandable]="multiExpandable()"
[disabled]="disabledGroup()"
[softDisabled]="softDisabled()"
[wrap]="wrap()"
>
@for (item of items(); track item.value) {
@for (item of items(); track item.panelId) {
<div class="item-container">
<button
ngAccordionTrigger
[value]="item.value"
[panelId]="item.panelId"
[disabled]="item.disabled"
>{{ item.header }}</button>
<div
ngAccordionPanel
[value]="item.value"
[panelId]="item.panelId"
>
<ng-template ngAccordionContent>
{{ item.content }}
Expand All @@ -411,20 +416,20 @@ describe('AccordionGroup', () => {
})
class AccordionGroupExample {
items = signal([
{value: 'item-1', header: 'Item 1 Header', content: 'Item 1 Content', disabled: false},
{value: 'item-2', header: 'Item 2 Header', content: 'Item 2 Content', disabled: false},
{value: 'item-3', header: 'Item 3 Header', content: 'Item 3 Content', disabled: false},
{panelId: 'item-1', header: 'Item 1 Header', content: 'Item 1 Content', disabled: false},
{panelId: 'item-2', header: 'Item 2 Header', content: 'Item 2 Content', disabled: false},
{panelId: 'item-3', header: 'Item 3 Header', content: 'Item 3 Content', disabled: false},
]);

value = model<string[]>([]);
expandedPanels = model<string[]>([]);
multiExpandable = signal(false);
disabledGroup = signal(false);
softDisabled = signal(true);
wrap = signal(false);

disableItem(itemValue: string, disabled: boolean) {
this.items.update(items =>
items.map(item => (item.value === itemValue ? {...item, disabled} : item)),
items.map(item => (item.panelId === itemValue ? {...item, disabled} : item)),
);
}
}
20 changes: 10 additions & 10 deletions src/aria/accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ export class AccordionPanel {
/** A global unique identifier for the panel. */
private readonly _id = inject(_IdGenerator).getId('accordion-trigger-', true);

/** A local unique identifier for the panel, used to match with its trigger's value. */
value = input.required<string>();
/** A local unique identifier for the panel, used to match with its trigger's `panelId`. */
panelId = input.required<string>();

/** The parent accordion trigger pattern that controls this panel. This is set by AccordionGroup. */
readonly accordionTrigger: WritableSignal<AccordionTriggerPattern | undefined> =
Expand All @@ -67,7 +67,7 @@ export class AccordionPanel {
/** The UI pattern instance for this panel. */
readonly _pattern: AccordionPanelPattern = new AccordionPanelPattern({
id: () => this._id,
value: this.value,
panelId: this.panelId,
accordionTrigger: () => this.accordionTrigger(),
});

Expand Down Expand Up @@ -111,8 +111,8 @@ export class AccordionTrigger {
/** The parent AccordionGroup. */
private readonly _accordionGroup = inject(AccordionGroup);

/** A local unique identifier for the trigger, used to match with its panel's value. */
value = input.required<string>();
/** A local unique identifier for the trigger, used to match with its panel's `panelId`. */
panelId = input.required<string>();

/** Whether the trigger is disabled. */
disabled = input(false, {transform: booleanAttribute});
Expand All @@ -130,7 +130,7 @@ export class AccordionTrigger {
/** The UI pattern instance for this trigger. */
readonly _pattern: AccordionTriggerPattern = new AccordionTriggerPattern({
id: () => this._id,
value: this.value,
panelId: this.panelId,
disabled: this.disabled,
element: () => this._elementRef.nativeElement,
accordionGroup: computed(() => this._accordionGroup._pattern),
Expand Down Expand Up @@ -168,8 +168,8 @@ export class AccordionGroup {
/** Whether multiple accordion items can be expanded simultaneously. */
multiExpandable = input(true, {transform: booleanAttribute});

/** The values of the current selected/expanded accordions. */
value = model<string[]>([]);
/** The ids of the current expanded accordion panels. */
expandedPanels = model<string[]>([]);

/** Whether to allow disabled items to receive focus. */
softDisabled = input(true, {transform: booleanAttribute});
Expand All @@ -184,7 +184,7 @@ export class AccordionGroup {
// `setDefaultState` in the CDK.
activeItem: signal(undefined),
items: computed(() => this._triggers().map(trigger => trigger._pattern)),
expandedIds: this.value,
expandedIds: this.expandedPanels,
// TODO(ok7sai): Investigate whether an accordion should support horizontal mode.
orientation: () => 'vertical',
element: () => this._elementRef.nativeElement,
Expand All @@ -197,7 +197,7 @@ export class AccordionGroup {
const panels = this._panels();

for (const trigger of triggers) {
const panel = panels.find(p => p.value() === trigger.value());
const panel = panels.find(p => p.panelId() === trigger.panelId());
trigger.accordionPanel.set(panel?._pattern);
if (panel) {
panel.accordionTrigger.set(trigger._pattern);
Expand Down
12 changes: 6 additions & 6 deletions src/aria/private/accordion/accordion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,23 +80,23 @@ describe('Accordion Pattern', () => {
id: signal('trigger-1-id'),
element: signal(createAccordionTriggerElement()),
disabled: signal(false),
value: signal('panel-1'), // Value should match the panel it controls
panelId: signal('panel-1'), // Value should match the panel it controls
},
{
accordionGroup: signal(groupPattern),
accordionPanel: signal(undefined),
id: signal('trigger-2-id'),
element: signal(createAccordionTriggerElement()),
disabled: signal(false),
value: signal('panel-2'),
panelId: signal('panel-2'),
},
{
accordionGroup: signal(groupPattern),
accordionPanel: signal(undefined),
id: signal('trigger-3-id'),
element: signal(createAccordionTriggerElement()),
disabled: signal(false),
value: signal('panel-3'),
panelId: signal('panel-3'),
},
];
triggerPatterns = [
Expand All @@ -111,17 +111,17 @@ describe('Accordion Pattern', () => {
panelInputs = [
{
id: signal('panel-1-id'),
value: signal('panel-1'),
panelId: signal('panel-1'),
accordionTrigger: signal(undefined),
},
{
id: signal('panel-2-id'),
value: signal('panel-2'),
panelId: signal('panel-2'),
accordionTrigger: signal(undefined),
},
{
id: signal('panel-3-id'),
value: signal('panel-3'),
panelId: signal('panel-3'),
accordionTrigger: signal(undefined),
},
];
Expand Down
14 changes: 7 additions & 7 deletions src/aria/private/accordion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ export class AccordionGroupPattern {
/** Inputs for the AccordionTriggerPattern. */
export type AccordionTriggerInputs = Omit<ListNavigationItem & ListFocusItem, 'index'> &
Omit<ExpansionItem, 'expansionId' | 'expandable'> & {
/** A local unique identifier for the trigger. */
value: SignalLike<string>;
/** A local unique identifier for the trigger's corresponding panel. */
panelId: SignalLike<string>;

/** The parent accordion group that controls this trigger. */
accordionGroup: SignalLike<AccordionGroupPattern>;
Expand Down Expand Up @@ -115,10 +115,10 @@ export class AccordionTriggerPattern {
constructor(readonly inputs: AccordionTriggerInputs) {
this.id = inputs.id;
this.element = inputs.element;
this.value = inputs.value;
this.panelId = inputs.panelId;
this.expansionControl = new ExpansionControl({
...inputs,
expansionId: inputs.value,
expansionId: inputs.panelId,
expandable: () => true,
expansionManager: inputs.accordionGroup().expansionManager,
});
Expand Down Expand Up @@ -203,8 +203,8 @@ export interface AccordionPanelInputs {
/** A global unique identifier for the panel. */
id: SignalLike<string>;

/** A local unique identifier for the panel, matching its trigger's value. */
value: SignalLike<string>;
/** A local unique identifier for the panel, matching its trigger's panelId. */
panelId: SignalLike<string>;

/** The parent accordion trigger that controls this panel. */
accordionTrigger: SignalLike<AccordionTriggerPattern | undefined>;
Expand All @@ -218,7 +218,7 @@ export class AccordionPanelPattern {

constructor(readonly inputs: AccordionPanelInputs) {
this.id = inputs.id;
this.value = inputs.value;
this.panelId = inputs.panelId;
this.accordionTrigger = inputs.accordionTrigger;
this.hidden = computed(() => inputs.accordionTrigger()?.expanded() === false);
}
Expand Down
Loading
Loading