Fix grid scrolling issue (#3583)

* Fix grid scrolling issue

* Remove outline

* Strict null checks

* Strict null checks

* Strict null checks
This commit is contained in:
Michał Miszczyszyn 2023-05-02 12:10:40 +02:00 committed by GitHub
parent cb3f7d971c
commit 7c933f0d9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 38 deletions

2
package-lock.json generated
View file

@ -17,7 +17,7 @@
"@editorjs/list": "^1.7.0", "@editorjs/list": "^1.7.0",
"@editorjs/paragraph": "^2.8.0", "@editorjs/paragraph": "^2.8.0",
"@editorjs/quote": "^2.4.0", "@editorjs/quote": "^2.4.0",
"@glideapps/glide-data-grid": "^5.0.0", "@glideapps/glide-data-grid": "5.2.1",
"@glideapps/glide-data-grid-cells": "^5.2.1", "@glideapps/glide-data-grid-cells": "^5.2.1",
"@graphiql/plugin-explorer": "^0.1.12", "@graphiql/plugin-explorer": "^0.1.12",
"@graphiql/react": "^0.15.0", "@graphiql/react": "^0.15.0",

View file

@ -24,7 +24,7 @@
"@editorjs/list": "^1.7.0", "@editorjs/list": "^1.7.0",
"@editorjs/paragraph": "^2.8.0", "@editorjs/paragraph": "^2.8.0",
"@editorjs/quote": "^2.4.0", "@editorjs/quote": "^2.4.0",
"@glideapps/glide-data-grid": "^5.0.0", "@glideapps/glide-data-grid": "5.2.1",
"@glideapps/glide-data-grid-cells": "^5.2.1", "@glideapps/glide-data-grid-cells": "^5.2.1",
"@graphiql/plugin-explorer": "^0.1.12", "@graphiql/plugin-explorer": "^0.1.12",
"@graphiql/react": "^0.15.0", "@graphiql/react": "^0.15.0",
@ -185,6 +185,16 @@
}, },
"optionalDependencies": { "optionalDependencies": {
"@storybook/react": "^5.1.9", "@storybook/react": "^5.1.9",
"@swc/core-darwin-arm64": "1.3.40",
"@swc/core-darwin-x64": "1.3.40",
"@swc/core-linux-arm-gnueabihf": "1.3.40",
"@swc/core-linux-arm64-gnu": "1.3.40",
"@swc/core-linux-arm64-musl": "1.3.40",
"@swc/core-linux-x64-gnu": "1.3.40",
"@swc/core-linux-x64-musl": "1.3.40",
"@swc/core-win32-arm64-msvc": "1.3.40",
"@swc/core-win32-ia32-msvc": "1.3.40",
"@swc/core-win32-x64-msvc": "1.3.40",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5", "@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^8.0.1", "@testing-library/react-hooks": "^8.0.1",
@ -224,17 +234,7 @@
"mochawesome-report-generator": "^6.0.1", "mochawesome-report-generator": "^6.0.1",
"prettier": "^2.8.4", "prettier": "^2.8.4",
"setup-polly-jest": "^0.9.1", "setup-polly-jest": "^0.9.1",
"ts-jest": "^27.1.5", "ts-jest": "^27.1.5"
"@swc/core-darwin-arm64": "1.3.40",
"@swc/core-darwin-x64": "1.3.40",
"@swc/core-linux-arm-gnueabihf": "1.3.40",
"@swc/core-linux-arm64-gnu": "1.3.40",
"@swc/core-linux-arm64-musl": "1.3.40",
"@swc/core-linux-x64-gnu": "1.3.40",
"@swc/core-linux-x64-musl": "1.3.40",
"@swc/core-win32-arm64-msvc": "1.3.40",
"@swc/core-win32-ia32-msvc": "1.3.40",
"@swc/core-win32-x64-msvc": "1.3.40"
}, },
"//@swc/*": "swc packages are required until https://github.com/npm/cli/issues/4828 is fixed", "//@swc/*": "swc packages are required until https://github.com/npm/cli/issues/4828 is fixed",
"jest": { "jest": {

View file

@ -1,8 +1,10 @@
import "@glideapps/glide-data-grid/dist/index.css"; import "@glideapps/glide-data-grid/dist/index.css";
import { getAppMountUri } from "@dashboard/config";
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack"; import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack";
import DataEditor, { import DataEditor, {
CellClickedEventArgs,
DataEditorProps, DataEditorProps,
DataEditorRef, DataEditorRef,
EditableGridCell, EditableGridCell,
@ -129,10 +131,10 @@ export const Datagrid: React.FC<DatagridProps> = ({
const classes = useStyles(); const classes = useStyles();
const { themeValues } = useTheme(); const { themeValues } = useTheme();
const datagridTheme = useDatagridTheme(readonly, readonly); const datagridTheme = useDatagridTheme(readonly, readonly);
const editor = useRef<DataEditorRef>(); const editor = useRef<DataEditorRef | null>(null);
const customRenderers = useCustomCellRenderers(); const customRenderers = useCustomCellRenderers();
const hackARef = useRef<HTMLAnchorElement>(null); const hackARef = useRef<HTMLAnchorElement | null>(null);
const navigate = useNavigator(); const navigate = useNavigator();
const { scrolledToRight, scroller } = useScrollRight(); const { scrolledToRight, scroller } = useScrollRight();
@ -210,6 +212,10 @@ export const Datagrid: React.FC<DatagridProps> = ({
const handleOnCellEdited = useCallback( const handleOnCellEdited = useCallback(
([column, row]: Item, newValue: EditableGridCell): void => { ([column, row]: Item, newValue: EditableGridCell): void => {
onCellEdited([column, row], newValue); onCellEdited([column, row], newValue);
if (!editor.current) {
return;
}
editor.current.updateCells( editor.current.updateCells(
range(availableColumns.length).map(offset => ({ range(availableColumns.length).map(offset => ({
cell: [column + offset, row], cell: [column + offset, row],
@ -219,15 +225,6 @@ export const Datagrid: React.FC<DatagridProps> = ({
[onCellEdited, availableColumns], [onCellEdited, availableColumns],
); );
const handleCellClick = useCallback(
(item: Item) => {
if (onRowClick && item[0] !== -1) {
onRowClick(item);
}
},
[onRowClick],
);
const handleRowHover = useCallback( const handleRowHover = useCallback(
(args: GridMouseEventArgs) => { (args: GridMouseEventArgs) => {
if (hasRowHover) { if (hasRowHover) {
@ -249,11 +246,26 @@ export const Datagrid: React.FC<DatagridProps> = ({
hackARef.current.style.width = `${args.bounds.width}px`; hackARef.current.style.width = `${args.bounds.width}px`;
hackARef.current.style.top = `${window.scrollY + args.bounds.y}px`; hackARef.current.style.top = `${window.scrollY + args.bounds.y}px`;
hackARef.current.style.height = `${args.bounds.height}px`; hackARef.current.style.height = `${args.bounds.height}px`;
hackARef.current.href = href; hackARef.current.href =
getAppMountUri() + (href.startsWith("/") ? href.slice(1) : href);
hackARef.current.dataset.reactRouterPath = href;
}, },
[hasRowHover, rowAnchor], [hasRowHover, rowAnchor],
); );
const handleCellClick = useCallback(
(item: Item, args: CellClickedEventArgs) => {
if (onRowClick && item[0] !== -1) {
onRowClick(item);
}
handleRowHover(args);
if (hackARef.current) {
hackARef.current.click();
}
},
[onRowClick, handleRowHover],
);
const handleGridSelectionChange = (gridSelection: GridSelection) => { const handleGridSelectionChange = (gridSelection: GridSelection) => {
// In readonly we not allow selecting cells, but we allow selcting column // In readonly we not allow selecting cells, but we allow selcting column
if (readonly && !gridSelection.current) { if (readonly && !gridSelection.current) {
@ -275,7 +287,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
themeValues.colors.background.interactiveNeutralSecondaryHovering, themeValues.colors.background.interactiveNeutralSecondaryHovering,
bgCellMedium: bgCellMedium:
themeValues.colors.background.interactiveNeutralSecondaryHovering, themeValues.colors.background.interactiveNeutralSecondaryHovering,
accentLight: undefined, accentLight: undefined as string | undefined,
}; };
if (readonly) { if (readonly) {
@ -321,6 +333,9 @@ export const Datagrid: React.FC<DatagridProps> = ({
clearTooltip(); clearTooltip();
} }
if (!onColumnResize) {
return;
}
onColumnResize(column, newSize); onColumnResize(column, newSize);
}, },
[clearTooltip, onColumnResize, tooltip], [clearTooltip, onColumnResize, tooltip],
@ -331,6 +346,9 @@ export const Datagrid: React.FC<DatagridProps> = ({
if (tooltip) { if (tooltip) {
clearTooltip(); clearTooltip();
} }
if (!onColumnMoved) {
return;
}
onColumnMoved(startIndex, endIndex); onColumnMoved(startIndex, endIndex);
}, },
[clearTooltip, onColumnMoved, tooltip], [clearTooltip, onColumnMoved, tooltip],
@ -338,7 +356,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
const selectionActionsComponent = useMemo( const selectionActionsComponent = useMemo(
() => () =>
selection?.rows.length > 0 selection?.rows && selection?.rows.length > 0
? selectionActions(Array.from(selection.rows), { ? selectionActions(Array.from(selection.rows), {
removeRows: handleRemoveRows, removeRows: handleRemoveRows,
}) })
@ -346,6 +364,29 @@ export const Datagrid: React.FC<DatagridProps> = ({
[selection, selectionActions, handleRemoveRows], [selection, selectionActions, handleRemoveRows],
); );
// Hide the link when scrolling over it so that the scroll/wheel events go through to the Datagrid
// Show the link quickly after the last scroll/wheel event
const hideLinkAndShowAfterDelay = useCallback(
(() => {
let timer: ReturnType<typeof setTimeout> | null = null;
return () => {
if (timer) {
clearTimeout(timer);
}
if (hackARef.current) {
hackARef.current.style.display = "none";
}
timer = setTimeout(() => {
if (hackARef.current) {
hackARef.current.style.display = "block";
}
}, 100);
};
})(),
[hackARef],
);
if (loading) { if (loading) {
return ( return (
<Box display="flex" justifyContent="center" marginY={12}> <Box display="flex" justifyContent="center" marginY={12}>
@ -387,7 +428,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
<CardContent classes={{ root: classes.cardContentRoot }}> <CardContent classes={{ root: classes.cardContentRoot }}>
{rowsTotal > 0 ? ( {rowsTotal > 0 ? (
<> <>
{selection?.rows.length > 0 && ( {selection?.rows && selection?.rows.length > 0 && (
<div className={classes.actionBtnBar}> <div className={classes.actionBtnBar}>
{selectionActionsComponent} {selectionActionsComponent}
</div> </div>
@ -510,16 +551,21 @@ export const Datagrid: React.FC<DatagridProps> = ({
bounds={tooltip?.bounds} bounds={tooltip?.bounds}
title={tooltip?.title} title={tooltip?.title}
/> />
<a {rowAnchor && (
ref={hackARef} <a
style={{ position: "absolute" }} ref={hackARef}
tabIndex={-1} style={{ position: "absolute" }}
aria-hidden={true} tabIndex={-1}
onClick={e => { aria-hidden={true}
e.preventDefault(); onWheelCapture={hideLinkAndShowAfterDelay}
navigate(e.currentTarget.pathname); onClick={e => {
}} e.preventDefault();
/> if (e.currentTarget.dataset.reactRouterPath) {
navigate(e.currentTarget.dataset.reactRouterPath);
}
}}
/>
)}
</FullScreenContainer> </FullScreenContainer>
); );
}; };