Skip to content

Commit 8dc092c

Browse files
committed
Fix after review
1 parent 8953d6b commit 8dc092c

2 files changed

Lines changed: 42 additions & 17 deletions

File tree

src/OptionList.tsx

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,27 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
257257
},
258258
}));
259259

260-
// Skip group headers to match native <select> announcements with <optgroup>
261-
const optionPositions = React.useMemo(() => {
260+
// `optionPositions`: skip group headers to match native <select> announcements with <optgroup>
261+
// `itemGroups`: owning group header of each option, so lookups stay O(1) while rendering
262+
const [optionPositions, itemGroups] = React.useMemo(() => {
262263
let count = 0;
263-
return memoFlattenOptions.map((item) => (item.group ? count : (count += 1)));
264+
let group: FlattenOptionData<BaseOptionType> | null = null;
265+
266+
const positions: number[] = [];
267+
const groups: (FlattenOptionData<BaseOptionType> | null)[] = [];
268+
269+
memoFlattenOptions.forEach((item) => {
270+
if (item.group) {
271+
group = item;
272+
positions.push(count);
273+
groups.push(null);
274+
} else {
275+
positions.push((count += 1));
276+
groups.push(item.groupOption ? group : null);
277+
}
278+
});
279+
280+
return [positions, groups] as const;
264281
}, [memoFlattenOptions]);
265282

266283
// ========================== Render ==========================
@@ -315,18 +332,6 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
315332
);
316333
};
317334

318-
const getGroupItem = (index: number) => {
319-
for (let i = index; i >= 0; i -= 1) {
320-
const current = memoFlattenOptions[i];
321-
if (current?.group) {
322-
return current;
323-
}
324-
}
325-
// Unreachable: a grouped option always has a preceding group header
326-
/* istanbul ignore next */
327-
return null;
328-
};
329-
330335
// Nest options inside `role="group"` wrappers
331336
const renderHiddenItems = () => {
332337
const segments: {
@@ -340,7 +345,7 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
340345
return;
341346
}
342347

343-
const groupItem = item.groupOption ? getGroupItem(index) : null;
348+
const groupItem = itemGroups[index];
344349
const lastSegment = segments[segments.length - 1];
345350

346351
if (lastSegment && lastSegment.group === groupItem) {
@@ -360,7 +365,7 @@ const OptionList: React.ForwardRefRenderFunction<RefOptionListProps, {}> = (_, r
360365
<div
361366
key={group.key}
362367
role="group"
363-
aria-label={isTitleType(groupLabel) ? String(groupLabel) : null}
368+
aria-label={group.data.title ?? (isTitleType(groupLabel) ? String(groupLabel) : null)}
364369
>
365370
{indexes.map(renderItem)}
366371
</div>

tests/Accessibility.test.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,26 @@ describe('Select.Accessibility', () => {
343343
expect(topLevelOption).toHaveAttribute('aria-setsize', '4');
344344
});
345345

346+
it('should use group title in aria-label', () => {
347+
render(
348+
<Select
349+
id="virtual-select"
350+
open
351+
options={[
352+
{
353+
label: 'Group',
354+
title: 'Group title',
355+
options: [{ value: '1' }, { value: '2' }],
356+
},
357+
]}
358+
/>,
359+
);
360+
361+
const hiddenContainer = document.querySelector('#virtual-select_list');
362+
const groupWrapper = hiddenContainer.querySelector('div[role="group"]');
363+
expect(groupWrapper).toHaveAttribute('aria-label', 'Group title');
364+
});
365+
346366
it('should have correct aria and role attributes in virtual false', () => {
347367
render(
348368
<Select

0 commit comments

Comments
 (0)