Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
164805e
[data-table] added separate view for sticky headers with horisontal s…
ilyabrower Mar 18, 2026
3c2e84a
[data-table] fixed sticky styles
ilyabrower Mar 18, 2026
4ae0182
[chore] fixed pnpm version
ilyabrower Mar 18, 2026
9244de7
[data-table] fixed sticky styles
ilyabrower Mar 18, 2026
b4ff569
[chore] fixed pnpm version
ilyabrower Mar 18, 2026
5e5751e
[chore] fixed pnpm version
ilyabrower Mar 18, 2026
ab10470
[data-table] added header scroll for non separate sticky header
ilyabrower Mar 18, 2026
37c4d35
[data-table] don't render separate sticky header if data is empty
ilyabrower Mar 18, 2026
5289072
Merge branch 'release/v16' into UIK-4060/sticky-header-v1
Valeria-Zimnitskaya Mar 18, 2026
14eb50f
[data-table] removed ts-expect-error
ilyabrower Mar 18, 2026
8dae748
[chore] Merge branch 'UIK-4060/sticky-header-v1' of github.com:semrus…
ilyabrower Mar 18, 2026
1d71c7d
Merge branch 'release/v16' into UIK-4060/sticky-header-v1
Valeria-Zimnitskaya Mar 19, 2026
3d9cdb2
[stories] test stories
Valeria-Zimnitskaya Mar 19, 2026
b45bf9f
[data-table] fixed keyboard navigation UIK-4974
ilyabrower Mar 20, 2026
44c11e0
[data-table] fixed sticky+fixed for header+columns UIK-4973
ilyabrower Mar 20, 2026
cc3e551
[chore] Merge remote-tracking branch 'origin/release/v16' into UIK-40…
ilyabrower Mar 20, 2026
6cb3714
[data-table] marked mode sticky in Header as internal
ilyabrower Mar 20, 2026
faf0613
[data-table] test stories
Valeria-Zimnitskaya Mar 23, 2026
431412e
[data-table] fixed styles for Head UIK-4995 + (partially UIK-4973)
ilyabrower Mar 23, 2026
72d0ebc
[data-table] fixed focus after loading state UIK-4999
ilyabrower Mar 23, 2026
e7d93cc
[data-table] fixed loading state offset UIK-4996
ilyabrower Mar 23, 2026
3cb9a6b
[data-table] fixed leyboard navigation in accordions UIK-4998
ilyabrower Mar 23, 2026
da765cd
[data-table] upadte some test stories and test
Valeria-Zimnitskaya Mar 23, 2026
1ebe97f
[data-table] upadte some test stories and test
Valeria-Zimnitskaya Mar 23, 2026
d101fff
[data-table] fixed fixed columns UIK-5003
ilyabrower Mar 24, 2026
ce349e0
[data-table] typo fix
ilyabrower Mar 24, 2026
edb3607
[data-table] fixed perf in virtual scroll UIK-5002
ilyabrower Mar 24, 2026
c09b519
[data-table] fixed keybord navigation in closing accordions UIK-5004
ilyabrower Mar 24, 2026
bc11094
[data-table] fixed header calculation issues UIK-5025
ilyabrower Mar 25, 2026
865e142
[data-table] fixed keyboard navigation UIK-5023
ilyabrower Mar 25, 2026
4be8a1d
[data-table] fixed types for build
ilyabrower Mar 25, 2026
a7e7a7e
[data-table] update story and test
Valeria-Zimnitskaya Mar 25, 2026
01d8711
[data-table] update story
Valeria-Zimnitskaya Mar 25, 2026
828f96b
[data-table] update axe
Valeria-Zimnitskaya Mar 25, 2026
0626b80
[data-table] update axe
Valeria-Zimnitskaya Mar 25, 2026
6047fa8
[data-table] update axe
Valeria-Zimnitskaya Mar 25, 2026
92f3935
[data-table] fixed limit overlay
ilyabrower Mar 26, 2026
115ed4c
[data-table] fixed spinner layout UIK-5032
ilyabrower Mar 26, 2026
641a4fa
[data-table] upd test
Valeria-Zimnitskaya Mar 26, 2026
10b0aa3
[data-table] update test story
Valeria-Zimnitskaya Mar 27, 2026
e0d6b5b
[chore] Merge remote-tracking branch 'origin/release/v16' into UIK-40…
ilyabrower Mar 27, 2026
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
2 changes: 1 addition & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const preview: Preview = {
<div style={{ display: 'grid', gridTemplateRows: '20px auto 20px' }}>
<div tabIndex={0} />
<PortalProvider value={rootRef}>
<div ref={rootRef}>
<div ref={rootRef} style={{ minWidth: 0 }}>
<Story />
</div>
</PortalProvider>
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@
"vue-demi"
],
"onlyBuiltDependencies": [
"pngquant-bin",
"esbuild"
"esbuild",
"pngquant-bin"
]
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ test.describe(`${TAG.VISUAL}`, () => {
expect(isShadowExist).toBe(false);

await page.setViewportSize({ width: 400, height: 700 });
await page.waitForTimeout(100);

isShadowExist = await lastColumn.evaluate((node) => {
return window.getComputedStyle(node, '::after').getPropertyValue('left') === '0px';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ test.describe(`${TAG.VISUAL}`, () => {
if (item.wMax === '300px') {
await test.step('Verify horizontal scroll', async () => {
const nowNumberInitial = await checkAriaMaxValue(scrollBar.nth(1));
await locators.dataTable(page).nth(0).hover();
await page.mouse.wheel(600, 0);
await page.waitForTimeout(1000);
await locators.dataTable(page).nth(1).hover();
await page.mouse.wheel(600, 0);
await page.waitForTimeout(1000);
Expand Down Expand Up @@ -266,6 +269,9 @@ test.describe(`${TAG.VISUAL}`, () => {

if (item.wMax == '300px') {
await test.step('Verify horizontal scroll', async () => {
await locators.dataTable(page).nth(0).hover();
await page.mouse.wheel(600, 0);
await page.waitForTimeout(1000);
await locators.dataTable(page).nth(1).hover();
await page.mouse.wheel(600, 0);
await page.waitForTimeout(1000);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 35 additions & 30 deletions semcore/data-table/__tests__/data-table.axe-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,213 +2,218 @@ import { expect, getAccessibilityViolations, test } from '@semcore/testing-utils
import { loadPage } from '@semcore/testing-utils/shared/helpers';
import { TAG } from '@semcore/testing-utils/shared/tags';

const skipExpectedErrors = (violations: Awaited<ReturnType<typeof getAccessibilityViolations>>) =>
violations.filter(
(v) => v.id !== 'aria-required-children' && v.id !== 'scrollable-region-focusable' && v.id !== 'focusable-content',
);

test.describe(`@data-table ${TAG.ACCESSIBILITY}`, () => {
test('access to cells', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/access-to-cells.tsx', 'en');
const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('access to set of cells', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/access-to-set-of-cells.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('base', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/base.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('borders', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/borders.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('column alignment', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/column-alignment.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('column sizes', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/column-sizes.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('columns merging', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/columns-merging.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('compact', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/compact.tsx', 'en');
const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('custom rows rendering', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/custom-rows-rendering.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('customizing header', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/customizing-header.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('export in image', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/export-in-image.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('fixed columns', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/fixed-columns.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('fixed header', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/fixed-header.tsx', 'en');
const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('multi level header', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/multi-level-header.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('pagination', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/pagination.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('rows merging', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/rows-merging.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('scroll in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/scroll-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('secondary table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/secondary-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('skeleton in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/skeleton-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('spin container in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/spin-container-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('sorting changing size', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/sorting-changing-size.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('sorting', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/sorting.tsx', 'en');

const standPath = 'stories/components/data-table/docs/examples/sorting.tsx';
const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('table in table with fixed column', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/table-in-table-with-fixed-column.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('table in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/table-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('virtual scroll in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/virtual-scroll-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('checkbox in table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/checkbox-in-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('empty table', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/empty-table.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('row with themes', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/row-themes.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('table in card', async ({ page }) => {
await loadPage(page, 'stories/components/card/docs/examples/card_layout_for_tables.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
test('limited mode', async ({ page }) => {
await loadPage(page, 'stories/components/data-table/docs/examples/limited-mode.tsx', 'en');

const violations = await getAccessibilityViolations({ page });

expect(violations).toEqual([]);
expect(skipExpectedErrors(violations)).toEqual([]);
});
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Box } from '@semcore/base-components';
import { sstyled } from '@semcore/core';
import trottle from '@semcore/core/lib/utils/rafTrottle';
import React from 'react';

import type { CellRenderProps } from '../Body/Body.types';
Expand All @@ -16,8 +15,6 @@ type AccordionRowsProps<Data extends DataTableData, UniqKeyType> = {
expanded: boolean;
expandedForAnimation: boolean;

tableRef: React.RefObject<HTMLDivElement>;

use: DTUse;
columns: DTColumn[];
row: DTRow<UniqKeyType> | DTRow<UniqKeyType>[];
Expand Down
18 changes: 7 additions & 11 deletions semcore/data-table/src/components/Body/Body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,19 @@ class BodyRoot<Data extends DataTableData, UniqKeyType> extends Component<DataTa
this.setRowHeight = this.setRowHeight.bind(this);
}

componentDidMount() {
this.calculateAriaRowIndex();
}

componentDidUpdate(prevProps: DataTableBodyProps<Data, UniqKeyType> & BodyPropsInner<Data, UniqKeyType>) {
const { loading, tableRef } = this.asProps;
const { loading, gridContainerRef } = this.asProps;
if (prevProps.loading !== loading) {
if (loading) {
const activeElement = document.activeElement; // need to define it here because of FF
setTimeout(() => {
if ((tableRef.current && hasParent(activeElement, tableRef.current))) {
tableRef.current?.focus();
if ((gridContainerRef.current && hasParent(activeElement, gridContainerRef.current))) {
gridContainerRef.current?.focus();
}
});
} else if (!loading && this.spinContainerIsFocused) {
setTimeout(() => {
tableRef.current?.focus();
gridContainerRef.current?.focus();
});
this.spinContainerIsFocused = false;
}
Expand All @@ -86,7 +82,7 @@ class BodyRoot<Data extends DataTableData, UniqKeyType> extends Component<DataTa
});

if (!this.asProps.totalRows) {
this.asProps.tableRef.current?.setAttribute('aria-rowcount', (visibleRows?.length ?? 0).toString());
this.asProps.gridContainerRef.current?.setAttribute('aria-rowcount', (visibleRows?.length ?? 0).toString());
}
});
};
Expand Down Expand Up @@ -132,7 +128,7 @@ class BodyRoot<Data extends DataTableData, UniqKeyType> extends Component<DataTa
accordionAnimationRows,
getI18nText,
renderCell,
tableRef,
gridContainerRef,
onCellClick,
rawData,
shadowVertical,
Expand Down Expand Up @@ -179,7 +175,7 @@ class BodyRoot<Data extends DataTableData, UniqKeyType> extends Component<DataTa
flatRows,
getI18nText,
renderCell,
tableRef,
gridContainerRef,
onCellClick,
rawData,
expandedRows,
Expand Down
3 changes: 2 additions & 1 deletion semcore/data-table/src/components/Body/Body.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ export type BodyPropsInner<Data extends DataTableData, UniqKeyType> = DataTableB
expandedRows: Set<UniqKeyType>;
onExpandRow: (row: DTRow<UniqKeyType>) => void;
spinnerRef: React.RefObject<HTMLDivElement>;
tableContainerRef: React.RefObject<HTMLDivElement>;
gridContainerRef: React.RefObject<HTMLDivElement>;
tableRef: React.RefObject<HTMLDivElement>;
tableContainerRef: React.RefObject<HTMLDivElement>;
scrollAreaRef: React.RefObject<HTMLDivElement>;
scrollTop: number;
scrollDirection: 'down' | 'up';
Expand Down
4 changes: 2 additions & 2 deletions semcore/data-table/src/components/Body/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ class CellRoot<Data extends DataTableData, UniqKeyType> extends Component<DataTa
cellRef = React.createRef<HTMLDivElement>();

componentWillUnmount() {
const { virtualScroll, tableRef } = this.asProps;
const { virtualScroll, gridContainerRef } = this.asProps;
if (virtualScroll && this.cellRef.current && isFocusInside(this.cellRef.current)) {
tableRef.current?.setAttribute('tabIndex', '0');
gridContainerRef.current?.setAttribute('tabIndex', '0');
}
}

Expand Down
2 changes: 1 addition & 1 deletion semcore/data-table/src/components/Body/Cell.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export type DataTableCellProps<Data extends DataTableData, UniqKeyType> = Interg
style?: CSSProperties;

virtualScroll: boolean;
tableRef: React.RefObject<HTMLDivElement>;
gridContainerRef: React.RefObject<HTMLDivElement>;
accordionDuration?: number | [number, number];
onClick: (e: React.SyntheticEvent<HTMLElement>, opt: { rowIndex: number; colIndex: number; row?: DTRow<UniqKeyType> }) => void;
flatRows: DTRow<UniqKeyType>[];
Expand Down
Loading
Loading