Skip to content

Commit d754884

Browse files
authored
Merge branch 'main' into aria-methods
2 parents e7a1a13 + b4d11cb commit d754884

File tree

12 files changed

+120
-44
lines changed

12 files changed

+120
-44
lines changed

goldens/cdk/overlay/index.api.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ export class Overlay {
378378
static ɵprov: i0.ɵɵInjectableDeclaration<Overlay>;
379379
}
380380

381+
// @public
382+
export const OVERLAY_DEFAULT_CONFIG: InjectionToken<OverlayDefaultConfig>;
383+
381384
// @public
382385
export class OverlayConfig {
383386
constructor(config?: OverlayConfig);
@@ -428,6 +431,12 @@ export class OverlayContainer implements OnDestroy {
428431
static ɵprov: i0.ɵɵInjectableDeclaration<OverlayContainer>;
429432
}
430433

434+
// @public
435+
export interface OverlayDefaultConfig {
436+
// (undocumented)
437+
usePopover?: boolean;
438+
}
439+
431440
// @public
432441
export class OverlayKeyboardDispatcher extends BaseOverlayDispatcher {
433442
add(overlayRef: OverlayRef): void;

src/aria/accordion/accordion.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ import {
4747
'role': 'region',
4848
'[attr.id]': '_pattern.id()',
4949
'[attr.aria-labelledby]': '_pattern.accordionTrigger()?.id()',
50-
'[attr.inert]': '_pattern.hidden() ? true : null',
50+
'[attr.inert]': '!visible() ? true : null',
5151
},
5252
})
5353
export class AccordionPanel {
@@ -60,6 +60,9 @@ export class AccordionPanel {
6060
/** A local unique identifier for the panel, used to match with its trigger's value. */
6161
value = input.required<string>();
6262

63+
/** Whether the accordion panel is visible. True if the associated trigger is expanded. */
64+
readonly visible = computed(() => !this._pattern.hidden());
65+
6366
/** The parent accordion trigger pattern that controls this panel. This is set by AccordionGroup. */
6467
readonly accordionTrigger: WritableSignal<AccordionTriggerPattern | undefined> =
6568
signal(undefined);
@@ -74,7 +77,7 @@ export class AccordionPanel {
7477
constructor() {
7578
// Connect the panel's hidden state to the DeferredContentAware's visibility.
7679
afterRenderEffect(() => {
77-
this._deferredContentAware.contentVisible.set(!this._pattern.hidden());
80+
this._deferredContentAware.contentVisible.set(this.visible());
7881
});
7982
}
8083

@@ -103,10 +106,10 @@ export class AccordionPanel {
103106
exportAs: 'ngAccordionTrigger',
104107
host: {
105108
'class': 'ng-accordion-trigger',
106-
'[attr.data-active]': '_pattern.active()',
109+
'[attr.data-active]': 'active()',
107110
'role': 'button',
108111
'[id]': '_pattern.id()',
109-
'[attr.aria-expanded]': '_pattern.expanded()',
112+
'[attr.aria-expanded]': 'expanded()',
110113
'[attr.aria-controls]': '_pattern.controls()',
111114
'[attr.aria-disabled]': '_pattern.disabled()',
112115
'[attr.disabled]': 'hardDisabled() ? true : null',
@@ -132,6 +135,12 @@ export class AccordionTrigger {
132135
/** Whether the trigger is disabled. */
133136
disabled = input(false, {transform: booleanAttribute});
134137

138+
/** Whether the trigger is active. */
139+
readonly active = computed(() => this._pattern.active());
140+
141+
/** Whether the trigger is expanded. */
142+
readonly expanded = computed(() => this._pattern.expanded());
143+
135144
/**
136145
* Whether this trigger is completely inaccessible.
137146
*

src/aria/combobox/combobox.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,6 @@ export class Combobox<V> {
6969
/** The filter mode for the combobox. */
7070
filterMode = input<'manual' | 'auto-select' | 'highlight'>('manual');
7171

72-
/** Whether the combobox is focused. */
73-
readonly isFocused = signal(false);
74-
75-
/** Whether the combobox has received focus yet. */
76-
private _hasBeenFocused = signal(false);
77-
7872
/** Whether the combobox is disabled. */
7973
readonly disabled = input(false);
8074

@@ -122,12 +116,6 @@ export class Combobox<V> {
122116
this._deferredContentAware?.contentVisible.set(true);
123117
}
124118
});
125-
126-
afterRenderEffect(() => {
127-
if (!this._hasBeenFocused() && this._pattern.isFocused()) {
128-
this._hasBeenFocused.set(true);
129-
}
130-
});
131119
}
132120

133121
/** Opens the combobox to the selected item. */

src/aria/menu/menu.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ import {Directionality} from '@angular/cdk/bidi';
4545
host: {
4646
'class': 'ng-menu-trigger',
4747
'[attr.tabindex]': '_pattern.tabIndex()',
48-
'[attr.aria-haspopup]': '_pattern.hasPopup()',
49-
'[attr.aria-expanded]': '_pattern.expanded()',
48+
'[attr.aria-haspopup]': 'hasPopup()',
49+
'[attr.aria-expanded]': 'expanded()',
5050
'[attr.aria-controls]': '_pattern.menu()?.id()',
5151
'(click)': '_pattern.onClick()',
5252
'(keydown)': '_pattern.onKeydown($event)',
5353
'(focusout)': '_pattern.onFocusOut($event)',
54-
'(focusin)': 'onFocusIn()',
54+
'(focusin)': '_pattern.onFocusIn()',
5555
},
5656
})
5757
export class MenuTrigger<V> {
@@ -67,8 +67,11 @@ export class MenuTrigger<V> {
6767
/** The menu associated with the trigger. */
6868
menu = input<Menu<V> | undefined>(undefined);
6969

70-
/** Whether the menu item has been focused. */
71-
readonly hasBeenFocused = signal(false);
70+
/** Whether the menu is expanded. */
71+
readonly expanded = computed(() => this._pattern.expanded());
72+
73+
/** Whether the menu trigger has a popup. */
74+
readonly hasPopup = computed(() => this._pattern.hasPopup());
7275

7376
/** The menu trigger ui pattern instance. */
7477
_pattern: MenuTriggerPattern<V> = new MenuTriggerPattern({
@@ -120,7 +123,7 @@ export class MenuTrigger<V> {
120123
'role': 'menu',
121124
'class': 'ng-menu',
122125
'[attr.id]': '_pattern.id()',
123-
'[attr.data-visible]': '_pattern.isVisible()',
126+
'[attr.data-visible]': 'isVisible()',
124127
'(keydown)': '_pattern.onKeydown($event)',
125128
'(mouseover)': '_pattern.onMouseOver($event)',
126129
'(mouseout)': '_pattern.onMouseOut($event)',
@@ -181,7 +184,7 @@ export class Menu<V> {
181184
readonly items = () => this._items().map(i => i._pattern);
182185

183186
/** Whether the menu is visible. */
184-
isVisible = computed(() => this._pattern.isVisible());
187+
readonly isVisible = computed(() => this._pattern.isVisible());
185188

186189
/** A callback function triggered when a menu item is selected. */
187190
onSelect = output<V>();
@@ -209,7 +212,7 @@ export class Menu<V> {
209212
this._deferredContentAware?.contentVisible.set(true);
210213
} else {
211214
this._deferredContentAware?.contentVisible.set(
212-
this._pattern.isVisible() || !!this.parent()?.hasBeenFocused(),
215+
this._pattern.isVisible() || !!this.parent()?._pattern.hasBeenFocused(),
213216
);
214217
}
215218
});
@@ -333,11 +336,11 @@ export class MenuBar<V> {
333336
host: {
334337
'role': 'menuitem',
335338
'class': 'ng-menu-item',
336-
'(focusin)': 'onFocusIn()',
339+
'(focusin)': '_pattern.onFocusIn()',
337340
'[attr.tabindex]': '_pattern.tabIndex()',
338-
'[attr.data-active]': '_pattern.isActive()',
339-
'[attr.aria-haspopup]': '_pattern.hasPopup()',
340-
'[attr.aria-expanded]': '_pattern.expanded()',
341+
'[attr.data-active]': 'isActive()',
342+
'[attr.aria-haspopup]': 'hasPopup()',
343+
'[attr.aria-expanded]': 'expanded()',
341344
'[attr.aria-disabled]': '_pattern.disabled()',
342345
'[attr.aria-controls]': '_pattern.submenu()?.id()',
343346
},
@@ -375,8 +378,14 @@ export class MenuItem<V> {
375378
/** The submenu associated with the menu item. */
376379
readonly submenu = input<Menu<V> | undefined>(undefined);
377380

378-
/** Whether the menu item has been focused. */
379-
readonly hasBeenFocused = signal(false);
381+
/** Whether the menu item is active. */
382+
readonly isActive = computed(() => this._pattern.isActive());
383+
384+
/** Whether the menu is expanded. */
385+
readonly expanded = computed(() => this._pattern.expanded());
386+
387+
/** Whether the menu item has a popup. */
388+
readonly hasPopup = computed(() => this._pattern.hasPopup());
380389

381390
/** The menu item ui pattern instance. */
382391
readonly _pattern: MenuItemPattern<V> = new MenuItemPattern<V>({

src/aria/private/menu/menu.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ export class MenuTriggerPattern<V> {
583583
/** Whether the menu is expanded. */
584584
expanded = signal(false);
585585

586+
/** Whether the menu trigger has received focus. */
587+
hasBeenFocused = signal(false);
588+
586589
/** The role of the menu trigger. */
587590
role = () => 'button';
588591

@@ -619,6 +622,11 @@ export class MenuTriggerPattern<V> {
619622
this.expanded() ? this.close() : this.open({first: true});
620623
}
621624

625+
/** Handles focusin events for the menu trigger. */
626+
onFocusIn() {
627+
this.hasBeenFocused.set(true);
628+
}
629+
622630
/** Handles focusout events for the menu trigger. */
623631
onFocusOut(event: FocusEvent) {
624632
const element = this.inputs.element();
@@ -684,6 +692,9 @@ export class MenuItemPattern<V> implements ListItem<V> {
684692
/** Whether the menu item is active. */
685693
isActive = computed(() => this.inputs.parent()?.inputs.activeItem() === this);
686694

695+
/** Whether the menu item has received focus. */
696+
hasBeenFocused = signal(false);
697+
687698
/** The tab index of the menu item. */
688699
tabIndex = computed(() => {
689700
if (this.submenu() && this.submenu()?.inputs.activeItem()) {
@@ -761,4 +772,9 @@ export class MenuItemPattern<V> implements ListItem<V> {
761772
}
762773
}
763774
}
775+
776+
/** Handles focusin events for the menu item. */
777+
onFocusIn() {
778+
this.hasBeenFocused.set(true);
779+
}
764780
}

src/aria/private/tree/tree.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ export class TreePattern<V> {
185185
/** The root is always expanded. */
186186
readonly expanded = () => true;
187187

188-
/** The roow is always visible. */
188+
/** The root is always visible. */
189189
readonly visible = () => true;
190190

191191
/** The tab index of the tree. */

src/aria/tabs/tabs.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -241,10 +241,10 @@ export class TabList implements OnInit, OnDestroy {
241241
host: {
242242
'role': 'tab',
243243
'class': 'ng-tab',
244-
'[attr.data-active]': '_pattern.active()',
244+
'[attr.data-active]': 'active()',
245245
'[attr.id]': '_pattern.id()',
246246
'[attr.tabindex]': '_pattern.tabIndex()',
247-
'[attr.aria-selected]': '_pattern.selected()',
247+
'[attr.aria-selected]': 'selected()',
248248
'[attr.aria-disabled]': '_pattern.disabled()',
249249
'[attr.aria-controls]': '_pattern.controls()',
250250
},
@@ -279,6 +279,15 @@ export class Tab implements HasElement, OnInit, OnDestroy {
279279
/** A local unique identifier for the tab. */
280280
readonly value = input.required<string>();
281281

282+
/** Whether the tab is active. */
283+
readonly active = computed(() => this._pattern.active());
284+
285+
/** Whether the tab is expanded. */
286+
readonly expanded = computed(() => this._pattern.expanded());
287+
288+
/** Whether the tab is selected. */
289+
readonly selected = computed(() => this._pattern.selected());
290+
282291
/** The Tab UIPattern. */
283292
readonly _pattern: TabPattern = new TabPattern({
284293
...this,
@@ -318,7 +327,7 @@ export class Tab implements HasElement, OnInit, OnDestroy {
318327
'class': 'ng-tabpanel',
319328
'[attr.id]': '_pattern.id()',
320329
'[attr.tabindex]': '_pattern.tabIndex()',
321-
'[attr.inert]': '_pattern.hidden() ? true : null',
330+
'[attr.inert]': '!visible() ? true : null',
322331
'[attr.aria-labelledby]': '_pattern.labelledBy()',
323332
},
324333
hostDirectives: [
@@ -344,6 +353,9 @@ export class TabPanel implements OnInit, OnDestroy {
344353
/** A local unique identifier for the tabpanel. */
345354
readonly value = input.required<string>();
346355

356+
/** Whether the tab panel is visible. */
357+
readonly visible = computed(() => !this._pattern.hidden());
358+
347359
/** The TabPanel UIPattern. */
348360
readonly _pattern: TabPanelPattern = new TabPanelPattern({
349361
...this,
@@ -352,7 +364,7 @@ export class TabPanel implements OnInit, OnDestroy {
352364
});
353365

354366
constructor() {
355-
afterRenderEffect(() => this._deferredContentAware.contentVisible.set(!this._pattern.hidden()));
367+
afterRenderEffect(() => this._deferredContentAware.contentVisible.set(this.visible()));
356368
}
357369

358370
ngOnInit() {

src/aria/toolbar/toolbar.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export class Toolbar<V> {
168168
exportAs: 'ngToolbarWidget',
169169
host: {
170170
'class': 'ng-toolbar-widget',
171-
'[attr.data-active]': '_pattern.active()',
171+
'[attr.data-active]': 'active()',
172172
'[attr.tabindex]': '_pattern.tabIndex()',
173173
'[attr.inert]': 'hardDisabled() ? true : null',
174174
'[attr.disabled]': 'hardDisabled() ? true : null',

src/aria/tree/tree.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,14 @@ export class Tree<V> {
220220
exportAs: 'ngTreeItem',
221221
host: {
222222
'class': 'ng-treeitem',
223-
'[attr.data-active]': '_pattern.active()',
223+
'[attr.data-active]': 'active()',
224224
'role': 'treeitem',
225225
'[id]': '_pattern.id()',
226-
'[attr.aria-expanded]': '_pattern.expandable() ? _pattern.expanded() : null',
227-
'[attr.aria-selected]': '_pattern.selected()',
226+
'[attr.aria-expanded]': 'expanded()',
227+
'[attr.aria-selected]': 'selected()',
228228
'[attr.aria-current]': '_pattern.current()',
229229
'[attr.aria-disabled]': '_pattern.disabled()',
230-
'[attr.aria-level]': '_pattern.level()',
230+
'[attr.aria-level]': 'level()',
231231
'[attr.aria-setsize]': '_pattern.setsize()',
232232
'[attr.aria-posinset]': '_pattern.posinset()',
233233
'[attr.tabindex]': '_pattern.tabIndex()',
@@ -272,6 +272,23 @@ export class TreeItem<V> extends DeferredContentAware implements OnInit, OnDestr
272272
return (this.parent() as TreeItemGroup<V>).ownedBy().tree();
273273
});
274274

275+
/** Whether the item is active. */
276+
readonly active = computed(() => this._pattern.active());
277+
278+
/** Whether this item is currently expanded, returning null if not expandable. */
279+
readonly expanded = computed(() =>
280+
this._pattern.expandable() ? this._pattern.expanded() : null,
281+
);
282+
283+
/** The level of the current item in a tree. */
284+
readonly level = computed(() => this._pattern.level());
285+
286+
/** Whether the item is selected. */
287+
readonly selected = computed(() => this._pattern.selected());
288+
289+
/** Whether this item is visible due to all of its parents being expanded. */
290+
readonly visible = computed(() => this._pattern.visible());
291+
275292
/** The UI pattern for this item. */
276293
_pattern: TreeItemPattern<V>;
277294

src/cdk/overlay/overlay-directives.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import {
2929
import {_getEventTarget} from '../platform';
3030
import {Subscription} from 'rxjs';
3131
import {takeWhile} from 'rxjs/operators';
32-
import {createOverlayRef} from './overlay';
32+
import {createOverlayRef, OVERLAY_DEFAULT_CONFIG} from './overlay';
3333
import {OverlayConfig} from './overlay-config';
3434
import {OverlayRef} from './overlay-ref';
3535
import {ConnectedOverlayPositionChange, ViewportMargin} from './position/connected-position';
@@ -253,7 +253,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
253253

254254
/** Whether the connected overlay should be rendered inside a popover element or the overlay container. */
255255
@Input({alias: 'cdkConnectedOverlayUsePopover'})
256-
usePopover: FlexibleOverlayPopoverLocation | null = 'global';
256+
usePopover: FlexibleOverlayPopoverLocation | null;
257257

258258
/** Whether the overlay should match the trigger's width. */
259259
@Input({alias: 'cdkConnectedOverlayMatchWidth', transform: booleanAttribute})
@@ -293,7 +293,9 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges {
293293
const templateRef = inject<TemplateRef<any>>(TemplateRef);
294294
const viewContainerRef = inject(ViewContainerRef);
295295
const defaultConfig = inject(CDK_CONNECTED_OVERLAY_DEFAULT_CONFIG, {optional: true});
296+
const globalConfig = inject(OVERLAY_DEFAULT_CONFIG, {optional: true});
296297

298+
this.usePopover = globalConfig?.usePopover === false ? null : 'global';
297299
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
298300
this.scrollStrategy = this._scrollStrategyFactory();
299301

0 commit comments

Comments
 (0)