From e35507a27d9c8df3fe5947c7227d6072d007dfa5 Mon Sep 17 00:00:00 2001 From: Marco Vettorello Date: Mon, 14 Oct 2024 19:34:36 +0200 Subject: [PATCH] [Lens] Correctly use UserMessage longMessage as function (#192492) ## Summary After https://github.com/elastic/kibana/pull/167205 was merged, the `UserMessage.longMessage` was typed as `longMessage: string | React.ReactNode | ((closePopover: () => void) => React.ReactNode);` With the upcoming React 18 upgrade, an error will become visible because `((closePopover: () => void) => React.ReactNode);` can't be used as a ReactNode but it correctly needs to be called. In this PR I've made the `closePopover` function being optional (to simplify the refactoring) and I've added the typecheck where needed. --- .../get_application_user_messages.test.tsx | 11 +++------- .../datasources/form_based/form_based.test.ts | 8 ++----- .../datasources/form_based/form_based.tsx | 3 ++- .../datasources/form_based/utils.test.tsx | 21 ++++++++++++------- .../editor_frame/config_panel/layer_panel.tsx | 16 +++++++++----- .../editor_frame/editor_frame.tsx | 5 ++--- .../workspace_panel/workspace_errors.tsx | 3 ++- .../lens/public/embeddable/embeddable.tsx | 9 ++++---- .../embeddable/embeddable_info_badges.tsx | 5 +++-- x-pack/plugins/lens/public/types.ts | 2 +- .../lens/public/user_messages_utils.ts | 12 +++++++++++ 11 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 x-pack/plugins/lens/public/user_messages_utils.ts diff --git a/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.test.tsx b/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.test.tsx index 4345ae6795a36..1afa1974de351 100644 --- a/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.test.tsx +++ b/x-pack/plugins/lens/public/app_plugin/get_application_user_messages.test.tsx @@ -18,6 +18,7 @@ import { } from './get_application_user_messages'; import { cleanup, render, screen } from '@testing-library/react'; import { I18nProvider } from '@kbn/i18n-react'; +import { getLongMessage } from '../user_messages_utils'; jest.mock('@kbn/shared-ux-link-redirect-app', () => { const original = jest.requireActual('@kbn/shared-ux-link-redirect-app'); @@ -164,15 +165,9 @@ describe('application-level user messages', () => { visualization: {} as Visualization, visualizationState: { activeId: 'foo', state: {} }, }; + const firstMessage = getApplicationUserMessages({ ...props, ...propsOverrides }).at(0); const rtlRender = render( - - { - getApplicationUserMessages({ - ...props, - ...propsOverrides, - })[0].longMessage as React.ReactNode - } - + {firstMessage && getLongMessage(firstMessage)} ); return rtlRender; }; diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.test.ts b/x-pack/plugins/lens/public/datasources/form_based/form_based.test.ts index 784ee32f28b4f..b399f8eaa7b54 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.test.ts +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.test.ts @@ -3154,9 +3154,7 @@ describe('IndexPattern Data Source', () => { values={ Object { "position": 1, - "wrappedMessage": - error 1 - , + "wrappedMessage": "error 1", } } />, @@ -3177,9 +3175,7 @@ describe('IndexPattern Data Source', () => { values={ Object { "position": 1, - "wrappedMessage": - error 2 - , + "wrappedMessage": "error 2", } } />, diff --git a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx index 1bdd12a5cb49c..da893707ab2bc 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/form_based.tsx @@ -101,6 +101,7 @@ import { LayerSettingsPanel } from './layer_settings'; import { FormBasedLayer, LastValueIndexPatternColumn } from '../..'; import { filterAndSortUserMessages } from '../../app_plugin/get_application_user_messages'; import { EDITOR_INVALID_DIMENSION } from '../../user_messages_ids'; +import { getLongMessage } from '../../user_messages_utils'; export type { OperationType, GenericIndexPatternColumn } from './operations'; export { deleteColumn } from './operations'; @@ -995,7 +996,7 @@ function getLayerErrorMessages( defaultMessage="Layer {position} error: {wrappedMessage}" values={{ position: index + 1, - wrappedMessage: <>{error.longMessage}, + wrappedMessage: getLongMessage(error), }} /> ), diff --git a/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx b/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx index b1f57635590ec..8acff913a04f0 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/utils.test.tsx @@ -21,6 +21,7 @@ import type { FramePublicAPI, IndexPattern } from '../../types'; import { TermsIndexPatternColumn } from './operations'; import { FormBasedLayer } from './types'; import { createMockedIndexPatternWithAdditionalFields } from './mocks'; +import { getLongMessage } from '../../user_messages_utils'; describe('indexpattern_datasource utils', () => { describe('getPrecisionErrorWarningMessages', () => { @@ -121,10 +122,11 @@ describe('indexpattern_datasource utils', () => { ); expect(warningMessages).toHaveLength(1); + const { longMessage, ...rest } = warningMessages[0]; - expect({ ...warningMessages[0], longMessage: '' }).toMatchSnapshot(); + expect({ ...rest, longMessage: '' }).toMatchSnapshot(); - render({warningMessages[0].longMessage as React.ReactNode}); + render({getLongMessage(warningMessages[0])}); expect(screen.getByTestId('lnsPrecisionWarningEnableAccuracy')).toBeInTheDocument(); await userEvent.click(screen.getByTestId('lnsPrecisionWarningEnableAccuracy')); @@ -145,11 +147,12 @@ describe('indexpattern_datasource utils', () => { ); expect(warningMessages).toHaveLength(1); + const { longMessage, ...rest } = warningMessages[0]; - expect({ ...warningMessages[0], longMessage: '' }).toMatchSnapshot(); + expect({ ...rest, longMessage: '' }).toMatchSnapshot(); const { container } = render( - {warningMessages[0].longMessage as React.ReactNode} + {getLongMessage(warningMessages[0])} ); expect(container).toHaveTextContent( 'might be an approximation. For more precise results, try increasing the number of Top Values or using Filters instead.' @@ -178,7 +181,7 @@ describe('indexpattern_datasource utils', () => { } as unknown as GenericIndexPatternColumn, }; const setState = jest.fn(); - const warnings = getPrecisionErrorWarningMessages( + const warningMessages = getPrecisionErrorWarningMessages( datatableUtilitites, state, framePublicAPI, @@ -186,10 +189,12 @@ describe('indexpattern_datasource utils', () => { setState ); - expect(warnings).toHaveLength(1); - expect({ ...warnings[0], longMessage: '' }).toMatchSnapshot(); + expect(warningMessages).toHaveLength(1); + const { longMessage, ...rest } = warningMessages[0]; + + expect({ ...rest, longMessage: '' }).toMatchSnapshot(); - render({warnings[0].longMessage as React.ReactNode}); + render({getLongMessage(warningMessages[0])}); await userEvent.click(screen.getByText('Rank by rarity')); const stateSetter = setState.mock.calls[0][0]; const newState = stateSetter(state); diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx index 342d275b45e39..cfdcdba4632e3 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/layer_panel.tsx @@ -39,6 +39,7 @@ import { import { getSharedActions } from './layer_actions/layer_actions'; import { FlyoutContainer } from '../../../shared_components/flyout_container'; import { FakeDimensionButton } from './buttons/fake_dimension_button'; +import { getLongMessage } from '../../../user_messages_utils'; export function LayerPanel(props: LayerPanelProps) { const [openDimension, setOpenDimension] = useState<{ @@ -518,6 +519,7 @@ export function LayerPanel(props: LayerPanelProps) { props?.getUserMessages?.('dimensionButton', { dimensionId: columnId, }) ?? []; + const firstMessage = messages.at(0); return ( {layerDatasource ? ( <> diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx index 5020d1837470f..2b290cf80019c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/editor_frame.tsx @@ -39,6 +39,7 @@ import { import type { LensInspector } from '../../lens_inspector_service'; import { ErrorBoundary, showMemoizedErrorNotification } from '../../lens_ui_errors'; import { IndexPatternServiceAPI } from '../../data_views_service/service'; +import { getLongMessage } from '../../user_messages_utils'; export interface EditorFrameProps { datasourceMap: DatasourceMap; @@ -128,9 +129,7 @@ export function EditorFrame(props: EditorFrameProps) { bannerMessages={ bannerMessages.length ? ( - longMessage as React.ReactNode)} - /> + ) : undefined } diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_errors.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_errors.tsx index 949ae6648c183..a00fa978b5cc9 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_errors.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/workspace_panel/workspace_errors.tsx @@ -15,6 +15,7 @@ import { EuiText, } from '@elastic/eui'; import type { UserMessage } from '../../../types'; +import { getLongMessage } from '../../../user_messages_utils'; interface Props { errors: Array; @@ -56,7 +57,7 @@ export function WorkspaceErrors(props: Props) { {activeError.longMessage ? ( <> - {activeError.longMessage as React.ReactNode} + {getLongMessage(activeError)} ) : null} diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 2d8dad7571c1a..5ef2a8d202984 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -146,6 +146,7 @@ import { EmbeddableFeatureBadge } from './embeddable_info_badges'; import { getDatasourceLayers } from '../state_management/utils'; import type { EditLensConfigurationProps } from '../app_plugin/shared/edit_on_the_fly/get_edit_lens_configuration'; import { TextBasedPersistedState } from '../datasources/text_based/types'; +import { getLongMessage } from '../user_messages_utils'; export type LensSavedObjectAttributes = Omit; @@ -251,7 +252,7 @@ export interface ViewUnderlyingDataArgs { } function VisualizationErrorPanel({ errors, canEdit }: { errors: UserMessage[]; canEdit: boolean }) { - const showMore = errors.length > 1; + const firstError = errors.at(0); const canFixInLens = canEdit && errors.some(({ fixableInEditor }) => fixableInEditor); return (
@@ -261,10 +262,10 @@ function VisualizationErrorPanel({ errors, canEdit }: { errors: UserMessage[]; c data-test-subj="embeddable-lens-failure" body={ <> - {errors.length ? ( + {firstError ? ( <> -

{errors[0].longMessage as React.ReactNode}

- {showMore && !canFixInLens ? ( +

{getLongMessage(firstError)}

+ {errors.length > 1 && !canFixInLens ? (

{ const { euiTheme } = useEuiTheme(); @@ -98,8 +99,8 @@ export const EmbeddableFeatureBadge = ({ messages }: { messages: UserMessage[] }

{shortMessage}

    - {messageGroup.map(({ longMessage }, i) => ( - {longMessage as React.ReactNode} + {messageGroup.map((message, i) => ( + {getLongMessage(message)} ))}
diff --git a/x-pack/plugins/lens/public/types.ts b/x-pack/plugins/lens/public/types.ts index f4063747e9b77..d22016f75620a 100644 --- a/x-pack/plugins/lens/public/types.ts +++ b/x-pack/plugins/lens/public/types.ts @@ -302,7 +302,7 @@ export interface UserMessage { severity: 'error' | 'warning' | 'info'; hidePopoverIcon?: boolean; shortMessage: string; - longMessage: string | React.ReactNode | ((closePopover: () => void) => React.ReactNode); + longMessage: string | React.ReactNode | ((closePopover?: () => void) => React.ReactNode); fixableInEditor: boolean; displayLocations: UserMessageDisplayLocation[]; } diff --git a/x-pack/plugins/lens/public/user_messages_utils.ts b/x-pack/plugins/lens/public/user_messages_utils.ts new file mode 100644 index 0000000000000..9a0a7e2425dc9 --- /dev/null +++ b/x-pack/plugins/lens/public/user_messages_utils.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { UserMessage } from './types'; + +export function getLongMessage(msg: UserMessage) { + return typeof msg.longMessage === 'function' ? msg.longMessage() : msg.longMessage; +}