Chore: More backwards compatible changes needed for react 19 (#115422)

backwards compatible changes needed for react 19
This commit is contained in:
Ashley Harrison
2025-12-17 09:21:39 +00:00
committed by GitHub
parent aa3b9dc4da
commit fc4c699d85
12 changed files with 52 additions and 49 deletions
-5
View File
@@ -763,11 +763,6 @@
"count": 1
}
},
"packages/grafana-ui/src/components/Select/resetSelectStyles.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 1
}
},
"packages/grafana-ui/src/components/Select/types.ts": {
"@typescript-eslint/no-explicit-any": {
"count": 6
@@ -1,5 +1,5 @@
import { fireEvent, render, screen } from '@testing-library/react';
import * as React from 'react';
import { type ComponentProps, useRef } from 'react';
import { createDataFrame } from '@grafana/data';
@@ -16,14 +16,14 @@ jest.mock('react-use', () => {
return {
...reactUse,
useMeasure: () => {
const ref = React.useRef();
const ref = useRef(null);
return [ref, { width: 1600 }];
},
};
});
describe('FlameGraph', () => {
function setup(props?: Partial<React.ComponentProps<typeof FlameGraph>>) {
function setup(props?: Partial<ComponentProps<typeof FlameGraph>>) {
const flameGraphData = createDataFrame(data);
const container = new FlameGraphDataContainer(flameGraphData, { collapsing: true });
@@ -21,7 +21,7 @@ jest.mock('@grafana/assistant', () => ({
jest.mock('react-use', () => ({
...jest.requireActual('react-use'),
useMeasure: () => {
const ref = useRef();
const ref = useRef(null);
return [ref, { width: 1600 }];
},
}));
@@ -262,24 +262,18 @@ function createComponent<Props extends JSX.IntrinsicAttributes>(
pluginId?: string,
id?: string
): ComponentTypeWithExtensionMeta<Props> {
function ComponentWithMeta(props: Props) {
if (Implementation) {
return <Implementation {...props} />;
const ComponentWithMeta: ComponentTypeWithExtensionMeta<Props> = Object.assign(
Implementation || (() => <div>Test</div>),
{
meta: {
id: id ?? '',
pluginId: pluginId ?? '',
title: '',
description: '',
type: PluginExtensionTypes.component,
} satisfies PluginExtensionComponentMeta,
}
return <div>Test</div>;
}
ComponentWithMeta.displayName = '';
ComponentWithMeta.propTypes = {};
ComponentWithMeta.contextTypes = {};
ComponentWithMeta.meta = {
id: id ?? '',
pluginId: pluginId ?? '',
title: '',
description: '',
type: PluginExtensionTypes.component,
} satisfies PluginExtensionComponentMeta;
);
return ComponentWithMeta;
}
@@ -1,9 +1,9 @@
import { useMemo } from 'react';
import { CSSObjectWithLabel } from 'react-select';
import { StylesConfig } from 'react-select';
import { GrafanaTheme2 } from '@grafana/data';
export default function resetSelectStyles(theme: GrafanaTheme2) {
export default function resetSelectStyles(theme: GrafanaTheme2): Partial<StylesConfig> {
return {
clearIndicator: () => ({}),
container: () => ({}),
@@ -13,7 +13,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
groupHeading: () => ({}),
indicatorsContainer: () => ({}),
indicatorSeparator: () => ({}),
input: function (originalStyles: CSSObjectWithLabel) {
input: function (originalStyles) {
return {
...originalStyles,
color: 'inherit',
@@ -27,7 +27,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
loadingIndicator: () => ({}),
loadingMessage: () => ({}),
menu: () => ({}),
menuList: ({ maxHeight }: { maxHeight: number }) => ({
menuList: ({ maxHeight }) => ({
maxHeight,
}),
multiValue: () => ({}),
@@ -38,7 +38,7 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
multiValueRemove: () => ({}),
noOptionsMessage: () => ({}),
option: () => ({}),
placeholder: (originalStyles: CSSObjectWithLabel) => ({
placeholder: (originalStyles) => ({
...originalStyles,
color: theme.colors.text.secondary,
}),
@@ -47,11 +47,11 @@ export default function resetSelectStyles(theme: GrafanaTheme2) {
};
}
export function useCustomSelectStyles(theme: GrafanaTheme2, width: number | string | undefined) {
export function useCustomSelectStyles(theme: GrafanaTheme2, width: number | string | undefined): Partial<StylesConfig> {
return useMemo(() => {
return {
...resetSelectStyles(theme),
menuPortal: (base: CSSObjectWithLabel) => {
menuPortal: (base) => {
// Would like to correct top position when menu is placed bottom, but have props are not sent to this style function.
// Only state is. https://github.com/JedWatson/react-select/blob/master/packages/react-select/src/components/Menu.tsx#L605
return {
@@ -60,7 +60,7 @@ export function useCustomSelectStyles(theme: GrafanaTheme2, width: number | stri
};
},
//These are required for the menu positioning to function
menu: ({ top, bottom, position }: CSSObjectWithLabel) => {
menu: ({ top, bottom, position }) => {
return {
top,
bottom,
@@ -73,7 +73,7 @@ export function useCustomSelectStyles(theme: GrafanaTheme2, width: number | stri
width: width ? theme.spacing(width) : '100%',
display: width === 'auto' ? 'inline-flex' : 'flex',
}),
option: (provided: CSSObjectWithLabel, state: any) => ({
option: (provided, state) => ({
...provided,
opacity: state.isDisabled ? 0.5 : 1,
}),
@@ -263,7 +263,16 @@ export const Footer: StoryFn<typeof Table> = (args) => {
);
};
export const Pagination: StoryFn<typeof Table> = (args) => <Basic {...args} />;
export const Pagination: StoryFn<typeof Table> = (args) => {
const theme = useTheme2();
const data = buildData(theme, {});
return (
<DashboardStoryCanvas>
<Table {...args} data={data} />
</DashboardStoryCanvas>
);
};
Pagination.args = {
enablePagination: true,
};
@@ -119,11 +119,11 @@ export const LoginCtrl = memo(({ resetCode, children }: Props) => {
}, []);
const login = useCallback(
(formModel: FormModel) => {
async (formModel: FormModel) => {
setLoginErrorMessage(undefined);
setIsLoggingIn(true);
getBackendSrv()
return getBackendSrv()
.post<LoginDTO>('/login', formModel, { showErrorAlert: false })
.then((result) => {
setResult(result);
@@ -165,10 +165,10 @@ describe('CloneRuleEditor', function () {
);
await waitFor(() => {
expect(ui.inputs.name.get()).toHaveValue('First Ruler Rule (copy)');
expect(ui.inputs.namespace.get()).toHaveTextContent('namespace-one');
});
expect(ui.inputs.name.get()).toHaveValue('First Ruler Rule (copy)');
expect(ui.inputs.expr.get()).toHaveValue('vector(1) > 0');
expect(ui.inputs.namespace.get()).toHaveTextContent('namespace-one');
expect(ui.inputs.group.get()).toHaveTextContent('group1');
expect(
byRole('listitem', {
@@ -50,7 +50,7 @@ describe('Explore: handle running/not running query', () => {
jest.mocked(datasources.loki.query).mockReturnValueOnce(makeLogsQueryResponse());
// Make sure we render the logs panel
await screen.findByText(/^Logs$/);
await screen.findByRole('heading', { name: /^Logs$/ });
// Make sure we render the log line
await screen.findByText(/custom log line/i);
@@ -122,7 +122,7 @@ describe('Handles open/close splits and related events in UI and URL', () => {
// Make sure we render the logs panel
await waitFor(() => {
const logsPanels = screen.getAllByText(/^Logs$/);
const logsPanels = screen.getAllByRole('heading', { name: /^Logs$/ });
expect(logsPanels.length).toBe(2);
});
@@ -185,11 +185,12 @@ export const LogRowMenuCell = memo(
}
);
type AddonOnClickListener = (event: MouseEvent, row: LogRowModel) => void | undefined;
type AddonOnClickListener = (event: MouseEvent<HTMLElement>, row: LogRowModel) => void | undefined;
type ChildElementProps = Record<string, unknown> & { onClick: AddonOnClickListener };
function addClickListenersToNode(nodes: ReactNode[], row: LogRowModel) {
return nodes.map((node, index) => {
if (isValidElement(node)) {
const onClick: AddonOnClickListener = node.props.onClick;
if (isValidElement<ChildElementProps>(node)) {
const onClick = node.props.onClick;
if (!onClick) {
return node;
}
@@ -79,7 +79,7 @@ export class GeomapPanel extends Component<Props, State> {
this.subs.add(
this.props.eventBus.subscribe(PanelEditExitedEvent, (evt) => {
if (this.mapDiv && this.props.id === evt.payload) {
this.initMapRef(this.mapDiv);
this.initMapAsync(this.mapDiv);
}
})
);
@@ -97,7 +97,7 @@ export class GeomapPanel extends Component<Props, State> {
});
if (hasDependencies) {
this.initMapRef(this.mapDiv);
this.initMapAsync(this.mapDiv);
}
}
})
@@ -182,7 +182,7 @@ export class GeomapPanel extends Component<Props, State> {
if (noRepeatChanged) {
if (this.mapDiv) {
this.initMapRef(this.mapDiv);
this.initMapAsync(this.mapDiv);
}
// Skip other options processing
return;
@@ -227,7 +227,7 @@ export class GeomapPanel extends Component<Props, State> {
this.setState({ legends: this.getLegends() });
}
initMapRef = async (div: HTMLDivElement) => {
initMapAsync = async (div: HTMLDivElement | null) => {
if (!div) {
// Do not initialize new map or dispose old map
return;
@@ -437,6 +437,10 @@ export class GeomapPanel extends Component<Props, State> {
return legends;
}
initMapRef = (div: HTMLDivElement | null) => {
this.initMapAsync(div);
};
render() {
let { ttip, ttipOpen, topRight1, legends, topRight2 } = this.state;
const { options } = this.props;