@astryxdesign/core 0.1.7 → 0.1.8-canary.08a0957
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/CHANGELOG.md +65 -0
- package/README.md +30 -19
- package/dist/AppShell/AppShell.d.ts.map +1 -1
- package/dist/AppShell/AppShell.js +6 -2
- package/dist/AppShell/AppShellMobileContext.d.ts +8 -0
- package/dist/AppShell/AppShellMobileContext.d.ts.map +1 -1
- package/dist/Avatar/Avatar.d.ts +10 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -8
- package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
- package/dist/Avatar/AvatarSizeContext.js +1 -1
- package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
- package/dist/Avatar/AvatarStatusDot.js +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +2 -2
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
- package/dist/Banner/Banner.d.ts.map +1 -1
- package/dist/Button/Button.d.ts.map +1 -1
- package/dist/Button/Button.js +1 -0
- package/dist/Carousel/Carousel.d.ts +3 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +32 -2
- package/dist/Chat/ChatLayout.d.ts.map +1 -1
- package/dist/Chat/ChatLayoutScrollButton.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.d.ts +1 -1
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +1 -1
- package/dist/Chat/ChatMessageMetadata.d.ts.map +1 -1
- package/dist/Chat/ChatSendButton.d.ts.map +1 -1
- package/dist/Chat/useTriggerMenu.d.ts.map +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +10 -4
- package/dist/CodeBlock/CodeBlock.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.d.ts +11 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +36 -4
- package/dist/CommandPalette/CommandPaletteInput.d.ts +7 -1
- package/dist/CommandPalette/CommandPaletteInput.d.ts.map +1 -1
- package/dist/CommandPalette/CommandPaletteInput.js +6 -1
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/ContextMenu/index.d.ts +3 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +7 -1
- package/dist/DateTimeInput/DateTimeInput.d.ts.map +1 -1
- package/dist/Dialog/Dialog.d.ts.map +1 -1
- package/dist/Dialog/Dialog.js +2 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +8 -4
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +88 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +169 -0
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +68 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
- package/dist/DropdownMenu/index.d.ts +5 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +10 -1
- package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemRoles.js +15 -0
- package/dist/FileInput/FileInput.d.ts.map +1 -1
- package/dist/Icon/Icon.d.ts +43 -13
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +62 -27
- package/dist/MobileNav/MobileNav.d.ts.map +1 -1
- package/dist/MobileNav/MobileNav.js +6 -1
- package/dist/MobileNav/MobileNavToggle.d.ts.map +1 -1
- package/dist/MobileNav/MobileNavToggle.js +4 -0
- package/dist/MultiSelector/MultiSelector.d.ts.map +1 -1
- package/dist/MultiSelector/MultiSelector.js +36 -10
- package/dist/NumberInput/NumberInput.d.ts.map +1 -1
- package/dist/Outline/Outline.d.ts +68 -3
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +128 -26
- package/dist/Outline/useScrollSpy.d.ts +27 -7
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +202 -70
- package/dist/PowerSearch/usePowerSearchSource.d.ts.map +1 -1
- package/dist/SideNav/SideNavCollapseButton.d.ts.map +1 -1
- package/dist/SideNav/SideNavItem.d.ts.map +1 -1
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +2 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/Table.d.ts +1 -1
- package/dist/Table/Table.js +1 -1
- package/dist/Table/index.d.ts +4 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +2 -0
- package/dist/Table/plugins/groupedRows/useTableGroupedRows.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
- package/dist/Table/plugins/rowStatus/index.d.ts +3 -0
- package/dist/Table/plugins/rowStatus/index.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/index.js +3 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts +60 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.d.ts.map +1 -0
- package/dist/Table/plugins/rowStatus/useTableRowStatus.js +145 -0
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
- package/dist/Table/plugins/tree/index.d.ts +5 -0
- package/dist/Table/plugins/tree/index.d.ts.map +1 -0
- package/dist/Table/plugins/tree/index.js +4 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +6 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +8 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +5 -14
- package/dist/astryx.css +26 -4
- package/dist/astryx.umd.js +54 -54
- package/dist/astryx.umd.js.map +4 -4
- package/dist/authoring/doc.d.ts.map +1 -1
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/hct.d.ts +0 -1
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +0 -3
- package/docs.mjs +32 -461
- package/locales/en.json +200 -172
- package/locales/pseudo.json +21 -0
- package/package.json +3 -1
- package/scripts/postinstall.mjs +93 -0
- package/src/AppShell/AppShell.tsx +6 -0
- package/src/AppShell/AppShellMobileContext.tsx +8 -0
- package/src/AppShell/useAppShellMobile.doc.mjs +8 -0
- package/src/Avatar/Avatar.doc.mjs +6 -6
- package/src/Avatar/Avatar.tsx +16 -11
- package/src/Avatar/AvatarSizeContext.ts +1 -1
- package/src/Avatar/AvatarStatusDot.tsx +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
- package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
- package/src/AvatarGroup/AvatarGroup.tsx +3 -3
- package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
- package/src/Banner/Banner.tsx +10 -2
- package/src/Button/Button.test.tsx +21 -0
- package/src/Button/Button.tsx +1 -0
- package/src/Carousel/Carousel.doc.mjs +1 -0
- package/src/Carousel/Carousel.test.tsx +96 -1
- package/src/Carousel/Carousel.tsx +29 -2
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatLayout.tsx +3 -1
- package/src/Chat/ChatLayoutScrollButton.tsx +3 -1
- package/src/Chat/ChatMessage.tsx +4 -2
- package/src/Chat/ChatMessageMetadata.tsx +1 -2
- package/src/Chat/ChatSendButton.tsx +5 -1
- package/src/Chat/useTriggerMenu.tsx +3 -1
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/Citation/Citation.test.tsx +51 -0
- package/src/Citation/Citation.tsx +8 -2
- package/src/CodeBlock/CodeBlock.tsx +3 -1
- package/src/Collapsible/Collapsible.doc.mjs +7 -0
- package/src/Collapsible/Collapsible.test.tsx +80 -0
- package/src/Collapsible/Collapsible.tsx +58 -5
- package/src/CommandPalette/CommandPaletteInput.doc.mjs +26 -9
- package/src/CommandPalette/CommandPaletteInput.test.tsx +34 -0
- package/src/CommandPalette/CommandPaletteInput.tsx +11 -0
- package/src/ContextMenu/ContextMenu.test.tsx +75 -1
- package/src/ContextMenu/ContextMenu.tsx +10 -5
- package/src/ContextMenu/index.ts +15 -0
- package/src/DateTimeInput/DateTimeInput.tsx +3 -1
- package/src/Dialog/Dialog.tsx +12 -2
- package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
- package/src/DropdownMenu/DropdownMenu.tsx +11 -6
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +236 -0
- package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +118 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
- package/src/DropdownMenu/index.ts +23 -3
- package/src/DropdownMenu/menuItemRoles.ts +22 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/FieldLabel.doc.mjs +1 -1
- package/src/FileInput/FileInput.tsx +4 -1
- package/src/Icon/Icon.doc.mjs +12 -0
- package/src/Icon/Icon.test.tsx +142 -0
- package/src/Icon/Icon.tsx +98 -30
- package/src/Item/Item.doc.mjs +1 -1
- package/src/MobileNav/MobileNav.tsx +12 -1
- package/src/MobileNav/MobileNavToggle.test.tsx +116 -0
- package/src/MobileNav/MobileNavToggle.tsx +9 -1
- package/src/MultiSelector/MultiSelector.doc.mjs +4 -2
- package/src/MultiSelector/MultiSelector.test.tsx +79 -1
- package/src/MultiSelector/MultiSelector.tsx +51 -11
- package/src/NumberInput/NumberInput.doc.mjs +2 -2
- package/src/NumberInput/NumberInput.tsx +1 -2
- package/src/Outline/Outline.doc.mjs +123 -3
- package/src/Outline/Outline.test.tsx +735 -0
- package/src/Outline/Outline.tsx +183 -33
- package/src/Outline/useScrollSpy.ts +248 -72
- package/src/PowerSearch/usePowerSearchSource.ts +1 -2
- package/src/Selector/SelectorOption.doc.mjs +1 -1
- package/src/SideNav/SideNavCollapseButton.tsx +6 -1
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/SideNav/SideNavItem.tsx +5 -1
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/TabList/Tab.tsx +5 -1
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/TabList.tsx +8 -0
- package/src/TabList/TabMenu.tsx +4 -1
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.tsx +1 -1
- package/src/Table/index.ts +12 -0
- package/src/Table/plugins/groupedRows/useTableGroupedRows.tsx +6 -2
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
- package/src/Table/plugins/rowStatus/index.ts +7 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.test.tsx +138 -0
- package/src/Table/plugins/rowStatus/useTableRowStatus.tsx +189 -0
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
- package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
- package/src/Table/plugins/tree/index.ts +12 -0
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
- package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
- package/src/Table/useBaseTablePlugins.ts +6 -2
- package/src/Table/useTableRowStatus.doc.mjs +30 -0
- package/src/Table/useTableTreeData.doc.mjs +75 -0
- package/src/Table/useTableTreeState.doc.mjs +98 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/Toolbar/Toolbar.tsx +10 -3
- package/src/TreeList/TreeList.test.tsx +32 -1
- package/src/TreeList/TreeListItem.tsx +19 -10
- package/src/authoring/doc.ts +1 -3
- package/src/docs-script.test.mjs +18 -13
- package/src/postinstall.test.mjs +71 -0
- package/src/theme/Theme.doc.mjs +4 -4
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/hct.ts +0 -4
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
- package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
- package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
|
@@ -12,11 +12,9 @@ import {render, screen, fireEvent} from '@testing-library/react';
|
|
|
12
12
|
import userEvent from '@testing-library/user-event';
|
|
13
13
|
import {useState} from 'react';
|
|
14
14
|
import {Table} from '../../Table';
|
|
15
|
+
import {InternationalizationProvider} from '../../../i18n';
|
|
15
16
|
import type {TableColumn} from '../../types';
|
|
16
|
-
import {
|
|
17
|
-
useTableSortable,
|
|
18
|
-
type TableSortState,
|
|
19
|
-
} from './useTableSortable';
|
|
17
|
+
import {useTableSortable, type TableSortState} from './useTableSortable';
|
|
20
18
|
|
|
21
19
|
// =============================================================================
|
|
22
20
|
// Test Data
|
|
@@ -73,9 +71,7 @@ function SortableTable({
|
|
|
73
71
|
isMultiSortEnabled,
|
|
74
72
|
});
|
|
75
73
|
|
|
76
|
-
return
|
|
77
|
-
<Table data={data} columns={columns} plugins={{sort: sortPlugin}} />
|
|
78
|
-
);
|
|
74
|
+
return <Table data={data} columns={columns} plugins={{sort: sortPlugin}} />;
|
|
79
75
|
}
|
|
80
76
|
|
|
81
77
|
// =============================================================================
|
|
@@ -672,11 +668,15 @@ describe('useTableSortable — context menu actions', () => {
|
|
|
672
668
|
|
|
673
669
|
// Apply ascending → "Clear sort" now appears.
|
|
674
670
|
fireEvent.click(
|
|
675
|
-
screen.getAllByRole('menuitem', {
|
|
671
|
+
screen.getAllByRole('menuitem', {
|
|
672
|
+
name: 'Sort ascending',
|
|
673
|
+
hidden: true,
|
|
674
|
+
})[0],
|
|
676
675
|
);
|
|
677
676
|
fireEvent.contextMenu(screen.getByText('Name'));
|
|
678
677
|
expect(
|
|
679
|
-
screen.getAllByRole('menuitem', {name: 'Clear sort', hidden: true})
|
|
678
|
+
screen.getAllByRole('menuitem', {name: 'Clear sort', hidden: true})
|
|
679
|
+
.length,
|
|
680
680
|
).toBeGreaterThan(0);
|
|
681
681
|
});
|
|
682
682
|
|
|
@@ -709,3 +709,89 @@ describe('useTableSortable — context menu actions', () => {
|
|
|
709
709
|
expect(onSortChange).toHaveBeenLastCalledWith([]);
|
|
710
710
|
});
|
|
711
711
|
});
|
|
712
|
+
|
|
713
|
+
// =============================================================================
|
|
714
|
+
// i18n (header-button aria-labels route through the catalog)
|
|
715
|
+
// =============================================================================
|
|
716
|
+
|
|
717
|
+
describe('useTableSortable — i18n', () => {
|
|
718
|
+
it('localizes the unsorted sort-by aria-label via @astryx.table.sort.sortBy', () => {
|
|
719
|
+
render(
|
|
720
|
+
<InternationalizationProvider
|
|
721
|
+
locale="fr"
|
|
722
|
+
overrides={{
|
|
723
|
+
fr: {'@astryx.table.sort.sortBy': 'Trier par {label}'},
|
|
724
|
+
}}>
|
|
725
|
+
<SortableTable />
|
|
726
|
+
</InternationalizationProvider>,
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
expect(
|
|
730
|
+
screen.getByRole('button', {name: 'Trier par Name'}),
|
|
731
|
+
).toBeInTheDocument();
|
|
732
|
+
expect(
|
|
733
|
+
screen.getByRole('button', {name: 'Trier par Age'}),
|
|
734
|
+
).toBeInTheDocument();
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
it('localizes the multi-sort priority aria-label with ICU number args', () => {
|
|
738
|
+
render(
|
|
739
|
+
<InternationalizationProvider
|
|
740
|
+
locale="fr"
|
|
741
|
+
overrides={{
|
|
742
|
+
fr: {
|
|
743
|
+
'@astryx.table.sort.sortedByWithPriority':
|
|
744
|
+
'Trier par {label}, tri {direction}, priorité {rank, number} sur {total, number}',
|
|
745
|
+
'@astryx.table.sort.direction.ascending': 'croissant',
|
|
746
|
+
'@astryx.table.sort.direction.descending': 'décroissant',
|
|
747
|
+
},
|
|
748
|
+
}}>
|
|
749
|
+
<SortableTable
|
|
750
|
+
isMultiSortEnabled
|
|
751
|
+
initialSort={[
|
|
752
|
+
{sortKey: 'name', direction: 'ascending'},
|
|
753
|
+
{sortKey: 'age', direction: 'descending'},
|
|
754
|
+
]}
|
|
755
|
+
/>
|
|
756
|
+
</InternationalizationProvider>,
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
// The overridden direction words deliberately differ from the raw enum
|
|
760
|
+
// values ('ascending'/'descending'), so an implementation interpolating
|
|
761
|
+
// the enum without translating it cannot pass — for either direction.
|
|
762
|
+
expect(
|
|
763
|
+
screen.getByRole('button', {
|
|
764
|
+
name: 'Trier par Name, tri croissant, priorité 1 sur 2',
|
|
765
|
+
}),
|
|
766
|
+
).toBeInTheDocument();
|
|
767
|
+
expect(
|
|
768
|
+
screen.getByRole('button', {
|
|
769
|
+
name: 'Trier par Age, tri décroissant, priorité 2 sur 2',
|
|
770
|
+
}),
|
|
771
|
+
).toBeInTheDocument();
|
|
772
|
+
});
|
|
773
|
+
|
|
774
|
+
it('localizes the sorted aria-label and direction word via their own keys', () => {
|
|
775
|
+
render(
|
|
776
|
+
<InternationalizationProvider
|
|
777
|
+
locale="fr"
|
|
778
|
+
overrides={{
|
|
779
|
+
fr: {
|
|
780
|
+
'@astryx.table.sort.sortedBy': 'Trié par {label}, {direction}',
|
|
781
|
+
'@astryx.table.sort.direction.ascending': 'croissant',
|
|
782
|
+
},
|
|
783
|
+
}}>
|
|
784
|
+
<SortableTable
|
|
785
|
+
initialSort={[{sortKey: 'name', direction: 'ascending'}]}
|
|
786
|
+
/>
|
|
787
|
+
</InternationalizationProvider>,
|
|
788
|
+
);
|
|
789
|
+
|
|
790
|
+
// Both the composed template and the direction word resolve through
|
|
791
|
+
// their own catalog keys; 'croissant' differs from the raw enum value,
|
|
792
|
+
// so neither a hardcoded English frame nor raw-enum interpolation passes.
|
|
793
|
+
expect(
|
|
794
|
+
screen.getByRole('button', {name: 'Trié par Name, croissant'}),
|
|
795
|
+
).toBeInTheDocument();
|
|
796
|
+
});
|
|
797
|
+
});
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @file useTableSortable.tsx
|
|
7
|
-
* @input React, types, Icon, theme tokens
|
|
7
|
+
* @input React, types, Icon, theme tokens, i18n (useTranslator)
|
|
8
8
|
* @output Exports useTableSortable hook and sort-related types
|
|
9
9
|
* @position Sortable plugin; consumed by Table via plugins prop
|
|
10
10
|
*
|
|
@@ -18,7 +18,7 @@ import * as stylex from '@stylexjs/stylex';
|
|
|
18
18
|
import {colorVars, spacingVars, radiusVars} from '../../../theme/tokens.stylex';
|
|
19
19
|
import {Icon} from '../../../Icon';
|
|
20
20
|
import {resolveContextActions} from '../../tableContextMenu';
|
|
21
|
-
import {useTranslator} from '../../../i18n';
|
|
21
|
+
import {useTranslator, type TranslatorFn} from '../../../i18n';
|
|
22
22
|
import type {
|
|
23
23
|
TablePlugin,
|
|
24
24
|
HeaderCellRenderProps,
|
|
@@ -184,6 +184,7 @@ function getHeaderLabel<T extends Record<string, unknown>>(
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
function buildAriaLabel<T extends Record<string, unknown>>(
|
|
187
|
+
t: TranslatorFn,
|
|
187
188
|
column: TableColumn<T>,
|
|
188
189
|
direction: TableSortDirection | null,
|
|
189
190
|
rank: number | null,
|
|
@@ -191,13 +192,21 @@ function buildAriaLabel<T extends Record<string, unknown>>(
|
|
|
191
192
|
): string {
|
|
192
193
|
const label = getHeaderLabel(column);
|
|
193
194
|
if (direction == null) {
|
|
194
|
-
return
|
|
195
|
+
return t('@astryx.table.sort.sortBy', {label});
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
const directionLabel =
|
|
198
|
+
direction === 'ascending'
|
|
199
|
+
? t('@astryx.table.sort.direction.ascending')
|
|
200
|
+
: t('@astryx.table.sort.direction.descending');
|
|
197
201
|
if (rank != null && total > 1) {
|
|
198
|
-
|
|
202
|
+
return t('@astryx.table.sort.sortedByWithPriority', {
|
|
203
|
+
label,
|
|
204
|
+
direction: directionLabel,
|
|
205
|
+
rank,
|
|
206
|
+
total,
|
|
207
|
+
});
|
|
199
208
|
}
|
|
200
|
-
return
|
|
209
|
+
return t('@astryx.table.sort.sortedBy', {label, direction: directionLabel});
|
|
201
210
|
}
|
|
202
211
|
|
|
203
212
|
function getNextDirection(
|
|
@@ -227,6 +236,7 @@ function SortHeaderButton<T extends Record<string, unknown>>({
|
|
|
227
236
|
children: ReactNode;
|
|
228
237
|
configRef: React.RefObject<UseTableSortableConfig>;
|
|
229
238
|
}) {
|
|
239
|
+
const t = useTranslator();
|
|
230
240
|
const config = configRef.current;
|
|
231
241
|
const sortKey = resolveSortKey(column) ?? '';
|
|
232
242
|
const entryIndex = config.sort.findIndex(e => e.sortKey === sortKey);
|
|
@@ -244,7 +254,13 @@ function SortHeaderButton<T extends Record<string, unknown>>({
|
|
|
244
254
|
? 'arrowDown'
|
|
245
255
|
: 'arrowsUpDown';
|
|
246
256
|
|
|
247
|
-
const ariaLabel = buildAriaLabel(
|
|
257
|
+
const ariaLabel = buildAriaLabel(
|
|
258
|
+
t,
|
|
259
|
+
column,
|
|
260
|
+
direction,
|
|
261
|
+
rank,
|
|
262
|
+
config.sort.length,
|
|
263
|
+
);
|
|
248
264
|
|
|
249
265
|
const handleClick = (e: React.MouseEvent | React.KeyboardEvent) => {
|
|
250
266
|
const cfg = configRef.current;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
export {useTableTreeData} from './useTableTreeData';
|
|
4
|
+
export type {
|
|
5
|
+
TableTreeRowMeta,
|
|
6
|
+
UseTableTreeDataConfig,
|
|
7
|
+
} from './useTableTreeData';
|
|
8
|
+
export {useTableTreeState} from './useTableTreeState';
|
|
9
|
+
export type {
|
|
10
|
+
UseTableTreeStateConfig,
|
|
11
|
+
UseTableTreeStateResult,
|
|
12
|
+
} from './useTableTreeState';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file useTableTreeData-perf.test.tsx
|
|
5
|
+
* @input Table, useTableTreeData, useTableTreeState, React testing utilities
|
|
6
|
+
* @output Performance tests for tree plugin render behavior
|
|
7
|
+
* @position Test file; validates toggle render efficiency and DOM cost
|
|
8
|
+
*
|
|
9
|
+
* The tree plugin uses an external store so a toggle re-renders only the
|
|
10
|
+
* affected cells — not the whole table body. Note: expanding inserts rows,
|
|
11
|
+
* which shifts the rowIndex of every row *after* the insertion point (the
|
|
12
|
+
* row memo comparator includes rowIndex by design), so the guarantee is
|
|
13
|
+
* scoped to rows before the toggled row.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {describe, it, expect} from 'vitest';
|
|
17
|
+
import {render, screen, act} from '@testing-library/react';
|
|
18
|
+
import {useMemo} from 'react';
|
|
19
|
+
import userEvent from '@testing-library/user-event';
|
|
20
|
+
import {Table} from '../../Table';
|
|
21
|
+
import type {TableColumn} from '../../types';
|
|
22
|
+
import {useTableTreeData} from './useTableTreeData';
|
|
23
|
+
import {useTableTreeState} from './useTableTreeState';
|
|
24
|
+
|
|
25
|
+
interface TreeRow extends Record<string, unknown> {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
children?: TreeRow[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 5 roots; the last root has 2 children. */
|
|
32
|
+
const roots: TreeRow[] = [
|
|
33
|
+
...Array.from({length: 4}, (_, i) => ({
|
|
34
|
+
id: `root-${i}`,
|
|
35
|
+
name: `Root ${i}`,
|
|
36
|
+
})),
|
|
37
|
+
{
|
|
38
|
+
id: 'root-4',
|
|
39
|
+
name: 'Root 4',
|
|
40
|
+
children: [
|
|
41
|
+
{id: 'child-0', name: 'Child 0'},
|
|
42
|
+
{id: 'child-1', name: 'Child 1'},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
function TreeRenderCountTable({
|
|
48
|
+
renderCounts,
|
|
49
|
+
}: {
|
|
50
|
+
renderCounts: Record<string, number>;
|
|
51
|
+
}) {
|
|
52
|
+
const columns = useMemo<TableColumn<TreeRow>[]>(
|
|
53
|
+
() => [
|
|
54
|
+
{
|
|
55
|
+
key: 'name',
|
|
56
|
+
header: 'Name',
|
|
57
|
+
renderCell: (item: TreeRow) => {
|
|
58
|
+
renderCounts[item.id] = (renderCounts[item.id] ?? 0) + 1;
|
|
59
|
+
return item.name;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
[renderCounts],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const {visibleData, treeConfig} = useTableTreeState<TreeRow>({
|
|
67
|
+
data: roots,
|
|
68
|
+
idKey: 'id',
|
|
69
|
+
});
|
|
70
|
+
const tree = useTableTreeData(treeConfig);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Table data={visibleData} columns={columns} idKey="id" plugins={{tree}} />
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe('Tree plugin render performance', () => {
|
|
78
|
+
it('expanding the last root does not re-render preceding rows', async () => {
|
|
79
|
+
const user = userEvent.setup();
|
|
80
|
+
const renderCounts: Record<string, number> = {};
|
|
81
|
+
|
|
82
|
+
render(<TreeRenderCountTable renderCounts={renderCounts} />);
|
|
83
|
+
|
|
84
|
+
const initialCounts = {...renderCounts};
|
|
85
|
+
expect(Object.keys(initialCounts)).toHaveLength(5);
|
|
86
|
+
|
|
87
|
+
await act(async () => {
|
|
88
|
+
await user.click(screen.getByRole('button', {name: 'Expand row'}));
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
for (let i = 0; i < 4; i++) {
|
|
92
|
+
expect(renderCounts[`root-${i}`]).toBe(initialCounts[`root-${i}`]);
|
|
93
|
+
}
|
|
94
|
+
// The children mounted exactly once.
|
|
95
|
+
expect(renderCounts['child-0']).toBe(1);
|
|
96
|
+
expect(renderCounts['child-1']).toBe(1);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('collapsing unmounts the subtree rows entirely', async () => {
|
|
100
|
+
const user = userEvent.setup();
|
|
101
|
+
const renderCounts: Record<string, number> = {};
|
|
102
|
+
|
|
103
|
+
render(<TreeRenderCountTable renderCounts={renderCounts} />);
|
|
104
|
+
|
|
105
|
+
await act(async () => {
|
|
106
|
+
await user.click(screen.getByRole('button', {name: 'Expand row'}));
|
|
107
|
+
});
|
|
108
|
+
expect(screen.getAllByRole('row')).toHaveLength(8); // header + 5 roots + 2 children
|
|
109
|
+
|
|
110
|
+
await act(async () => {
|
|
111
|
+
await user.click(screen.getByRole('button', {name: 'Collapse row'}));
|
|
112
|
+
});
|
|
113
|
+
expect(screen.getAllByRole('row')).toHaveLength(6); // header + 5 roots
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('adds at most one wrapper, one expander/spacer to the tree column and zero DOM to other columns', () => {
|
|
117
|
+
const {container} = render(
|
|
118
|
+
<Table
|
|
119
|
+
data={
|
|
120
|
+
[{id: 'a', name: 'a', children: [{id: 'b', name: 'b'}]}] as TreeRow[]
|
|
121
|
+
}
|
|
122
|
+
columns={[
|
|
123
|
+
{key: 'name', header: 'Name'},
|
|
124
|
+
{key: 'id', header: 'Id'},
|
|
125
|
+
]}
|
|
126
|
+
idKey="id"
|
|
127
|
+
plugins={{}}
|
|
128
|
+
/>,
|
|
129
|
+
);
|
|
130
|
+
const plainSecondCellHTML =
|
|
131
|
+
container.querySelectorAll('tbody td')[1].innerHTML;
|
|
132
|
+
|
|
133
|
+
function DomBudgetTable() {
|
|
134
|
+
const {visibleData, treeConfig} = useTableTreeState<TreeRow>({
|
|
135
|
+
data: [{id: 'a', name: 'a', children: [{id: 'b', name: 'b'}]}],
|
|
136
|
+
idKey: 'id',
|
|
137
|
+
});
|
|
138
|
+
const tree = useTableTreeData(treeConfig);
|
|
139
|
+
return (
|
|
140
|
+
<Table
|
|
141
|
+
data={visibleData}
|
|
142
|
+
columns={[
|
|
143
|
+
{key: 'name', header: 'Name'},
|
|
144
|
+
{key: 'id', header: 'Id'},
|
|
145
|
+
]}
|
|
146
|
+
idKey="id"
|
|
147
|
+
plugins={{tree}}
|
|
148
|
+
/>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const {container: treeContainer} = render(<DomBudgetTable />);
|
|
152
|
+
const cells = treeContainer.querySelectorAll('tbody td');
|
|
153
|
+
|
|
154
|
+
// Tree column: one flex wrapper containing one expander button + text.
|
|
155
|
+
const wrapper = cells[0].firstElementChild as HTMLElement;
|
|
156
|
+
expect(wrapper.tagName).toBe('DIV');
|
|
157
|
+
expect(wrapper.children).toHaveLength(1);
|
|
158
|
+
expect(wrapper.querySelector('button')).not.toBeNull();
|
|
159
|
+
|
|
160
|
+
// Other columns: byte-identical to the plugin-free render.
|
|
161
|
+
expect(cells[1].innerHTML).toBe(plainSecondCellHTML);
|
|
162
|
+
});
|
|
163
|
+
});
|