Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ $root: ".widget-datagrid";
display: grid !important;
min-width: fit-content;
margin-bottom: 0;
&.infinite-loading {
// in order to restrict the scroll to row area
// we need to prevent table itself to expanding beyond available position
min-width: 0;
}
}
}

Expand All @@ -406,14 +411,6 @@ $root: ".widget-datagrid";
overflow-x: auto;
}

&-grid-head {
display: contents;
}

&-grid-body {
display: contents;
}

&-footer {
container: widget-datagrid-footer / inline-size;
}
Expand Down Expand Up @@ -530,24 +527,70 @@ $root: ".widget-datagrid";
margin: var(--spacing-small, 8px) 0;
}

.infinite-loading.widget-datagrid-grid-body {
// when virtual scroll is enabled we make area that holds rows scrollable
// (while the area that holds column headers still stays in place)
overflow-y: auto;
.infinite-loading {
.widget-datagrid-grid-head {
// lock header width
// and prevent it from having own scrolling
// as scrolling is synchronized in JS
width: calc(var(--widgets-grid-width) - var(--widgets-grid-scrollbar-size));
overflow-x: hidden;
}
.widget-datagrid-grid-head[data-scrolled-y="true"] {
// add shadow under the header
// implying that grid is scrolled vertically (there are rows hidden under header)
// the data attribute added in JS
box-shadow: 0 5px 5px -5px gray;
}

.widget-datagrid-grid-body {
// lock the size of the body
// and enable it to have own scrolling
// body is the leading element
// header scroll will be synced to match it
width: var(--widgets-grid-width);
overflow-y: auto;
max-height: var(--widgets-grid-body-height);
}

.widget-datagrid-grid-head[data-scrolled-x="true"]:after {
// add inner shadow to the left side of the grid
// implying that the grid is scrolled horizontally (there are rows hidden on the left)
// the data attribute added in JS
content: "";
position: absolute;
left: 0;
width: 10px;
box-shadow: inset 5px 0 5px -5px gray;
top: 0;
bottom: 0;
}
}

.widget-datagrid-grid-head {
display: grid;
min-width: 0;

// this head is not part of the grid, so it has dedicated column template --widgets-grid-template-columns-head
// but it might not be available at the initial render, so we use template from the grid --widgets-grid-template-columns
// using template from the grid might to misalignment from the grid itself,
// but in practice:
// - grid has no data at that moment, so misalignment is not visible.
// - as soon as the grid itself gets rendered --widgets-grid-template-columns-head gets calculated
// and everything looks like it should.
grid-template-columns: var(--widgets-grid-template-columns-head, var(--widgets-grid-template-columns));
}

.widget-datagrid-grid-head,
.widget-datagrid-grid-body {
// this element has to position their children (columns or headers)
// as grid and have those aligned with the parent grid
display: grid;
// this property makes sure we align our own grid columns
// to the columns defined in the global grid
grid-template-columns: subgrid;
grid-template-columns: var(--widgets-grid-template-columns);
}

// ensure that we cover all columns of original top level grid
// so our own columns get aligned with the parent
grid-column: 1 / -1;
.grid-mock-header {
display: contents;
}

:where(#{$root}-paging-bottom, #{$root}-padding-top) {
Expand Down
6 changes: 5 additions & 1 deletion packages/pluggableWidgets/datagrid-web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

### Fixed

- We added missing dutch translations for Datagrid 2.
- We added missing Dutch translations for Datagrid 2.

### Changed

- We improved virtual scrolling behavior when horizontal scrolling is present due to grid size.

### Added

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.
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,6 @@ function useGridStyle(): CSSProperties {
}

return {
gridTemplateColumns: sizes.join(" ")
};
"--widgets-grid-template-columns": sizes.join(" ")
} as CSSProperties;
}
10 changes: 8 additions & 2 deletions packages/pluggableWidgets/datagrid-web/src/components/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { PropsWithChildren, ReactElement } from "react";
import { useDatagridConfig, useGridStyle } from "../model/hooks/injection-hooks";
import { useDatagridConfig, useGridSizeStore, useGridStyle } from "../model/hooks/injection-hooks";

