Skip to content

Commit 8ccc93f

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

31 files changed

+394
-629
lines changed

goldens/aria/private/index.api.md

Lines changed: 6 additions & 10 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'> {
686-
tablist: SignalLike<TabListPattern>;
687-
tabpanel: SignalLike<TabPanelPattern | undefined>;
688-
value: SignalLike<string>;
685+
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable' | 'expanded'> {
686+
tabList: SignalLike<TabListPattern>;
687+
tabPanel: SignalLike<TabPanelPattern | undefined>;
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,7 @@ export class TabListPattern {
722721
// @public
723722
export interface TabPanelInputs extends LabelControlOptionalInputs {
724723
id: SignalLike<string>;
725-
tab: SignalLike<TabPattern | undefined>;
726-
value: SignalLike<string>;
724+
readonly tab: SignalLike<TabPattern | undefined>;
727725
}
728726

729727
// @public
@@ -736,7 +734,6 @@ export class TabPanelPattern {
736734
readonly labelledBy: SignalLike<string | undefined>;
737735
readonly labelManager: LabelControl;
738736
readonly tabIndex: SignalLike<-1 | 0>;
739-
readonly value: SignalLike<string>;
740737
}
741738

742739
// @public
@@ -747,15 +744,14 @@ 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>;
752-
readonly index: SignalLike<number>;
753750
// (undocumented)
754751
readonly inputs: TabInputs;
755752
open(): boolean;
756753
readonly selected: SignalLike<boolean>;
757754
readonly tabIndex: SignalLike<0 | -1>;
758-
readonly value: SignalLike<string>;
759755
}
760756

761757
// @public

goldens/aria/tabs/index.api.md

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,26 @@
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';
11+
import { WritableSignal } from '@angular/core';
1112

1213
// @public
13-
export class Tab implements HasElement, OnInit, OnDestroy {
14+
export class Tab implements AfterViewInit {
1415
readonly active: _angular_core.Signal<boolean>;
1516
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1617
readonly element: HTMLElement;
1718
readonly id: _angular_core.InputSignal<string>;
1819
// (undocumented)
19-
ngOnDestroy(): void;
20-
// (undocumented)
21-
ngOnInit(): void;
20+
ngAfterViewInit(): void;
2221
open(): void;
22+
readonly panel: _angular_core.InputSignal<TabPanel>;
2323
readonly _pattern: TabPattern;
2424
readonly selected: _angular_core.Signal<boolean>;
25-
readonly value: _angular_core.InputSignal<string>;
2625
// (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>;
26+
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>;
2827
// (undocumented)
2928
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Tab, never>;
3029
}
@@ -38,69 +37,42 @@ export class TabContent {
3837
}
3938

4039
// @public
41-
export class TabList implements OnInit, OnDestroy {
40+
export class TabList implements AfterViewInit {
4241
constructor();
4342
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4443
readonly element: HTMLElement;
4544
readonly focusMode: _angular_core.InputSignal<"roving" | "activedescendant">;
4645
// (undocumented)
47-
ngOnDestroy(): void;
48-
// (undocumented)
49-
ngOnInit(): void;
50-
// (undocumented)
51-
_onFocus(): void;
52-
open(value: string): boolean;
46+
ngAfterViewInit(): void;
5347
readonly orientation: _angular_core.InputSignal<"vertical" | "horizontal">;
5448
readonly _pattern: TabListPattern;
55-
// (undocumented)
56-
_register(child: Tab): void;
57-
readonly selectedTab: _angular_core.ModelSignal<string | 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>;
67+
// (undocumented)
68+
readonly _tabPattern: WritableSignal<TabPattern | undefined>;
8269
readonly visible: _angular_core.Signal<boolean>;
8370
// (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: {}; }]>;
71+
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: {}; }]>;
8572
// (undocumented)
8673
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TabPanel, never>;
8774
}
8875

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-
10476
// (No @packageDocumentation comment for this package)
10577

10678
```

src/aria/private/behaviors/list-focus/list-focus.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ export interface ListFocusItem {
1818

1919
/** Whether an item is disabled. */
2020
disabled: SignalLike<boolean>;
21-
22-
/** The index of the item in the list. */
23-
index: SignalLike<number>;
2421
}
2522

2623
/** Represents the required inputs for a collection that contains focusable items. */

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

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -65,37 +65,32 @@ 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

7172
// Initiate a list of TabPatterns.
7273
tabInputs = [
7374
{
74-
tablist: signal(tabListPattern),
75-
tabpanel: signal(undefined),
75+
tabList: signal(tabListPattern),
76+
tabPanel: signal(undefined),
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
{
83-
tablist: signal(tabListPattern),
84-
tabpanel: signal(undefined),
82+
tabList: signal(tabListPattern),
83+
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
{
92-
tablist: signal(tabListPattern),
93-
tabpanel: signal(undefined),
89+
tabList: signal(tabListPattern),
90+
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 = [
@@ -109,17 +104,14 @@ describe('Tabs Pattern', () => {
109104
{
110105
id: signal('tabpanel-1-id'),
111106
tab: signal(undefined),
112-
value: signal('tab-1'),
113107
},
114108
{
115109
id: signal('tabpanel-2-id'),
116110
tab: signal(undefined),
117-
value: signal('tab-2'),
118111
},
119112
{
120113
id: signal('tabpanel-3-id'),
121114
tab: signal(undefined),
122-
value: signal('tab-3'),
123115
},
124116
];
125117
tabPanelPatterns = [
@@ -129,9 +121,9 @@ describe('Tabs Pattern', () => {
129121
];
130122

131123
// Binding between tabs and tabpanels.
132-
tabInputs[0].tabpanel.set(tabPanelPatterns[0]);
133-
tabInputs[1].tabpanel.set(tabPanelPatterns[1]);
134-
tabInputs[2].tabpanel.set(tabPanelPatterns[2]);
124+
tabInputs[0].tabPanel.set(tabPanelPatterns[0]);
125+
tabInputs[1].tabPanel.set(tabPanelPatterns[1]);
126+
tabInputs[2].tabPanel.set(tabPanelPatterns[2]);
135127
tabPanelInputs[0].tab.set(tabPatterns[0]);
136128
tabPanelInputs[1].tab.set(tabPatterns[1]);
137129
tabPanelInputs[2].tab.set(tabPatterns[2]);
@@ -143,8 +135,8 @@ describe('Tabs Pattern', () => {
143135
describe('#open', () => {
144136
it('should open a tab with value', () => {
145137
expect(tabListPattern.selectedTab()).toBeUndefined();
146-
tabListPattern.open('tab-1');
147-
expect(tabListPattern.selectedTab()!.value()).toBe('tab-1');
138+
tabListPattern.open(tabPatterns[0]);
139+
expect(tabListPattern.selectedTab()!).toBe(tabPatterns[0]);
148140
});
149141

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

0 commit comments

Comments
 (0)