mirror of
https://github.com/grafana/grafana.git
synced 2026-08-27 05:47:26 -05:00
Chore: Tweaks to support React 19 (#90653)
changes needed for react 19, mostly don't spread key
This commit is contained in:
@@ -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
-2
@@ -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 />
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user