mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Unify style for embeddable-stack loaders (#171238)
## Summary Fix https://github.com/elastic/kibana/issues/170428 The bug this is intended to resolve requires some in-depth steps to reproduce. Follow the instructions in the issue above. Then, merge in this branch and compare. ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
9f5651d3bf
commit
a8647151cb
23 changed files with 111 additions and 22 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -566,6 +566,7 @@ packages/kbn-osquery-io-ts-types @elastic/security-asset-management
|
|||
x-pack/plugins/osquery @elastic/security-defend-workflows
|
||||
examples/partial_results_example @elastic/kibana-data-discovery
|
||||
x-pack/plugins/painless_lab @elastic/platform-deployment-management
|
||||
packages/kbn-panel-loader @elastic/kibana-presentation
|
||||
packages/kbn-peggy @elastic/kibana-operations
|
||||
packages/kbn-peggy-loader @elastic/kibana-operations
|
||||
packages/kbn-performance-testing-dataset-extractor @elastic/kibana-performance-testing
|
||||
|
|
|
@ -581,6 +581,7 @@
|
|||
"@kbn/osquery-plugin": "link:x-pack/plugins/osquery",
|
||||
"@kbn/paertial-results-example-plugin": "link:examples/partial_results_example",
|
||||
"@kbn/painless-lab-plugin": "link:x-pack/plugins/painless_lab",
|
||||
"@kbn/panel-loader": "link:packages/kbn-panel-loader",
|
||||
"@kbn/portable-dashboards-example": "link:examples/portable_dashboards_example",
|
||||
"@kbn/preboot-example-plugin": "link:examples/preboot_example",
|
||||
"@kbn/presentation-util-plugin": "link:src/plugins/presentation_util",
|
||||
|
|
3
packages/kbn-panel-loader/README.md
Normal file
3
packages/kbn-panel-loader/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# @kbn/panel-loader
|
||||
|
||||
Contains a generic loader which should be used to indicate that a chart is loading
|
|
@ -9,14 +9,14 @@
|
|||
import React from 'react';
|
||||
import { EuiLoadingChart, EuiPanel } from '@elastic/eui';
|
||||
|
||||
export const EmbeddableLoadingIndicator = ({ showShadow }: { showShadow?: boolean }) => {
|
||||
export const PanelLoader = (props: { showShadow?: boolean; dataTestSubj?: string }) => {
|
||||
return (
|
||||
<EuiPanel
|
||||
role="figure"
|
||||
paddingSize="none"
|
||||
hasShadow={showShadow}
|
||||
hasShadow={props.showShadow ?? false}
|
||||
className={'embPanel embPanel--loading embPanel-isLoading'}
|
||||
data-test-subj="embeddablePanelLoadingIndicator"
|
||||
data-test-subj={props.dataTestSubj}
|
||||
>
|
||||
<EuiLoadingChart size="l" mono />
|
||||
</EuiPanel>
|
13
packages/kbn-panel-loader/jest.config.js
Normal file
13
packages/kbn-panel-loader/jest.config.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test/jest_node',
|
||||
rootDir: '../..',
|
||||
roots: ['<rootDir>/packages/kbn-panel-loader'],
|
||||
};
|
5
packages/kbn-panel-loader/kibana.jsonc
Normal file
5
packages/kbn-panel-loader/kibana.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"type": "shared-browser",
|
||||
"id": "@kbn/panel-loader",
|
||||
"owner": "@elastic/kibana-presentation"
|
||||
}
|
6
packages/kbn-panel-loader/package.json
Normal file
6
packages/kbn-panel-loader/package.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "@kbn/panel-loader",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"license": "SSPL-1.0 OR Elastic License 2.0"
|
||||
}
|
18
packages/kbn-panel-loader/tsconfig.json
Normal file
18
packages/kbn-panel-loader/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types",
|
||||
"types": [
|
||||
"jest",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
],
|
||||
"kbn_references": []
|
||||
}
|
|
@ -108,6 +108,7 @@ export const Item = React.forwardRef<HTMLDivElement, Props>(
|
|||
key={type}
|
||||
index={index}
|
||||
showBadges={true}
|
||||
showShadow={true}
|
||||
showNotifications={true}
|
||||
onPanelStatusChange={onPanelStatusChange}
|
||||
embeddable={() => container.untilEmbeddableLoaded(id)}
|
||||
|
|
|
@ -12,9 +12,8 @@ import { distinct, map } from 'rxjs';
|
|||
import React, { ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiPanel, htmlIdGenerator } from '@elastic/eui';
|
||||
|
||||
import { isPromise } from '@kbn/std';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/common';
|
||||
|
||||
import { PanelLoader } from '@kbn/panel-loader';
|
||||
import {
|
||||
EditPanelAction,
|
||||
RemovePanelAction,
|
||||
|
@ -51,6 +50,7 @@ const getEventStatus = (output: EmbeddableOutput): EmbeddablePhase => {
|
|||
export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => {
|
||||
const { hideHeader, showShadow, embeddable, hideInspector, onPanelStatusChange } = panelProps;
|
||||
const [node, setNode] = useState<ReactNode | undefined>();
|
||||
const [initialLoadComplete, setInitialLoadComplete] = useState(false);
|
||||
const embeddableRoot: React.RefObject<HTMLDivElement> = useMemo(() => React.createRef(), []);
|
||||
|
||||
const headerId = useMemo(() => htmlIdGenerator()(), []);
|
||||
|
@ -129,6 +129,11 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => {
|
|||
* Select state from the embeddable
|
||||
*/
|
||||
const loading = useSelectFromEmbeddableOutput('loading', embeddable);
|
||||
|
||||
if (loading === false && !initialLoadComplete) {
|
||||
setInitialLoadComplete(true);
|
||||
}
|
||||
|
||||
const viewMode = useSelectFromEmbeddableInput('viewMode', embeddable);
|
||||
|
||||
/**
|
||||
|
@ -136,21 +141,30 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => {
|
|||
*/
|
||||
useEffect(() => {
|
||||
if (!embeddableRoot.current) return;
|
||||
const nextNode = embeddable.render(embeddableRoot.current) ?? undefined;
|
||||
if (isPromise(nextNode)) {
|
||||
nextNode.then((resolved) => setNode(resolved));
|
||||
} else {
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
const render = async (root: HTMLDivElement) => {
|
||||
const nextNode = (await embeddable.render(root)) ?? undefined;
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
setNode(nextNode);
|
||||
}
|
||||
};
|
||||
|
||||
render(embeddableRoot.current);
|
||||
|
||||
const errorSubscription = embeddable.getOutput$().subscribe({
|
||||
next: (output) => {
|
||||
setOutputError(output.error);
|
||||
},
|
||||
error: (error) => setOutputError(error),
|
||||
});
|
||||
|
||||
return () => {
|
||||
embeddable?.destroy();
|
||||
errorSubscription?.unsubscribe();
|
||||
cancelled = true;
|
||||
};
|
||||
}, [embeddable, embeddableRoot]);
|
||||
|
||||
|
@ -207,7 +221,13 @@ export const EmbeddablePanel = (panelProps: UnwrappedEmbeddablePanelProps) => {
|
|||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<div className="embPanel__content" ref={embeddableRoot} {...contentAttrs}>
|
||||
{!initialLoadComplete && <PanelLoader />}
|
||||
<div
|
||||
css={initialLoadComplete ? undefined : { display: 'none !important' }}
|
||||
className="embPanel__content"
|
||||
ref={embeddableRoot}
|
||||
{...contentAttrs}
|
||||
>
|
||||
{node}
|
||||
</div>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -8,16 +8,19 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import { PanelLoader } from '@kbn/panel-loader';
|
||||
import { EmbeddablePanelProps } from './types';
|
||||
import { useEmbeddablePanel } from './use_embeddable_panel';
|
||||
import { EmbeddableLoadingIndicator } from './embeddable_loading_indicator';
|
||||
|
||||
/**
|
||||
* Loads and renders an embeddable.
|
||||
*/
|
||||
export const EmbeddablePanel = (props: EmbeddablePanelProps) => {
|
||||
const result = useEmbeddablePanel({ embeddable: props.embeddable });
|
||||
if (!result) return <EmbeddableLoadingIndicator />;
|
||||
if (!result)
|
||||
return (
|
||||
<PanelLoader showShadow={props.showShadow} dataTestSubj="embeddablePanelLoadingIndicator" />
|
||||
);
|
||||
const { embeddable, ...passThroughProps } = props;
|
||||
return <result.Panel embeddable={result.unwrappedEmbeddable} {...passThroughProps} />;
|
||||
};
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"@kbn/react-kibana-mount",
|
||||
"@kbn/unified-search-plugin",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/panel-loader",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
import React, { useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { EuiLoadingChart, EuiProgress, useEuiTheme } from '@elastic/eui';
|
||||
import { PanelLoader } from '@kbn/panel-loader';
|
||||
import { EuiProgress, useEuiTheme } from '@elastic/eui';
|
||||
import { ExpressionRenderError } from '../types';
|
||||
import type { ExpressionRendererParams } from './use_expression_renderer';
|
||||
import { useExpressionRenderer } from './use_expression_renderer';
|
||||
|
@ -56,7 +57,7 @@ export function ReactExpressionRenderer({
|
|||
|
||||
return (
|
||||
<div {...dataAttrs} className={classes}>
|
||||
{isEmpty && <EuiLoadingChart mono size="l" />}
|
||||
{isEmpty && <PanelLoader />}
|
||||
{isLoading && (
|
||||
<EuiProgress size="xs" color="accent" position="absolute" style={{ zIndex: 1 }} />
|
||||
)}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import React, { lazy, Suspense } from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { PanelLoader } from '@kbn/panel-loader';
|
||||
import type { ReactExpressionRendererProps } from './react_expression_renderer';
|
||||
|
||||
const ReactExpressionRendererComponent = lazy(async () => {
|
||||
|
@ -17,7 +17,7 @@ const ReactExpressionRendererComponent = lazy(async () => {
|
|||
});
|
||||
|
||||
export const ReactExpressionRenderer = (props: ReactExpressionRendererProps) => (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<Suspense fallback={<PanelLoader />}>
|
||||
<ReactExpressionRendererComponent {...props} />
|
||||
</Suspense>
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
"@kbn/std",
|
||||
"@kbn/core-execution-context-common",
|
||||
"@kbn/tinymath",
|
||||
"@kbn/panel-loader",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -1126,6 +1126,8 @@
|
|||
"@kbn/paertial-results-example-plugin/*": ["examples/partial_results_example/*"],
|
||||
"@kbn/painless-lab-plugin": ["x-pack/plugins/painless_lab"],
|
||||
"@kbn/painless-lab-plugin/*": ["x-pack/plugins/painless_lab/*"],
|
||||
"@kbn/panel-loader": ["packages/kbn-panel-loader"],
|
||||
"@kbn/panel-loader/*": ["packages/kbn-panel-loader/*"],
|
||||
"@kbn/peggy": ["packages/kbn-peggy"],
|
||||
"@kbn/peggy/*": ["packages/kbn-peggy/*"],
|
||||
"@kbn/peggy-loader": ["packages/kbn-peggy-loader"],
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { type FC } from 'react';
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
|
||||
export const NoChangePointsWarning: FC = () => {
|
||||
export const NoChangePointsWarning = (props: { onRenderComplete?: () => void }) => {
|
||||
props.onRenderComplete?.();
|
||||
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="search"
|
||||
|
|
|
@ -243,7 +243,7 @@ export const ChartGridEmbeddableWrapper: FC<
|
|||
) : emptyState ? (
|
||||
emptyState
|
||||
) : (
|
||||
<NoChangePointsWarning />
|
||||
<NoChangePointsWarning onRenderComplete={onRenderComplete} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@ import React, { FC, useEffect } from 'react';
|
|||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type { Action, UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public';
|
||||
import { PanelLoader } from '@kbn/panel-loader';
|
||||
import { EuiLoadingChart } from '@elastic/eui';
|
||||
import {
|
||||
EmbeddableFactory,
|
||||
|
@ -160,7 +161,7 @@ const EmbeddablePanelWrapper: FC<EmbeddablePanelWrapperProps> = ({
|
|||
}, [embeddable, input]);
|
||||
|
||||
if (loading || !embeddable) {
|
||||
return <EuiLoadingChart />;
|
||||
return <PanelLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -92,12 +92,13 @@
|
|||
"@kbn/core-plugins-server",
|
||||
"@kbn/text-based-languages",
|
||||
"@kbn/field-utils",
|
||||
"@kbn/panel-loader",
|
||||
"@kbn/shared-ux-button-toolbar",
|
||||
"@kbn/cell-actions",
|
||||
"@kbn/calculate-width-from-char-count",
|
||||
"@kbn/discover-utils"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
"target/**/*"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -409,6 +409,10 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
|
||||
const noSwimLaneData = !isLoading && !showSwimlane && !!noDataWarning;
|
||||
|
||||
if (noSwimLaneData) {
|
||||
onRenderComplete?.();
|
||||
}
|
||||
|
||||
// A resize observer is required to compute the bucket span based on the chart width to fetch the data accordingly
|
||||
return (
|
||||
<EuiResizeObserver onResize={resizeHandler}>
|
||||
|
|
|
@ -108,6 +108,7 @@ export const EmbeddableSwimLaneContainer: FC<ExplorerSwimlaneContainerProps> = (
|
|||
);
|
||||
|
||||
if (error) {
|
||||
onRenderComplete();
|
||||
return (
|
||||
<EuiCallOut
|
||||
title={
|
||||
|
|
|
@ -5189,6 +5189,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/panel-loader@link:packages/kbn-panel-loader":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/peggy-loader@link:packages/kbn-peggy-loader":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue