Skip to content

Commit

Permalink
Update EmbeddablePanel component to trigger edit panel action on er…
Browse files Browse the repository at this point in the history
…ror click
  • Loading branch information
dokmic committed Sep 7, 2022
1 parent 76052ff commit 13acb6b
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
.embPanel__content--fullWidth {
width: 100%;
}

.embPanel__content--error {
&:hover {
box-shadow: none;
transform: none;
}
}
}

// HEADER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ describe('HelloWorldContainer in error state', () => {
test('renders a custom fatal error', () => {
embeddable.triggerError(new Error('something'), true);
component.update();
component.mount();

const embeddableError = findTestSubject(component, 'embeddableError');

Expand Down
46 changes: 7 additions & 39 deletions src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '../embeddables/i_embeddable';
import { ViewMode } from '../types';

import { EmbeddablePanelError } from './embeddable_panel_error';
import { RemovePanelAction } from './panel_header/panel_actions';
import { AddPanelAction } from './panel_header/panel_actions/add_panel/add_panel_action';
import { CustomizePanelTitleAction } from './panel_header/panel_actions/customize_title/customize_panel_action';
Expand All @@ -40,7 +41,7 @@ import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_a
import { EditPanelAction } from '../actions';
import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal';
import { EmbeddableStart } from '../../plugin';
import { EmbeddableStateTransfer, ErrorEmbeddable, isSelfStyledEmbeddable } from '..';
import { EmbeddableStateTransfer, isSelfStyledEmbeddable } from '..';

const sortByOrderField = (
{ order: orderA }: { order?: number },
Expand Down Expand Up @@ -118,18 +119,10 @@ interface BasePanelActions {
editPanel: EditPanelAction;
}

const emptyObject = {};
type EmptyObject = typeof emptyObject;

type PanelUniversalActions =
| BasePanelActions
| InspectorPanelAction
| (BasePanelActions & InspectorPanelAction)
| EmptyObject;
interface PanelUniversalActions extends Partial<InspectorPanelAction>, Partial<BasePanelActions> {}

export class EmbeddablePanel extends React.Component<Props, State> {
private embeddableRoot = React.createRef<HTMLDivElement>();
private errorRoot = React.createRef<HTMLDivElement>();
private parentSubscription?: Subscription;
private subscription: Subscription = new Subscription();
private mounted: boolean = false;
Expand All @@ -154,13 +147,6 @@ export class EmbeddablePanel extends React.Component<Props, State> {
};
}

componentDidUpdate(prevProps: Props, prevState: State) {
if (this.state.error !== prevState.error) {
prevState.destroyError?.();
this.setState({ destroyError: this.renderError() });
}
}

private async refreshBadges() {
if (!this.mounted) {
return;
Expand Down Expand Up @@ -264,24 +250,6 @@ export class EmbeddablePanel extends React.Component<Props, State> {
}
};

private renderError() {
if (!this.state.error || !this.errorRoot.current) {
return;
}

if (this.props.embeddable.renderError) {
return this.props.embeddable.renderError(this.errorRoot.current, this.state.error);
}

const errorEmbeddable = new ErrorEmbeddable(this.state.error, {
id: this.props.embeddable.id,
});

errorEmbeddable.render(this.errorRoot.current);

return () => errorEmbeddable.destroy();
}

public render() {
const viewOnlyMode = [ViewMode.VIEW, ViewMode.PRINT].includes(this.state.viewMode);
const classes = classNames('embPanel', {
Expand Down Expand Up @@ -330,10 +298,10 @@ export class EmbeddablePanel extends React.Component<Props, State> {
/>
)}
{this.state.error && (
<div
className="embPanel__content"
data-test-subj="embeddableError"
ref={this.errorRoot}
<EmbeddablePanelError
editPanelAction={this.state.universalActions.editPanel}
embeddable={this.props.embeddable}
error={this.state.error}
/>
)}
<div className="embPanel__content" ref={this.embeddableRoot} {...contentAttrs} />
Expand Down
89 changes: 89 additions & 0 deletions src/plugins/embeddable/public/lib/panel/embeddable_panel_error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* 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.
*/

import React, { useEffect, useMemo, useRef, useState } from 'react';
import { EuiPanel } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { ErrorLike } from '@kbn/expressions-plugin/common';
import { distinctUntilChanged, merge, of, switchMap } from 'rxjs';
import { EditPanelAction } from '../actions';
import { ErrorEmbeddable, IEmbeddable } from '../embeddables';

interface EmbeddablePanelErrorProps {
editPanelAction?: EditPanelAction;
embeddable: IEmbeddable;
error: ErrorLike;
}

export function EmbeddablePanelError({
editPanelAction,
embeddable,
error,
}: EmbeddablePanelErrorProps) {
const [isEditable, setEditable] = useState(false);
const ref = useRef<HTMLDivElement>(null);
const handleErrorClick = useMemo(
() => (isEditable ? () => editPanelAction?.execute({ embeddable }) : undefined),
[editPanelAction, embeddable, isEditable]
);

const title = embeddable.getTitle();
const actionDisplayName = useMemo(
() => editPanelAction?.getDisplayName({ embeddable }),
[editPanelAction, embeddable]
);
const ariaLabel = useMemo(
() =>
!title
? actionDisplayName
: i18n.translate('embeddableApi.panel.editPanel.displayName', {
defaultMessage: 'Edit {value}',
values: { value: title },
}),
[title, actionDisplayName]
);

useEffect(() => {
const subscription = merge(embeddable.getInput$(), embeddable.getOutput$())
.pipe(
switchMap(() => editPanelAction?.isCompatible({ embeddable }) ?? of(false)),
distinctUntilChanged()
)
.subscribe(setEditable);

return () => subscription.unsubscribe();
}, [editPanelAction, embeddable]);
useEffect(() => {
if (!ref.current) {
return;
}

if (embeddable.renderError) {
return embeddable.renderError(ref.current, error);
}

const errorEmbeddable = new ErrorEmbeddable(error, { id: embeddable.id });
errorEmbeddable.render(ref.current);

return () => errorEmbeddable.destroy();
}, [embeddable, error]);

return (
<EuiPanel
element="div"
className="embPanel__content embPanel__content--error"
color="transparent"
paddingSize="none"
data-test-subj="embeddableError"
panelRef={ref}
role={isEditable ? 'button' : undefined}
aria-label={isEditable ? ariaLabel : undefined}
onClick={handleErrorClick}
/>
);
}

0 comments on commit 13acb6b

Please sign in to comment.