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:
parent
6acf43bab5
commit
2d0f40c021
5 changed files with 73 additions and 11 deletions
|
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue