diff --git a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx b/src/plugins/embeddable/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx similarity index 79% rename from src/plugins/embeddable/public/components/embeddable_panel/index.tsx rename to src/plugins/embeddable/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx index 7089efa4bca88..5e8ad38a97fdc 100644 --- a/src/plugins/embeddable/public/components/embeddable_panel/index.tsx +++ b/src/plugins/embeddable/public/components/drilldown_hello_bar/__examples__/drilldown_hello_bar.examples.tsx @@ -17,13 +17,10 @@ * under the License. */ -import { EuiPanel } from '@elastic/eui'; import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { DrilldownHelloBar } from '..'; -export const EmbeddablePanel = () => { - return ( - - Hello world - - ); -}; +storiesOf('components/DrilldownHelloBar', module).add('default', () => { + return ; +}); diff --git a/src/plugins/embeddable/public/components/drilldown_hello_bar/index.tsx b/src/plugins/embeddable/public/components/drilldown_hello_bar/index.tsx new file mode 100644 index 0000000000000..cdb606a777b7e --- /dev/null +++ b/src/plugins/embeddable/public/components/drilldown_hello_bar/index.tsx @@ -0,0 +1,40 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; + +export interface DrilldownHelloBarProps { + docsLink?: string; +} + +export const DrilldownHelloBar: React.FC = ({ docsLink }) => { + return ( +
+

+ Drilldowns provide the ability to define a new behavior when interacting with a panel. You + can add multiple options or simply override the default filtering behavior. +

+ View docs + +
+ ); +}; diff --git a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx b/src/plugins/embeddable/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx similarity index 86% rename from src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx rename to src/plugins/embeddable/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx index 7ec8848b8cebd..4415d9b66c408 100644 --- a/src/plugins/embeddable/public/components/embeddable_panel/__examples__/embeddable_panel.examples.tsx +++ b/src/plugins/embeddable/public/components/drilldown_picker/__examples__/drilldown_picker.examples.tsx @@ -19,6 +19,8 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; -import { EmbeddablePanel } from '..'; +import { DrilldownPicker } from '..'; -storiesOf('components/EmbeddablePanel', module).add('default', () => ); +storiesOf('components/DrilldownPicker', module).add('default', () => { + return ; +}); diff --git a/src/plugins/embeddable/public/components/drilldown_picker/index.tsx b/src/plugins/embeddable/public/components/drilldown_picker/index.tsx new file mode 100644 index 0000000000000..71e6c23c18ec0 --- /dev/null +++ b/src/plugins/embeddable/public/components/drilldown_picker/index.tsx @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; + +// eslint-disable-next-line +export interface DrilldownPickerProps {} + +export const DrilldownPicker: React.FC = () => { + return ( + + ); +}; diff --git a/src/plugins/embeddable/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx b/src/plugins/embeddable/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx new file mode 100644 index 0000000000000..ff7bbdc937e34 --- /dev/null +++ b/src/plugins/embeddable/public/components/form_create_drilldown/__examples__/form_create_drilldown.examples.tsx @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { FormCreateDrilldown } from '..'; + +storiesOf('components/FormCreateDrilldown', module).add('default', () => { + return ; +}); diff --git a/src/plugins/embeddable/public/components/form_create_drilldown/i18n.ts b/src/plugins/embeddable/public/components/form_create_drilldown/i18n.ts new file mode 100644 index 0000000000000..577065a2abee1 --- /dev/null +++ b/src/plugins/embeddable/public/components/form_create_drilldown/i18n.ts @@ -0,0 +1,41 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; + +export const txtNameOfDrilldown = i18n.translate( + 'embeddableApi.components.form_create_drilldown.nameOfDrilldown', + { + defaultMessage: 'Name of drilldown', + } +); + +export const txtUntitledDrilldown = i18n.translate( + 'embeddableApi.components.form_create_drilldown.untitledDrilldown', + { + defaultMessage: 'Untitled drilldown', + } +); + +export const txtDrilldownAction = i18n.translate( + 'embeddableApi.components.form_create_drilldown.drilldownAction', + { + defaultMessage: 'Drilldown action', + } +); diff --git a/src/plugins/embeddable/public/components/form_create_drilldown/index.tsx b/src/plugins/embeddable/public/components/form_create_drilldown/index.tsx new file mode 100644 index 0000000000000..2b8f6eff0aaf1 --- /dev/null +++ b/src/plugins/embeddable/public/components/form_create_drilldown/index.tsx @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; +import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; +import { DrilldownPicker } from '../drilldown_picker'; + +// eslint-disable-next-line +export interface FormCreateDrilldownProps {} + +export const FormCreateDrilldown: React.FC = () => { + return ( +
+ + + + + + + + + +
+ ); +}; diff --git a/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx new file mode 100644 index 0000000000000..33724068a6ba8 --- /dev/null +++ b/src/plugins/embeddable/public/components/panel_options_menu/__examples__/panel_options_menu.examples.tsx @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { action } from '@storybook/addon-actions'; +import { withKnobs, boolean } from '@storybook/addon-knobs'; +import { PanelOptionsMenu } from '..'; + +const euiContextDescriptors = { + id: 'mainMenu', + title: 'Options', + items: [ + { + name: 'Inspect', + icon: 'inspect', + onClick: action('onClick(inspect)'), + }, + { + name: 'Full screen', + icon: 'expand', + onClick: action('onClick(expand)'), + }, + ], +}; + +storiesOf('components/PanelOptionsMenu', module) + .addDecorator(withKnobs) + .add('default', () => { + const isViewMode = boolean('isViewMode', false); + + return ( +
+ +
+ ); + }); diff --git a/src/plugins/embeddable/public/components/panel_options_menu/index.tsx b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx new file mode 100644 index 0000000000000..4a95027269587 --- /dev/null +++ b/src/plugins/embeddable/public/components/panel_options_menu/index.tsx @@ -0,0 +1,93 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import React, { useState, useEffect } from 'react'; +import { + EuiButtonIcon, + EuiContextMenu, + EuiContextMenuPanelDescriptor, + EuiPopover, +} from '@elastic/eui'; + +export interface PanelOptionsMenuProps { + panelDescriptor?: EuiContextMenuPanelDescriptor; + close?: boolean; + isViewMode?: boolean; + title?: string; +} + +export const PanelOptionsMenu: React.FC = ({ + panelDescriptor, + close, + isViewMode, + title, +}) => { + const [open, setOpen] = useState(false); + useEffect(() => { + if (!close) setOpen(false); + }, [close]); + + const handleContextMenuClick = () => { + setOpen(isOpen => !isOpen); + }; + + const handlePopoverClose = () => { + setOpen(false); + }; + + const enhancedAriaLabel = i18n.translate( + 'embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel', + { + defaultMessage: 'Panel options for {title}', + values: { title }, + } + ); + const ariaLabelWithoutTitle = i18n.translate( + 'embeddableApi.panel.optionsMenu.panelOptionsButtonAriaLabel', + { + defaultMessage: 'Panel options', + } + ); + + const button = ( + + ); + + return ( + + + + ); +}; diff --git a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss index 52a9ea594ff1d..9de20b73af0f8 100644 --- a/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss +++ b/src/plugins/embeddable/public/lib/panel/_embeddable_panel.scss @@ -100,7 +100,6 @@ } } -.embPanel__optionsMenuPopover[class*='-isOpen'], .embPanel:hover { .embPanel__optionsMenuButton { opacity: 1; diff --git a/src/plugins/embeddable/public/lib/panel/actions/index.ts b/src/plugins/embeddable/public/lib/panel/actions/index.ts new file mode 100644 index 0000000000000..45ef9ad009c70 --- /dev/null +++ b/src/plugins/embeddable/public/lib/panel/actions/index.ts @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './open_flyout_add_drilldown'; diff --git a/src/plugins/embeddable/public/lib/panel/actions/open_flyout_add_drilldown/index.tsx b/src/plugins/embeddable/public/lib/panel/actions/open_flyout_add_drilldown/index.tsx new file mode 100644 index 0000000000000..5efcc311c3cf1 --- /dev/null +++ b/src/plugins/embeddable/public/lib/panel/actions/open_flyout_add_drilldown/index.tsx @@ -0,0 +1,62 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { Action } from '../../../../../../ui_actions/public'; +import { toMountPoint } from '../../../../../../kibana_react/public'; +import { IEmbeddable } from '../../../embeddables'; +import { CoreStart } from '../../../../../../../core/public'; +import { FormCreateDrilldown } from '../../../../components/form_create_drilldown'; + +export const OPEN_FLYOUT_ADD_DRILLDOWN = ''; + +interface ActionContext { + embeddable: IEmbeddable; +} + +export interface OpenFlyoutAddDrilldownParams { + overlays: CoreStart['overlays']; +} + +export class OpenFlyoutAddDrilldown implements Action { + public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN; + public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN; + public order = 100; + + constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {} + + public getDisplayName() { + return i18n.translate('embeddableApi.panel.openFlyoutAddDrilldown.displayName', { + defaultMessage: 'Add drilldown', + }); + } + + public getIconType() { + return 'empty'; + } + + public async isCompatible({ embeddable }: ActionContext) { + return true; + } + + public async execute({ embeddable }: ActionContext) { + this.params.overlays.openFlyout(toMountPoint()); + } +} diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx index c5f4265ac3b0d..918aeadaecdeb 100644 --- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx +++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx @@ -36,6 +36,7 @@ import { PanelHeader } from './panel_header/panel_header'; import { InspectPanelAction } from './panel_header/panel_actions/inspect_panel_action'; import { EditPanelAction } from '../actions'; import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal'; +import { OpenFlyoutAddDrilldown } from './actions/open_flyout_add_drilldown'; interface Props { embeddable: IEmbeddable; @@ -243,6 +244,9 @@ export class EmbeddablePanel extends React.Component { new InspectPanelAction(this.props.inspector), new RemovePanelAction(), new EditPanelAction(this.props.getEmbeddableFactory), + new OpenFlyoutAddDrilldown({ + overlays: this.props.overlays, + }), ]; const sorted = actions.concat(extraActions).sort((a: Action, b: Action) => { diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_options_menu.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_options_menu.tsx index 5548c9a0596b4..279033f18a80a 100644 --- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_options_menu.tsx +++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_options_menu.tsx @@ -17,15 +17,9 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; import React from 'react'; - -import { - EuiButtonIcon, - EuiContextMenu, - EuiContextMenuPanelDescriptor, - EuiPopover, -} from '@elastic/eui'; +import { EuiContextMenuPanelDescriptor } from '@elastic/eui'; +import { PanelOptionsMenu as PanelOptionsMenuUi } from '../../../components/panel_options_menu'; export interface PanelOptionsMenuProps { getActionContextMenuPanel: () => Promise; @@ -41,6 +35,7 @@ interface State { export class PanelOptionsMenu extends React.Component { private mounted = false; + public static getDerivedStateFromProps(props: PanelOptionsMenuProps, state: State) { if (props.closeContextMenu) { return { @@ -64,9 +59,9 @@ export class PanelOptionsMenu extends React.Component - ); + const { isViewMode, title, closeContextMenu } = this.props; return ( - - - + close={closeContextMenu} + isViewMode={isViewMode} + title={title} + /> ); } - private closePopover = () => { - if (this.mounted) { - this.setState({ - isPopoverOpen: false, - }); - } - }; - - private toggleContextMenu = () => { - if (!this.mounted) return; - const after = () => { - if (!this.state.isPopoverOpen) return; - this.setState({ actionContextMenuPanel: undefined }); - this.props - .getActionContextMenuPanel() - .then(actionContextMenuPanel => { - if (!this.mounted) return; - this.setState({ actionContextMenuPanel }); - }) - .catch(error => console.error(error)); // eslint-disable-line no-console - }; - this.setState(({ isPopoverOpen }) => ({ isPopoverOpen: !isPopoverOpen }), after); - }; }