Skip to content

Commit d913ebb

Browse files
committed
refactor(aria/tabs): simplify code by using template references instead of user id to link tabs and panels
1 parent 3da71ce commit d913ebb

30 files changed

+354
-614
lines changed

goldens/aria/private/index.api.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -682,14 +682,14 @@ export function signal<T>(initialValue: T): WritableSignalLike<T>;
682682
export type SignalLike<T> = () => T;
683683

684684
// @public
685-
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable'> {
685+
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable' | 'expanded'> {
686686
tablist: SignalLike<TabListPattern>;
687687
tabpanel: SignalLike<TabPanelPattern | undefined>;
688-
value: SignalLike<string>;
689688
}
690689

691690
// @public
692691
export interface TabListInputs extends Omit<ListNavigationInputs<TabPattern>, 'multi'>, Omit<ListExpansionInputs, 'multiExpandable' | 'items'> {
692+
selectedTab: WritableSignalLike<TabPattern | undefined>;
693693
selectionMode: SignalLike<'follow' | 'explicit'>;
694694
}
695695

@@ -709,7 +709,6 @@ export class TabListPattern {
709709
readonly nextKey: SignalLike<"ArrowRight" | "ArrowLeft" | "ArrowDown">;
710710
onKeydown(event: KeyboardEvent): void;
711711
onPointerdown(event: PointerEvent): void;
712-
open(value: string): boolean;
713712
open(tab?: TabPattern): boolean;
714713
readonly orientation: SignalLike<'vertical' | 'horizontal'>;
715714
readonly pointerdown: SignalLike<PointerEventManager<PointerEvent>>;
@@ -722,8 +721,6 @@ export class TabListPattern {
722721
// @public
723722
export interface TabPanelInputs extends LabelControlOptionalInputs {
724723
id: SignalLike<string>;
725-
tab: SignalLike<TabPattern | undefined>;
726-
value: SignalLike<string>;
727724
}
728725

729726
// @public
@@ -735,8 +732,8 @@ export class TabPanelPattern {
735732
readonly inputs: TabPanelInputs;
736733
readonly labelledBy: SignalLike<string | undefined>;
737734
readonly labelManager: LabelControl;
735+
readonly tab: WritableSignalLike<TabPattern | undefined>;
738736
readonly tabIndex: SignalLike<-1 | 0>;
739-
readonly value: SignalLike<string>;
740737
}
741738

742739
// @public
@@ -747,6 +744,7 @@ export class TabPattern {
747744
readonly disabled: SignalLike<boolean>;
748745
readonly element: SignalLike<HTMLElement>;
749746
readonly expandable: SignalLike<boolean>;
747+
// (undocumented)
750748
readonly expanded: WritableSignalLike<boolean>;
751749
readonly id: SignalLike<string>;
752750
readonly index: SignalLike<number>;
@@ -755,7 +753,6 @@ export class TabPattern {
755753
open(): boolean;
756754
readonly selected: SignalLike<boolean>;
757755
readonly tabIndex: SignalLike<0 | -1>;
758-
readonly value: SignalLike<string>;
759756
}
760757

761758
// @public

goldens/aria/tabs/index.api.md

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,25 @@
44
55
```ts
66

7+
import { AfterViewInit } from '@angular/core';
78
import * as _angular_cdk_bidi from '@angular/cdk/bidi';
89
import * as _angular_core from '@angular/core';
910
import { OnDestroy } from '@angular/core';
10-
import { OnInit } from '@angular/core';
1111

1212
// @public
13-
export class Tab implements HasElement, OnInit, OnDestroy {
13+
export class Tab implements AfterViewInit {
1414
readonly active: _angular_core.Signal<boolean>;
1515
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1616
readonly element: HTMLElement;
1717
readonly id: _angular_core.InputSignal<string>;
1818
// (undocumented)
19-
ngOnDestroy(): void;
20-
// (undocumented)
21-
ngOnInit(): void;
19+
ngAfterViewInit(): void;
2220
open(): void;
21+
readonly panel: _angular_core.InputSignal<TabPanel>;
2322
readonly _pattern: TabPattern;
2423
readonly selected: _angular_core.Signal<boolean>;
25-
readonly value: _angular_core.InputSignal<string>;
2624
// (undocumented)
27-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Tab, "[ngTab]", ["ngTab"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
25+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Tab, "[ngTab]", ["ngTab"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "panel": { "alias": "panel"; "required": true; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2826
// (undocumented)
2927
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Tab, never>;
3028
}
@@ -38,69 +36,41 @@ export class TabContent {
3836
}
3937

4038
// @public
41-
export class TabList implements OnInit, OnDestroy {
39+
export class TabList implements AfterViewInit {
4240
constructor();
4341
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4442
readonly element: HTMLElement;
4543
readonly focusMode: _angular_core.InputSignal<"roving" | "activedescendant">;
4644
// (undocumented)
47-
ngOnDestroy(): void;
48-
// (undocumented)
49-
ngOnInit(): void;
50-
// (undocumented)
51-
_onFocus(): void;
52-
open(value: string): boolean;
45+
ngAfterViewInit(): void;
5346
readonly orientation: _angular_core.InputSignal<"vertical" | "horizontal">;
5447
readonly _pattern: TabListPattern;
55-
// (undocumented)
56-
_register(child: Tab): void;
57-
readonly selectedTab: _angular_core.ModelSignal<string | undefined>;
48+
readonly selectedTab: _angular_core.WritableSignal<TabPattern | undefined>;
49+
readonly selectedTabIndex: _angular_core.ModelSignal<number>;
5850
readonly selectionMode: _angular_core.InputSignal<"follow" | "explicit">;
5951
readonly softDisabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
6052
readonly _tabPatterns: _angular_core.Signal<TabPattern[]>;
6153
readonly textDirection: _angular_core.WritableSignal<_angular_cdk_bidi.Direction>;
62-
// (undocumented)
63-
_unregister(child: Tab): void;
6454
readonly wrap: _angular_core.InputSignalWithTransform<boolean, unknown>;
6555
// (undocumented)
66-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabList, "[ngTabList]", ["ngTabList"], { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "wrap": { "alias": "wrap"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "focusMode": { "alias": "focusMode"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "selectedTab": { "alias": "selectedTab"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "selectedTab": "selectedTabChange"; }, never, never, true, never>;
56+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabList, "[ngTabList]", ["ngTabList"], { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "wrap": { "alias": "wrap"; "required": false; "isSignal": true; }; "softDisabled": { "alias": "softDisabled"; "required": false; "isSignal": true; }; "focusMode": { "alias": "focusMode"; "required": false; "isSignal": true; }; "selectionMode": { "alias": "selectionMode"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "selectedTabIndex": { "alias": "selectedTabIndex"; "required": false; "isSignal": true; }; }, { "selectedTabIndex": "selectedTabIndexChange"; }, ["_tabs"], never, true, never>;
6757
// (undocumented)
6858
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabList, never>;
6959
}
7060

7161
// @public
72-
export class TabPanel implements OnInit, OnDestroy {
62+
export class TabPanel {
7363
constructor();
7464
readonly element: HTMLElement;
7565
readonly id: _angular_core.InputSignal<string>;
76-
// (undocumented)
77-
ngOnDestroy(): void;
78-
// (undocumented)
79-
ngOnInit(): void;
8066
readonly _pattern: TabPanelPattern;
81-
readonly value: _angular_core.InputSignal<string>;
8267
readonly visible: _angular_core.Signal<boolean>;
8368
// (undocumented)
84-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabPanel, "[ngTabPanel]", ["ngTabPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
69+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TabPanel, "[ngTabPanel]", ["ngTabPanel"], { "id": { "alias": "id"; "required": false; "isSignal": true; }; }, {}, never, never, true, [{ directive: typeof DeferredContentAware; inputs: { "preserveContent": "preserveContent"; }; outputs: {}; }]>;
8570
// (undocumented)
8671
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabPanel, never>;
8772
}
8873

89-
// @public
90-
export class Tabs {
91-
readonly element: HTMLElement;
92-
// (undocumented)
93-
_register(child: TabList | TabPanel): void;
94-
readonly _tabPatterns: _angular_core.Signal<TabPattern[] | undefined>;
95-
readonly _unorderedTabpanelPatterns: _angular_core.Signal<TabPanelPattern[]>;
96-
// (undocumented)
97-
_unregister(child: TabList | TabPanel): void;
98-
// (undocumented)
99-
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<Tabs, "[ngTabs]", ["ngTabs"], {}, {}, never, never, true, never>;
100-
// (undocumented)
101-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Tabs, never>;
102-
}
103-
10474
// (No @packageDocumentation comment for this package)
10575

10676
```

src/aria/private/tabs/tabs.spec.ts

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ describe('Tabs Pattern', () => {
6565
softDisabled: signal(true),
6666
items: signal([]),
6767
element: signal(document.createElement('div')),
68+
selectedTab: signal(undefined),
6869
};
6970
tabListPattern = new TabListPattern(tabListInputs);
7071

@@ -76,26 +77,20 @@ describe('Tabs Pattern', () => {
7677
id: signal('tab-1-id'),
7778
element: signal(createTabElement()),
7879
disabled: signal(false),
79-
value: signal('tab-1'),
80-
expanded: signal(false),
8180
},
8281
{
8382
tablist: signal(tabListPattern),
8483
tabpanel: signal(undefined),
8584
id: signal('tab-2-id'),
8685
element: signal(createTabElement()),
8786
disabled: signal(false),
88-
value: signal('tab-2'),
89-
expanded: signal(false),
9087
},
9188
{
9289
tablist: signal(tabListPattern),
9390
tabpanel: signal(undefined),
9491
id: signal('tab-3-id'),
9592
element: signal(createTabElement()),
9693
disabled: signal(false),
97-
value: signal('tab-3'),
98-
expanded: signal(false),
9994
},
10095
];
10196
tabPatterns = [
@@ -108,18 +103,12 @@ describe('Tabs Pattern', () => {
108103
tabPanelInputs = [
109104
{
110105
id: signal('tabpanel-1-id'),
111-
tab: signal(undefined),
112-
value: signal('tab-1'),
113106
},
114107
{
115108
id: signal('tabpanel-2-id'),
116-
tab: signal(undefined),
117-
value: signal('tab-2'),
118109
},
119110
{
120111
id: signal('tabpanel-3-id'),
121-
tab: signal(undefined),
122-
value: signal('tab-3'),
123112
},
124113
];
125114
tabPanelPatterns = [
@@ -132,9 +121,9 @@ describe('Tabs Pattern', () => {
132121
tabInputs[0].tabpanel.set(tabPanelPatterns[0]);
133122
tabInputs[1].tabpanel.set(tabPanelPatterns[1]);
134123
tabInputs[2].tabpanel.set(tabPanelPatterns[2]);
135-
tabPanelInputs[0].tab.set(tabPatterns[0]);
136-
tabPanelInputs[1].tab.set(tabPatterns[1]);
137-
tabPanelInputs[2].tab.set(tabPatterns[2]);
124+
tabPanelPatterns[0].tab.set(tabPatterns[0]);
125+
tabPanelPatterns[1].tab.set(tabPatterns[1]);
126+
tabPanelPatterns[2].tab.set(tabPatterns[2]);
138127
tabListInputs.items.set(tabPatterns);
139128
tabListInputs.activeItem.set(tabPatterns[0]);
140129
});
@@ -143,8 +132,8 @@ describe('Tabs Pattern', () => {
143132
describe('#open', () => {
144133
it('should open a tab with value', () => {
145134
expect(tabListPattern.selectedTab()).toBeUndefined();
146-
tabListPattern.open('tab-1');
147-
expect(tabListPattern.selectedTab()!.value()).toBe('tab-1');
135+
tabListPattern.open(tabPatterns[0]);
136+
expect(tabListPattern.selectedTab()!).toBe(tabPatterns[0]);
148137
});
149138

150139
it('should open a tab with tab pattern instance', () => {

0 commit comments

Comments
 (0)