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 useNavigator from "@dashboard/hooks/useNavigator";
import { usePreventHistoryBack } from "@dashboard/hooks/usePreventHistoryBack";
import DataEditor, {
DataEditorProps,
@ -94,6 +95,7 @@ export interface DatagridProps {
freezeColumns?: DataEditorProps["freezeColumns"];
verticalBorder?: DataEditorProps["verticalBorder"];
columnSelect?: DataEditorProps["columnSelect"];
rowAnchor?: (item: Item) => string;
}
export const Datagrid: React.FC<DatagridProps> = ({
@ -120,6 +122,7 @@ export const Datagrid: React.FC<DatagridProps> = ({
onColumnMoved,
onColumnResize,
loading,
rowAnchor,
hasRowHover = false,
...datagridProps
}): ReactElement => {
@ -129,6 +132,9 @@ export const Datagrid: React.FC<DatagridProps> = ({
const editor = useRef<DataEditorRef>();
const customRenderers = useCustomCellRenderers();
const hackARef = useRef<HTMLAnchorElement>(null);
const navigate = useNavigator();
const { scrolledToRight, scroller } = useScrollRight();
const defualtColumnPickerProps = getDefultColumnPickerProps(
@ -227,8 +233,25 @@ export const Datagrid: React.FC<DatagridProps> = ({
if (hasRowHover) {
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) => {
@ -487,6 +510,16 @@ export const Datagrid: React.FC<DatagridProps> = ({
bounds={tooltip?.bounds}
title={tooltip?.title}
/>
<a
ref={hackARef}
style={{ position: "absolute" }}
tabIndex={-1}
aria-hidden={true}
onClick={e => {
e.preventDefault();
navigate(e.currentTarget.pathname);
}}
/>
</FullScreenContainer>
);
};

View file

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

View file

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

View file

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

View file

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