Skip to content

Commit

Permalink
refactor(material/tree): docs updates and rename variables (#28238)
Browse files Browse the repository at this point in the history
Make miscelaneous fixes to documentation. Rename variables. Responding
to PR feedback.

 * remove comment about API contract of CdkTreeNodeToggle
 * rename CdkTree#_groups to CdkTree#_ariaSets
 * move documentation about default key manager configuration from
   TreeKeyManagerStrategy interface to TreeKeyManager class.
 * add JSDoc style comments for NoopTreeKeyManager
  • Loading branch information
zarend authored Dec 6, 2023
1 parent 4bc94e0 commit ab5cb56
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 43 deletions.
21 changes: 21 additions & 0 deletions src/cdk/a11y/key-manager/noop-tree-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import {
/**
* @docs-private
*
* Opt-out of Tree of key manager behavior.
*
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
* - Tree does not respond to keyboard interaction
* - Tree node allows tabindex to be set by Input binding
* - Tree node allows tabindex to be set by attribute binding
*
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
* TreeKeyManagerStrategy instead. To be removed in a future version.
*
Expand Down Expand Up @@ -62,6 +69,13 @@ export class NoopTreeKeyManager<T extends TreeKeyManagerItem> implements TreeKey
/**
* @docs-private
*
* Opt-out of Tree of key manager behavior.
*
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
* - Tree does not respond to keyboard interaction
* - Tree node allows tabindex to be set by Input binding
* - Tree node allows tabindex to be set by attribute binding
*
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
* TreeKeyManagerStrategy instead. To be removed in a future version.
*
Expand All @@ -76,6 +90,13 @@ export function NOOP_TREE_KEY_MANAGER_FACTORY<
/**
* @docs-private
*
* Opt-out of Tree of key manager behavior.
*
* When provided, Tree has same focus management behavior as before TreeKeyManager was introduced.
* - Tree does not respond to keyboard interaction
* - Tree node allows tabindex to be set by Input binding
* - Tree node allows tabindex to be set by attribute binding
*
* @deprecated NoopTreeKeyManager deprecated. Use TreeKeyManager or inject a
* TreeKeyManagerStrategy instead. To be removed in a future version.
*
Expand Down
11 changes: 5 additions & 6 deletions src/cdk/a11y/key-manager/tree-key-manager-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,20 @@ export interface TreeKeyManagerItem {
export interface TreeKeyManagerOptions<T extends TreeKeyManagerItem> {
/**
* If true, then the key manager will call `activate` in addition to calling `focus` when a
* particular item is focused. By default, this is false.
* particular item is focused.
*/
activationFollowsFocus?: boolean;
shouldActivationFollowFocus?: boolean;

/**
* The direction in which the tree items are laid out horizontally. This influences which key
* will be interpreted as expand or collapse. Defaults to 'ltr'.
* will be interpreted as expand or collapse.
*/
horizontalOrientation?: 'rtl' | 'ltr';

/**
* Sets the predicate function that determines which items should be skipped by the tree key
* manager. By default, disabled items are skipped.
* If provided, navigation "skips" over items that pass the given predicate.
*
* If the item is to be skipped, this function should return false.
* If the item is to be skipped, predicate function should return false.
*/
skipPredicate?: (item: T) => boolean;

Expand Down
29 changes: 20 additions & 9 deletions src/cdk/a11y/key-manager/tree-key-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ function coerceObservable<T>(data: T | Observable<T>): Observable<T> {
export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyManagerStrategy<T> {
private _activeItemIndex = -1;
private _activeItem: T | null = null;
private _activationFollowsFocus = false;
private _horizontal: 'ltr' | 'rtl' = 'ltr';
private _shouldActivationFollowFocus = false;
private _horizontalOrientation: 'ltr' | 'rtl' = 'ltr';

// Keep tree items focusable when disabled. Align with
// https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#focusabilityofdisabledcontrols.
Expand Down Expand Up @@ -85,6 +85,14 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
this._hasInitialFocused = true;
}

/**
*
* @param items List of TreeKeyManager options. Can be synchronous or asynchronous.
* @param config Optional configuration options. By default, use 'ltr' horizontal orientation. By
* default, do not skip any nodes. By default, key manager only calls `focus` method when items
* are focused and does not call `activate`. If `typeaheadDefaultInterval` is `true`, use a
* default interval of 200ms.
*/
constructor(items: Observable<T[]> | QueryList<T> | T[], config: TreeKeyManagerOptions<T>) {
// We allow for the items to be an array or Observable because, in some cases, the consumer may
// not have access to a QueryList of the items they want to manage (e.g. when the
Expand All @@ -109,11 +117,11 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
this._initialFocus();
}

if (typeof config.activationFollowsFocus === 'boolean') {
this._activationFollowsFocus = config.activationFollowsFocus;
if (typeof config.shouldActivationFollowFocus === 'boolean') {
this._shouldActivationFollowFocus = config.shouldActivationFollowFocus;
}
if (config.horizontalOrientation) {
this._horizontal = config.horizontalOrientation;
this._horizontalOrientation = config.horizontalOrientation;
}
if (config.skipPredicate) {
this._skipPredicateFn = config.skipPredicate;
Expand Down Expand Up @@ -157,11 +165,15 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
break;

case RIGHT_ARROW:
this._horizontal === 'rtl' ? this._collapseCurrentItem() : this._expandCurrentItem();
this._horizontalOrientation === 'rtl'
? this._collapseCurrentItem()
: this._expandCurrentItem();
break;

case LEFT_ARROW:
this._horizontal === 'rtl' ? this._expandCurrentItem() : this._collapseCurrentItem();
this._horizontalOrientation === 'rtl'
? this._expandCurrentItem()
: this._collapseCurrentItem();
break;

case HOME:
Expand Down Expand Up @@ -264,11 +276,10 @@ export class TreeKeyManager<T extends TreeKeyManagerItem> implements TreeKeyMana
previousActiveItem?.unfocus();

if (options.emitChangeEvent) {
// Emit to `change` stream as required by TreeKeyManagerStrategy interface.
this.change.next(this._activeItem);
}

if (this._activationFollowsFocus) {
if (this._shouldActivationFollowFocus) {
this._activateCurrentItem();
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/cdk/tree/toggle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ import {CdkTree, CdkTreeNode} from './tree';

/**
* Node toggle to expand and collapse the node.
*
* CdkTreeNodeToggle is intended only to be used on native button elements, elements with button role,
* or elements with treeitem role.
*/
@Directive({
selector: '[cdkTreeNodeToggle]',
Expand Down
46 changes: 22 additions & 24 deletions src/cdk/tree/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,14 @@ export class CdkTree<T, K = T>
private _parents: Map<K, T | null> = new Map<K, T | null>();

/**
* The internal node groupings for each node; we use this to determine where
* a particular node is within each group. This allows us to compute the
* correct aria attribute values.
* Nodes grouped into each set, which is a list of nodes displayed together in the DOM.
*
* The structure of this is that:
* - the outer index is the level
* - the inner index is the parent node for this particular group. If there is no parent node, we
* use `null`.
* Lookup key is the parent of a set. Root nodes have key of null.
*
* Values is a 'set' of tree nodes. Each tree node maps to a treeitem element. Sets are in the
* order that it is rendered. Each set maps directly to aria-posinset and aria-setsize attributes.
*/
private _groups: Map<K | null, T[]> = new Map<K | null, T[]>();
private _ariaSets: Map<K | null, T[]> = new Map<K | null, T[]>();

/**
* Provides a stream containing the latest data array to render. Influenced by the tree's
Expand Down Expand Up @@ -500,10 +498,10 @@ export class CdkTree<T, K = T>
this.insertNode(data[currentIndex!], currentIndex!, viewContainer, parentData);
} else if (currentIndex == null) {
viewContainer.remove(adjustedPreviousIndex!);
const group = this._getNodeGroup(item.item);
const set = this._getAriaSet(item.item);
const key = this._getExpansionKey(item.item);
group.splice(
group.findIndex(groupItem => this._getExpansionKey(groupItem) === key),
set.splice(
set.findIndex(groupItem => this._getExpansionKey(groupItem) === key),
1,
);
} else {
Expand Down Expand Up @@ -784,8 +782,8 @@ export class CdkTree<T, K = T>
* This is intended to be used for `aria-setsize`.
*/
_getSetSize(dataNode: T) {
const group = this._getNodeGroup(dataNode);
return group.length;
const set = this._getAriaSet(dataNode);
return set.length;
}

/**
Expand All @@ -794,9 +792,9 @@ export class CdkTree<T, K = T>
* This is intended to be used for `aria-posinset`.
*/
_getPositionInSet(dataNode: T) {
const group = this._getNodeGroup(dataNode);
const set = this._getAriaSet(dataNode);
const key = this._getExpansionKey(dataNode);
return group.findIndex(node => this._getExpansionKey(node) === key) + 1;
return set.findIndex(node => this._getExpansionKey(node) === key) + 1;
}

/** Given a CdkTreeNode, gets the node that renders that node's parent's data. */
Expand Down Expand Up @@ -902,12 +900,12 @@ export class CdkTree<T, K = T>
return this.expansionKey?.(dataNode) ?? (dataNode as unknown as K);
}

private _getNodeGroup(node: T) {
private _getAriaSet(node: T) {
const key = this._getExpansionKey(node);
const parent = this._parents.get(key);
const parentKey = parent ? this._getExpansionKey(parent) : null;
const group = this._groups.get(parentKey);
return group ?? [node];
const set = this._ariaSets.get(parentKey);
return set ?? [node];
}

/**
Expand Down Expand Up @@ -963,7 +961,7 @@ export class CdkTree<T, K = T>
children.pipe(
take(1),
tap(childNodes => {
this._groups.set(parentKey, [...(childNodes ?? [])]);
this._ariaSets.set(parentKey, [...(childNodes ?? [])]);
for (const child of childNodes ?? []) {
const childKey = this._getExpansionKey(child);
this._parents.set(childKey, node);
Expand Down Expand Up @@ -1006,7 +1004,7 @@ export class CdkTree<T, K = T>
// nested.
if (this.childrenAccessor && nodeType === 'flat') {
// This flattens children into a single array.
this._groups.set(null, [...nodes]);
this._ariaSets.set(null, [...nodes]);
return this._flattenNestedNodesWithExpansion(nodes).pipe(
map(flattenedNodes => ({
renderNodes: flattenedNodes,
Expand Down Expand Up @@ -1039,7 +1037,7 @@ export class CdkTree<T, K = T>
} else {
// For nested nodes, we still need to perform the node flattening in order
// to maintain our caches for various tree operations.
this._groups.set(null, [...nodes]);
this._ariaSets.set(null, [...nodes]);
return this._flattenNestedNodesWithExpansion(nodes).pipe(
map(flattenedNodes => ({
renderNodes: nodes,
Expand All @@ -1065,7 +1063,7 @@ export class CdkTree<T, K = T>
}

this._parents.clear();
this._groups.clear();
this._ariaSets.clear();

for (let index = 0; index < flattenedNodes.length; index++) {
const dataNode = flattenedNodes[index];
Expand All @@ -1075,9 +1073,9 @@ export class CdkTree<T, K = T>
this._parents.set(key, parent);
const parentKey = parent ? this._getExpansionKey(parent) : null;

const group = this._groups.get(parentKey) ?? [];
const group = this._ariaSets.get(parentKey) ?? [];
group.splice(index, 0, dataNode);
this._groups.set(parentKey, group);
this._ariaSets.set(parentKey, group);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion tools/public_api_guard/cdk/a11y.md
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,8 @@ export interface TreeKeyManagerItem {

// @public
export interface TreeKeyManagerOptions<T extends TreeKeyManagerItem> {
activationFollowsFocus?: boolean;
horizontalOrientation?: 'rtl' | 'ltr';
shouldActivationFollowFocus?: boolean;
skipPredicate?: (item: T) => boolean;
trackBy?: (treeItem: T) => unknown;
typeAheadDebounceInterval?: true | number;
Expand Down

0 comments on commit ab5cb56

Please sign in to comment.