export const Grid = observer(function Grid(props: PropsWithChildren): ReactElement {
const config = useDatagridConfig();
const gridSizeStore = useGridSizeStore();

const style = useGridStyle().get();
return (
<div
aria-multiselectable={config.multiselectable}
className={"widget-datagrid-grid table"}
className={classNames("widget-datagrid-grid table", {
"infinite-loading": gridSizeStore.hasVirtualScrolling
})}
role="grid"
style={style}
ref={gridSizeStore.gridContainerRef}
>
{props.children}
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import classNames from "classnames";
import { observer } from "mobx-react-lite";
import { Fragment, PropsWithChildren, ReactElement, ReactNode } from "react";
import {
useDatagridConfig,
useGridSizeStore,
useItemCount,
useLoaderViewModel,
usePaginationService,
Expand All @@ -14,14 +14,14 @@ import { SpinnerLoader } from "./loader/SpinnerLoader";

export const GridBody = observer(function GridBody(props: PropsWithChildren): ReactElement {
const { children } = props;
const { bodySize, containerRef, isInfinite, handleScroll } = useBodyScroll();
const gridSizeStore = useGridSizeStore();
const { handleScroll } = useBodyScroll();

return (
<div
className={classNames("widget-datagrid-grid-body table-content", { "infinite-loading": isInfinite })}
style={isInfinite && bodySize > 0 ? { maxHeight: `${bodySize}px` } : {}}
className={"widget-datagrid-grid-body table-content"}
role="rowgroup"
ref={containerRef}
ref={gridSizeStore.gridBodyRef}
onScroll={handleScroll}
>
<ContentGuard>{children}</ContentGuard>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ReactElement, useState } from "react";
import { useColumnsStore, useDatagridConfig } from "../model/hooks/injection-hooks";
import { useColumnsStore, useDatagridConfig, useGridSizeStore } from "../model/hooks/injection-hooks";
import { ColumnId } from "../typings/GridColumn";
import { CheckboxColumnHeader } from "./CheckboxColumnHeader";
import { ColumnProvider } from "./ColumnProvider";
Expand All @@ -11,6 +11,7 @@ import { HeaderSkeletonLoader } from "./loader/HeaderSkeletonLoader";
export function GridHeader(): ReactElement {
const { columnsHidable, id: gridId } = useDatagridConfig();
const columnsStore = useColumnsStore();
const gridSizeStore = useGridSizeStore();
const columns = columnsStore.visibleColumns;
const [dragOver, setDragOver] = useState<[ColumnId, "before" | "after"] | undefined>(undefined);
const [isDragging, setIsDragging] = useState<[ColumnId | undefined, ColumnId, ColumnId | undefined] | undefined>();
Expand All @@ -20,7 +21,7 @@ export function GridHeader(): ReactElement {
}

return (
<div className="widget-datagrid-grid-head" role="rowgroup">
<div className="widget-datagrid-grid-head" role="rowgroup" ref={gridSizeStore.gridHeaderRef}>
<div key="headers_row" className="tr" role="row">
<CheckboxColumnHeader key="headers_column_select_all" />
{columns.map(column => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export function Header(props: HeaderProps): ReactElement {
role="columnheader"
style={!canSort ? { cursor: "unset" } : undefined}
title={caption}
ref={ref => column.setHeaderElementRef(ref)}
data-column-id={column.columnId}
onDrop={draggableProps.onDrop}
onDragEnter={draggableProps.onDragEnter}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ReactNode, useCallback, useEffect } from "react";
import { useColumnsStore, useDatagridConfig, useGridSizeStore } from "../model/hooks/injection-hooks";

export function MockHeader(): ReactNode {
const columnsStore = useColumnsStore();
const config = useDatagridConfig();
const gridSizeStore = useGridSizeStore();
const resizeCallback = useCallback<ResizeObserverCallback>(
entries => {
const container = entries[0].target.parentElement!;
const sizes = new Map<string, number>();
container.querySelectorAll<HTMLDivElement>("[data-column-id]").forEach(c => {
const columnId = c.dataset.columnId;
if (!columnId) {
console.debug("getColumnSizes: can't find id on:", c);
return;
}

sizes.set(columnId, c.getBoundingClientRect().width);
});
gridSizeStore.updateColumnSizes(sizes.values().toArray());
},
[gridSizeStore]
);

useEffect(() => {
const observer = new ResizeObserver(resizeCallback);

columnsStore.visibleColumns.forEach(c => {
if (c.headerElementRef) observer.observe(c.headerElementRef);
});

return () => {
observer.disconnect();
};
}, [resizeCallback, columnsStore.visibleColumns]);

return (
<div className={"grid-mock-header"} aria-hidden>
{config.checkboxColumnEnabled && <div data-column-id="checkboxes" key={"checkboxes"}></div>}
{columnsStore.visibleColumns.map(c => (
<div
data-column-id={c.columnId}
key={c.columnId}
// we set header ref here instead of the real header
// as this mock header is aligned with CSS grid, so it is more reliable
// the real header is aligned programmatically based on this header
ref={ref => c.setHeaderElementRef(ref)}
></div>
))}
{config.selectorColumnEnabled && <div data-column-id="selector" key={"selector"}></div>}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { WidgetFooter } from "./WidgetFooter";
import { WidgetHeader } from "./WidgetHeader";
import { WidgetRoot } from "./WidgetRoot";
import { WidgetTopBar } from "./WidgetTopBar";
import { MockHeader } from "./MockHeader";

export function Widget(props: { onExportCancel?: () => void }): ReactElement {
return (
Expand All @@ -25,6 +26,7 @@ export function Widget(props: { onExportCancel?: () => void }): ReactElement {
<SelectAllBar />
<RefreshStatus />
<GridBody>
<MockHeader />
<RowsRenderer />
<EmptyPlaceholder />
</GridBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`Grid renders without crashing 1`] = `
<div
class="widget-datagrid-grid table"
role="grid"
style="grid-template-columns: 1fr 1fr 54px;"
style="--widgets-grid-template-columns: 1fr 1fr 54px;"
>
Test
</div>
Expand All @@ -17,7 +17,7 @@ exports[`Grid renders without selector column 1`] = `
<div
class="widget-datagrid-grid table"
role="grid"
style="grid-template-columns: 1fr 1fr;"
style="--widgets-grid-template-columns: 1fr 1fr;"
>
Test
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ export class ColumnStore implements GridColumn {
size: number | undefined = undefined;
orderWeight: number;

private headerElementRef: HTMLDivElement | null = null;
headerElementRef: HTMLDivElement | null = null;

private baseInfo: BaseColumnInfo;
private parentStore: IColumnParentStore;

// this holds size of the column that it had prior to resizing started
// this is needed to prevent personalization being saved while resizing
private frozenSize: number | undefined;

// dynamic props from PW API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { DerivedLoaderController } from "../services/DerivedLoaderController";
import { PaginationController } from "../services/PaginationController";
import { SelectionGate } from "../services/SelectionGate.service";
import { CORE_TOKENS as CORE, DG_TOKENS as DG, SA_TOKENS } from "../tokens";
import { GridSizeStore } from "../stores/GridSize.store";

// base
injected(ColumnGroupStore, CORE.setupService, CORE.mainGate, CORE.config, DG.filterHost);
Expand All @@ -40,6 +41,7 @@ injected(DatasourceService, CORE.setupService, DG.queryGate, DG.refreshInterval.
injected(PaginationController, CORE.setupService, DG.paginationConfig, DG.query);
injected(GridBasicData, CORE.mainGate);
injected(WidgetRootViewModel, CORE.mainGate, CORE.config, DG.exportProgressService, SA_TOKENS.selectionDialogVM);
injected(GridSizeStore, DG.paginationService);

// loader
injected(DerivedLoaderController, DG.query, DG.exportProgressService, CORE.columnsStore, DG.loaderConfig);
Expand All @@ -58,7 +60,7 @@ injected(GridPersonalizationStore, CORE.setupService, CORE.mainGate, CORE.column
// selection
injected(SelectionGate, CORE.mainGate);
injected(createSelectionHelper, CORE.setupService, DG.selectionGate, CORE.config.optional);
injected(gridStyleAtom, CORE.columnsStore, CORE.config);
injected(gridStyleAtom, CORE.columnsStore, CORE.config, DG.gridSizeStore);
injected(rowClassProvider, CORE.mainGate);

// row-interaction
Expand Down Expand Up @@ -98,6 +100,8 @@ export class DatagridContainer extends Container {
this.bind(DG.query).toInstance(DatasourceService).inSingletonScope();
// Pagination service
this.bind(DG.paginationService).toInstance(PaginationController).inSingletonScope();
// Grid sizing and scrolling store
this.bind(DG.gridSizeStore).toInstance(GridSizeStore).inSingletonScope();
// Datasource params service
this.bind(DG.paramsService).toInstance(DatasourceParamsController).inSingletonScope();
// FilterAPI
Expand Down Expand Up @@ -229,5 +233,7 @@ export class DatagridContainer extends Container {

// Hydrate filters from props
this.get(DG.combinedFilter).hydrate(props.datasource.filter);

this.get(DG.gridSizeStore);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const [useExportProgressService] = createInjectionHooks(DG.exportProgress
export const [useLoaderViewModel] = createInjectionHooks(DG.loaderVM);
export const [useMainGate] = createInjectionHooks(CORE.mainGate);
export const [usePaginationService] = createInjectionHooks(DG.paginationService);
export const [useGridSizeStore] = createInjectionHooks(DG.gridSizeStore);
export const [useSelectionHelper] = createInjectionHooks(DG.selectionHelper);
export const [useGridStyle] = createInjectionHooks(DG.gridColumnsStyle);
export const [useQueryService] = createInjectionHooks(DG.query);
Expand Down
Loading
Loading