Skip to content

Opening new tabs using the mouse wheel/cmd + clicking on a row in the Table component #18

@Radomir-Drukh

Description

@Radomir-Drukh

Objective

Currently, Table does not have the ability to emulate the behavior of ordinary links when clicking on a row - namely, the ability to open a new tab without focusing on it. Modern browsers do not allow you to process the click event via onRowClick or onRowMouseDown using JS and do the necessary action, and the Table component does not provide other convenient options to achieve this. Despite this, going through the list of entities and opening several tabs at once to look at them later in turn is a fairly common scenario.

Solution Proposal

Add a util function or HOC, which will allow you to "wrap" an entire row into the desired link, while allowing you to interact with interactive elements in cells.

Proposed implementation

LinkTableUtils.tsx
import {Link, TableColumnConfig, TableProps} from '@gravity-ui/uikit';

import {cn} from 'src/helpers';
import _get from 'lodash/get';
import _has from 'lodash/has';
import cx from 'classnames';
import {ReactNode} from 'react';

import './LinkTableUtils.scss';

const b = cn('link-table-utils');

export type TableColumnConfigWithRowLink<T> = Omit<TableColumnConfig<T>, 'meta'> & {
    meta: Omit<NonNullable<TableColumnConfig<T>['meta']>, 'isInteractive'> & {
        isInteractive: boolean;
    };
};

export const defaultLinkRowClassName = b('row');

export const interactiveElementClassName = b('interactive-element');

export const getDefaultLinkRowDescriptor = () => ({classNames: [defaultLinkRowClassName]});

export const prepareGetRowDescriptor = <T,>(
    originalGetRowDescriptor: NonNullable<TableProps<T>['getRowDescriptor']>,
) => {
    const newGetRowDescriptor: TableProps<T>['getRowDescriptor'] = (...args) => {
        const descriptor = originalGetRowDescriptor(...args);

        return {
            ...descriptor,
            classNames: [cx(descriptor?.classNames, defaultLinkRowClassName)],
        };
    };

    return newGetRowDescriptor;
};

export const prepareLinkColumns = <T,>(
    columns: TableColumnConfigWithRowLink<T>[],
    getLinkProps: (entity: T, index: number) => {href?: string; target?: string},
): TableColumnConfig<T>[] => {
    const preparedColumns: TableColumnConfig<T>[] = [];

    columns.forEach((column) => {
        const meta = column.meta;

        if (meta.isInteractive) {
            const newClassName = cx(column.className, b('interactive-cell'));

            preparedColumns.push({
                ...column,
                className: newClassName,
            });
        } else {
            preparedColumns.push(column);
        }
    });

    preparedColumns.push({
        id: '_row-link-internal-column',
        name: '',
        className: b('link-internal-column'),
        template: (entity, index) => {
            const linkProps = getLinkProps(entity, index);

            if (linkProps.href) {
                return (
                    <Link
                        className={b('main-link-cell')}
                        href={linkProps.href}
                        target="_blank"
                        aria-label={linkProps.href}
                    ></Link>
                );
            }

            return null;
        },
    });

    return preparedColumns;
};
LinkTableUtils.scss
.link-table-utils {
$block: &;

    &__row {
        // new context, transform instead of position relative because of safari
        transform: translateZ(0);

        &:hover {
            background-color: var(--g-color-base-simple-hover-solid);
        }
    }

    &__main-link-cell {
        vertical-align: top;

        &::before {
            position: absolute;
            inset: 0;

            content: '';
        }
    }

    &__interactive-element {
        z-index: 1;
        position: relative;
    }

    &__interactive-cell {
        >* {
            @extend #{$block}__interactive-element;
        }
    }

    &__link-internal-column {
        overflow: hidden;
        width: 0 !important;
        max-width: 0 !important;
        padding: 0 !important;
        border-width: 0 !important;
        font-size: 0 !important;
    }
}
Basic usage
// Add to TableColumnConfig

meta: { isInteractive: false, },


// Change <Table> props

columns={prepareLinkColumns(tableColumns, getLinkProps)}
getRowDescriptor={getDefaultLinkRowDescriptor}

Definition of done

Utilities or HOC are added to gravity uikit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status

    Postponed

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions