@wallarm-org/design-system 0.66.1 → 0.66.2-rc-fix-table-empty-state-story.1
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/components/Page/PageContent.js +1 -1
- package/dist/components/Page/PageHeader.js +1 -1
- package/dist/components/Pagination/PaginationPageSize.js +11 -6
- package/dist/components/Pagination/index.d.ts +1 -0
- package/dist/components/Pagination/index.js +2 -1
- package/dist/components/Pagination/lib/index.d.ts +1 -0
- package/dist/components/Pagination/lib/index.js +2 -1
- package/dist/components/Pagination/lib/useClientPagination.d.ts +12 -0
- package/dist/components/Pagination/lib/useClientPagination.js +28 -0
- package/dist/components/Select/SelectButton.d.ts +1 -0
- package/dist/components/Select/SelectButton.js +2 -2
- package/dist/components/Table/TableEmptyState.js +2 -2
- package/dist/components/Table/lib/createSelectionColumn.js +3 -1
- package/dist/metadata/components.json +17 -8
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ const PageContent = ({ ref, children, className, ...props })=>{
|
|
|
8
8
|
ref: ref,
|
|
9
9
|
"data-testid": testId,
|
|
10
10
|
"data-slot": "page-content",
|
|
11
|
-
className: cn('flex-1 min-h-0 overflow-auto px-16', className),
|
|
11
|
+
className: cn('flex-1 min-h-0 overflow-auto px-16 pt-8 pb-16', className),
|
|
12
12
|
children: children
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -8,7 +8,7 @@ const PageHeader = ({ ref, sticky, children, className, ...props })=>{
|
|
|
8
8
|
ref: ref,
|
|
9
9
|
"data-testid": testId,
|
|
10
10
|
"data-slot": "page-header",
|
|
11
|
-
className: cn('flex items-center px-16 pb-
|
|
11
|
+
className: cn('flex items-center px-16 pb-8 gap-12', sticky && 'sticky top-0 z-10 bg-bg-surface-2', className),
|
|
12
12
|
children: children
|
|
13
13
|
});
|
|
14
14
|
};
|
|
@@ -3,7 +3,7 @@ import { useMemo } from "react";
|
|
|
3
3
|
import { usePaginationContext } from "@ark-ui/react/pagination";
|
|
4
4
|
import { cn } from "../../utils/cn.js";
|
|
5
5
|
import { useTestId } from "../../utils/testId.js";
|
|
6
|
-
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionText, SelectPositioner } from "../Select/index.js";
|
|
6
|
+
import { Select, SelectButton, SelectContent, SelectOption, SelectOptionIndicator, SelectOptionText, SelectPositioner } from "../Select/index.js";
|
|
7
7
|
import { Separator } from "../Separator/index.js";
|
|
8
8
|
import { Text } from "../Text/index.js";
|
|
9
9
|
import { PAGINATION_PAGE_SIZE_LABEL } from "./constants.js";
|
|
@@ -29,7 +29,7 @@ const PaginationPageSize = ({ options, label = PAGINATION_PAGE_SIZE_LABEL, class
|
|
|
29
29
|
})
|
|
30
30
|
}),
|
|
31
31
|
/*#__PURE__*/ jsx("div", {
|
|
32
|
-
className: "w-
|
|
32
|
+
className: "w-60",
|
|
33
33
|
children: /*#__PURE__*/ jsxs(Select, {
|
|
34
34
|
collection: collection,
|
|
35
35
|
value: [
|
|
@@ -39,15 +39,20 @@ const PaginationPageSize = ({ options, label = PAGINATION_PAGE_SIZE_LABEL, class
|
|
|
39
39
|
"data-testid": testId,
|
|
40
40
|
children: [
|
|
41
41
|
/*#__PURE__*/ jsx(SelectButton, {
|
|
42
|
+
size: "medium",
|
|
42
43
|
"aria-label": label
|
|
43
44
|
}),
|
|
44
45
|
/*#__PURE__*/ jsx(SelectPositioner, {
|
|
46
|
+
className: "min-w-0 w-88",
|
|
45
47
|
children: /*#__PURE__*/ jsx(SelectContent, {
|
|
46
|
-
children: collection.items.map((item)=>/*#__PURE__*/
|
|
48
|
+
children: collection.items.map((item)=>/*#__PURE__*/ jsxs(SelectOption, {
|
|
47
49
|
item: item,
|
|
48
|
-
children:
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
children: [
|
|
51
|
+
/*#__PURE__*/ jsx(SelectOptionText, {
|
|
52
|
+
children: item.label
|
|
53
|
+
}),
|
|
54
|
+
/*#__PURE__*/ jsx(SelectOptionIndicator, {})
|
|
55
|
+
]
|
|
51
56
|
}, item.value))
|
|
52
57
|
})
|
|
53
58
|
})
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useClientPagination } from "./lib/index.js";
|
|
1
2
|
import { Pagination } from "./Pagination.js";
|
|
2
3
|
import { usePaginationSizeContext } from "./PaginationContext.js";
|
|
3
4
|
import { PaginationEllipsis } from "./PaginationEllipsis.js";
|
|
@@ -6,4 +7,4 @@ import { PaginationList } from "./PaginationList.js";
|
|
|
6
7
|
import { PaginationNext } from "./PaginationNext.js";
|
|
7
8
|
import { PaginationPageSize } from "./PaginationPageSize.js";
|
|
8
9
|
import { PaginationPrevious } from "./PaginationPrevious.js";
|
|
9
|
-
export { Pagination, PaginationEllipsis, PaginationItem, PaginationList, PaginationNext, PaginationPageSize, PaginationPrevious, usePaginationSizeContext };
|
|
10
|
+
export { Pagination, PaginationEllipsis, PaginationItem, PaginationList, PaginationNext, PaginationPageSize, PaginationPrevious, useClientPagination, usePaginationSizeContext };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PaginationPageChangeDetails, PaginationPageSizeChangeDetails } from '../types';
|
|
2
|
+
interface ClientPagination<T> {
|
|
3
|
+
count: number;
|
|
4
|
+
page: number;
|
|
5
|
+
pageSize: number;
|
|
6
|
+
pageData: T[];
|
|
7
|
+
onPageChange: (details: PaginationPageChangeDetails) => void;
|
|
8
|
+
onPageSizeChange: (details: PaginationPageSizeChangeDetails) => void;
|
|
9
|
+
}
|
|
10
|
+
/** Client-side paging: slices `data` for the current page and tracks page state. */
|
|
11
|
+
export declare function useClientPagination<T>(data: T[], initialPageSize?: number): ClientPagination<T>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
function useClientPagination(data, initialPageSize = 10) {
|
|
3
|
+
const [requestedPage, setRequestedPage] = useState(1);
|
|
4
|
+
const [pageSize, setPageSize] = useState(initialPageSize);
|
|
5
|
+
const count = data.length;
|
|
6
|
+
const totalPages = Math.max(1, Math.ceil(count / pageSize));
|
|
7
|
+
const page = Math.min(requestedPage, totalPages);
|
|
8
|
+
const pageData = useMemo(()=>{
|
|
9
|
+
const start = (page - 1) * pageSize;
|
|
10
|
+
return data.slice(start, start + pageSize);
|
|
11
|
+
}, [
|
|
12
|
+
data,
|
|
13
|
+
page,
|
|
14
|
+
pageSize
|
|
15
|
+
]);
|
|
16
|
+
return {
|
|
17
|
+
count,
|
|
18
|
+
page,
|
|
19
|
+
pageSize,
|
|
20
|
+
pageData,
|
|
21
|
+
onPageChange: ({ page })=>setRequestedPage(page),
|
|
22
|
+
onPageSizeChange: ({ pageSize })=>{
|
|
23
|
+
setPageSize(pageSize);
|
|
24
|
+
setRequestedPage(1);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export { useClientPagination };
|
|
@@ -5,6 +5,7 @@ type SelectButtonBaseProps = Omit<ButtonProps, 'variant' | 'color' | 'size' | 'd
|
|
|
5
5
|
export interface SelectButtonVariantProps {
|
|
6
6
|
variant?: Exclude<ButtonProps['variant'], 'primary'>;
|
|
7
7
|
color?: Exclude<ButtonProps['color'], 'destructive'>;
|
|
8
|
+
size?: ButtonProps['size'];
|
|
8
9
|
}
|
|
9
10
|
type SelectButtonProps = SelectButtonBaseProps & SelectButtonVariantProps & SelectValueTextProps;
|
|
10
11
|
export declare const SelectButton: FC<SelectButtonProps>;
|
|
@@ -6,7 +6,7 @@ import { SelectArrow } from "./SelectArrow.js";
|
|
|
6
6
|
import { useSelectSharedContext } from "./SelectSharedContext/index.js";
|
|
7
7
|
import { SelectValueIcon } from "./SelectValueIcon.js";
|
|
8
8
|
import { SelectValueText } from "./SelectValueText.js";
|
|
9
|
-
const SelectButton = ({ placeholder = 'Choose...', variant = 'outline', color = 'neutral', 'data-testid': testIdProp, ...props })=>{
|
|
9
|
+
const SelectButton = ({ placeholder = 'Choose...', variant = 'outline', color = 'neutral', size = 'large', 'data-testid': testIdProp, ...props })=>{
|
|
10
10
|
const testId = useTestId('button', testIdProp);
|
|
11
11
|
const { loading } = useSelectSharedContext();
|
|
12
12
|
const { disabled } = useSelectContext();
|
|
@@ -18,7 +18,7 @@ const SelectButton = ({ placeholder = 'Choose...', variant = 'outline', color =
|
|
|
18
18
|
"data-testid": testId,
|
|
19
19
|
variant: variant,
|
|
20
20
|
color: color,
|
|
21
|
-
size:
|
|
21
|
+
size: size,
|
|
22
22
|
loading: loading,
|
|
23
23
|
disabled: disabled,
|
|
24
24
|
fullWidth: true,
|
|
@@ -4,9 +4,9 @@ import { useTestId } from "../../utils/testId.js";
|
|
|
4
4
|
import { useTableContext } from "./TableContext/index.js";
|
|
5
5
|
const TableEmptyState = ({ children })=>{
|
|
6
6
|
const testId = useTestId('empty-state');
|
|
7
|
-
const { table } = useTableContext();
|
|
7
|
+
const { table, isLoading } = useTableContext();
|
|
8
8
|
const { rows } = table.getRowModel();
|
|
9
|
-
const tableIsEmpty = !rows.length;
|
|
9
|
+
const tableIsEmpty = !rows.length && !isLoading;
|
|
10
10
|
if (!tableIsEmpty) return null;
|
|
11
11
|
return /*#__PURE__*/ jsx("div", {
|
|
12
12
|
"data-testid": testId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useIsKeyPressed } from "../../../hooks/index.js";
|
|
3
3
|
import { Checkbox, CheckboxIndicator } from "../../Checkbox/index.js";
|
|
4
|
-
import { useTableContext } from "../TableContext/
|
|
4
|
+
import { useTableContext } from "../TableContext/index.js";
|
|
5
5
|
import { TABLE_SELECT_COLUMN_ID, TABLE_SELECT_COLUMN_WIDTH } from "./constants.js";
|
|
6
6
|
const applyRangeSelection = (rows, fromIndex, toIndex, table, clickedRow)=>{
|
|
7
7
|
const start = Math.min(fromIndex, toIndex);
|
|
@@ -57,9 +57,11 @@ const createSelectionColumn = ()=>({
|
|
|
57
57
|
header: ({ table })=>{
|
|
58
58
|
const checked = table.getIsAllPageRowsSelected();
|
|
59
59
|
const indeterminate = table.getIsSomePageRowsSelected();
|
|
60
|
+
const rowCount = table.getRowCount();
|
|
60
61
|
return /*#__PURE__*/ jsx(Checkbox, {
|
|
61
62
|
checked: indeterminate ? 'indeterminate' : checked,
|
|
62
63
|
onCheckedChange: ()=>table.toggleAllPageRowsSelected(!checked),
|
|
64
|
+
disabled: 0 === rowCount,
|
|
63
65
|
children: /*#__PURE__*/ jsx(CheckboxIndicator, {})
|
|
64
66
|
});
|
|
65
67
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.66.
|
|
3
|
-
"generatedAt": "2026-06-
|
|
2
|
+
"version": "0.66.1",
|
|
3
|
+
"generatedAt": "2026-06-23T05:30:21.081Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Accordion",
|
|
@@ -43997,7 +43997,7 @@
|
|
|
43997
43997
|
},
|
|
43998
43998
|
{
|
|
43999
43999
|
"name": "WithPageSize",
|
|
44000
|
-
"code": "() => (\n <Pagination count={120} defaultPageSize={25} defaultPage={2} align='
|
|
44000
|
+
"code": "() => (\n <Pagination count={120} defaultPageSize={25} defaultPage={2} align='right'>\n <PaginationPageSize options={[10, 25, 50]} />\n <PaginationPrevious />\n <PaginationList />\n <PaginationNext />\n </Pagination>\n)"
|
|
44001
44001
|
},
|
|
44002
44002
|
{
|
|
44003
44003
|
"name": "Sizes",
|
|
@@ -44017,8 +44017,7 @@
|
|
|
44017
44017
|
},
|
|
44018
44018
|
{
|
|
44019
44019
|
"name": "InTable",
|
|
44020
|
-
"code": "() => {\n const
|
|
44021
|
-
"description": "A table whose rows are paged by a Pagination footer: \"Rows per page\" on the\nleft, page navigation on the right. Changing the page or page size slices the\ndata set client-side (`count` = total rows, Ark derives the page count)."
|
|
44020
|
+
"code": "() => {\n const { pageData, ...pagination } = useClientPagination(apiEndpoints, 10);\n\n return (\n <div className='w-800'>\n <VStack gap={12}>\n <Table data={pageData} columns={endpointColumns} getRowId={row => row.id} />\n <Pagination {...pagination} align='right' aria-label='API endpoints'>\n <PaginationPageSize options={[5, 10, 25]} />\n <PaginationPrevious />\n <PaginationList />\n <PaginationNext />\n </Pagination>\n </VStack>\n </div>\n );\n}"
|
|
44022
44021
|
}
|
|
44023
44022
|
]
|
|
44024
44023
|
},
|
|
@@ -70409,6 +70408,10 @@
|
|
|
70409
70408
|
"name": "Basic",
|
|
70410
70409
|
"code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([{ id: 'firstDetected', desc: true }]);\n\n return (\n <Table\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n />\n );\n}"
|
|
70411
70410
|
},
|
|
70411
|
+
{
|
|
70412
|
+
"name": "WithPagination",
|
|
70413
|
+
"code": "() => {\n const allData = useMemo(() => createLargeSecurityEvents(47), []);\n const { pageData, ...pagination } = useClientPagination(allData, 10);\n\n return (\n <VStack gap={12}>\n <Table data={pageData} columns={securityColumns} getRowId={row => row.id} />\n <Pagination {...pagination} align='right' aria-label='Security events'>\n <PaginationPageSize options={[10, 25, 50]} />\n <PaginationPrevious />\n <PaginationList />\n <PaginationNext />\n </Pagination>\n </VStack>\n );\n}"
|
|
70414
|
+
},
|
|
70412
70415
|
{
|
|
70413
70416
|
"name": "Sorting",
|
|
70414
70417
|
"code": "() => {\n const [sorting, setSorting] = useState<TableSortingState>([]);\n\n return (\n <Table\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n sorting={sorting}\n onSortingChange={setSorting}\n />\n );\n}"
|
|
@@ -70430,8 +70433,14 @@
|
|
|
70430
70433
|
"code": "() => (\n <Table\n className='h-500'\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n isLoading\n />\n)"
|
|
70431
70434
|
},
|
|
70432
70435
|
{
|
|
70433
|
-
"name": "
|
|
70434
|
-
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <
|
|
70436
|
+
"name": "EmptyCollection",
|
|
70437
|
+
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='collection-empty'>\n <EmptyStateIllustration>\n <Database size='lg' />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>No events yet</EmptyStateTitle>\n <EmptyStateDescription>\n Security events will appear here as soon as Wallarm detects traffic.\n </EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium'>Configure source</Button>\n </EmptyStateActions>\n </EmptyState>\n </TableEmptyState>\n </Table>\n)",
|
|
70438
|
+
"description": "When `data` is empty and the table is not loading, the `TableEmptyState` slot\nrenders its children. Compose the `EmptyState` component inside it to get the\nstandard illustration / message / actions layout.\n\nUse `type='collection-empty'` when there is genuinely no data yet (nothing has\nbeen created), and offer a primary action that creates the first item."
|
|
70439
|
+
},
|
|
70440
|
+
{
|
|
70441
|
+
"name": "NoResults",
|
|
70442
|
+
"code": "() => (\n <Table data={[]} columns={securityColumns} getRowId={row => row.id}>\n <TableEmptyState>\n <EmptyState type='no-results'>\n <EmptyStateIllustration>\n <SearchX size='lg' />\n </EmptyStateIllustration>\n <EmptyStateMessage>\n <EmptyStateTitle>No results found</EmptyStateTitle>\n <EmptyStateDescription>\n No events match your current filters. Try adjusting or resetting them.\n </EmptyStateDescription>\n </EmptyStateMessage>\n <EmptyStateActions>\n <Button size='medium' variant='outline' color='neutral'>\n <FilterX />\n Reset filters\n </Button>\n </EmptyStateActions>\n </EmptyState>\n </TableEmptyState>\n </Table>\n)",
|
|
70443
|
+
"description": "Use `type='no-results'` when data exists but the current filters or search\nmatched nothing. Offer a neutral action that clears the filters rather than\na primary \"create\" action."
|
|
70435
70444
|
},
|
|
70436
70445
|
{
|
|
70437
70446
|
"name": "ColumnResizing",
|
|
@@ -70488,7 +70497,7 @@
|
|
|
70488
70497
|
},
|
|
70489
70498
|
{
|
|
70490
70499
|
"name": "ScrollToRow",
|
|
70491
|
-
"code": "() => {\n const largeData = useMemo(() => createLargeSecurityEvents(1000), []);\n const tableRef = useRef<TableHandle>(null);\n const [lastResult, setLastResult] = useState<string | null>(null);\n\n const scroll = (index: number, align: 'start' | 'center' | 'end' | 'auto' = 'center') => {\n const row = largeData[index];\n if (!row) return;\n const ok = tableRef.current?.scrollToRow(row.id, { align, behavior: 'smooth' }) ?? false;\n setLastResult(`scrollToRow(\"${row.id}\", { align: \"${align}\" }) → ${ok}`);\n };\n\n return (\n <VStack gap=
|
|
70500
|
+
"code": "() => {\n const largeData = useMemo(() => createLargeSecurityEvents(1000), []);\n const tableRef = useRef<TableHandle>(null);\n const [lastResult, setLastResult] = useState<string | null>(null);\n\n const scroll = (index: number, align: 'start' | 'center' | 'end' | 'auto' = 'center') => {\n const row = largeData[index];\n if (!row) return;\n const ok = tableRef.current?.scrollToRow(row.id, { align, behavior: 'smooth' }) ?? false;\n setLastResult(`scrollToRow(\"${row.id}\", { align: \"${align}\" }) → ${ok}`);\n };\n\n return (\n <VStack gap={8}>\n <HStack gap={8}>\n <Button onClick={() => scroll(0, 'start')}>Top</Button>\n <Button onClick={() => scroll(499, 'center')}>Middle</Button>\n <Button onClick={() => scroll(999, 'end')}>Bottom</Button>\n <Button\n variant='ghost'\n onClick={() => {\n const ok = tableRef.current?.scrollToRow('does-not-exist') ?? false;\n setLastResult(`scrollToRow(\"does-not-exist\") → ${ok}`);\n }}\n >\n Missing id (returns false)\n </Button>\n </HStack>\n {lastResult && (\n <Text size='sm' color='secondary'>\n {lastResult}\n </Text>\n )}\n <Table\n ref={tableRef}\n className='h-500'\n data={largeData}\n columns={securityColumns}\n getRowId={row => row.id}\n virtualized='container'\n />\n </VStack>\n );\n}"
|
|
70492
70501
|
},
|
|
70493
70502
|
{
|
|
70494
70503
|
"name": "InfiniteScroll",
|
package/package.json
CHANGED