Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/@react-aria/collections/src/BaseCollection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ export class SectionNode<T> extends FilterableNode<T> {
* custom collection behaviors.
*/
export class BaseCollection<T> implements ICollection<Node<T>> {
private keyMap: Map<Key, CollectionNode<T>> = new Map();
private firstKey: Key | null = null;
private lastKey: Key | null = null;
private frozen = false;
private itemCount: number = 0;
protected keyMap: Map<Key, CollectionNode<T>> = new Map();
protected firstKey: Key | null = null;
protected lastKey: Key | null = null;
protected frozen = false;
protected itemCount: number = 0;

get size(): number {
return this.itemCount;
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/collections/src/Document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export class ElementNode<T> extends BaseNode<T> {

get level(): number {
if (this.parentNode instanceof ElementNode) {
return this.parentNode.level + (this.node?.type === 'item' ? 1 : 0);
return this.parentNode.level + (this.parentNode.node?.type === 'item' ? 1 : 0);
}

return 0;
Expand Down
6 changes: 5 additions & 1 deletion packages/@react-aria/gridlist/src/useGridListItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,16 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt

let isExpanded = hasChildRows ? state.expandedKeys.has(node.key) : undefined;
let setSize = 1;
let index = node.index;
if (node.level >= 0 && node?.parentKey != null) {
let parent = state.collection.getItem(node.parentKey);
if (parent) {
// siblings must exist because our original node exists
let siblings = getDirectChildren(parent, state.collection);
setSize = [...siblings].filter(row => row.type === 'item').length;
if (index > 0 && siblings[0].type !== 'item') {
index -= 1; // subtract one for the parent item's content node
}
}
} else {
setSize = [...state.collection].filter(row => row.level === 0 && row.type === 'item').length;
Expand All @@ -114,7 +118,7 @@ export function useGridListItem<T>(props: AriaGridListItemOptions, state: ListSt
treeGridRowProps = {
'aria-expanded': isExpanded,
'aria-level': node.level + 1,
'aria-posinset': node?.index + 1,
'aria-posinset': index + 1,
'aria-setsize': setSize
};
}
Expand Down
15 changes: 13 additions & 2 deletions packages/@react-aria/menu/src/useMenuItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import {DOMAttributes, DOMProps, FocusableElement, FocusEvents, HoverEvents, Key, KeyboardEvents, PressEvent, PressEvents, RefObject} from '@react-types/shared';
import {filterDOMProps, getEventTarget, handleLinkClick, mergeProps, useLinkProps, useRouter, useSlotId} from '@react-aria/utils';
import {getItemCount} from '@react-stately/collections';
import {isFocusVisible, useFocusable, useHover, useKeyboard, usePress} from '@react-aria/interactions';
import {isFocusVisible, setInteractionModality, useFocusable, useHover, useKeyboard, usePress} from '@react-aria/interactions';
import {menuData} from './utils';
import {MouseEvent, useRef} from 'react';
import {SelectionManager} from '@react-stately/selection';
Expand Down Expand Up @@ -97,6 +97,9 @@ export interface AriaMenuItemProps extends DOMProps, PressEvents, HoverEvents, K
/** Identifies the menu item's popup element whose contents or presence is controlled by the menu item. */
'aria-controls'?: string,

/** Identifies the element(s) that describe the menu item. */
'aria-describedby'?: string,

/** Override of the selection manager. By default, `state.selectionManager` is used. */
selectionManager?: SelectionManager
}
Expand Down Expand Up @@ -177,7 +180,7 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
role,
'aria-label': props['aria-label'],
'aria-labelledby': labelId,
'aria-describedby': [descriptionId, keyboardId].filter(Boolean).join(' ') || undefined,
'aria-describedby': [props['aria-describedby'], descriptionId, keyboardId].filter(Boolean).join(' ') || undefined,
'aria-controls': props['aria-controls'],
'aria-haspopup': hasPopup,
'aria-expanded': props['aria-expanded']
Expand Down Expand Up @@ -287,6 +290,10 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
case ' ':
interaction.current = {pointerType: 'keyboard', key: ' '};
(getEventTarget(e) as HTMLElement).click();

// click above sets modality to "virtual", need to set interaction modality back to 'keyboard' so focusSafely calls properly move focus
// to the newly opened submenu's first item.
setInteractionModality('keyboard');
break;
case 'Enter':
interaction.current = {pointerType: 'keyboard', key: 'Enter'};
Expand All @@ -295,6 +302,10 @@ export function useMenuItem<T>(props: AriaMenuItemProps, state: TreeState<T>, re
if ((getEventTarget(e) as HTMLElement).tagName !== 'A') {
(getEventTarget(e) as HTMLElement).click();
}

// click above sets modality to "virtual", need to set interaction modality back to 'keyboard' so focusSafely calls properly move focus
// to the newly opened submenu's first item.
setInteractionModality('keyboard');
break;
default:
if (!isTrigger) {
Expand Down
2 changes: 1 addition & 1 deletion packages/@react-aria/menu/src/useSubmenuTrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface AriaSubmenuTriggerProps {
shouldUseVirtualFocus?: boolean
}

interface SubmenuTriggerProps extends Omit<AriaMenuItemProps, 'key'> {
interface SubmenuTriggerProps extends Omit<AriaMenuItemProps, 'key' | 'onAction'> {
/** Whether the submenu trigger is in an expanded state. */
isOpen: boolean
}
Expand Down
54 changes: 1 addition & 53 deletions packages/@react-aria/selection/src/ListKeyboardDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ interface ListKeyboardDelegateOptions<T> {
direction?: Direction,
disabledKeys?: Set<Key>,
disabledBehavior?: DisabledBehavior,
layoutDelegate?: LayoutDelegate,
expandedKeys?: Set<Key>
layoutDelegate?: LayoutDelegate
}

export class ListKeyboardDelegate<T> implements KeyboardDelegate {
Expand All @@ -37,7 +36,6 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
private orientation?: Orientation;
private direction?: Direction;
private layoutDelegate: LayoutDelegate;
private expandedKeys?: Set<Key>;

constructor(collection: Collection<Node<T>>, disabledKeys: Set<Key>, ref: RefObject<HTMLElement | null>, collator?: Intl.Collator, expandedKeys?: Set<Key>);
constructor(options: ListKeyboardDelegateOptions<T>);
Expand All @@ -53,7 +51,6 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
this.direction = opts.direction;
this.layout = opts.layout || 'stack';
this.layoutDelegate = opts.layoutDelegate || new DOMLayoutDelegate(opts.ref);
this.expandedKeys = opts.expandedKeys;
} else {
this.collection = args[0];
this.disabledKeys = args[1];
Expand All @@ -63,7 +60,6 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
this.orientation = 'vertical';
this.disabledBehavior = 'all';
this.layoutDelegate = new DOMLayoutDelegate(this.ref);
this.expandedKeys = args[4];
}

// If this is a vertical stack, remove the left/right methods completely
Expand Down Expand Up @@ -92,50 +88,6 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
return null;
}

// Returns the first key that's visible starting from and inclusive of the provided key
private findNextVisible(key: Key | null): Key | null {
let node = key ? this.collection.getItem(key) : null;
if (!node) {
return null;
}

// If the node's parent is expanded, then we can assume that this is a visible node
if (node.parentKey && this.expandedKeys?.has(node.parentKey)) {
return node.key;
}

// If the node's parent is not expanded, find the top-most non-expanded node since it's possible for them to be nested
let parentNode = node.parentKey ? this.collection.getItem(node.parentKey) : null;
// if the the parent node is not a section, and the parent node is not included in expanded keys
while (parentNode && parentNode.type !== 'section' && node && node.parentKey && this.expandedKeys && !this.expandedKeys.has(parentNode.key)) {
node = this.collection.getItem(node.parentKey);
parentNode = node && node.parentKey ? this.collection.getItem(node.parentKey) : null;
}

return node?.key ?? null;
}

// Returns the first key that's visible and non-disabled starting from and inclusive of the provided key
private findNextNonDisabledVisible(key: Key | null, getNext: (key: Key) => Key | null) {
let nextKey = key;
while (nextKey !== null) {
let visibleKey = this.findNextVisible(nextKey);
// If visibleKey is null, that means there are no visibleKeys (don't feel like this is a real use case though, I would assume that there is always one visible node)
if (visibleKey == null) {
return null;
}

let node = this.collection.getItem(visibleKey);
if (node?.type === 'item' && !this.isDisabled(node)) {
return visibleKey;
}

nextKey = getNext(visibleKey);
}

return null;
}

getNextKey(key: Key): Key | null {
let nextKey: Key | null = key;
nextKey = this.collection.getKeyAfter(nextKey);
Expand Down Expand Up @@ -249,10 +201,6 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {

getLastKey(): Key | null {
let key = this.collection.getLastKey();
// we only need to check for visible keys if items can be expanded/collapsed
if (this.expandedKeys) {
return this.findNextNonDisabledVisible(key, key => this.collection.getKeyBefore(key));
}
return this.findNextNonDisabled(key, key => this.collection.getKeyBefore(key));
}

Expand Down
18 changes: 17 additions & 1 deletion packages/@react-spectrum/s2/chromatic/Menu.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
* governing permissions and limitations under the License.
*/

import {BlendModes, DynamicExample, Example, KeyboardShortcuts, PublishAndExport} from '../stories/Menu.stories';
import {BlendModes, DynamicExample, Example, KeyboardShortcuts, PublishAndExport, UnavailableMenuItem} from '../stories/Menu.stories';
import {expect} from '@storybook/jest';
import {Menu} from '../src';
import type {Meta, StoryObj} from '@storybook/react';
import {userEvent, within} from 'storybook/test';
Expand Down Expand Up @@ -56,3 +57,18 @@ export const Dynamic: Story = {
...DynamicExample,
play: async (context) => await Default.play!(context)
};

export const WithUnavailableItem: Story = {
...UnavailableMenuItem,
play: async ({canvasElement}) => {
await userEvent.tab();
await userEvent.keyboard('{ArrowDown}');
let body = canvasElement.ownerDocument.body;
await within(body).findByRole('menu');
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{ArrowDown}');
await userEvent.keyboard('{ArrowRight}');
let menus = await within(body).findAllByRole('dialog');
expect(menus).toHaveLength(2);
}
};
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/ar-AE.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(اختياري)",
"label.(required)": "(مطلوب)",
"menu.moreActions": "المزيد من الإجراءات",
"menu.unavailable": "غير مُتوفر، قُم بالتوسيع للحصول على التفاصيل",
"notificationbadge.indicatorOnly": "نشاط جديد",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "تحديد…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/bg-BG.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(незадължително)",
"label.(required)": "(задължително)",
"menu.moreActions": "Повече действия",
"menu.unavailable": "Недостъпно, разгънете за подробности",
"notificationbadge.indicatorOnly": "Нова дейност",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Изберете…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/cs-CZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(volitelně)",
"label.(required)": "(požadováno)",
"menu.moreActions": "Další akce",
"menu.unavailable": "Není k dispozici, rozbalením zobrazíte podrobnosti",
"notificationbadge.indicatorOnly": "Nová aktivita",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Vybrat…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/da-DK.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(valgfrit)",
"label.(required)": "(obligatorisk)",
"menu.moreActions": "Flere handlinger",
"menu.unavailable": "Ikke tilgængelig, udvid for detaljer",
"notificationbadge.indicatorOnly": "Ny aktivitet",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Vælg…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(optional)",
"label.(required)": "(erforderlich)",
"menu.moreActions": "Mehr Aktionen",
"menu.unavailable": "Nicht verfügbar, für Details erweitern",
"notificationbadge.indicatorOnly": "Neue Aktivität",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Auswählen…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/el-GR.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(προαιρετικό)",
"label.(required)": "(απαιτείται)",
"menu.moreActions": "Περισσότερες ενέργειες",
"menu.unavailable": "Μη διαθέσιμο, ανάπτυξη για λεπτομέρειες",
"notificationbadge.indicatorOnly": "Νέα δραστηριότητα",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Επιλογή…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(optional)",
"label.(required)": "(required)",
"menu.moreActions": "More actions",
"menu.unavailable": "Unavailable, expand for details",
"notificationbadge.indicatorOnly": "New activity",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Select…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(opcional)",
"label.(required)": "(obligatorio)",
"menu.moreActions": "Más acciones",
"menu.unavailable": "No disponible, ampliar para más detalles",
"notificationbadge.indicatorOnly": "Nueva actividad",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Seleccione…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/et-EE.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(valikuline)",
"label.(required)": "(nõutav)",
"menu.moreActions": "Veel toiminguid",
"menu.unavailable": "Pole kättesaadav, üksikasjade vaatamiseks laiendage",
"notificationbadge.indicatorOnly": "Uus tegevus",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Valige…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/fi-FI.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(valinnainen)",
"label.(required)": "(pakollinen)",
"menu.moreActions": "Lisää toimintoja",
"menu.unavailable": "Ei saatavilla, laajenna saadaksesi lisätietoja",
"notificationbadge.indicatorOnly": "Uusi toiminta",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Valitse…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(facultatif)",
"label.(required)": "(requis)",
"menu.moreActions": "Autres actions",
"menu.unavailable": "Indisponible, développer pour plus de détails",
"notificationbadge.indicatorOnly": "Nouvelle activité",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Sélectionner…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/he-IL.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(אופציונלי)",
"label.(required)": "(נדרש)",
"menu.moreActions": "פעולות נוספות",
"menu.unavailable": "לא זמין, הרחב לפרטים",
"notificationbadge.indicatorOnly": "פעילות חדשה",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "בחר…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/hr-HR.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(opcionalno)",
"label.(required)": "(obvezno)",
"menu.moreActions": "Dodatne radnje",
"menu.unavailable": "Nije dostupno, proširi za detalje",
"notificationbadge.indicatorOnly": "Nova aktivnost",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Odaberite…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/hu-HU.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(opcionális)",
"label.(required)": "(kötelező)",
"menu.moreActions": "További lehetőségek",
"menu.unavailable": "Nem érhető el, a részletekért bontsa ki",
"notificationbadge.indicatorOnly": "Új tevékenység",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Kiválasztás…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(facoltativo)",
"label.(required)": "(obbligatorio)",
"menu.moreActions": "Altre azioni",
"menu.unavailable": "Non disponibile, espandi per i dettagli",
"notificationbadge.indicatorOnly": "Nuova attività",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Seleziona…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(オプション)",
"label.(required)": "(必須)",
"menu.moreActions": "その他のアクション",
"menu.unavailable": "利用できません。詳しくは、展開して確認してください",
"notificationbadge.indicatorOnly": "新規アクティビティ",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "選択…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(선택 사항)",
"label.(required)": "(필수 사항)",
"menu.moreActions": "기타 액션",
"menu.unavailable": "사용할 수 없음, 자세히 보려면 펼치기",
"notificationbadge.indicatorOnly": "새로운 활동",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "선택…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/lt-LT.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(pasirenkama)",
"label.(required)": "(privaloma)",
"menu.moreActions": "Daugiau veiksmų",
"menu.unavailable": "Nepasiekiama, norėdami gauti daugiau informacijos, išskleiskite",
"notificationbadge.indicatorOnly": "Nauja veikla",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Pasirinkite…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/lv-LV.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(neobligāti)",
"label.(required)": "(obligāti)",
"menu.moreActions": "Citas darbības",
"menu.unavailable": "Nav pieejams, izvērsiet, lai skatītu sīkāku informāciju",
"notificationbadge.indicatorOnly": "Jauna aktivitāte",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Izvēlēties…",
Expand Down
1 change: 1 addition & 0 deletions packages/@react-spectrum/s2/intl/nb-NO.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"label.(optional)": "(valgfritt)",
"label.(required)": "(obligatorisk)",
"menu.moreActions": "Flere handlinger",
"menu.unavailable": "Utilgjengelig, utvid for detaljer",
"notificationbadge.indicatorOnly": "Ny aktivitet",
"notificationbadge.plus": "{notifications}+",
"picker.placeholder": "Velg …",
Expand Down
Loading
Loading