@astryxdesign/core 0.1.4-canary.37c19d3 → 0.1.4-canary.39ffd21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Banner/Banner.js +1 -8
- package/dist/Calendar/Calendar.d.ts.map +1 -1
- package/dist/Calendar/Calendar.js +11 -31
- package/dist/Calendar/styles.d.ts +2 -2
- package/dist/Calendar/styles.d.ts.map +1 -1
- package/dist/Calendar/styles.js +2 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +0 -1
- package/dist/Chat/ChatToolCalls.d.ts.map +1 -1
- package/dist/Chat/ChatToolCalls.js +1 -8
- package/dist/CheckboxInput/CheckboxInput.d.ts +1 -1
- package/dist/CheckboxInput/CheckboxInput.d.ts.map +1 -1
- package/dist/CheckboxInput/CheckboxInput.js +1 -3
- package/dist/ClickableCard/ClickableCard.d.ts.map +1 -1
- package/dist/ClickableCard/ClickableCard.js +3 -26
- package/dist/CodeBlock/CodeBlock.d.ts +1 -10
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/CodeBlock/CodeBlock.js +5 -23
- package/dist/Collapsible/Collapsible.d.ts +3 -9
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +13 -101
- package/dist/Collapsible/CollapsibleGroup.d.ts +7 -39
- package/dist/Collapsible/CollapsibleGroup.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroup.js +9 -70
- package/dist/Collapsible/CollapsibleGroupContext.d.ts +0 -31
- package/dist/Collapsible/CollapsibleGroupContext.d.ts.map +1 -1
- package/dist/Collapsible/CollapsibleGroupContext.js +3 -33
- package/dist/Collapsible/index.d.ts +0 -1
- package/dist/Collapsible/index.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +0 -2
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/DateTimeInput/DateTimeInput.js +0 -2
- package/dist/Grid/Grid.d.ts +3 -4
- package/dist/Grid/Grid.d.ts.map +1 -1
- package/dist/Grid/Grid.js +9 -26
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +1 -5
- package/dist/Lightbox/Lightbox.d.ts.map +1 -1
- package/dist/Lightbox/Lightbox.js +2 -26
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/NumberInput/NumberInput.js +16 -32
- package/dist/Pagination/Pagination.d.ts.map +1 -1
- package/dist/Pagination/Pagination.js +36 -87
- package/dist/PowerSearch/PowerSearch.d.ts.map +1 -1
- package/dist/PowerSearch/PowerSearch.js +21 -39
- package/dist/ProgressBar/ProgressBar.d.ts.map +1 -1
- package/dist/ProgressBar/ProgressBar.js +1 -2
- package/dist/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/Resizable/ResizeHandle.js +2 -10
- package/dist/SideNav/SideNav.d.ts.map +1 -1
- package/dist/SideNav/SideNav.js +2 -6
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +7 -34
- package/dist/Table/BaseTable.d.ts.map +1 -1
- package/dist/Table/BaseTable.js +2 -12
- package/dist/Table/plugins/columnResize/useTableColumnResize.d.ts.map +1 -1
- package/dist/Table/plugins/columnResize/useTableColumnResize.js +2 -5
- package/dist/Table/plugins/pagination/paginateData.d.ts.map +1 -1
- package/dist/Table/plugins/pagination/paginateData.js +1 -5
- package/dist/Table/plugins/selection/useTableSelectionState.d.ts.map +1 -1
- package/dist/Table/plugins/selection/useTableSelectionState.js +1 -11
- package/dist/Table/plugins/sortable/useTableSortableState.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortableState.js +4 -8
- package/dist/Thumbnail/Thumbnail.d.ts +2 -3
- package/dist/Thumbnail/Thumbnail.d.ts.map +1 -1
- package/dist/Thumbnail/Thumbnail.js +0 -1
- package/dist/TimeInput/TimeInput.d.ts.map +1 -1
- package/dist/TimeInput/TimeInput.js +1 -6
- package/dist/Toast/Toast.d.ts.map +1 -1
- package/dist/Toast/Toast.js +3 -11
- package/dist/Toast/ToastViewport.d.ts.map +1 -1
- package/dist/Toast/ToastViewport.js +0 -12
- package/dist/Typeahead/BaseTypeahead.d.ts.map +1 -1
- package/dist/Typeahead/BaseTypeahead.js +2 -5
- package/dist/astryx.css +0 -7
- package/dist/astryx.umd.js +48 -48
- package/dist/astryx.umd.js.map +4 -4
- package/dist/hooks/focusableSelector.d.ts +1 -1
- package/dist/hooks/focusableSelector.d.ts.map +1 -1
- package/dist/hooks/focusableSelector.js +1 -1
- package/dist/hooks/useFocusTrap.d.ts +0 -3
- package/dist/hooks/useFocusTrap.d.ts.map +1 -1
- package/dist/hooks/useFocusTrap.js +1 -38
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.d.ts.map +1 -1
- package/dist/theme/expandColorScale.d.ts.map +1 -1
- package/dist/theme/expandColorScale.js +3 -10
- package/dist/theme/hct.d.ts +15 -0
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +9 -9
- package/dist/theme/tokens.d.ts +2 -10
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +4 -220
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -2
- package/dist/utils/timeParser.d.ts.map +1 -1
- package/dist/utils/timeParser.js +5 -8
- package/package.json +1 -1
- package/src/Avatar/Avatar.doc.mjs +0 -11
- package/src/Banner/Banner.doc.mjs +0 -8
- package/src/Banner/Banner.test.tsx +0 -38
- package/src/Banner/Banner.tsx +1 -8
- package/src/Calendar/Calendar.doc.mjs +1 -1
- package/src/Calendar/Calendar.test.tsx +4 -112
- package/src/Calendar/Calendar.tsx +5 -26
- package/src/Calendar/styles.ts +2 -12
- package/src/Carousel/Carousel.doc.mjs +12 -12
- package/src/Carousel/Carousel.test.tsx +0 -13
- package/src/Carousel/Carousel.tsx +0 -1
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatToolCalls.test.tsx +0 -55
- package/src/Chat/ChatToolCalls.tsx +12 -10
- package/src/CheckboxInput/CheckboxInput.doc.mjs +3 -3
- package/src/CheckboxInput/CheckboxInput.test.tsx +4 -71
- package/src/CheckboxInput/CheckboxInput.tsx +0 -2
- package/src/Citation/Citation.doc.mjs +0 -5
- package/src/ClickableCard/ClickableCard.tsx +2 -45
- package/src/CodeBlock/CodeBlock.doc.mjs +2 -7
- package/src/CodeBlock/CodeBlock.test.tsx +3 -87
- package/src/CodeBlock/CodeBlock.tsx +3 -43
- package/src/Collapsible/Collapsible.doc.mjs +4 -8
- package/src/Collapsible/Collapsible.tsx +10 -86
- package/src/Collapsible/CollapsibleGroup.doc.mjs +3 -27
- package/src/Collapsible/CollapsibleGroup.test.tsx +0 -288
- package/src/Collapsible/CollapsibleGroup.tsx +9 -107
- package/src/Collapsible/CollapsibleGroupContext.tsx +2 -42
- package/src/Collapsible/index.ts +0 -4
- package/src/CommandPalette/CommandPaletteEmpty.doc.mjs +0 -5
- package/src/CommandPalette/CommandPaletteGroup.doc.mjs +0 -10
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteItem.doc.mjs +0 -6
- package/src/CommandPalette/CommandPaletteList.doc.mjs +0 -9
- package/src/ContextMenu/ContextMenu.doc.mjs +0 -15
- package/src/ContextMenu/ContextMenu.test.tsx +0 -15
- package/src/ContextMenu/ContextMenu.tsx +0 -2
- package/src/ContextMenu/ContextMenuItem.doc.mjs +0 -6
- package/src/DateTimeInput/DateTimeInput.test.tsx +0 -70
- package/src/DateTimeInput/DateTimeInput.tsx +0 -2
- package/src/Dialog/DialogHeader.doc.mjs +0 -45
- package/src/DropdownMenu/DropdownMenu.doc.mjs +1 -1
- package/src/Field/FieldLabel.test.tsx +2 -2
- package/src/FormLayout/__snapshots__/FormLayout.test.tsx.snap +3 -0
- package/src/Grid/Grid.test.tsx +18 -43
- package/src/Grid/Grid.tsx +9 -26
- package/src/Icon/Icon.test.tsx +29 -41
- package/src/Icon/Icon.tsx +2 -6
- package/src/Layout/LayoutContent.doc.mjs +0 -12
- package/src/Layout/LayoutPanel.doc.mjs +0 -40
- package/src/Lightbox/Lightbox.doc.mjs +0 -5
- package/src/Lightbox/Lightbox.test.tsx +2 -123
- package/src/Lightbox/Lightbox.tsx +1 -29
- package/src/NavMenu/NavMenu.doc.mjs +0 -15
- package/src/NumberInput/NumberInput.test.tsx +3 -48
- package/src/NumberInput/NumberInput.tsx +15 -32
- package/src/Overlay/Overlay.doc.mjs +0 -6
- package/src/Pagination/Pagination.test.tsx +0 -143
- package/src/Pagination/Pagination.tsx +23 -75
- package/src/Popover/Popover.test.tsx +0 -65
- package/src/PowerSearch/PowerSearch.doc.mjs +2 -2
- package/src/PowerSearch/PowerSearch.test.tsx +1 -109
- package/src/PowerSearch/PowerSearch.tsx +16 -37
- package/src/ProgressBar/ProgressBar.test.tsx +0 -10
- package/src/ProgressBar/ProgressBar.tsx +1 -2
- package/src/Resizable/ResizeHandle.tsx +2 -10
- package/src/SegmentedControl/SegmentedControl.doc.mjs +1 -1
- package/src/SelectableCard/SelectableCard.doc.mjs +1 -1
- package/src/SideNav/SideNav.doc.mjs +1 -7
- package/src/SideNav/SideNav.test.tsx +0 -20
- package/src/SideNav/SideNav.tsx +2 -6
- package/src/TabList/TabList.test.tsx +1 -185
- package/src/TabList/TabMenu.tsx +9 -45
- package/src/Table/BaseTable.tsx +1 -11
- package/src/Table/Table.test.tsx +0 -51
- package/src/Table/plugins/columnResize/useTableColumnResize.test.tsx +0 -13
- package/src/Table/plugins/columnResize/useTableColumnResize.tsx +1 -3
- package/src/Table/plugins/pagination/paginateData.ts +1 -5
- package/src/Table/plugins/pagination/useTablePagination.test.tsx +0 -13
- package/src/Table/plugins/selection/useTableSelectionState.test.tsx +0 -23
- package/src/Table/plugins/selection/useTableSelectionState.tsx +6 -11
- package/src/Table/plugins/sortable/useTableSortableState.test.tsx +1 -35
- package/src/Table/plugins/sortable/useTableSortableState.tsx +4 -8
- package/src/TextArea/TextArea.test.tsx +3 -3
- package/src/TextInput/TextInput.test.tsx +3 -3
- package/src/Thumbnail/Thumbnail.test.tsx +2 -36
- package/src/Thumbnail/Thumbnail.tsx +2 -4
- package/src/TimeInput/TimeInput.test.tsx +0 -37
- package/src/TimeInput/TimeInput.tsx +1 -11
- package/src/Timestamp/Timestamp.doc.mjs +1 -1
- package/src/Toast/Toast.tsx +3 -11
- package/src/Toast/ToastViewport.test.tsx +0 -64
- package/src/Toast/ToastViewport.tsx +0 -12
- package/src/ToggleButton/ToggleButton.doc.mjs +0 -1
- package/src/Tokenizer/Tokenizer.doc.mjs +2 -2
- package/src/TopNav/TopNav.doc.mjs +1 -1
- package/src/TreeList/TreeList.doc.mjs +2 -2
- package/src/Typeahead/BaseTypeahead.doc.mjs +0 -19
- package/src/Typeahead/BaseTypeahead.tsx +2 -5
- package/src/Typeahead/Typeahead.doc.mjs +1 -1
- package/src/Typeahead/Typeahead.test.tsx +1 -45
- package/src/hooks/focusableSelector.ts +1 -1
- package/src/hooks/useFocusTrap.doc.mjs +2 -4
- package/src/hooks/useFocusTrap.test.tsx +1 -149
- package/src/hooks/useFocusTrap.ts +1 -50
- package/src/theme/SyntaxTheme.doc.mjs +4 -4
- package/src/theme/defineTheme.ts +13 -4
- package/src/theme/derivedVarRegistry.test.ts +2 -2
- package/src/theme/expandColorScale.test.ts +0 -29
- package/src/theme/expandColorScale.ts +6 -11
- package/src/theme/hct.ts +17 -9
- package/src/theme/tokens.test.ts +9 -182
- package/src/theme/tokens.ts +4 -239
- package/src/theme/useTheme.test.tsx +0 -20
- package/src/utils/index.ts +0 -3
- package/src/utils/timeParser.test.ts +0 -13
- package/src/utils/timeParser.ts +5 -8
- package/dist/utils/color.d.ts +0 -42
- package/dist/utils/color.d.ts.map +0 -1
- package/dist/utils/color.js +0 -150
- package/src/Resizable/ResizeHandle.test.tsx +0 -180
- package/src/__tests__/TestIcon.tsx +0 -26
- package/src/docPropReferences.test.ts +0 -178
- package/src/utils/color.test.ts +0 -133
- package/src/utils/color.ts +0 -148
|
@@ -10,11 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {useState} from 'react';
|
|
13
|
-
import {describe, it, expect, vi, beforeAll, afterAll
|
|
13
|
+
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
14
14
|
import {render, screen, act, fireEvent, waitFor} from '@testing-library/react';
|
|
15
15
|
import userEvent from '@testing-library/user-event';
|
|
16
16
|
import {PowerSearch} from './PowerSearch';
|
|
17
|
-
import {__resetLiveRegionsForTest} from '../hooks/useAnnounce';
|
|
18
17
|
import type {PowerSearchConfig, PowerSearchFilter} from './types';
|
|
19
18
|
|
|
20
19
|
// =============================================================================
|
|
@@ -60,12 +59,6 @@ afterAll(() => {
|
|
|
60
59
|
(HTMLElement.prototype as any).matches = originalMatches;
|
|
61
60
|
});
|
|
62
61
|
|
|
63
|
-
// Reset the singleton live regions between tests so result-count
|
|
64
|
-
// announcements from one test don't leak into the next.
|
|
65
|
-
afterEach(() => {
|
|
66
|
-
__resetLiveRegionsForTest();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
62
|
// =============================================================================
|
|
70
63
|
// Fixtures
|
|
71
64
|
// =============================================================================
|
|
@@ -302,105 +295,4 @@ describe('PowerSearch', () => {
|
|
|
302
295
|
expect(screen.getByRole('combobox')).toBeDisabled();
|
|
303
296
|
});
|
|
304
297
|
});
|
|
305
|
-
|
|
306
|
-
describe('result count announcements', () => {
|
|
307
|
-
const politeRegion = () =>
|
|
308
|
-
document.querySelector('[data-astryx-live-region="polite"]');
|
|
309
|
-
|
|
310
|
-
it('announces the result count to a polite live region when it changes', async () => {
|
|
311
|
-
const {rerender} = render(
|
|
312
|
-
<PowerSearch
|
|
313
|
-
config={config}
|
|
314
|
-
filters={[]}
|
|
315
|
-
onChange={() => {}}
|
|
316
|
-
resultCount={0}
|
|
317
|
-
/>,
|
|
318
|
-
);
|
|
319
|
-
rerender(
|
|
320
|
-
<PowerSearch
|
|
321
|
-
config={config}
|
|
322
|
-
filters={[]}
|
|
323
|
-
onChange={() => {}}
|
|
324
|
-
resultCount={5}
|
|
325
|
-
/>,
|
|
326
|
-
);
|
|
327
|
-
await waitFor(() => {
|
|
328
|
-
expect(politeRegion()).toHaveTextContent('5 results');
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
it('announces "1 result" (singular) for a single match', async () => {
|
|
333
|
-
const {rerender} = render(
|
|
334
|
-
<PowerSearch
|
|
335
|
-
config={config}
|
|
336
|
-
filters={[]}
|
|
337
|
-
onChange={() => {}}
|
|
338
|
-
resultCount={0}
|
|
339
|
-
/>,
|
|
340
|
-
);
|
|
341
|
-
rerender(
|
|
342
|
-
<PowerSearch
|
|
343
|
-
config={config}
|
|
344
|
-
filters={[]}
|
|
345
|
-
onChange={() => {}}
|
|
346
|
-
resultCount={1}
|
|
347
|
-
/>,
|
|
348
|
-
);
|
|
349
|
-
await waitFor(() => {
|
|
350
|
-
expect(politeRegion()).toHaveTextContent('1 result');
|
|
351
|
-
});
|
|
352
|
-
expect(politeRegion()?.textContent).not.toMatch(/results/);
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
it('announces a string result count verbatim', async () => {
|
|
356
|
-
const {rerender} = render(
|
|
357
|
-
<PowerSearch
|
|
358
|
-
config={config}
|
|
359
|
-
filters={[]}
|
|
360
|
-
onChange={() => {}}
|
|
361
|
-
resultCount="0 items"
|
|
362
|
-
/>,
|
|
363
|
-
);
|
|
364
|
-
rerender(
|
|
365
|
-
<PowerSearch
|
|
366
|
-
config={config}
|
|
367
|
-
filters={[]}
|
|
368
|
-
onChange={() => {}}
|
|
369
|
-
resultCount="Showing 1.2k matches"
|
|
370
|
-
/>,
|
|
371
|
-
);
|
|
372
|
-
await waitFor(() => {
|
|
373
|
-
expect(politeRegion()).toHaveTextContent('Showing 1.2k matches');
|
|
374
|
-
});
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('does not announce the result count present on initial mount', async () => {
|
|
378
|
-
render(
|
|
379
|
-
<PowerSearch
|
|
380
|
-
config={config}
|
|
381
|
-
filters={[]}
|
|
382
|
-
onChange={() => {}}
|
|
383
|
-
resultCount={42}
|
|
384
|
-
/>,
|
|
385
|
-
);
|
|
386
|
-
// Flush effects and any pending live-region rAF writes.
|
|
387
|
-
await act(async () => {
|
|
388
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
389
|
-
});
|
|
390
|
-
expect(politeRegion()?.textContent ?? '').not.toContain('42');
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
it('leaves Typeahead dropdown announcements intact and stays silent when no resultCount is set', async () => {
|
|
394
|
-
const user = userEvent.setup();
|
|
395
|
-
render(<PowerSearchWrapper config={config} />);
|
|
396
|
-
const input = screen.getByRole('combobox');
|
|
397
|
-
await user.click(input);
|
|
398
|
-
await user.type(input, 'Status');
|
|
399
|
-
// BaseTypeahead announces the dropdown suggestion count; PowerSearch adds
|
|
400
|
-
// no result-count announcement because resultCount is unset.
|
|
401
|
-
await waitFor(() => {
|
|
402
|
-
expect(politeRegion()?.textContent).toMatch(/\d+ results?/);
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
298
|
});
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
import React, {
|
|
18
18
|
useCallback,
|
|
19
|
-
useEffect,
|
|
20
19
|
useImperativeHandle,
|
|
21
20
|
useMemo,
|
|
22
21
|
useRef,
|
|
@@ -39,7 +38,6 @@ import type {IconType} from '../Icon';
|
|
|
39
38
|
import type {IconName} from '../Icon/globalIconRegistry';
|
|
40
39
|
import type {InputStatus} from '../Field';
|
|
41
40
|
import {usePopover} from '../Popover/usePopover';
|
|
42
|
-
import {useAnnounce} from '../hooks/useAnnounce';
|
|
43
41
|
import {
|
|
44
42
|
spacingVars,
|
|
45
43
|
colorVars,
|
|
@@ -947,42 +945,23 @@ export function PowerSearch({
|
|
|
947
945
|
isReadOnly,
|
|
948
946
|
]);
|
|
949
947
|
|
|
950
|
-
// Plain-text form of the result count, shared by the visible label and the
|
|
951
|
-
// screen-reader announcement so the two never drift.
|
|
952
|
-
const resultCountText = useMemo((): string | null => {
|
|
953
|
-
if (resultCount == null) {
|
|
954
|
-
return null;
|
|
955
|
-
}
|
|
956
|
-
if (typeof resultCount === 'number') {
|
|
957
|
-
const formatted = new Intl.NumberFormat().format(resultCount);
|
|
958
|
-
return `${formatted} ${resultCount === 1 ? 'result' : 'results'}`;
|
|
959
|
-
}
|
|
960
|
-
return resultCount;
|
|
961
|
-
}, [resultCount]);
|
|
962
|
-
|
|
963
|
-
// Announce result-count changes to screen readers through a polite live
|
|
964
|
-
// region, mirroring the way Typeahead announces its dropdown result count.
|
|
965
|
-
// The count is otherwise only shown visually and stays silent to assistive
|
|
966
|
-
// tech. Skip the first run so the count already present on mount isn't
|
|
967
|
-
// announced unprompted — only user-driven changes are spoken.
|
|
968
|
-
const announce = useAnnounce();
|
|
969
|
-
const hasMountedRef = useRef(false);
|
|
970
|
-
useEffect(() => {
|
|
971
|
-
if (!hasMountedRef.current) {
|
|
972
|
-
hasMountedRef.current = true;
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
if (resultCountText != null) {
|
|
976
|
-
announce(resultCountText);
|
|
977
|
-
}
|
|
978
|
-
}, [resultCountText, announce]);
|
|
979
|
-
|
|
980
948
|
// Build combined endContent from resultCount + endContent props
|
|
981
949
|
const combinedEndContent = useMemo((): React.ReactNode => {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
950
|
+
let resultCountNode: React.ReactNode = null;
|
|
951
|
+
if (resultCount != null) {
|
|
952
|
+
if (typeof resultCount === 'number') {
|
|
953
|
+
const formatted = new Intl.NumberFormat().format(resultCount);
|
|
954
|
+
resultCountNode = (
|
|
955
|
+
<span {...stylex.props(resultCountStyles.text)}>
|
|
956
|
+
{formatted} {resultCount === 1 ? 'result' : 'results'}
|
|
957
|
+
</span>
|
|
958
|
+
);
|
|
959
|
+
} else {
|
|
960
|
+
resultCountNode = (
|
|
961
|
+
<span {...stylex.props(resultCountStyles.text)}>{resultCount}</span>
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
986
965
|
|
|
987
966
|
if (resultCountNode && endContent) {
|
|
988
967
|
return (
|
|
@@ -993,7 +972,7 @@ export function PowerSearch({
|
|
|
993
972
|
);
|
|
994
973
|
}
|
|
995
974
|
return resultCountNode || endContent || undefined;
|
|
996
|
-
}, [
|
|
975
|
+
}, [resultCount, endContent]);
|
|
997
976
|
|
|
998
977
|
return (
|
|
999
978
|
<>
|
|
@@ -140,16 +140,6 @@ describe('ProgressBar', () => {
|
|
|
140
140
|
expect(progressbar).toHaveAttribute('aria-valuemax', '0');
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
it('does not render NaN in the value label when max is zero', () => {
|
|
144
|
-
render(<ProgressBar value={0} max={0} label="Empty" hasValueLabel />);
|
|
145
|
-
expect(screen.queryByText(/NaN|Infinity/)).not.toBeInTheDocument();
|
|
146
|
-
const progressbar = screen.getByRole('progressbar');
|
|
147
|
-
expect(progressbar.getAttribute('aria-valuetext') ?? '').not.toMatch(
|
|
148
|
-
/NaN|Infinity/,
|
|
149
|
-
);
|
|
150
|
-
expect(screen.getByText('0%')).toBeInTheDocument();
|
|
151
|
-
});
|
|
152
|
-
|
|
153
143
|
// Disabled state
|
|
154
144
|
describe('disabled state', () => {
|
|
155
145
|
it('renders with isDisabled', () => {
|
|
@@ -228,8 +228,7 @@ const variantStyles = stylex.create({
|
|
|
228
228
|
});
|
|
229
229
|
|
|
230
230
|
function defaultFormatValueLabel(value: number, max: number): string {
|
|
231
|
-
|
|
232
|
-
return `${pct}%`;
|
|
231
|
+
return `${Math.round((value / max) * 100)}%`;
|
|
233
232
|
}
|
|
234
233
|
|
|
235
234
|
/**
|
|
@@ -510,16 +510,7 @@ export function ResizeHandle({
|
|
|
510
510
|
),
|
|
511
511
|
className,
|
|
512
512
|
)}
|
|
513
|
-
{...props}
|
|
514
|
-
// Keyboard resizing must fire from the focusable separator, not a child:
|
|
515
|
-
// keydown fires on the focused element and bubbles up, so a handler on a
|
|
516
|
-
// descendant never runs (per the WAI-ARIA window-splitter pattern).
|
|
517
|
-
// Placed after {...props} and composed with any consumer onKeyDown so
|
|
518
|
-
// this accessibility-critical handler can't be clobbered by a passed prop.
|
|
519
|
-
onKeyDown={e => {
|
|
520
|
-
props.onKeyDown?.(e);
|
|
521
|
-
handleKeyDown(e);
|
|
522
|
-
}}>
|
|
513
|
+
{...props}>
|
|
523
514
|
{/* Wider invisible hit area for pointer interaction */}
|
|
524
515
|
<div
|
|
525
516
|
{...stylex.props(
|
|
@@ -537,6 +528,7 @@ export function ResizeHandle({
|
|
|
537
528
|
setIsHovered(false);
|
|
538
529
|
}
|
|
539
530
|
}}
|
|
531
|
+
onKeyDown={handleKeyDown}
|
|
540
532
|
/>
|
|
541
533
|
{/* Pill grip indicator — themed via .astryx-resize-handle-pill */}
|
|
542
534
|
{children ?? (
|
|
@@ -16,7 +16,7 @@ export const docs = {
|
|
|
16
16
|
theming: {
|
|
17
17
|
targets: [
|
|
18
18
|
{className: 'astryx-segmented-control', visualProps: ['size']},
|
|
19
|
-
{className: 'astryx-segmented-control-item'
|
|
19
|
+
{className: 'astryx-segmented-control-item'},
|
|
20
20
|
],
|
|
21
21
|
vars: [
|
|
22
22
|
{name: '--_segmented-control-radius', description: 'Border radius of the segmented control', default: 'var(--radius-element)', private: true},
|
|
@@ -34,7 +34,7 @@ export const docs = {
|
|
|
34
34
|
],
|
|
35
35
|
theming: {
|
|
36
36
|
container: true,
|
|
37
|
-
targets: [{className: 'astryx-selectable-card', visualProps: ['selected'
|
|
37
|
+
targets: [{className: 'astryx-selectable-card', visualProps: ['selected']}],
|
|
38
38
|
},
|
|
39
39
|
playground: {
|
|
40
40
|
defaults: {
|
|
@@ -21,7 +21,7 @@ export const docs = {
|
|
|
21
21
|
targets: [
|
|
22
22
|
{className: 'astryx-side-nav', visualProps: ['mode']},
|
|
23
23
|
{className: 'astryx-side-nav-heading'},
|
|
24
|
-
{className: 'astryx-side-nav-item'
|
|
24
|
+
{className: 'astryx-side-nav-item'},
|
|
25
25
|
{className: 'astryx-side-nav-section'},
|
|
26
26
|
],
|
|
27
27
|
},
|
|
@@ -102,12 +102,6 @@ export const docs = {
|
|
|
102
102
|
description: 'Enables collapse behavior. true for uncontrolled with default toggle button, or an object for controlled mode and advanced config (defaultIsCollapsed, isCollapsed + onCollapsedChange, hasButton, buttonLabel).',
|
|
103
103
|
default: 'false',
|
|
104
104
|
},
|
|
105
|
-
{
|
|
106
|
-
name: 'resizable',
|
|
107
|
-
type: 'boolean | { defaultWidth?: number; minWidth?: number; maxWidth?: number; autoSaveId?: string; onWidthChange?: (width: number) => void }',
|
|
108
|
-
description: 'Enables a resize handle at the inline-end edge. true for defaults (260px initial, 180-480px range), or a ResizableConfig object (defaultWidth, minWidth, maxWidth, autoSaveId for localStorage persistence, onWidthChange). The handle is hidden while collapsed.',
|
|
109
|
-
default: 'false',
|
|
110
|
-
},
|
|
111
105
|
{
|
|
112
106
|
name: 'handleRef',
|
|
113
107
|
type: 'Ref<SideNavImperativeCollapseHandle>',
|
|
@@ -162,26 +162,6 @@ describe('SideNav', () => {
|
|
|
162
162
|
screen.getByRole('button', {name: 'Expand sidebar'}),
|
|
163
163
|
).toBeInTheDocument();
|
|
164
164
|
});
|
|
165
|
-
|
|
166
|
-
it('does not render an empty footer container when collapsible.hasButton is false', () => {
|
|
167
|
-
render(
|
|
168
|
-
<SideNav data-testid="nav" collapsible={{hasButton: false}}>
|
|
169
|
-
Content
|
|
170
|
-
</SideNav>,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
// The built-in collapse button is opted out (consumers render their own
|
|
174
|
-
// SideNavCollapseButton in the header), so it must not appear...
|
|
175
|
-
expect(
|
|
176
|
-
screen.queryByRole('button', {name: 'Collapse sidebar'}),
|
|
177
|
-
).not.toBeInTheDocument();
|
|
178
|
-
|
|
179
|
-
// ...and no empty sticky-bottom container should be left behind. With no
|
|
180
|
-
// footer/footerIcons and no built-in button, the scrollable content region
|
|
181
|
-
// is the nav's only child.
|
|
182
|
-
const nav = screen.getByTestId('nav');
|
|
183
|
-
expect(nav.children).toHaveLength(1);
|
|
184
|
-
});
|
|
185
165
|
});
|
|
186
166
|
|
|
187
167
|
// =============================================================================
|
package/src/SideNav/SideNav.tsx
CHANGED
|
@@ -490,10 +490,6 @@ export function SideNav({
|
|
|
490
490
|
// =========================================================================
|
|
491
491
|
const hasStickyTop = !!(header || topContent);
|
|
492
492
|
const hasStickyBottom = !!(footer || footerIcons);
|
|
493
|
-
// The built-in collapse button only renders when collapse is enabled and it
|
|
494
|
-
// hasn't been opted out via `collapsible.hasButton: false` (e.g. when the
|
|
495
|
-
// consumer places a SideNavCollapseButton in the header instead).
|
|
496
|
-
const showCollapseButton = isCollapsible && hasCollapseButton;
|
|
497
493
|
|
|
498
494
|
// When resizable, override the nav width via inline style
|
|
499
495
|
const resizableNavStyle: React.CSSProperties | undefined = isResizable
|
|
@@ -536,7 +532,7 @@ export function SideNav({
|
|
|
536
532
|
)}>
|
|
537
533
|
{children}
|
|
538
534
|
</div>
|
|
539
|
-
{(hasStickyBottom ||
|
|
535
|
+
{(hasStickyBottom || isCollapsible) && (
|
|
540
536
|
<div
|
|
541
537
|
{...stylex.props(
|
|
542
538
|
styles.stickyBottom,
|
|
@@ -548,7 +544,7 @@ export function SideNav({
|
|
|
548
544
|
styles.footerRow,
|
|
549
545
|
collapsed && styles.footerRowCollapsed,
|
|
550
546
|
)}>
|
|
551
|
-
{
|
|
547
|
+
{isCollapsible && hasCollapseButton && <SideNavCollapseButton />}
|
|
552
548
|
{footerIcons}
|
|
553
549
|
</div>
|
|
554
550
|
</div>
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import {describe, it, expect, vi, beforeAll, afterAll} from 'vitest';
|
|
13
|
-
import {render, screen
|
|
13
|
+
import {render, screen} from '@testing-library/react';
|
|
14
14
|
import userEvent from '@testing-library/user-event';
|
|
15
15
|
import {TabList} from './TabList';
|
|
16
16
|
import {Tab} from './Tab';
|
|
@@ -702,187 +702,3 @@ describe('TabMenu', () => {
|
|
|
702
702
|
expect(reportsItem).not.toHaveAttribute('aria-current');
|
|
703
703
|
});
|
|
704
704
|
});
|
|
705
|
-
|
|
706
|
-
describe('TabMenu keyboard navigation (roving tabindex)', () => {
|
|
707
|
-
const menuOptions = [
|
|
708
|
-
{value: 'analytics', label: 'Analytics'},
|
|
709
|
-
{value: 'reports', label: 'Reports'},
|
|
710
|
-
];
|
|
711
|
-
|
|
712
|
-
it('exposes the overflow menu as a single Tab stop (one item tabbable, rest -1)', async () => {
|
|
713
|
-
const user = userEvent.setup();
|
|
714
|
-
render(
|
|
715
|
-
<TabList value="home" onChange={() => {}}>
|
|
716
|
-
<Tab value="home" label="Home" />
|
|
717
|
-
<TabMenu label="More" options={menuOptions} />
|
|
718
|
-
</TabList>,
|
|
719
|
-
);
|
|
720
|
-
|
|
721
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
722
|
-
|
|
723
|
-
const analytics = screen.getByRole('menuitem', {
|
|
724
|
-
name: 'Analytics',
|
|
725
|
-
hidden: true,
|
|
726
|
-
});
|
|
727
|
-
const reports = screen.getByRole('menuitem', {
|
|
728
|
-
name: 'Reports',
|
|
729
|
-
hidden: true,
|
|
730
|
-
});
|
|
731
|
-
|
|
732
|
-
// Exactly one menu item is in the Tab sequence; arrow keys reach the rest.
|
|
733
|
-
expect(analytics).toHaveAttribute('tabindex', '0');
|
|
734
|
-
expect(reports).toHaveAttribute('tabindex', '-1');
|
|
735
|
-
|
|
736
|
-
const tabbable = [analytics, reports].filter(
|
|
737
|
-
el => el.getAttribute('tabindex') === '0',
|
|
738
|
-
);
|
|
739
|
-
expect(tabbable).toHaveLength(1);
|
|
740
|
-
});
|
|
741
|
-
|
|
742
|
-
it('moves focus between items with ArrowDown and ArrowUp', async () => {
|
|
743
|
-
const user = userEvent.setup();
|
|
744
|
-
render(
|
|
745
|
-
<TabList value="home" onChange={() => {}}>
|
|
746
|
-
<Tab value="home" label="Home" />
|
|
747
|
-
<TabMenu label="More" options={menuOptions} />
|
|
748
|
-
</TabList>,
|
|
749
|
-
);
|
|
750
|
-
|
|
751
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
752
|
-
const menu = screen.getByRole('menu', {hidden: true});
|
|
753
|
-
const analytics = screen.getByRole('menuitem', {
|
|
754
|
-
name: 'Analytics',
|
|
755
|
-
hidden: true,
|
|
756
|
-
});
|
|
757
|
-
const reports = screen.getByRole('menuitem', {
|
|
758
|
-
name: 'Reports',
|
|
759
|
-
hidden: true,
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
fireEvent.keyDown(menu, {key: 'ArrowDown'});
|
|
763
|
-
expect(analytics).toHaveFocus();
|
|
764
|
-
|
|
765
|
-
fireEvent.keyDown(menu, {key: 'ArrowDown'});
|
|
766
|
-
expect(reports).toHaveFocus();
|
|
767
|
-
|
|
768
|
-
fireEvent.keyDown(menu, {key: 'ArrowUp'});
|
|
769
|
-
expect(analytics).toHaveFocus();
|
|
770
|
-
});
|
|
771
|
-
|
|
772
|
-
it('moves the roving tab stop with arrow navigation', async () => {
|
|
773
|
-
const user = userEvent.setup();
|
|
774
|
-
render(
|
|
775
|
-
<TabList value="home" onChange={() => {}}>
|
|
776
|
-
<Tab value="home" label="Home" />
|
|
777
|
-
<TabMenu label="More" options={menuOptions} />
|
|
778
|
-
</TabList>,
|
|
779
|
-
);
|
|
780
|
-
|
|
781
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
782
|
-
const menu = screen.getByRole('menu', {hidden: true});
|
|
783
|
-
const analytics = screen.getByRole('menuitem', {
|
|
784
|
-
name: 'Analytics',
|
|
785
|
-
hidden: true,
|
|
786
|
-
});
|
|
787
|
-
const reports = screen.getByRole('menuitem', {
|
|
788
|
-
name: 'Reports',
|
|
789
|
-
hidden: true,
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus analytics
|
|
793
|
-
fireEvent.keyDown(menu, {key: 'ArrowDown'}); // focus reports
|
|
794
|
-
|
|
795
|
-
// The tab stop follows focus, so it is still a single stop after moving.
|
|
796
|
-
expect(reports).toHaveAttribute('tabindex', '0');
|
|
797
|
-
expect(analytics).toHaveAttribute('tabindex', '-1');
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
it('jumps to first and last item with Home and End', async () => {
|
|
801
|
-
const user = userEvent.setup();
|
|
802
|
-
const options = [
|
|
803
|
-
{value: 'analytics', label: 'Analytics'},
|
|
804
|
-
{value: 'reports', label: 'Reports'},
|
|
805
|
-
{value: 'exports', label: 'Exports'},
|
|
806
|
-
];
|
|
807
|
-
render(
|
|
808
|
-
<TabList value="home" onChange={() => {}}>
|
|
809
|
-
<Tab value="home" label="Home" />
|
|
810
|
-
<TabMenu label="More" options={options} />
|
|
811
|
-
</TabList>,
|
|
812
|
-
);
|
|
813
|
-
|
|
814
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
815
|
-
const menu = screen.getByRole('menu', {hidden: true});
|
|
816
|
-
const analytics = screen.getByRole('menuitem', {
|
|
817
|
-
name: 'Analytics',
|
|
818
|
-
hidden: true,
|
|
819
|
-
});
|
|
820
|
-
const exports = screen.getByRole('menuitem', {
|
|
821
|
-
name: 'Exports',
|
|
822
|
-
hidden: true,
|
|
823
|
-
});
|
|
824
|
-
|
|
825
|
-
fireEvent.keyDown(menu, {key: 'End'});
|
|
826
|
-
expect(exports).toHaveFocus();
|
|
827
|
-
|
|
828
|
-
fireEvent.keyDown(menu, {key: 'Home'});
|
|
829
|
-
expect(analytics).toHaveFocus();
|
|
830
|
-
});
|
|
831
|
-
|
|
832
|
-
it('selects an item with Enter and calls onChange', async () => {
|
|
833
|
-
const user = userEvent.setup();
|
|
834
|
-
const handleChange = vi.fn();
|
|
835
|
-
render(
|
|
836
|
-
<TabList value="home" onChange={handleChange}>
|
|
837
|
-
<Tab value="home" label="Home" />
|
|
838
|
-
<TabMenu label="More" options={menuOptions} />
|
|
839
|
-
</TabList>,
|
|
840
|
-
);
|
|
841
|
-
|
|
842
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
843
|
-
const analytics = screen.getByRole('menuitem', {
|
|
844
|
-
name: 'Analytics',
|
|
845
|
-
hidden: true,
|
|
846
|
-
});
|
|
847
|
-
analytics.focus();
|
|
848
|
-
fireEvent.keyDown(analytics, {key: 'Enter'});
|
|
849
|
-
|
|
850
|
-
expect(handleChange).toHaveBeenCalledWith('analytics');
|
|
851
|
-
});
|
|
852
|
-
|
|
853
|
-
it('closes the menu when Tab is pressed inside it (APG menu-button)', async () => {
|
|
854
|
-
const user = userEvent.setup();
|
|
855
|
-
render(
|
|
856
|
-
<TabList value="home" onChange={() => {}}>
|
|
857
|
-
<Tab value="home" label="Home" />
|
|
858
|
-
<TabMenu label="More" options={menuOptions} />
|
|
859
|
-
</TabList>,
|
|
860
|
-
);
|
|
861
|
-
|
|
862
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
863
|
-
const menu = screen.getByRole('menu', {hidden: true});
|
|
864
|
-
|
|
865
|
-
const hidePopover = vi.mocked(HTMLElement.prototype.hidePopover);
|
|
866
|
-
hidePopover.mockClear();
|
|
867
|
-
fireEvent.keyDown(menu, {key: 'Tab'});
|
|
868
|
-
expect(hidePopover).toHaveBeenCalled();
|
|
869
|
-
});
|
|
870
|
-
|
|
871
|
-
it('closes the menu when Escape is pressed', async () => {
|
|
872
|
-
const user = userEvent.setup();
|
|
873
|
-
render(
|
|
874
|
-
<TabList value="home" onChange={() => {}}>
|
|
875
|
-
<Tab value="home" label="Home" />
|
|
876
|
-
<TabMenu label="More" options={menuOptions} />
|
|
877
|
-
</TabList>,
|
|
878
|
-
);
|
|
879
|
-
|
|
880
|
-
await user.click(screen.getByRole('button', {name: /More/}));
|
|
881
|
-
const menu = screen.getByRole('menu', {hidden: true});
|
|
882
|
-
|
|
883
|
-
const hidePopover = vi.mocked(HTMLElement.prototype.hidePopover);
|
|
884
|
-
hidePopover.mockClear();
|
|
885
|
-
fireEvent.keyDown(menu, {key: 'Escape'});
|
|
886
|
-
expect(hidePopover).toHaveBeenCalled();
|
|
887
|
-
});
|
|
888
|
-
});
|
package/src/TabList/TabMenu.tsx
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* - /packages/cli/templates/blocks/components/TabList/ (showcase blocks)
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import React, {useCallback, useId,
|
|
19
|
+
import React, {useCallback, useId, type ReactNode} from 'react';
|
|
20
20
|
import * as stylex from '@stylexjs/stylex';
|
|
21
21
|
import {Icon} from '../Icon';
|
|
22
22
|
import {renderIconSlot, type IconType} from '../Icon';
|
|
@@ -262,7 +262,6 @@ export function TabMenu({
|
|
|
262
262
|
}: TabMenuProps) {
|
|
263
263
|
const tabListCtx = useTabListContext();
|
|
264
264
|
const menuId = useId();
|
|
265
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
266
265
|
|
|
267
266
|
const popover = usePopover({
|
|
268
267
|
hasLightDismiss: true,
|
|
@@ -271,50 +270,20 @@ export function TabMenu({
|
|
|
271
270
|
// The popup's own role="menu" is the exposed semantics; a modal dialog
|
|
272
271
|
// wrapper would announce an unnamed dialog around the menu.
|
|
273
272
|
role: 'none',
|
|
274
|
-
// Return focus to the trigger when the menu closes (Escape, Tab, select,
|
|
275
|
-
// or light dismiss) so keyboard focus is never dropped to <body>.
|
|
276
|
-
onHide: useCallback(() => {
|
|
277
|
-
buttonRef.current?.focus();
|
|
278
|
-
}, []),
|
|
279
273
|
});
|
|
280
274
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
listRef,
|
|
286
|
-
handleKeyDown: handleListKeyDown,
|
|
287
|
-
handleFocus,
|
|
288
|
-
focusFirst,
|
|
289
|
-
} = useListFocus<HTMLDivElement>({
|
|
290
|
-
hasRovingTabIndex: true,
|
|
291
|
-
onEscape: () => popover.hide(),
|
|
292
|
-
});
|
|
275
|
+
const {listRef, handleKeyDown: handleListKeyDown} =
|
|
276
|
+
useListFocus<HTMLDivElement>({
|
|
277
|
+
onEscape: () => popover.hide(),
|
|
278
|
+
});
|
|
293
279
|
|
|
294
280
|
const handleToggle = useCallback(() => {
|
|
295
281
|
if (popover.isOpen) {
|
|
296
282
|
popover.hide();
|
|
297
283
|
} else {
|
|
298
284
|
popover.show();
|
|
299
|
-
// Move focus into the menu on open so arrow navigation works and the
|
|
300
|
-
// roving tab stop lands on a real item (APG menu-button focus-on-open).
|
|
301
|
-
requestAnimationFrame(() => focusFirst());
|
|
302
285
|
}
|
|
303
|
-
}, [popover
|
|
304
|
-
|
|
305
|
-
const handleMenuKeyDown = useCallback(
|
|
306
|
-
(e: React.KeyboardEvent) => {
|
|
307
|
-
// APG menu-button: Tab leaves the menu rather than walking its items.
|
|
308
|
-
// Close and let onHide return focus to the trigger, from which the
|
|
309
|
-
// browser's default Tab continues to the next element.
|
|
310
|
-
if (e.key === 'Tab') {
|
|
311
|
-
popover.hide();
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
handleListKeyDown(e);
|
|
315
|
-
},
|
|
316
|
-
[handleListKeyDown, popover],
|
|
317
|
-
);
|
|
286
|
+
}, [popover]);
|
|
318
287
|
|
|
319
288
|
const selectedOption = options.find(o => o.value === tabListCtx.value);
|
|
320
289
|
const triggerLabel = selectedOption?.label ?? label;
|
|
@@ -330,11 +299,7 @@ export function TabMenu({
|
|
|
330
299
|
[tabListCtx, popover],
|
|
331
300
|
);
|
|
332
301
|
|
|
333
|
-
const setButtonRef = mergeRefs<HTMLButtonElement>(
|
|
334
|
-
popover.triggerRef,
|
|
335
|
-
buttonRef,
|
|
336
|
-
ref,
|
|
337
|
-
);
|
|
302
|
+
const setButtonRef = mergeRefs<HTMLButtonElement>(popover.triggerRef, ref);
|
|
338
303
|
|
|
339
304
|
return (
|
|
340
305
|
<>
|
|
@@ -392,8 +357,7 @@ export function TabMenu({
|
|
|
392
357
|
id={menuId}
|
|
393
358
|
role="menu"
|
|
394
359
|
aria-label={label}
|
|
395
|
-
onKeyDown={
|
|
396
|
-
onFocus={handleFocus}
|
|
360
|
+
onKeyDown={handleListKeyDown}
|
|
397
361
|
{...mergeProps(
|
|
398
362
|
themeProps('tab-menu-dropdown'),
|
|
399
363
|
stylex.props(styles.dropdown),
|
|
@@ -407,7 +371,7 @@ export function TabMenu({
|
|
|
407
371
|
<div
|
|
408
372
|
key={option.value}
|
|
409
373
|
role="menuitem"
|
|
410
|
-
tabIndex={
|
|
374
|
+
tabIndex={0}
|
|
411
375
|
aria-current={isSelected ? 'true' : undefined}
|
|
412
376
|
onClick={() => handleSelect(option.value)}
|
|
413
377
|
onKeyDown={e => {
|