Fix Datagrid links regression (#3548)

* Add Datagrid hack anchor

* Add tabIndex=-1 and aria-hidden

* Use react-router-dom `navigate`  for smoother experience

* Remove debug

* Code review

* Add comment

* Typo
This commit is contained in:
Michał Miszczyszyn 2023-04-27 12:33:47 +02:00 committed by GitHub
parent 6acf43bab5
commit 2d0f40c021
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 11 deletions

View file

@ -1,5 +1,6 @@
import "@glideapps/glide-data-grid/dist/index.css"; import "@glideapps/glide-data-grid/dist/index.css";
import useNavigator from "@dashboard/hooks/useNavigator";
import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack"; import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack";
import DataEditor, { import DataEditor, {
DataEditorProps, DataEditorProps,
@ -94,6 +95,7 @@ export interface DatagridProps {
freezeColumns?: DataEditorProps["freezeColumns"]; freezeColumns?: DataEditorProps["freezeColumns"];
verticalBorder?: DataEditorProps["verticalBorder"]; verticalBorder?: DataEditorProps["verticalBorder"];
columnSelect?: DataEditorProps["columnSelect"]; columnSelect?: DataEditorProps["columnSelect"];
rowAnchor?: (item: Item) => string;
} }
export const Datagrid: React.FC<DatagridProps> = ({ export const Datagrid: React.FC<DatagridProps> = ({
@ -120,6 +122,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
onColumnMoved, onColumnMoved,
onColumnResize, onColumnResize,
loading, loading,
rowAnchor,
hasRowHover = false, hasRowHover = false,
...datagridProps ...datagridProps
}): ReactElement => { }): ReactElement => {
@ -129,6 +132,9 @@ export const Datagrid: React.FC<DatagridProps> = ({
const editor = useRef<DataEditorRef>(); const editor = useRef<DataEditorRef>();
const customRenderers = useCustomCellRenderers(); const customRenderers = useCustomCellRenderers();
const hackARef = useRef<HTMLAnchorElement>(null);
const navigate = useNavigator();
const { scrolledToRight, scroller } = useScrollRight(); const { scrolledToRight, scroller } = useScrollRight();
const defualtColumnPickerProps = getDefultColumnPickerProps( const defualtColumnPickerProps = getDefultColumnPickerProps(
@ -227,8 +233,25 @@ export const Datagrid: React.FC<DatagridProps> = ({
if (hasRowHover) { if (hasRowHover) {
setHoverRow(args.kind !== "cell" ? undefined : args.location[1]); setHoverRow(args.kind !== "cell" ? undefined : args.location[1]);
} }
// the code below is responsible for adding native <a> element when hovering over rows in the datagrid
// this makes it possible to open links in a new tab and copy them
if (args.kind !== "cell" || !hackARef.current || !rowAnchor) {
return;
}
const href = rowAnchor(args.location);
if (!href) {
return;
}
hackARef.current.style.left = `${window.scrollX + args.bounds.x}px`;
hackARef.current.style.width = `${args.bounds.width}px`;
hackARef.current.style.top = `${window.scrollY + args.bounds.y}px`;
hackARef.current.style.height = `${args.bounds.height}px`;
hackARef.current.href = href;
}, },
[hasRowHover], [hasRowHover, rowAnchor],
); );
const handleGridSelectionChange = (gridSelection: GridSelection) => { const handleGridSelectionChange = (gridSelection: GridSelection) => {
@ -487,6 +510,16 @@ export const Datagrid: React.FC<DatagridProps> = ({
bounds={tooltip?.bounds} bounds={tooltip?.bounds}
title={tooltip?.title} title={tooltip?.title}
/> />
<a
ref={hackARef}
style={{ position: "absolute" }}
tabIndex={-1}
aria-hidden={true}
onClick={e => {
e.preventDefault();
navigate(e.currentTarget.pathname);
}}
/>
</FullScreenContainer> </FullScreenContainer>
); );
}; };

View file

@ -22,7 +22,8 @@ interface OrderListDatagridProps
extends ListProps, extends ListProps,
SortPage<OrderListUrlSortField> { SortPage<OrderListUrlSortField> {
orders: RelayToFlat<OrderListQuery["orders"]>; orders: RelayToFlat<OrderListQuery["orders"]>;
onRowClick: (id: string) => void; onRowClick?: (id: string) => void;
rowAnchor?: (id: string) => string;
hasRowHover?: boolean; hasRowHover?: boolean;
} }
@ -35,6 +36,7 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
sort, sort,
onRowClick, onRowClick,
hasRowHover, hasRowHover,
rowAnchor,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const datagrid = useDatagridChangeState(); const datagrid = useDatagridChangeState();
@ -65,12 +67,27 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
const handleRowClick = useCallback( const handleRowClick = useCallback(
([_, row]: Item) => { ([_, row]: Item) => {
if (!onRowClick) {
return;
}
const rowData = orders[row]; const rowData = orders[row];
onRowClick(rowData.id); onRowClick(rowData.id);
}, },
[onRowClick, orders], [onRowClick, orders],
); );
const handleRowAnchor = useCallback(
([, row]: Item) => {
if (!rowAnchor) {
return;
}
const rowData = orders[row];
return rowAnchor(rowData.id);
},
[rowAnchor, orders],
);
const getCellContent = useGetCellContent({ const getCellContent = useGetCellContent({
columns, columns,
orders, orders,
@ -113,6 +130,7 @@ export const OrderListDatagrid: React.FC<OrderListDatagridProps> = ({
)} )}
fullScreenTitle={intl.formatMessage(messages.orders)} fullScreenTitle={intl.formatMessage(messages.orders)}
onRowClick={handleRowClick} onRowClick={handleRowClick}
rowAnchor={handleRowAnchor}
/> />
<Box paddingX={9}> <Box paddingX={9}>

View file

@ -12,7 +12,6 @@ import { useDevModeContext } from "@dashboard/components/DevModePanel/hooks";
import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect"; import { FilterPresetsSelect } from "@dashboard/components/FilterPresetsSelect";
import { ListPageLayout } from "@dashboard/components/Layouts"; import { ListPageLayout } from "@dashboard/components/Layouts";
import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql"; import { OrderListQuery, RefreshLimitsQuery } from "@dashboard/graphql";
import useNavigator from "@dashboard/hooks/useNavigator";
import { sectionNames } from "@dashboard/intl"; import { sectionNames } from "@dashboard/intl";
import { orderMessages } from "@dashboard/orders/messages"; import { orderMessages } from "@dashboard/orders/messages";
import { DevModeQuery } from "@dashboard/orders/queries"; import { DevModeQuery } from "@dashboard/orders/queries";
@ -87,7 +86,6 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles({}); const classes = useStyles({});
const navigate = useNavigator();
const filterStructure = createFilterStructure(intl, filterOpts); const filterStructure = createFilterStructure(intl, filterOpts);
const limitsReached = isLimitReached(limits, "orders"); const limitsReached = isLimitReached(limits, "orders");
const [isFilterPresetOpen, setFilterPresetOpen] = useState(false); const [isFilterPresetOpen, setFilterPresetOpen] = useState(false);
@ -221,9 +219,7 @@ const OrderListPage: React.FC<OrderListPageProps> = ({
<OrderListDatagrid <OrderListDatagrid
{...listProps} {...listProps}
hasRowHover={!isFilterPresetOpen} hasRowHover={!isFilterPresetOpen}
onRowClick={id => { rowAnchor={orderUrl}
navigate(orderUrl(id));
}}
/> />
</Card> </Card>
</ListPageLayout> </ListPageLayout>

View file

@ -49,7 +49,8 @@ interface ProductListDatagridProps
activeAttributeSortId: string; activeAttributeSortId: string;
gridAttributes: RelayToFlat<GridAttributesQuery["grid"]>; gridAttributes: RelayToFlat<GridAttributesQuery["grid"]>;
products: RelayToFlat<ProductListQuery["products"]>; products: RelayToFlat<ProductListQuery["products"]>;
onRowClick: (id: string) => void; onRowClick?: (id: string) => void;
rowAnchor?: (id: string) => string;
columnQuery: string; columnQuery: string;
availableInGridAttributes: RelayToFlat< availableInGridAttributes: RelayToFlat<
SearchAvailableInGridAttributesQuery["availableInGrid"] SearchAvailableInGridAttributesQuery["availableInGrid"]
@ -80,6 +81,7 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
activeAttributeSortId, activeAttributeSortId,
filterDependency, filterDependency,
hasRowHover, hasRowHover,
rowAnchor,
}) => { }) => {
const intl = useIntl(); const intl = useIntl();
const searchProductType = useSearchProductTypes(); const searchProductType = useSearchProductTypes();
@ -185,12 +187,26 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
const handleRowClick = useCallback( const handleRowClick = useCallback(
([_, row]: Item) => { ([_, row]: Item) => {
if (!onRowClick) {
return;
}
const rowData = products[row]; const rowData = products[row];
onRowClick(rowData.id); onRowClick(rowData.id);
}, },
[onRowClick, products], [onRowClick, products],
); );
const handleRowAnchor = useCallback(
([, row]: Item) => {
if (!rowAnchor) {
return;
}
const rowData = products[row];
return rowAnchor(rowData.id);
},
[rowAnchor, products],
);
const handleGetColumnTooltipContent = useCallback( const handleGetColumnTooltipContent = useCallback(
(colIndex: number): string => { (colIndex: number): string => {
const { columnName } = getColumnMetadata(columns[colIndex].id); const { columnName } = getColumnMetadata(columns[colIndex].id);
@ -245,6 +261,7 @@ export const ProductListDatagrid: React.FC<ProductListDatagridProps> = ({
selectionActions={() => null} selectionActions={() => null}
fullScreenTitle={intl.formatMessage(messages.products)} fullScreenTitle={intl.formatMessage(messages.products)}
onRowClick={handleRowClick} onRowClick={handleRowClick}
rowAnchor={handleRowAnchor}
renderColumnPicker={defaultProps => ( renderColumnPicker={defaultProps => (
<ColumnPicker <ColumnPicker
{...defaultProps} {...defaultProps}

View file

@ -287,9 +287,7 @@ export const ProductListPage: React.FC<ProductListPageProps> = props => {
settings={settings} settings={settings}
selectedChannelId={selectedChannelId} selectedChannelId={selectedChannelId}
onUpdateListSettings={onUpdateListSettings} onUpdateListSettings={onUpdateListSettings}
onRowClick={id => { rowAnchor={productUrl}
navigate(productUrl(id));
}}
/> />
) : ( ) : (
<ProductListTiles <ProductListTiles