Chore: Tweaks to support React 19 (#90653)

changes needed for react 19, mostly don't spread key
This commit is contained in:
Ashley Harrison
2024-07-19 13:40:02 +01:00
committed by GitHub
parent ec432fa314
commit c4570ecfdc
11 changed files with 56 additions and 35 deletions
@@ -68,6 +68,12 @@ export class UnThemedQueryField extends PureComponent<QueryFieldProps, QueryFiel
mounted = false;
editor: Editor | null = null;
// By default QueryField calls onChange if onBlur is not defined, this will trigger a rerender
// And slate will claim the focus, making it impossible to leave the field.
static defaultProps = {
onBlur: () => {},
};
constructor(props: QueryFieldProps) {
super(props);
@@ -236,12 +242,6 @@ export class UnThemedQueryField extends PureComponent<QueryFieldProps, QueryFiel
export const QueryField = withTheme2(UnThemedQueryField);
// By default QueryField calls onChange if onBlur is not defined, this will trigger a rerender
// And slate will claim the focus, making it impossible to leave the field.
QueryField.defaultProps = {
onBlur: () => {},
};
const getStyles = (theme: GrafanaTheme2) => {
const focusStyles = getFocusStyles(theme);
return {
@@ -83,9 +83,12 @@ export const DefaultCell = (props: TableCellProps) => {
cellProps.style = { ...cellProps.style, textWrap: 'wrap' };
}
const { key, ...rest } = cellProps;
return (
<div
{...cellProps}
key={key}
{...rest}
onMouseEnter={showActions ? onMouseEnter : undefined}
onMouseLeave={showActions ? onMouseLeave : undefined}
className={cellStyle}
@@ -40,7 +40,7 @@ export function FooterRow(props: FooterRowProps) {
}
function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles) {
const footerProps = column.getHeaderProps();
const { key, ...footerProps } = column.getHeaderProps();
if (!footerProps) {
return null;
@@ -51,7 +51,7 @@ function renderFooterCell(column: ColumnInstance, tableStyles: TableStyles) {
footerProps.style.justifyContent = (column as any).justifyContent;
return (
<div className={tableStyles.headerCell} {...footerProps}>
<div key={key} className={tableStyles.headerCell} {...footerProps}>
{column.render('Footer')}
</div>
);
@@ -43,7 +43,7 @@ export const HeaderRow = (props: HeaderRowProps) => {
};
function renderHeaderCell(column: any, tableStyles: TableStyles, showTypeIcons?: boolean) {
const headerProps = column.getHeaderProps();
const { key, ...headerProps } = column.getHeaderProps();
const field: Field = column.field ?? null;
const tableFieldOptions: TableFieldOptions | undefined = field?.config.custom;
@@ -81,7 +81,7 @@ function renderHeaderCell(column: any, tableStyles: TableStyles, showTypeIcons?:
}
return (
<div className={tableStyles.headerCell} {...headerProps} role="columnheader">
<div className={tableStyles.headerCell} key={key} {...headerProps} role="columnheader">
{column.canSort && sortHeaderContent}
{!column.canSort && headerContent}
{!column.canSort && column.canFilter && <Filter column={column} tableStyles={tableStyles} field={field} />}
@@ -301,10 +301,12 @@ export const RowsList = (props: RowsListProps) => {
);
style.height = bbox.height;
}
const { key, ...rowProps } = row.getRowProps({ style, ...additionalProps });
return (
<div
{...row.getRowProps({ style, ...additionalProps })}
key={key}
{...rowProps}
className={cx(tableStyles.row, expandedRowStyle)}
onMouseEnter={() => onRowHover(index, data)}
onMouseLeave={onRowLeave}
@@ -116,7 +116,6 @@ export const TagFilter = ({
};
const selectOptions = {
key: selectKey,
onFocus,
isLoading,
options,
@@ -160,7 +159,7 @@ export const TagFilter = ({
Clear tags
</button>
)}
<MultiSelect {...selectOptions} prefix={<Icon name="tag-alt" />} aria-label="Tag filter" />
<MultiSelect key={selectKey} {...selectOptions} prefix={<Icon name="tag-alt" />} aria-label="Tag filter" />
</div>
);
};
@@ -63,7 +63,6 @@ export const FieldRenderer = ({
required: !!fieldData.validation?.required,
invalid: !!errors[name],
error: fieldData.validation?.message,
key: name,
description: fieldData.description,
defaultValue: fieldData.defaultValue?.value,
};
@@ -71,13 +70,13 @@ export const FieldRenderer = ({
switch (fieldData.type) {
case 'text':
return (
<Field {...fieldProps}>
<Field key={name} {...fieldProps}>
<Input {...register(name, fieldData.validation)} type={fieldData.type} id={name} autoComplete={'off'} />
</Field>
);
case 'secret':
return (
<Field {...fieldProps} htmlFor={name}>
<Field key={name} {...fieldProps} htmlFor={name}>
<Controller
name={name}
control={control}
@@ -105,7 +104,7 @@ export const FieldRenderer = ({
options = isSelectableValue(watchOptions) ? watchOptions : [];
}
return (
<Field {...fieldProps} htmlFor={name}>
<Field key={name} {...fieldProps} htmlFor={name}>
<Controller
rules={fieldData.validation}
name={name}
@@ -134,13 +133,19 @@ export const FieldRenderer = ({
);
case 'switch':
return (
<Field {...fieldProps}>
<Field key={name} {...fieldProps}>
<Switch {...register(name)} id={name} />
</Field>
);
case 'checkbox':
return (
<Checkbox {...register(name)} id={name} {...fieldProps} className={css({ marginBottom: theme.spacing(2) })} />
<Checkbox
key={name}
{...register(name)}
id={name}
{...fieldProps}
className={css({ marginBottom: theme.spacing(2) })}
/>
);
default:
console.error(`Unknown field type: ${fieldData.type}`);
@@ -215,10 +215,11 @@ function VirtualListRow({ index, style, data }: VirtualListRowProps) {
prepareRow(row);
const dashboardItem = row.original.item;
const { key, ...rowProps } = row.getRowProps({ style });
if (dashboardItem.kind === 'ui' && dashboardItem.uiKind === 'divider') {
return (
<div {...row.getRowProps({ style })}>
<div key={key} {...rowProps}>
<hr className={styles.divider} />
</div>
);
@@ -226,7 +227,8 @@ function VirtualListRow({ index, style, data }: VirtualListRowProps) {
return (
<div
{...row.getRowProps({ style })}
key={key}
{...rowProps}
className={cx(styles.row, styles.bodyRow)}
aria-labelledby={makeRowID(treeID, dashboardItem)}
data-testid={selectors.pages.BrowseDashboards.table.row(
@@ -1,5 +1,4 @@
import { render, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import { act, render, screen } from '@testing-library/react';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import {
@@ -136,9 +136,10 @@ export const SearchResultsTable = React.memo(
if (rowIndex === highlightIndex.y) {
className += ' ' + styles.selectedRow;
}
const { key, ...rowProps } = row.getRowProps({ style });
return (
<div {...row.getRowProps({ style })} className={className}>
<div key={key} {...rowProps} className={className}>
{row.cells.map((cell: Cell, index: number) => {
return (
<TableCell
@@ -91,8 +91,9 @@ export const generateColumns = (
const kind = kindField ? kindField.values[p.row.index] : 'dashboard'; // HACK for now
const selected = selection(kind, uid);
const hasUID = uid != null; // Panels don't have UID! Likely should not be shown on pages with manage options
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={styles.cell}>
<div key={key} {...cellProps} className={styles.cell}>
<Checkbox
disabled={!hasUID}
value={selected && hasUID}
@@ -121,9 +122,10 @@ export const generateColumns = (
name = loading ? 'Loading...' : 'Missing title'; // normal for panels
classNames += ' ' + styles.missingTitleText;
}
const { key, ...cellProps } = p.cellProps;
return (
<div className={styles.cell} {...p.cellProps}>
<div key={key} className={styles.cell} {...cellProps}>
{!response.isItemLoaded(p.row.index) ? (
<Skeleton width={200} />
) : isDeleted ? (
@@ -180,8 +182,9 @@ export const generateColumns = (
columns.push({
Cell: (p) => {
const parts = (access.location?.values[p.row.index] ?? '').split('/');
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={styles.cell}>
<div key={key} {...cellProps} className={styles.cell}>
{!response.isItemLoaded(p.row.index) ? (
<Skeleton width={150} />
) : (
@@ -225,8 +228,9 @@ export const generateColumns = (
columns.push({
Header: getFieldDisplayName(sortField),
Cell: (p) => {
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={styles.cell}>
<div key={key} {...cellProps} className={styles.cell}>
{getDisplayValue({
sortField,
getDisplay: disp,
@@ -261,11 +265,13 @@ export const generateColumns = (
columns.push({
Header: () => <div className={styles.sortedHeader}>Score</div>,
Cell: (p) => {
const { key, ...cellProps } = p.cellProps;
return (
// TODO: fix keyboard a11y
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<div
{...p.cellProps}
key={key}
{...cellProps}
className={cx(styles.cell, styles.explainItem)}
onClick={() => showExplainPopup(p.row.index)}
>
@@ -309,8 +315,9 @@ function makeDataSourceColumn(
if (!dslist?.length) {
return null;
}
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={cx(datasourceItemClass)}>
<div key={key} {...cellProps} className={cx(datasourceItemClass)}>
{dslist.map((v, i) => {
const settings = srv.getInstanceSettings(v);
const icon = settings?.meta?.info?.logos?.small;
@@ -358,10 +365,11 @@ function makeDeletedRemainingColumn(
Cell: (p) => {
const i = p.row.index;
const deletedDate = deletedField.values[i];
const { key, ...cellProps } = p.cellProps;
if (!deletedDate || !response.isItemLoaded(p.row.index)) {
return (
<div {...p.cellProps} className={cx(styles.cell, styles.typeCell)}>
<div key={key} {...cellProps} className={cx(styles.cell, styles.typeCell)}>
<Skeleton width={100} />
</div>
);
@@ -374,7 +382,7 @@ function makeDeletedRemainingColumn(
: formatDuration(duration, { style: 'long' });
return (
<div {...p.cellProps} className={cx(styles.cell, styles.typeCell)}>
<div key={key} {...cellProps} className={cx(styles.cell, styles.typeCell)}>
<Tooltip content={formatDate(deletedDate, { dateStyle: 'medium', timeStyle: 'short' })}>
<span>{formatted}</span>
</Tooltip>
@@ -437,8 +445,9 @@ function makeTypeColumn(
break;
}
}
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={cx(styles.cell, styles.typeCell)}>
<div key={key} {...cellProps} className={cx(styles.cell, styles.typeCell)}>
{!response.isItemLoaded(p.row.index) ? (
<Skeleton width={100} />
) : (
@@ -464,8 +473,9 @@ function makeTagsColumn(
return {
Cell: (p) => {
const tags = field.values[p.row.index];
const { key, ...cellProps } = p.cellProps;
return (
<div {...p.cellProps} className={styles.cell}>
<div key={key} {...cellProps} className={styles.cell}>
{!response.isItemLoaded(p.row.index) ? (
<TagList.Skeleton />
) : (