Skip to content

Commit 96f2cbb

Browse files
committed
refactor(aria/tabs): simplify code by using template references instead of user id to link tabs and panels
1 parent 5b010dc commit 96f2cbb

30 files changed

+393
-625
lines changed

goldens/aria/private/index.api.md

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -664,14 +664,14 @@ export function signal<T>(initialValue: T): WritableSignalLike<T>;
664664
export type SignalLike<T> = () => T;
665665

666666
// @public
667-
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable'> {
668-
tablist: SignalLike<TabListPattern>;
669-
tabpanel: SignalLike<TabPanelPattern | undefined>;
670-
value: SignalLike<string>;
667+
export interface TabInputs extends Omit<ListNavigationItem, 'index'>, Omit<ExpansionItem, 'expandable' | 'expanded'> {
668+
tabList: SignalLike<TabListPattern>;
669+
tabPanel: SignalLike<TabPanelPattern | undefined>;
671670
}
672671

673672
// @public
674673
export interface TabListInputs extends Omit<ListNavigationInputs<TabPattern>, 'multi'>, Omit<ListExpansionInputs, 'multiExpandable' | 'items'> {
674+
selectedTab: WritableSignalLike<TabPattern | undefined>;
675675
selectionMode: SignalLike<'follow' | 'explicit'>;
676676
}
677677

@@ -691,7 +691,6 @@ export class TabListPattern {
691691
readonly nextKey: SignalLike<"ArrowRight" | "ArrowLeft" | "ArrowDown">;
692692
onKeydown(event: KeyboardEvent): void;
693693
onPointerdown(event: PointerEvent): void;
694-
open(value: string): boolean;
695694
open(tab?: TabPattern): boolean;
696695
readonly orientation: SignalLike<'vertical' | 'horizontal'>;
697696
readonly pointerdown: SignalLike<PointerEventManager<PointerEvent>>;
@@ -704,8 +703,7 @@ export class TabListPattern {
704703
// @public
705704
export interface TabPanelInputs extends LabelControlOptionalInputs {
706705
id: SignalLike<string>;
707-
tab: SignalLike<TabPattern | undefined>;
708-
value: SignalLike<string>;
706+
readonly tab: SignalLike<TabPattern | undefined>;
709707
}
710708

711709
// @public
@@ -718,7 +716,6 @@ export class TabPanelPattern {
718716
readonly labelledBy: SignalLike<string | undefined>;
719717
readonly labelManager: LabelControl;
720718
readonly tabIndex: SignalLike<-1 | 0>;
721-
readonly value: SignalLike<string>;
722719
}
723720

724721
// @public
@@ -729,15 +726,14 @@ export class TabPattern {
729726
readonly disabled: SignalLike<boolean>;
730727
readonly element: SignalLike<HTMLElement>;
731728
readonly expandable: SignalLike<boolean>;
729+
// (undocumented)
732730
readonly expanded: WritableSignalLike<boolean>;
733731
readonly id: SignalLike<string>;
734-
readonly index: SignalLike<number>;
735732
// (undocumented)
736733
readonly inputs: TabInputs;
737734
open(): boolean;
738735
readonly selected: SignalLike<boolean>;
739736
readonly tabIndex: SignalLike<0 | -1>;
740-
readonly value: SignalLike<string>;
741737
}
742738

743739
// @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/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)