From de86fbded9730127a34542e57473f2aee94c286a Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 28 Feb 2020 15:42:20 +0100 Subject: [PATCH 01/32] wip --- src/dev/storybook/aliases.ts | 1 + .../action_wizard/action_wizard.story.tsx | 184 ++++++++++++++++ .../action_wizard/action_wizard.tsx | 198 ++++++++++++++++++ .../components/action_wizard/index.scss | 6 + .../public/components/action_wizard/index.ts | 0 src/plugins/ui_actions/scripts/storybook.js | 26 +++ 6 files changed, 415 insertions(+) create mode 100644 src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx create mode 100644 src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx create mode 100644 src/plugins/ui_actions/public/components/action_wizard/index.scss create mode 100644 src/plugins/ui_actions/public/components/action_wizard/index.ts create mode 100644 src/plugins/ui_actions/scripts/storybook.js diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index fb91b865097fa..97fa2c51b2fa3 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -24,4 +24,5 @@ export const storybookAliases = { embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js', + ui_actions: 'src/plugins/ui_actions/scripts/storybook.js', }; diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx new file mode 100644 index 0000000000000..c0f844f70b196 --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx @@ -0,0 +1,184 @@ +/* + * 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 { ActionWizard, ActionFactory } from './action_wizard'; +import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; +import { useState } from 'react'; + +const DashboardDrilldownActionFactory: ActionFactory< + { + dashboardId: string; + useCurrentDashboardFilters: boolean; + useCurrentDashboardDataRange: boolean; + }, + { + dashboards: Array<{ id: string; title: string }>; + } +> = { + type: 'Dashboard', + displayName: 'Go to Dashboard', + iconType: 'dashboardApp', + wizard: props => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [config, setConfig] = useState( + props.config || { + dashboardId: undefined, + useCurrentDashboardDataRange: false, + useCurrentDashboardFilters: false, + } + ); + + function setAndSubmit(newConfig: { + dashboardId: string; + useCurrentDashboardFilters: boolean; + useCurrentDashboardDataRange: boolean; + }) { + // validate + if (newConfig.dashboardId) { + props.onConfig(newConfig as any); + } else { + props.onConfig(null); + } + + setConfig(newConfig); + } + + return ( + <> + + ({ id, text: title }))} + value={config.dashboardId} + onChange={e => { + setAndSubmit({ + ...config, + dashboardId: e.target.value, + }); + }} + aria-label="Use aria labels when no actual label is in use" + /> + + + + setAndSubmit({ + ...config, + useCurrentDashboardFilters: !config.useCurrentDashboardFilters, + } as any) + } + /> + + + + setAndSubmit({ + ...config, + useCurrentDashboardDataRange: !config.useCurrentDashboardDataRange, + } as any) + } + /> + + + ); + }, + context: { + dashboards: [ + { id: 'dashboard1', title: 'Dashboard 1' }, + { id: 'dashboard2', title: 'Dashboard 2' }, + ], + }, +}; + +const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { + type: 'Url', + displayName: 'Go to URL', + iconType: 'link', + wizard: props => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [config, setConfig] = useState(props.config || { url: '', openInNewTab: false }); + + function setAndSubmit(newConfig: { url: string; openInNewTab: boolean }) { + // validate + if (newConfig.url) { + props.onConfig(newConfig); + } else { + props.onConfig(null); + } + + setConfig(newConfig); + } + + return ( + <> + + setAndSubmit({ ...config, url: event.target.value })} + /> + + + setAndSubmit({ ...config, openInNewTab: !config.openInNewTab })} + /> + + + ); + }, + context: null, +}; + +function Demo() { + const [state, setState] = useState(); + + return ( + <> + { + setState({ + factory, + config, + }); + }} + /> +
+
+
Action Factory Type: {state?.factory?.type}
+
Action Factory Config: {JSON.stringify(state?.config)}
+ + ); +} + +storiesOf('components/ActionWizard', module).add('default', () => ); diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx new file mode 100644 index 0000000000000..10046cec4bdf9 --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -0,0 +1,198 @@ +/* + * 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, { useState } from 'react'; +import { + EuiButtonEmpty, + EuiFlexGroup, + EuiFlexItem, + EuiIcon, + EuiPanel, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +// TODO: +// import './index.scss'; + +// import { Action } from '../../actions'; +// don't use external interface for now. not sure if it is stabilized + +export interface ActionFactoryWizardProps { + /** + * Context represents environment where this component is being rendered. + */ + context: Context; + + /** + * Current (latest) config of the item. + */ + config: Config | null; + + /** + * Callback called when user updates the config in UI. + */ + onConfig: (config: Config | null) => void; +} + +export interface ActionFactory { + type: string; + displayName: string; + iconType?: string; + context: Context; + wizard: React.FC>; +} + +export interface ActionFactoryPickerProps { + actionFactories: Array>; + onActionFactoryPicked: (actionFactory: ActionFactory) => void; +} + +export interface SelectedActionFactoryProps { + actionFactory: ActionFactory; + onConfigChange: (config: Config | null) => void; + showDeselect: boolean; + onDeselect: () => void; +} +export interface ActionWizardProps { + actionFactories: Array>; + onChange: (actionFactory: ActionFactory | null, config: Config | null) => void; +} + +export const SelectedActionFactory: React.FC = ({ + actionFactory, + onDeselect, + showDeselect, + onConfigChange, +}) => { + const [config, setConfig] = useState(); + return ( +
+
+ + {actionFactory.iconType && ( + + + + )} + + +

{actionFactory.displayName}

+
+
+ {showDeselect && ( + + onDeselect()}> + change + + + )} +
+
+ +
+ {actionFactory.wizard({ + context: actionFactory.context, + config, + onConfig: newConfig => { + setConfig(newConfig); + onConfigChange(newConfig); + }, + })} +
+
+ ); +}; + +export const ActionFactoryPicker: React.FC = ({ + actionFactories, + onActionFactoryPicked, +}) => { + if (actionFactories.length === 0) return
No action factories to pick from :(
; + + return ( + + {actionFactories.map(actionFactory => ( + + onActionFactoryPicked(actionFactory)} + className="eui-textCenter" + paddingSize="s" + > + {actionFactory.iconType && ( + <> + + + + )} + +

{actionFactory.displayName}

+
+
+
+ ))} +
+ ); +}; + +export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { + // eslint-disable-next-line prefer-const + let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); + + // auto pick action factory if there is only 1 available + if (!selectedActionFactory && actionFactories.length === 1) { + selectedActionFactory = actionFactories[0]; + } + + if (selectedActionFactory) { + return ( + 1} + onDeselect={() => { + setSelectedActionFactory(null); + onChange(null, null); + }} + onConfigChange={newConfig => { + onChange(selectedActionFactory, newConfig); + }} + /> + ); + } + + return ( + { + setSelectedActionFactory(actionFactory); + onChange(actionFactory, null); + }} + /> + ); +}; diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.scss b/src/plugins/ui_actions/public/components/action_wizard/index.scss new file mode 100644 index 0000000000000..c098223792c49 --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/index.scss @@ -0,0 +1,6 @@ +// TODO: +// @import 'src/legacy/ui/public/styles/_styling_constants'; + +//.uiActions__selectedActionFactoryContainer { +// +//} diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.ts b/src/plugins/ui_actions/public/components/action_wizard/index.ts new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/plugins/ui_actions/scripts/storybook.js b/src/plugins/ui_actions/scripts/storybook.js new file mode 100644 index 0000000000000..cb2eda610170d --- /dev/null +++ b/src/plugins/ui_actions/scripts/storybook.js @@ -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 { join } from 'path'; + +// eslint-disable-next-line +require('@kbn/storybook').runStorybookCli({ + name: 'ui_actions', + storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.story.tsx')], +}); From 21d82328f494478b6fdb140bfa4fa8d7558ae053 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 2 Mar 2020 15:12:02 +0100 Subject: [PATCH 02/32] wip --- .../storybook_config/webpack.config.js | 28 ++- src/dev/jest/config.js | 1 + src/dev/jest/setup/react_testing_library.js | 23 +++ .../action_wizard/action_wizard.story.tsx | 161 ++-------------- .../action_wizard/action_wizard.test.tsx | 124 +++++++++++++ .../action_wizard/action_wizard.tsx | 173 ++++++++++-------- .../public/components/action_wizard/i18n.ts | 24 +++ .../components/action_wizard/index.scss | 15 +- .../components/action_wizard/test_data.tsx | 164 +++++++++++++++++ 9 files changed, 489 insertions(+), 224 deletions(-) create mode 100644 src/dev/jest/setup/react_testing_library.js create mode 100644 src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx create mode 100644 src/plugins/ui_actions/public/components/action_wizard/i18n.ts create mode 100644 src/plugins/ui_actions/public/components/action_wizard/test_data.tsx diff --git a/packages/kbn-storybook/storybook_config/webpack.config.js b/packages/kbn-storybook/storybook_config/webpack.config.js index 72ff9162ffe6c..230c8af81f8b5 100644 --- a/packages/kbn-storybook/storybook_config/webpack.config.js +++ b/packages/kbn-storybook/storybook_config/webpack.config.js @@ -72,6 +72,32 @@ module.exports = async ({ config }) => { ], }); + // Enable SASS + config.module.rules.push({ + test: /\.scss$/, + exclude: /\.module.(s(a|c)ss)$/, + use: [ + { loader: 'style-loader' }, + { loader: 'css-loader', options: { importLoaders: 2 } }, + { + loader: 'postcss-loader', + options: { + config: { + path: resolve(REPO_ROOT, 'src/optimize/'), + }, + }, + }, + { + loader: 'sass-loader', + options: { + sassOptions: { + includePaths: [resolve(REPO_ROOT, 'node_modules')], + }, + }, + }, + ], + }); + // Reference the built DLL file of static(ish) dependencies, which are removed // during kbn:bootstrap and rebuilt if missing. config.plugins.push( @@ -96,7 +122,7 @@ module.exports = async ({ config }) => { ); // Tell Webpack about the ts/x extensions - config.resolve.extensions.push('.ts', '.tsx'); + config.resolve.extensions.push('.ts', '.tsx', '.scss'); // Load custom Webpack config specified by a plugin. if (currentConfig.webpackHook) { diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 807a3fbf4782b..3ac044ce888df 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -67,6 +67,7 @@ export default { '/src/dev/jest/setup/babel_polyfill.js', '/src/dev/jest/setup/polyfills.js', '/src/dev/jest/setup/enzyme.js', + '/src/dev/jest/setup/react_testing_library.js', ], setupFilesAfterEnv: ['/src/dev/jest/setup/mocks.js'], coverageDirectory: '/target/kibana-coverage/jest', diff --git a/src/dev/jest/setup/react_testing_library.js b/src/dev/jest/setup/react_testing_library.js new file mode 100644 index 0000000000000..14a064acdc54f --- /dev/null +++ b/src/dev/jest/setup/react_testing_library.js @@ -0,0 +1,23 @@ +/* + * 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 { configure } from '@testing-library/react'; + +// instead of default 'data-test-id', use kibana's 'data-test-subj' +configure({ testIdAttribute: 'data-test-subj' }); diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx index c0f844f70b196..2886815eb5304 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx @@ -17,146 +17,11 @@ * under the License. */ -import * as React from 'react'; -import { storiesOf } from '@storybook/react'; -import { ActionWizard, ActionFactory } from './action_wizard'; -import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; -import { useState } from 'react'; - -const DashboardDrilldownActionFactory: ActionFactory< - { - dashboardId: string; - useCurrentDashboardFilters: boolean; - useCurrentDashboardDataRange: boolean; - }, - { - dashboards: Array<{ id: string; title: string }>; - } -> = { - type: 'Dashboard', - displayName: 'Go to Dashboard', - iconType: 'dashboardApp', - wizard: props => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const [config, setConfig] = useState( - props.config || { - dashboardId: undefined, - useCurrentDashboardDataRange: false, - useCurrentDashboardFilters: false, - } - ); - - function setAndSubmit(newConfig: { - dashboardId: string; - useCurrentDashboardFilters: boolean; - useCurrentDashboardDataRange: boolean; - }) { - // validate - if (newConfig.dashboardId) { - props.onConfig(newConfig as any); - } else { - props.onConfig(null); - } - - setConfig(newConfig); - } - - return ( - <> - - ({ id, text: title }))} - value={config.dashboardId} - onChange={e => { - setAndSubmit({ - ...config, - dashboardId: e.target.value, - }); - }} - aria-label="Use aria labels when no actual label is in use" - /> - - - - setAndSubmit({ - ...config, - useCurrentDashboardFilters: !config.useCurrentDashboardFilters, - } as any) - } - /> - - - - setAndSubmit({ - ...config, - useCurrentDashboardDataRange: !config.useCurrentDashboardDataRange, - } as any) - } - /> - - - ); - }, - context: { - dashboards: [ - { id: 'dashboard1', title: 'Dashboard 1' }, - { id: 'dashboard2', title: 'Dashboard 2' }, - ], - }, -}; +import React, { useState } from 'react'; -const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { - type: 'Url', - displayName: 'Go to URL', - iconType: 'link', - wizard: props => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const [config, setConfig] = useState(props.config || { url: '', openInNewTab: false }); - - function setAndSubmit(newConfig: { url: string; openInNewTab: boolean }) { - // validate - if (newConfig.url) { - props.onConfig(newConfig); - } else { - props.onConfig(null); - } - - setConfig(newConfig); - } - - return ( - <> - - setAndSubmit({ ...config, url: event.target.value })} - /> - - - setAndSubmit({ ...config, openInNewTab: !config.openInNewTab })} - /> - - - ); - }, - context: null, -}; +import { storiesOf } from '@storybook/react'; +import { ActionWizard } from './action_wizard'; +import { ACTION_FACTORIES } from './test_data'; function Demo() { const [state, setState] = useState(); @@ -164,8 +29,7 @@ function Demo() { return ( <> { setState({ factory, @@ -181,4 +45,17 @@ function Demo() { ); } -storiesOf('components/ActionWizard', module).add('default', () => ); +storiesOf('components/ActionWizard', module) + .add('default', () => ) + .add('Long list of action factories', () => ( + // to make sure layout doesn't break + {}} + /> + )); diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx new file mode 100644 index 0000000000000..0d0bec5f9469c --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -0,0 +1,124 @@ +/* + * 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 { render, cleanup, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global +import { + ActionFactory, + ActionWizard, + BaseConfig, + TEST_SUBJ_ACTION_FACTORY_ITEM, + TEST_SUBJ_SELECTED_ACTION_FACTORY, +} from './action_wizard'; +import { + DashboardDrilldownActionFactory, + UrlDrilldownActionFactory, + dashboards, +} from './test_data'; + +// TODO: for some reason global cleanup from RTL doesn't work +// afterEach is not available for it globally during setup +afterEach(cleanup); + +test('Pick and configure action', () => { + const wizardChangeFn = jest.fn(); + + const screen = render( + + > + } + onChange={wizardChangeFn} + /> + ); + + // check that all factories are displayed to pick + expect(screen.getAllByTestId(TEST_SUBJ_ACTION_FACTORY_ITEM)).toHaveLength(2); + + // select URL one + fireEvent.click(screen.getByText(/Go to URL/i)); + + // check that wizard emitted change event. null means config is invalid. this is because URL is empty string yet + expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, null); + + // Input url + const URL = 'https://elastic.co'; + fireEvent.change(screen.getByLabelText(/url/i), { + target: { value: URL }, + }); + + // check that wizard emitted change event + expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, { + url: URL, + openInNewTab: false, + }); + + // change to dashboard + fireEvent.click(screen.getByText(/change/i)); + fireEvent.click(screen.getByText(/Go to Dashboard/i)); + + // check that wizard emitted change event + // null config means it is invalid. This is because no dashboard selected yet + expect(wizardChangeFn).lastCalledWith(DashboardDrilldownActionFactory, null); + + // Select dashboard + fireEvent.change(screen.getByLabelText(/Choose destination dashboard/i), { + target: { value: dashboards[1].title }, + }); + + // check that wizard emitted change event + expect(wizardChangeFn).lastCalledWith(DashboardDrilldownActionFactory, { + dashboardId: dashboards[1].id, + useCurrentDashboardDataRange: false, + useCurrentDashboardFilters: false, + }); +}); + +test('If only one actions factory is available, then no selection step is rendered and no change button displayed', () => { + const wizardChangeFn = jest.fn(); + + const screen = render( + >} + onChange={wizardChangeFn} + /> + ); + + // check that no factories are displayed to pick from + expect(screen.queryByTestId(TEST_SUBJ_ACTION_FACTORY_ITEM)).not.toBeInTheDocument(); + expect(screen.queryByTestId(TEST_SUBJ_SELECTED_ACTION_FACTORY)).toBeInTheDocument(); + + // Input url + const URL = 'https://elastic.co'; + fireEvent.change(screen.getByLabelText(/url/i), { + target: { value: URL }, + }); + + // check that wizard emitted change event + expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, { + url: URL, + openInNewTab: false, + }); + + // check that can't change to action factory type + expect(screen.queryByTestId(/change/i)).not.toBeInTheDocument(); +}); diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 10046cec4bdf9..7914355d63f1a 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -27,68 +27,118 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import { txtChangeButton } from './i18n'; +import './index.scss'; + +// TODO: this interface is temporary for just moving forward with the component +// and it will be imported from the ../ui_actions when implemented properly +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type BaseConfig = {}; +export interface ActionFactory { + type: string; + displayName: string; + iconType?: string; + wizard: React.FC>; + context: Context; +} -// TODO: -// import './index.scss'; - -// import { Action } from '../../actions'; -// don't use external interface for now. not sure if it is stabilized - -export interface ActionFactoryWizardProps { +export interface ActionFactoryWizardProps { /** * Context represents environment where this component is being rendered. */ context: Context; /** - * Current (latest) config of the item. + * Current (latest) config of the item. (state) */ config: Config | null; /** * Callback called when user updates the config in UI. + * ActionFactory's wizard should do validations to the user's input + * In case input is complete and valid - config: Config object should be emitted + * In case input has changed to the invalid state: null should be emitted */ onConfig: (config: Config | null) => void; } -export interface ActionFactory { - type: string; - displayName: string; - iconType?: string; - context: Context; - wizard: React.FC>; -} +export interface ActionWizardProps { + /** + * List of available action factories + */ + actionFactories: Array>; -export interface ActionFactoryPickerProps { - actionFactories: Array>; - onActionFactoryPicked: (actionFactory: ActionFactory) => void; + /** + * Notifies when wizard's state changes because of user's interaction + * + * @param actionFactory - current selected action factory. null if none is selected + * @param config - current config for current action factory. null if no action factory or if wizard's inputs are invalid or incomplete + */ + onChange: ( + actionFactory: ActionFactory | null, + config: BaseConfig | null + ) => void; } +export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { + // eslint-disable-next-line prefer-const + let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); -export interface SelectedActionFactoryProps { - actionFactory: ActionFactory; + // auto pick action factory if there is only 1 available + if (!selectedActionFactory && actionFactories.length === 1) { + selectedActionFactory = actionFactories[0]; + } + + if (selectedActionFactory) { + return ( + 1} + onDeselect={() => { + setSelectedActionFactory(null); + onChange(null, null); + }} + onConfigChange={newConfig => { + onChange(selectedActionFactory, newConfig); + }} + /> + ); + } + + return ( + { + setSelectedActionFactory(actionFactory); + onChange(actionFactory, null); + }} + /> + ); +}; + +interface SelectedActionFactoryProps { + actionFactory: ActionFactory; onConfigChange: (config: Config | null) => void; showDeselect: boolean; onDeselect: () => void; } -export interface ActionWizardProps { - actionFactories: Array>; - onChange: (actionFactory: ActionFactory | null, config: Config | null) => void; -} - -export const SelectedActionFactory: React.FC = ({ +export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory'; +const SelectedActionFactory: React.FC> = ({ actionFactory, onDeselect, showDeselect, onConfigChange, }) => { - const [config, setConfig] = useState(); + const [config, setConfig] = useState(null); return (
- + {actionFactory.iconType && ( @@ -102,7 +152,7 @@ export const SelectedActionFactory: React.FC = ({ {showDeselect && ( onDeselect()}> - change + {txtChangeButton} )} @@ -123,22 +173,32 @@ export const SelectedActionFactory: React.FC = ({ ); }; -export const ActionFactoryPicker: React.FC = ({ +interface ActionFactorySelectorProps { + actionFactories: Array>; + onActionFactorySelected: (actionFactory: ActionFactory) => void; +} +export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; +const ActionFactorySelector: React.FC = ({ actionFactories, - onActionFactoryPicked, + onActionFactorySelected, }) => { - if (actionFactories.length === 0) return
No action factories to pick from :(
; + if (actionFactories.length === 0) { + // this is not user facing, as it would be impossible to get into this state + // just leaving for dev purposes for troubleshooting + return
No action factories to pick from
; + } return ( - + {actionFactories.map(actionFactory => ( onActionFactoryPicked(actionFactory)} + onClick={() => onActionFactorySelected(actionFactory)} className="eui-textCenter" paddingSize="s" > @@ -157,42 +217,3 @@ export const ActionFactoryPicker: React.FC = ({ ); }; - -export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { - // eslint-disable-next-line prefer-const - let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); - - // auto pick action factory if there is only 1 available - if (!selectedActionFactory && actionFactories.length === 1) { - selectedActionFactory = actionFactories[0]; - } - - if (selectedActionFactory) { - return ( - 1} - onDeselect={() => { - setSelectedActionFactory(null); - onChange(null, null); - }} - onConfigChange={newConfig => { - onChange(selectedActionFactory, newConfig); - }} - /> - ); - } - - return ( - { - setSelectedActionFactory(actionFactory); - onChange(actionFactory, null); - }} - /> - ); -}; diff --git a/src/plugins/ui_actions/public/components/action_wizard/i18n.ts b/src/plugins/ui_actions/public/components/action_wizard/i18n.ts new file mode 100644 index 0000000000000..dedd9d593fcf9 --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/i18n.ts @@ -0,0 +1,24 @@ +/* + * 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 txtChangeButton = i18n.translate('ui_actions.components.actionWizard.changeButton', { + defaultMessage: 'change', +}); diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.scss b/src/plugins/ui_actions/public/components/action_wizard/index.scss index c098223792c49..33368656ace4d 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/index.scss +++ b/src/plugins/ui_actions/public/components/action_wizard/index.scss @@ -1,6 +1,11 @@ -// TODO: -// @import 'src/legacy/ui/public/styles/_styling_constants'; +@import '../../../../../../src/legacy/ui/public/styles/_styling_constants'; -//.uiActions__selectedActionFactoryContainer { -// -//} +.uiActions__selectedActionFactoryContainer { + background-color: $euiColorLightestShade; + padding: $euiSize; +} + +.uiActions__ActionFactory { + width: 120px; + min-height: 100px; +} diff --git a/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx new file mode 100644 index 0000000000000..dd134930e26b4 --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx @@ -0,0 +1,164 @@ +/* + * 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, { useState } from 'react'; +import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; +import { ActionFactory, BaseConfig } from './action_wizard'; + +export const dashboards = [ + { id: 'dashboard1', title: 'Dashboard 1' }, + { id: 'dashboard2', title: 'Dashboard 2' }, +]; + +export const DashboardDrilldownActionFactory: ActionFactory< + { + dashboardId: string; + useCurrentDashboardFilters: boolean; + useCurrentDashboardDataRange: boolean; + }, + { + dashboards: Array<{ id: string; title: string }>; + } +> = { + type: 'Dashboard', + displayName: 'Go to Dashboard', + iconType: 'dashboardApp', + wizard: props => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [config, setConfig] = useState( + props.config || { + dashboardId: undefined, + useCurrentDashboardDataRange: false, + useCurrentDashboardFilters: false, + } + ); + + function setAndSubmit(newConfig: { + dashboardId: string | undefined; + useCurrentDashboardFilters: boolean; + useCurrentDashboardDataRange: boolean; + }) { + // validate + if (newConfig.dashboardId) { + props.onConfig({ ...newConfig, dashboardId: newConfig.dashboardId }); + } else { + props.onConfig(null); + } + + setConfig(newConfig); + } + + return ( + <> + + ({ id, text: title }))} + value={config.dashboardId} + onChange={e => { + setAndSubmit({ + ...config, + dashboardId: dashboards.find(d => d.title === e.target.value)?.id, + }); + }} + aria-label="Use aria labels when no actual label is in use" + /> + + + + setAndSubmit({ + ...config, + useCurrentDashboardFilters: !config.useCurrentDashboardFilters, + }) + } + /> + + + + setAndSubmit({ + ...config, + useCurrentDashboardDataRange: !config.useCurrentDashboardDataRange, + }) + } + /> + + + ); + }, + context: { + dashboards, + }, +}; + +export const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { + type: 'Url', + displayName: 'Go to URL', + iconType: 'link', + wizard: props => { + // eslint-disable-next-line react-hooks/rules-of-hooks + const [config, setConfig] = useState(props.config || { url: '', openInNewTab: false }); + + function setAndSubmit(newConfig: { url: string; openInNewTab: boolean }) { + // validate + if (newConfig.url) { + props.onConfig(newConfig); + } else { + props.onConfig(null); + } + + setConfig(newConfig); + } + + return ( + <> + + setAndSubmit({ ...config, url: event.target.value })} + /> + + + setAndSubmit({ ...config, openInNewTab: !config.openInNewTab })} + /> + + + ); + }, + context: null, +}; + +export const ACTION_FACTORIES = [ + DashboardDrilldownActionFactory, + UrlDrilldownActionFactory, +] as Array>; From bd601c60eaca10cc17224f5d8f4391556ef9ac46 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 2 Mar 2020 16:03:20 +0100 Subject: [PATCH 03/32] lint --- .../action_wizard/action_wizard.test.tsx | 4 ++-- .../action_wizard/action_wizard.tsx | 24 +++++++++---------- .../public/components/action_wizard/index.ts | 20 ++++++++++++++++ 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx index 0d0bec5f9469c..63459878c06a1 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -23,7 +23,7 @@ import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { ActionFactory, ActionWizard, - BaseConfig, + ActionFactoryBaseConfig, TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY, } from './action_wizard'; @@ -44,7 +44,7 @@ test('Pick and configure action', () => { + ActionFactory > } onChange={wizardChangeFn} diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 7914355d63f1a..14677890eb08a 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -33,8 +33,8 @@ import './index.scss'; // TODO: this interface is temporary for just moving forward with the component // and it will be imported from the ../ui_actions when implemented properly // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type BaseConfig = {}; -export interface ActionFactory { +export type ActionFactoryBaseConfig = {}; +export interface ActionFactory { type: string; displayName: string; iconType?: string; @@ -42,7 +42,7 @@ export interface ActionFactory { context: Context; } -export interface ActionFactoryWizardProps { +export interface ActionFactoryWizardProps { /** * Context represents environment where this component is being rendered. */ @@ -66,7 +66,7 @@ export interface ActionWizardProps { /** * List of available action factories */ - actionFactories: Array>; + actionFactories: Array>; /** * Notifies when wizard's state changes because of user's interaction @@ -75,14 +75,14 @@ export interface ActionWizardProps { * @param config - current config for current action factory. null if no action factory or if wizard's inputs are invalid or incomplete */ onChange: ( - actionFactory: ActionFactory | null, - config: BaseConfig | null + actionFactory: ActionFactory | null, + config: ActionFactoryBaseConfig | null ) => void; } export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { // eslint-disable-next-line prefer-const let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); @@ -118,20 +118,20 @@ export const ActionWizard: React.FC = ({ actionFactories, onC ); }; -interface SelectedActionFactoryProps { +interface SelectedActionFactoryProps { actionFactory: ActionFactory; onConfigChange: (config: Config | null) => void; showDeselect: boolean; onDeselect: () => void; } export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory'; -const SelectedActionFactory: React.FC> = ({ +const SelectedActionFactory: React.FC> = ({ actionFactory, onDeselect, showDeselect, onConfigChange, }) => { - const [config, setConfig] = useState(null); + const [config, setConfig] = useState(null); return (
> = }; interface ActionFactorySelectorProps { - actionFactories: Array>; - onActionFactorySelected: (actionFactory: ActionFactory) => void; + actionFactories: Array>; + onActionFactorySelected: (actionFactory: ActionFactory) => void; } export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; const ActionFactorySelector: React.FC = ({ diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.ts b/src/plugins/ui_actions/public/components/action_wizard/index.ts index e69de29bb2d1d..6a85c196dfda9 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/index.ts +++ b/src/plugins/ui_actions/public/components/action_wizard/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 { ActionFactory, ActionWizard } from './action_wizard'; From 9d181d39414b641842ee535e6b66d906e53298ac Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 2 Mar 2020 16:16:08 +0100 Subject: [PATCH 04/32] improve --- .../components/action_wizard/action_wizard.test.tsx | 2 +- .../public/components/action_wizard/test_data.tsx | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx index 63459878c06a1..18971e199253e 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -82,7 +82,7 @@ test('Pick and configure action', () => { // Select dashboard fireEvent.change(screen.getByLabelText(/Choose destination dashboard/i), { - target: { value: dashboards[1].title }, + target: { value: dashboards[1].id }, }); // check that wizard emitted change event diff --git a/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx index dd134930e26b4..cef991c4d4a86 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx @@ -19,7 +19,7 @@ import React, { useState } from 'react'; import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; -import { ActionFactory, BaseConfig } from './action_wizard'; +import { ActionFactory, ActionFactoryBaseConfig } from './action_wizard'; export const dashboards = [ { id: 'dashboard1', title: 'Dashboard 1' }, @@ -70,15 +70,14 @@ export const DashboardDrilldownActionFactory: ActionFactory< ({ id, text: title }))} + options={props.context.dashboards.map(({ id, title }) => ({ value: id, text: title }))} value={config.dashboardId} onChange={e => { setAndSubmit({ ...config, - dashboardId: dashboards.find(d => d.title === e.target.value)?.id, + dashboardId: e.target.value, }); }} - aria-label="Use aria labels when no actual label is in use" /> @@ -161,4 +160,4 @@ export const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa export const ACTION_FACTORIES = [ DashboardDrilldownActionFactory, UrlDrilldownActionFactory, -] as Array>; +] as Array>; From 9e994a1852523acb53a6de2857a86a5f1fe529dc Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Mon, 2 Mar 2020 17:10:51 +0100 Subject: [PATCH 05/32] fix --- src/plugins/ui_actions/public/components/action_wizard/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/i18n.ts b/src/plugins/ui_actions/public/components/action_wizard/i18n.ts index dedd9d593fcf9..fff52f43bc1db 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/i18n.ts +++ b/src/plugins/ui_actions/public/components/action_wizard/i18n.ts @@ -19,6 +19,6 @@ import { i18n } from '@kbn/i18n'; -export const txtChangeButton = i18n.translate('ui_actions.components.actionWizard.changeButton', { +export const txtChangeButton = i18n.translate('uiActions.components.actionWizard.changeButton', { defaultMessage: 'change', }); From a9144b7c3f0351c62d8d0e7bbdbbcb05e6395a07 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Tue, 3 Mar 2020 13:58:29 +0100 Subject: [PATCH 06/32] wip --- .../action_wizard/action_wizard.test.tsx | 11 +--- .../action_wizard/action_wizard.tsx | 35 ++++++------ .../public/components/action_wizard/index.ts | 2 +- .../components/action_wizard/test_data.tsx | 6 +- src/plugins/ui_actions/public/index.ts | 1 + .../drilldown_hello_bar.story.tsx | 16 +++++- .../drilldown_hello_bar.tsx | 47 +++++++++++++--- .../components/drilldown_hello_bar/i18n.ts | 29 ++++++++++ .../components/drilldown_hello_bar/index.scss | 5 ++ .../drilldown_picker.story.tsx | 13 ----- .../drilldown_picker/drilldown_picker.tsx | 21 ------- .../components/drilldown_picker/index.tsx | 7 --- .../flyout_create_drilldown.story.tsx | 4 +- .../flyout_create_drilldown.tsx | 55 ++++++++++++++++++- .../flyout_create_drilldown/i18n.ts | 2 +- .../components/flyout_frame/flyout_frame.tsx | 2 +- .../form_create_drilldown.tsx | 45 +++++++++++---- .../components/form_create_drilldown/i18n.ts | 2 +- .../form_create_drilldown/index.scss | 6 ++ 19 files changed, 209 insertions(+), 100 deletions(-) create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts create mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.scss delete mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx delete mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx delete mode 100644 x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.scss diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx index 18971e199253e..8c8c573d36538 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -23,13 +23,13 @@ import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { ActionFactory, ActionWizard, - ActionFactoryBaseConfig, TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY, } from './action_wizard'; import { DashboardDrilldownActionFactory, UrlDrilldownActionFactory, + ACTION_FACTORIES, dashboards, } from './test_data'; @@ -41,14 +41,7 @@ test('Pick and configure action', () => { const wizardChangeFn = jest.fn(); const screen = render( - - > - } - onChange={wizardChangeFn} - /> + ); // check that all factories are displayed to pick diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 14677890eb08a..70c7f16d3b3d7 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -34,7 +34,10 @@ import './index.scss'; // and it will be imported from the ../ui_actions when implemented properly // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ActionFactoryBaseConfig = {}; -export interface ActionFactory { +export interface ActionFactory< + Config extends ActionFactoryBaseConfig = ActionFactoryBaseConfig, + Context = unknown +> { type: string; displayName: string; iconType?: string; @@ -42,7 +45,10 @@ export interface ActionFactory { +export interface ActionFactoryWizardProps< + Config extends ActionFactoryBaseConfig = ActionFactoryBaseConfig, + Context = unknown +> { /** * Context represents environment where this component is being rendered. */ @@ -66,7 +72,7 @@ export interface ActionWizardProps { /** * List of available action factories */ - actionFactories: Array>; + actionFactories: ActionFactory[]; /** * Notifies when wizard's state changes because of user's interaction @@ -74,17 +80,11 @@ export interface ActionWizardProps { * @param actionFactory - current selected action factory. null if none is selected * @param config - current config for current action factory. null if no action factory or if wizard's inputs are invalid or incomplete */ - onChange: ( - actionFactory: ActionFactory | null, - config: ActionFactoryBaseConfig | null - ) => void; + onChange: (actionFactory: ActionFactory | null, config: ActionFactoryBaseConfig | null) => void; } export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { // eslint-disable-next-line prefer-const - let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); + let [selectedActionFactory, setSelectedActionFactory] = useState(null); // auto pick action factory if there is only 1 available if (!selectedActionFactory && actionFactories.length === 1) { @@ -118,14 +118,17 @@ export const ActionWizard: React.FC = ({ actionFactories, onC ); }; -interface SelectedActionFactoryProps { +interface SelectedActionFactoryProps< + Config extends ActionFactoryBaseConfig = ActionFactoryBaseConfig, + Context = unknown +> { actionFactory: ActionFactory; onConfigChange: (config: Config | null) => void; showDeselect: boolean; onDeselect: () => void; } export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory'; -const SelectedActionFactory: React.FC> = ({ +const SelectedActionFactory: React.FC = ({ actionFactory, onDeselect, showDeselect, @@ -134,7 +137,7 @@ const SelectedActionFactory: React.FC(null); return (
@@ -174,8 +177,8 @@ const SelectedActionFactory: React.FC>; - onActionFactorySelected: (actionFactory: ActionFactory) => void; + actionFactories: ActionFactory[]; + onActionFactorySelected: (actionFactory: ActionFactory) => void; } export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; const ActionFactorySelector: React.FC = ({ diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.ts b/src/plugins/ui_actions/public/components/action_wizard/index.ts index 6a85c196dfda9..23364b237e88c 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/index.ts +++ b/src/plugins/ui_actions/public/components/action_wizard/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { ActionFactory, ActionWizard } from './action_wizard'; +export { ActionFactory, ActionWizard, ActionFactoryBaseConfig } from './action_wizard'; diff --git a/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx index cef991c4d4a86..a34dfc5031a1f 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/test_data.tsx @@ -19,7 +19,7 @@ import React, { useState } from 'react'; import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; -import { ActionFactory, ActionFactoryBaseConfig } from './action_wizard'; +import { ActionFactory } from './action_wizard'; export const dashboards = [ { id: 'dashboard1', title: 'Dashboard 1' }, @@ -157,7 +157,7 @@ export const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa context: null, }; -export const ACTION_FACTORIES = [ +export const ACTION_FACTORIES = ([ DashboardDrilldownActionFactory, UrlDrilldownActionFactory, -] as Array>; +] as unknown) as ActionFactory[]; diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index eb69aefdbb50e..39c1f54aa5c22 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -30,3 +30,4 @@ export { Action, createAction, IncompatibleActionError } from './actions'; export { buildContextMenuForActions } from './context_menu'; export { Trigger, TriggerContext } from './triggers'; export { TriggerContextMapping, TriggerId } from './types'; +export { ActionWizard, ActionFactory, ActionFactoryBaseConfig } from './components/action_wizard'; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx index 7a9e19342f27c..c4a4630397f1c 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.story.tsx @@ -8,6 +8,16 @@ import * as React from 'react'; import { storiesOf } from '@storybook/react'; import { DrilldownHelloBar } from '.'; -storiesOf('components/DrilldownHelloBar', module).add('default', () => { - return ; -}); +const Demo = () => { + const [show, setShow] = React.useState(true); + return show ? ( + { + setShow(false); + }} + /> + ) : null; +}; + +storiesOf('components/DrilldownHelloBar', module).add('default', () => ); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx index 1ef714f7b86e2..2a77fa9b77917 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx @@ -5,22 +5,51 @@ */ import React from 'react'; +import { + EuiCallOut, + EuiFlexGroup, + EuiFlexItem, + EuiTextColor, + EuiLink, + EuiSpacer, + EuiButtonEmpty, +} from '@elastic/eui'; +import './index.scss'; +import { txtHideHelpButtonLabel, txtHelpText, txtViewDocsLinkLabel } from './i18n'; export interface DrilldownHelloBarProps { docsLink?: string; + onHideClick?: () => void; } /** - * @todo https://github.com/elastic/kibana/issues/55311 + * @todo improve with https://github.com/elastic/eui/pull/2837 when newer eui is merged into kibana */ -export const DrilldownHelloBar: React.FC = ({ docsLink }) => { +export const DrilldownHelloBar: React.FC = ({ + docsLink, + onHideClick = () => {}, +}) => { 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 -
+ + + {txtHelpText} + {docsLink && ( + <> + + {txtViewDocsLinkLabel} + + )} + + + + {txtHideHelpButtonLabel} + + + + } + /> ); }; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts new file mode 100644 index 0000000000000..63dc95dabc0fb --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/i18n.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const txtHelpText = i18n.translate( + 'xpack.drilldowns.components.DrilldownHelloBar.helpText', + { + defaultMessage: + '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.', + } +); + +export const txtViewDocsLinkLabel = i18n.translate( + 'xpack.drilldowns.components.DrilldownHelloBar.viewDocsLinkLabel', + { + defaultMessage: 'View docs', + } +); + +export const txtHideHelpButtonLabel = i18n.translate( + 'xpack.drilldowns.components.DrilldownHelloBar.hideHelpButtonLabel', + { + defaultMessage: 'Hide', + } +); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.scss b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.scss new file mode 100644 index 0000000000000..91105c27de84b --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/index.scss @@ -0,0 +1,5 @@ +@import '../../../../../../src/legacy/ui/public/styles/_styling_constants'; + +.drilldowns__helloBarContent .euiFlexItem { + margin: $euiSizeL; // increase spacing between elements +} diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx deleted file mode 100644 index 5627a5d6f4522..0000000000000 --- a/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.story.tsx +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import * as React from 'react'; -import { storiesOf } from '@storybook/react'; -import { DrilldownPicker } from '.'; - -storiesOf('components/DrilldownPicker', module).add('default', () => { - return ; -}); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx deleted file mode 100644 index 3748fc666c81c..0000000000000 --- a/x-pack/plugins/drilldowns/public/components/drilldown_picker/drilldown_picker.tsx +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React from 'react'; - -// eslint-disable-next-line -export interface DrilldownPickerProps {} - -export const DrilldownPicker: React.FC = () => { - return ( - - ); -}; diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx deleted file mode 100644 index 3be289fe6d46e..0000000000000 --- a/x-pack/plugins/drilldowns/public/components/drilldown_picker/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export * from './drilldown_picker'; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx index 4f024b7d9cd6a..6c31241754f27 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.story.tsx @@ -17,8 +17,8 @@ storiesOf('components/FlyoutCreateDrilldown', module) }) .add('open in flyout', () => { return ( - - + {}}> + {}} /> ); }); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx index b45ac9197c7e0..a0c9555b95bf8 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/flyout_create_drilldown.tsx @@ -4,12 +4,16 @@ * you may not use this file except in compliance with the Elastic License. */ -import React from 'react'; +import React, { useState } from 'react'; import { EuiButton } from '@elastic/eui'; import { FormCreateDrilldown } from '../form_create_drilldown'; import { FlyoutFrame } from '../flyout_frame'; import { txtCreateDrilldown } from './i18n'; import { FlyoutCreateDrilldownActionContext } from '../../actions'; +import { + ActionFactory, + ActionFactoryBaseConfig, +} from '../../../../../../src/plugins/ui_actions/public'; export interface FlyoutCreateDrilldownProps { context: FlyoutCreateDrilldownActionContext; @@ -20,15 +24,60 @@ export const FlyoutCreateDrilldown: React.FC = ({ context, onClose, }) => { + const [state, setState] = useState<{ + name: string; + action: { + actionFactory: ActionFactory | null; + config: ActionFactoryBaseConfig | null; + }; + }>({ + name: '', + action: { + actionFactory: null, + config: null, + }, + }); + + const isFormValid = () => { + if (!state.name) { + // name is required + return false; + } + + if (!state.action.actionFactory || !state.action.config) { + // action factory has to be selected and config has to be present + return false; + } + + return true; + }; + const footer = ( - {}} fill> + {}} fill isDisabled={!isFormValid()}> {txtCreateDrilldown} ); return ( - + { + setState({ + ...state, + name: newName, + }); + }} + onActionChange={(actionFactory, config) => { + setState({ + ...state, + action: { + actionFactory, + config, + }, + }); + }} + /> ); }; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts index ceabc6d3a9aa5..782a64dbe16e3 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/flyout_create_drilldown/i18n.ts @@ -9,6 +9,6 @@ import { i18n } from '@kbn/i18n'; export const txtCreateDrilldown = i18n.translate( 'xpack.drilldowns.components.FlyoutCreateDrilldown.CreateDrilldown', { - defaultMessage: 'Create drilldown', + defaultMessage: 'Create Drilldown', } ); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx index 2945cfd739482..9c1c51d3a0d9d 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx @@ -33,7 +33,7 @@ export const FlyoutFrame: React.FC = ({ }) => { const headerFragment = title && ( - +

{title}

diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx index 4422de604092b..b076100c832ed 100644 --- a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/form_create_drilldown.tsx @@ -5,24 +5,38 @@ */ import React from 'react'; -import { EuiForm, EuiFormRow, EuiFieldText } from '@elastic/eui'; +import './index.scss'; +import { EuiForm, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; import { DrilldownHelloBar } from '../drilldown_hello_bar'; import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; -import { DrilldownPicker } from '../drilldown_picker'; +import { + ActionWizard, + ActionFactory, + ActionFactoryBaseConfig, +} from '../../../../../../src/plugins/ui_actions/public'; + +// TODO: this should be actual input to the component and should not be using test data +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { ACTION_FACTORIES } from '../../../../../../src/plugins/ui_actions/public/components/action_wizard/test_data'; const noop = () => {}; export interface FormCreateDrilldownProps { name?: string; onNameChange?: (name: string) => void; + onActionChange?: ( + actionFactory: ActionFactory | null, + config: ActionFactoryBaseConfig | null + ) => void; } export const FormCreateDrilldown: React.FC = ({ name = '', onNameChange = noop, + onActionChange = noop, }) => { const nameFragment = ( - + = ({ ); - const triggerPicker =
Trigger Picker will be here
; - const actionPicker = ( - - + const actionWizard = ( + + { + onActionChange(actionFactory, config); + }} + /> ); return ( <> - {nameFragment} - {triggerPicker} - {actionPicker} + + + {nameFragment} + + {actionWizard} + ); }; diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts index 4c0e287935edd..2dfcd917a7900 100644 --- a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/i18n.ts @@ -23,6 +23,6 @@ export const txtUntitledDrilldown = i18n.translate( export const txtDrilldownAction = i18n.translate( 'xpack.drilldowns.components.FormCreateDrilldown.drilldownAction', { - defaultMessage: 'Drilldown action', + defaultMessage: 'Drilldown action:', } ); diff --git a/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.scss b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.scss new file mode 100644 index 0000000000000..55832eb433f9b --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/form_create_drilldown/index.scss @@ -0,0 +1,6 @@ +@import '../../../../../../src/legacy/ui/public/styles/_styling_constants'; + +.drilldowns__formCreateDrillDownEuiFormRow .euiFormRow__label { + font-size: #{$euiSize * 0.875}; // increase default euiFormRow label size + margin-bottom: $euiSizeS; // increase default euiFormRow label margin bottom +} From a08ea95025362ac17b1cb0426df662073d745855 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Tue, 3 Mar 2020 18:09:57 +0100 Subject: [PATCH 07/32] @cchaos review --- .../components/action_wizard/action_wizard.scss | 11 +++++++++++ .../public/components/action_wizard/action_wizard.tsx | 6 +++--- .../public/components/action_wizard/index.scss | 11 ----------- 3 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss delete mode 100644 src/plugins/ui_actions/public/components/action_wizard/index.scss diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss new file mode 100644 index 0000000000000..e0cff73b53e5c --- /dev/null +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss @@ -0,0 +1,11 @@ +@import '../../../../../../src/legacy/ui/public/styles/_styling_constants'; + +.uiaActionWizard__selectedActionFactoryContainer { + background-color: $euiColorLightestShade; + padding: $euiSize; +} + +.uiaActionWizard__actionFactoryItem { + width: #{$euiSize * 7.5}; + min-height: #{$euiSize * 6}; +} diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 14677890eb08a..8aec8821e8990 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -28,7 +28,7 @@ import { EuiText, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; -import './index.scss'; +import './action_wizard.scss'; // TODO: this interface is temporary for just moving forward with the component // and it will be imported from the ../ui_actions when implemented properly @@ -134,7 +134,7 @@ const SelectedActionFactory: React.FC(null); return (
@@ -192,7 +192,7 @@ const ActionFactorySelector: React.FC = ({ {actionFactories.map(actionFactory => ( Date: Tue, 3 Mar 2020 18:22:23 +0100 Subject: [PATCH 08/32] improve storybook setup to autoimport scss mixins --- packages/kbn-storybook/storybook_config/webpack.config.js | 7 +++++++ .../public/components/action_wizard/action_wizard.scss | 2 -- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/kbn-storybook/storybook_config/webpack.config.js b/packages/kbn-storybook/storybook_config/webpack.config.js index 230c8af81f8b5..1531c1d22b01b 100644 --- a/packages/kbn-storybook/storybook_config/webpack.config.js +++ b/packages/kbn-storybook/storybook_config/webpack.config.js @@ -19,6 +19,7 @@ const { resolve } = require('path'); const webpack = require('webpack'); +const { stringifyRequest } = require('loader-utils'); const CopyWebpackPlugin = require('copy-webpack-plugin'); const { REPO_ROOT, DLL_DIST_DIR } = require('../lib/constants'); // eslint-disable-next-line import/no-unresolved @@ -90,6 +91,12 @@ module.exports = async ({ config }) => { { loader: 'sass-loader', options: { + prependData(loaderContext) { + return `@import ${stringifyRequest( + loaderContext, + resolve(REPO_ROOT, 'src/legacy/ui/public/styles/_styling_constants.scss') + )};\n`; + }, sassOptions: { includePaths: [resolve(REPO_ROOT, 'node_modules')], }, diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss index e0cff73b53e5c..c22c3125143f4 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss @@ -1,5 +1,3 @@ -@import '../../../../../../src/legacy/ui/public/styles/_styling_constants'; - .uiaActionWizard__selectedActionFactoryContainer { background-color: $euiColorLightestShade; padding: $euiSize; From 34ee67d680c1bb694d7bda255dada2edaea1a8c0 Mon Sep 17 00:00:00 2001 From: Andrea Del Rio Date: Tue, 3 Mar 2020 13:52:49 -0800 Subject: [PATCH 09/32] using keypadmenuitem --- .../action_wizard/action_wizard.scss | 5 ++-- .../action_wizard/action_wizard.tsx | 25 ++++++------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss index c22c3125143f4..cd8ec0e7238e6 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss @@ -4,6 +4,7 @@ } .uiaActionWizard__actionFactoryItem { - width: #{$euiSize * 7.5}; - min-height: #{$euiSize * 6}; + .euiKeyPadMenuItem__label { + height: #{$euiSizeXL}; + } } diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 8aec8821e8990..f292b2618c6f5 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -26,6 +26,7 @@ import { EuiPanel, EuiSpacer, EuiText, + EuiKeyPadMenuItem, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; import './action_wizard.scss'; @@ -189,30 +190,18 @@ const ActionFactorySelector: React.FC = ({ } return ( - + {actionFactories.map(actionFactory => ( - onActionFactorySelected(actionFactory)} > - onActionFactorySelected(actionFactory)} - className="eui-textCenter" - paddingSize="s" - > - {actionFactory.iconType && ( - <> - - - - )} - -

{actionFactory.displayName}

-
-
-
+ {actionFactory.iconType && } + ))}
); From 56565dc4efacae5915637c9fced6a3d6611de1b5 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 4 Mar 2020 11:28:39 +0100 Subject: [PATCH 10/32] fix ts --- .../public/components/action_wizard/action_wizard.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index f292b2618c6f5..ebd94ee410f58 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -23,7 +23,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, - EuiPanel, EuiSpacer, EuiText, EuiKeyPadMenuItem, @@ -194,7 +193,6 @@ const ActionFactorySelector: React.FC = ({ {actionFactories.map(actionFactory => ( Date: Wed, 4 Mar 2020 14:05:44 +0100 Subject: [PATCH 11/32] wip --- .../action_wizard/action_wizard.tsx | 23 +++- .../actions/flyout_create_drilldown/index.tsx | 4 +- .../drilldown_hello_bar.tsx | 4 +- .../flyout_drilldown_wizard.story.tsx | 37 +++++- .../flyout_drilldown_wizard.tsx | 125 +++++++++++++----- .../flyout_drilldown_wizard/i18n.ts | 32 ++++- .../flyout_drilldown_wizard/index.ts | 2 +- .../form_drilldown_wizard.story.tsx | 26 ++-- .../form_drilldown_wizard.test.tsx | 18 +-- .../form_drilldown_wizard.tsx | 10 +- .../form_drilldown_wizard/index.tsx | 2 +- 11 files changed, 202 insertions(+), 81 deletions(-) diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx index 1203ce70d82b8..8df0ec5c3220e 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx @@ -69,6 +69,14 @@ export interface ActionFactoryWizardProps< } export interface ActionWizardProps { + /** + * Initial action factory & config. To be used in editing flow + */ + initialActionConfig?: { + actionFactory: ActionFactory | null; + config: ActionFactoryBaseConfig | null; + }; + /** * List of available action factories */ @@ -82,9 +90,15 @@ export interface ActionWizardProps { */ onChange: (actionFactory: ActionFactory | null, config: ActionFactoryBaseConfig | null) => void; } -export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { +export const ActionWizard: React.FC = ({ + actionFactories, + onChange, + initialActionConfig = null, +}) => { // eslint-disable-next-line prefer-const - let [selectedActionFactory, setSelectedActionFactory] = useState(null); + let [selectedActionFactory, setSelectedActionFactory] = useState( + initialActionConfig?.actionFactory ?? null + ); // auto pick action factory if there is only 1 available if (!selectedActionFactory && actionFactories.length === 1) { @@ -100,6 +114,7 @@ export const ActionWizard: React.FC = ({ actionFactories, onC setSelectedActionFactory(null); onChange(null, null); }} + initialConfig={initialActionConfig?.config} onConfigChange={newConfig => { onChange(selectedActionFactory, newConfig); }} @@ -123,6 +138,7 @@ interface SelectedActionFactoryProps< Context = unknown > { actionFactory: ActionFactory; + initialConfig?: Config | null; onConfigChange: (config: Config | null) => void; showDeselect: boolean; onDeselect: () => void; @@ -133,8 +149,9 @@ const SelectedActionFactory: React.FC = ({ onDeselect, showDeselect, onConfigChange, + initialConfig = null, }) => { - const [config, setConfig] = useState(null); + const [config, setConfig] = useState(initialConfig); return (
handle.close()} />) + toMountPoint( handle.close()} />) ); } } diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx index 2a77fa9b77917..3961315c2e98a 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx @@ -14,7 +14,7 @@ import { EuiSpacer, EuiButtonEmpty, } from '@elastic/eui'; -import './index.scss'; +import './drilldown_hello_bar.scss'; import { txtHideHelpButtonLabel, txtHelpText, txtViewDocsLinkLabel } from './i18n'; export interface DrilldownHelloBarProps { @@ -33,7 +33,7 @@ export const DrilldownHelloBar: React.FC = ({ + {txtHelpText} {docsLink && ( diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx index b56ee4b27b4d1..e6c0ae84559b9 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx @@ -9,16 +9,43 @@ import * as React from 'react'; import { EuiFlyout } from '@elastic/eui'; import { storiesOf } from '@storybook/react'; -import { FlyoutEditDrilldown } from '.'; +import { FlyoutDrilldownWizard } from '.'; +// eslint-disable-next-line @kbn/eslint/no-restricted-paths +import { UrlDrilldownActionFactory } from '../../../../../../src/plugins/ui_actions/public/components/action_wizard/test_data'; +import { + ActionFactoryBaseConfig, + ActionFactory, +} from '../../../../../../src/plugins/ui_actions/public/'; -storiesOf('components/FlyoutCreateDrilldown', module) +storiesOf('components/FlyoutDrilldownWizard', module) .add('default', () => { - return ; + return ; }) - .add('open in flyout', () => { + .add('open in flyout - create', () => { return ( {}}> - {}} /> + {}} /> + + ); + }) + .add('open in flyout - edit', () => { + return ( + {}}> + {}} + initialDrilldownWizardConfig={{ + name: 'My fancy drilldown', + actionConfig: { + actionFactory: (UrlDrilldownActionFactory as unknown) as ActionFactory, + config: { + url: 'https://elastic.co', + openInNewTab: true, + } as ActionFactoryBaseConfig, + }, + }} + mode={'edit'} + /> ); }); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 19270542e41f9..c4ba5565054da 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -5,46 +5,84 @@ */ import React, { useState } from 'react'; -import { EuiButton } from '@elastic/eui'; -import { FormCreateDrilldown } from '../form_edit_drilldown'; +import { EuiButton, EuiSpacer } from '@elastic/eui'; +import { FormDrilldownWizard } from '../form_drilldown_wizard'; import { FlyoutFrame } from '../flyout_frame'; -import { txtCreateDrilldown } from './i18n'; -import { FlyoutCreateDrilldownAction } from '../../actions'; +import { + txtCreateDrilldownButtonLabel, + txtCreateDrilldownTitle, + txtDeleteDrilldownButtonLabel, + txtEditDrilldownButtonLabel, + txtEditDrilldownTitle, +} from './i18n'; +import { FlyoutCreateDrilldownActionContext } from '../../actions'; import { ActionFactory, ActionFactoryBaseConfig, } from '../../../../../../src/plugins/ui_actions/public'; -export interface FlyoutEditDrilldownProps { - context: FlyoutCreateDrilldownAction; +export interface DrilldownWizardConfig { + name: string; + actionConfig: { + actionFactory: ActionFactory; + config: ActionFactoryBaseConfig; + }; +} + +/** + * Represent current wizard's form state in invalid or incomplete shape + */ +export interface PartialDrilldownWizardConfig { + name: string; + actionConfig: { + actionFactory: ActionFactory | null; + config: ActionFactoryBaseConfig | null; + }; +} + +export interface FlyoutDrilldownWizardProps { + context: FlyoutCreateDrilldownActionContext; + onSubmit?: (drilldownWizardConfig: DrilldownWizardConfig) => void; + onDelete?: () => void; onClose?: () => void; + + mode?: 'create' | 'edit'; + initialDrilldownWizardConfig?: DrilldownWizardConfig; } -export const FlyoutEditDrilldown: React.FC = ({ +export const FlyoutDrilldownWizard: React.FC = ({ context, onClose, + onSubmit = () => {}, + initialDrilldownWizardConfig, + mode = 'create', + onDelete = () => {}, }) => { - const [state, setState] = useState<{ - name: string; - action: { - actionFactory: ActionFactory | null; - config: ActionFactoryBaseConfig | null; - }; - }>({ - name: '', - action: { - actionFactory: null, - config: null, - }, - }); + const [wizardConfig, setWizardConfig] = useState< + DrilldownWizardConfig | PartialDrilldownWizardConfig + >( + () => + initialDrilldownWizardConfig ?? { + name: '', + actionConfig: { + actionFactory: null, + config: null, + }, + } + ); - const isFormValid = () => { - if (!state.name) { + const isFormValid = ( + currentWizardConfig: PartialDrilldownWizardConfig | DrilldownWizardConfig + ): currentWizardConfig is DrilldownWizardConfig => { + if (!currentWizardConfig.name) { // name is required return false; } - if (!state.action.actionFactory || !state.action.config) { + if ( + !currentWizardConfig.actionConfig.actionFactory || + !currentWizardConfig.actionConfig.config + ) { // action factory has to be selected and config has to be present return false; } @@ -53,31 +91,52 @@ export const FlyoutEditDrilldown: React.FC = ({ }; const footer = ( - {}} fill isDisabled={!isFormValid()}> - {txtCreateDrilldown} + { + if (isFormValid(wizardConfig)) { + onSubmit(wizardConfig); + } + }} + fill + isDisabled={!isFormValid(wizardConfig)} + > + {mode === 'edit' ? txtEditDrilldownButtonLabel : txtCreateDrilldownButtonLabel} ); return ( - - + { - setState({ - ...state, + setWizardConfig({ + ...wizardConfig, name: newName, }); }} + initialActionConfig={wizardConfig.actionConfig} onActionConfigChange={(actionFactory, config) => { - setState({ - ...state, - action: { + setWizardConfig({ + ...wizardConfig, + actionConfig: { actionFactory, config, }, }); }} /> + {mode === 'edit' && ( + <> + + + {txtDeleteDrilldownButtonLabel} + + + )} ); }; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/i18n.ts index 782a64dbe16e3..44f4a9c041a3b 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/i18n.ts @@ -6,9 +6,37 @@ import { i18n } from '@kbn/i18n'; -export const txtCreateDrilldown = i18n.translate( - 'xpack.drilldowns.components.FlyoutCreateDrilldown.CreateDrilldown', +export const txtCreateDrilldownTitle = i18n.translate( + 'xpack.drilldowns.components.FlyoutDrilldownWizard.CreateDrilldownTitle', { defaultMessage: 'Create Drilldown', } ); + +export const txtEditDrilldownTitle = i18n.translate( + 'xpack.drilldowns.components.FlyoutDrilldownWizard.EditDrilldownTitle', + { + defaultMessage: 'Edit Drilldown', + } +); + +export const txtCreateDrilldownButtonLabel = i18n.translate( + 'xpack.drilldowns.components.FlyoutDrilldownWizard.CreateDrilldownButtonLabel', + { + defaultMessage: 'Create drilldown', + } +); + +export const txtEditDrilldownButtonLabel = i18n.translate( + 'xpack.drilldowns.components.FlyoutDrilldownWizard.EditDrilldownButtonLabel', + { + defaultMessage: 'Save', + } +); + +export const txtDeleteDrilldownButtonLabel = i18n.translate( + 'xpack.drilldowns.components.FlyoutDrilldownWizard.DeleteDrilldownButtonLabel', + { + defaultMessage: 'Delete drilldown', + } +); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/index.ts b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/index.ts index fd0260ba412c3..96ed23bf112c9 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/index.ts +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export * from './flyout_edit_drilldown'; +export * from './flyout_drilldown_wizard'; diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx index e7e1d67473e8c..f158c46c15fef 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx @@ -4,31 +4,25 @@ * you may not use this file except in compliance with the Elastic License. */ -/* eslint-disable no-console */ - import * as React from 'react'; -import { EuiFlyout } from '@elastic/eui'; import { storiesOf } from '@storybook/react'; -import { FormCreateDrilldown } from '.'; +import { FormDrilldownWizard } from '.'; const DemoEditName: React.FC = () => { const [name, setName] = React.useState(''); - return ; + return ( + <> +
name: {name}
+ + ); }; -storiesOf('components/FormCreateDrilldown', module) +storiesOf('components/FormDrilldownWizard', module) .add('default', () => { - return ; + return ; }) .add('[name=foobar]', () => { - return ; + return ; }) - .add('can edit name', () => ) - .add('open in flyout', () => { - return ( - - - - ); - }); + .add('can edit name', () => ); diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx index 6691966e47e64..1b6df3cba37aa 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx @@ -6,21 +6,21 @@ import React from 'react'; import { render } from 'react-dom'; -import { FormCreateDrilldown } from '.'; +import { FormDrilldownWizard } from './form_drilldown_wizard'; import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; import { txtNameOfDrilldown } from './i18n'; -describe('', () => { +describe('', () => { test('renders without crashing', () => { const div = document.createElement('div'); - render( {}} />, div); + render( {}} />, div); }); describe('[name=]', () => { test('if name not provided, uses to empty string', () => { const div = document.createElement('div'); - render(, div); + render(, div); const input = div.querySelector( '[data-test-subj="dynamicActionNameInput"]' @@ -29,26 +29,22 @@ describe('', () => { expect(input?.value).toBe(''); }); - test('can set name input field value', () => { + test('can set initial name input field value', () => { const div = document.createElement('div'); - render(, div); + render(, div); const input = div.querySelector( '[data-test-subj="dynamicActionNameInput"]' ) as HTMLInputElement; expect(input?.value).toBe('foo'); - - render(, div); - - expect(input?.value).toBe('bar'); }); test('fires onNameChange callback on name change', () => { const onNameChange = jest.fn(); const utils = renderTestingLibrary( - + ); const input = utils.getByLabelText(txtNameOfDrilldown); diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx index 4e8260060245a..4026ff20ca079 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx @@ -5,7 +5,7 @@ */ import React, { useState } from 'react'; -import './index.scss'; +import './form_drilldown_wizard.scss'; import { EuiForm, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; import { DrilldownHelloBar } from '../drilldown_hello_bar'; import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; @@ -21,7 +21,7 @@ import { ACTION_FACTORIES } from '../../../../../../src/plugins/ui_actions/publi const noop = () => {}; -export interface FormCreateDrilldownProps { +export interface FormDrilldownWizardProps { /** * Initial name - to be used during editing flow */ @@ -48,7 +48,7 @@ export interface FormCreateDrilldownProps { ) => void; } -export const FormCreateDrilldown: React.FC = ({ +export const FormDrilldownWizard: React.FC = ({ initialName = '', initialActionConfig = { actionFactory: null, config: null }, onNameChange = noop, @@ -56,7 +56,7 @@ export const FormCreateDrilldown: React.FC = ({ }) => { const [name, setName] = useState(initialName); const nameFragment = ( - + = ({ Date: Wed, 4 Mar 2020 16:02:20 +0100 Subject: [PATCH 12/32] move to x-pack --- src/dev/storybook/aliases.ts | 2 +- .../public/components/action_wizard/i18n.ts | 24 ----------------- .../public/components/action_wizard/index.ts | 20 -------------- src/plugins/ui_actions/scripts/storybook.js | 26 ------------------- x-pack/dev-tools/jest/setup/setup_test.js | 4 +++ .../action_wizard/action_wizard.scss | 4 +-- .../action_wizard/action_wizard.story.tsx | 19 +++----------- .../action_wizard/action_wizard.test.tsx | 19 +++----------- .../action_wizard/action_wizard.tsx | 23 ++++------------ .../public/components/action_wizard/i18n.ts | 11 ++++++++ .../public/components/action_wizard/index.ts | 7 +++++ .../components/action_wizard/test_data.tsx | 19 +++----------- .../advanced_ui_actions/scripts/storybook.js | 13 ++++++++++ 13 files changed, 52 insertions(+), 139 deletions(-) delete mode 100644 src/plugins/ui_actions/public/components/action_wizard/i18n.ts delete mode 100644 src/plugins/ui_actions/public/components/action_wizard/index.ts delete mode 100644 src/plugins/ui_actions/scripts/storybook.js rename {src/plugins/ui_actions => x-pack/plugins/advanced_ui_actions}/public/components/action_wizard/action_wizard.scss (59%) rename {src/plugins/ui_actions => x-pack/plugins/advanced_ui_actions}/public/components/action_wizard/action_wizard.story.tsx (57%) rename {src/plugins/ui_actions => x-pack/plugins/advanced_ui_actions}/public/components/action_wizard/action_wizard.test.tsx (80%) rename {src/plugins/ui_actions => x-pack/plugins/advanced_ui_actions}/public/components/action_wizard/action_wizard.tsx (86%) create mode 100644 x-pack/plugins/advanced_ui_actions/public/components/action_wizard/i18n.ts create mode 100644 x-pack/plugins/advanced_ui_actions/public/components/action_wizard/index.ts rename {src/plugins/ui_actions => x-pack/plugins/advanced_ui_actions}/public/components/action_wizard/test_data.tsx (84%) create mode 100644 x-pack/plugins/advanced_ui_actions/scripts/storybook.js diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 97fa2c51b2fa3..de2dbd68b544b 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -24,5 +24,5 @@ export const storybookAliases = { embeddable: 'src/plugins/embeddable/scripts/storybook.js', infra: 'x-pack/legacy/plugins/infra/scripts/storybook.js', siem: 'x-pack/legacy/plugins/siem/scripts/storybook.js', - ui_actions: 'src/plugins/ui_actions/scripts/storybook.js', + ui_actions: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js', }; diff --git a/src/plugins/ui_actions/public/components/action_wizard/i18n.ts b/src/plugins/ui_actions/public/components/action_wizard/i18n.ts deleted file mode 100644 index fff52f43bc1db..0000000000000 --- a/src/plugins/ui_actions/public/components/action_wizard/i18n.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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 txtChangeButton = i18n.translate('uiActions.components.actionWizard.changeButton', { - defaultMessage: 'change', -}); diff --git a/src/plugins/ui_actions/public/components/action_wizard/index.ts b/src/plugins/ui_actions/public/components/action_wizard/index.ts deleted file mode 100644 index 6a85c196dfda9..0000000000000 --- a/src/plugins/ui_actions/public/components/action_wizard/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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 { ActionFactory, ActionWizard } from './action_wizard'; diff --git a/src/plugins/ui_actions/scripts/storybook.js b/src/plugins/ui_actions/scripts/storybook.js deleted file mode 100644 index cb2eda610170d..0000000000000 --- a/src/plugins/ui_actions/scripts/storybook.js +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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 { join } from 'path'; - -// eslint-disable-next-line -require('@kbn/storybook').runStorybookCli({ - name: 'ui_actions', - storyGlobs: [join(__dirname, '..', 'public', 'components', '**', '*.story.tsx')], -}); diff --git a/x-pack/dev-tools/jest/setup/setup_test.js b/x-pack/dev-tools/jest/setup/setup_test.js index f54be89f30955..4df2564aad01f 100644 --- a/x-pack/dev-tools/jest/setup/setup_test.js +++ b/x-pack/dev-tools/jest/setup/setup_test.js @@ -11,3 +11,7 @@ import 'jest-styled-components'; import '@testing-library/jest-dom/extend-expect'; +import { configure } from '@testing-library/react'; + +// instead of default 'data-test-id', use kibana's 'data-test-subj' +configure({ testIdAttribute: 'data-test-subj' }); diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.scss similarity index 59% rename from src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss rename to x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.scss index cd8ec0e7238e6..2ba6f9baca90d 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.scss +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.scss @@ -1,9 +1,9 @@ -.uiaActionWizard__selectedActionFactoryContainer { +.auaActionWizard__selectedActionFactoryContainer { background-color: $euiColorLightestShade; padding: $euiSize; } -.uiaActionWizard__actionFactoryItem { +.auaActionWizard__actionFactoryItem { .euiKeyPadMenuItem__label { height: #{$euiSizeXL}; } diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx similarity index 57% rename from src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx rename to x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx index 2886815eb5304..9921028c7cb0b 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.story.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx @@ -1,20 +1,7 @@ /* - * 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. + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. */ import React, { useState } from 'react'; diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx similarity index 80% rename from src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx rename to x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index 18971e199253e..24ba84c430963 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -1,20 +1,7 @@ /* - * 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. + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. */ import React from 'react'; diff --git a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx similarity index 86% rename from src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx rename to x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index ebd94ee410f58..3c94e80d91856 100644 --- a/src/plugins/ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -1,20 +1,7 @@ /* - * 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. + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. */ import React, { useState } from 'react'; @@ -134,7 +121,7 @@ const SelectedActionFactory: React.FC(null); return (
@@ -192,7 +179,7 @@ const ActionFactorySelector: React.FC = ({ {actionFactories.map(actionFactory => ( Date: Wed, 4 Mar 2020 16:16:18 +0100 Subject: [PATCH 13/32] fix i18n x-pack --- .../public/components/action_wizard/i18n.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/i18n.ts b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/i18n.ts index 1a52dceceddef..641f25176264a 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/i18n.ts +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/i18n.ts @@ -6,6 +6,9 @@ import { i18n } from '@kbn/i18n'; -export const txtChangeButton = i18n.translate('uiActions.components.actionWizard.changeButton', { - defaultMessage: 'change', -}); +export const txtChangeButton = i18n.translate( + 'xpack.advancedUiActions.components.actionWizard.changeButton', + { + defaultMessage: 'change', + } +); From 8d7eccfc8daf99e93e52fa680956c84925efd64a Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 4 Mar 2020 16:28:56 +0100 Subject: [PATCH 14/32] fix --- src/plugins/ui_actions/public/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index 39c1f54aa5c22..eb69aefdbb50e 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -30,4 +30,3 @@ export { Action, createAction, IncompatibleActionError } from './actions'; export { buildContextMenuForActions } from './context_menu'; export { Trigger, TriggerContext } from './triggers'; export { TriggerContextMapping, TriggerId } from './types'; -export { ActionWizard, ActionFactory, ActionFactoryBaseConfig } from './components/action_wizard'; From 068b4850be21053e0777d98f3da90504f977c003 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 4 Mar 2020 18:19:58 +0100 Subject: [PATCH 15/32] fix capitalization --- .../action_wizard/action_wizard.test.tsx | 18 +++++++++--------- .../components/action_wizard/test_data.tsx | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index 24ba84c430963..e7ad95c8829fa 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -15,8 +15,8 @@ import { TEST_SUBJ_SELECTED_ACTION_FACTORY, } from './action_wizard'; import { - DashboardDrilldownActionFactory, - UrlDrilldownActionFactory, + dashboardDrilldownActionFactory, + urlDrilldownActionFactory, dashboards, } from './test_data'; @@ -30,7 +30,7 @@ test('Pick and configure action', () => { const screen = render( > } @@ -45,7 +45,7 @@ test('Pick and configure action', () => { fireEvent.click(screen.getByText(/Go to URL/i)); // check that wizard emitted change event. null means config is invalid. this is because URL is empty string yet - expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, null); + expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, null); // Input url const URL = 'https://elastic.co'; @@ -54,7 +54,7 @@ test('Pick and configure action', () => { }); // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, { + expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, { url: URL, openInNewTab: false, }); @@ -65,7 +65,7 @@ test('Pick and configure action', () => { // check that wizard emitted change event // null config means it is invalid. This is because no dashboard selected yet - expect(wizardChangeFn).lastCalledWith(DashboardDrilldownActionFactory, null); + expect(wizardChangeFn).lastCalledWith(dashboardDrilldownActionFactory, null); // Select dashboard fireEvent.change(screen.getByLabelText(/Choose destination dashboard/i), { @@ -73,7 +73,7 @@ test('Pick and configure action', () => { }); // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(DashboardDrilldownActionFactory, { + expect(wizardChangeFn).lastCalledWith(dashboardDrilldownActionFactory, { dashboardId: dashboards[1].id, useCurrentDashboardDataRange: false, useCurrentDashboardFilters: false, @@ -85,7 +85,7 @@ test('If only one actions factory is available, then no selection step is render const screen = render( >} + actionFactories={[urlDrilldownActionFactory] as Array>} onChange={wizardChangeFn} /> ); @@ -101,7 +101,7 @@ test('If only one actions factory is available, then no selection step is render }); // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(UrlDrilldownActionFactory, { + expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, { url: URL, openInNewTab: false, }); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx index 14ccb7d44997b..d0222aa483105 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx @@ -13,7 +13,7 @@ export const dashboards = [ { id: 'dashboard2', title: 'Dashboard 2' }, ]; -export const DashboardDrilldownActionFactory: ActionFactory< +export const dashboardDrilldownActionFactory: ActionFactory< { dashboardId: string; useCurrentDashboardFilters: boolean; @@ -101,7 +101,7 @@ export const DashboardDrilldownActionFactory: ActionFactory< }, }; -export const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { +export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { type: 'Url', displayName: 'Go to URL', iconType: 'link', @@ -145,6 +145,6 @@ export const UrlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa }; export const ACTION_FACTORIES = [ - DashboardDrilldownActionFactory, - UrlDrilldownActionFactory, + dashboardDrilldownActionFactory, + urlDrilldownActionFactory, ] as Array>; From 845673163be26400afd1b5a6c7411264f9d140fe Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 4 Mar 2020 18:29:47 +0100 Subject: [PATCH 16/32] =?UTF-8?q?simplify=20-=20don=E2=80=99t=20expose=20c?= =?UTF-8?q?ontext=20on=20action=20factory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action_wizard/action_wizard.test.tsx | 9 ++---- .../action_wizard/action_wizard.tsx | 28 +++++++------------ .../components/action_wizard/test_data.tsx | 25 ++++++----------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index e7ad95c8829fa..b38036d478c26 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -10,7 +10,6 @@ import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { ActionFactory, ActionWizard, - ActionFactoryBaseConfig, TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY, } from './action_wizard'; @@ -29,11 +28,7 @@ test('Pick and configure action', () => { const screen = render( - > - } + actionFactories={[dashboardDrilldownActionFactory, urlDrilldownActionFactory]} onChange={wizardChangeFn} /> ); @@ -85,7 +80,7 @@ test('If only one actions factory is available, then no selection step is render const screen = render( >} + actionFactories={[urlDrilldownActionFactory] as Array>} onChange={wizardChangeFn} /> ); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 3c94e80d91856..91a4e55b656c0 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -21,20 +21,14 @@ import './action_wizard.scss'; // and it will be imported from the ../ui_actions when implemented properly // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ActionFactoryBaseConfig = {}; -export interface ActionFactory { +export interface ActionFactory { type: string; displayName: string; iconType?: string; - wizard: React.FC>; - context: Context; + wizard: React.FC>; } -export interface ActionFactoryWizardProps { - /** - * Context represents environment where this component is being rendered. - */ - context: Context; - +export interface ActionFactoryWizardProps { /** * Current (latest) config of the item. (state) */ @@ -53,7 +47,7 @@ export interface ActionWizardProps { /** * List of available action factories */ - actionFactories: Array>; + actionFactories: Array>; /** * Notifies when wizard's state changes because of user's interaction @@ -62,15 +56,14 @@ export interface ActionWizardProps { * @param config - current config for current action factory. null if no action factory or if wizard's inputs are invalid or incomplete */ onChange: ( - actionFactory: ActionFactory | null, + actionFactory: ActionFactory | null, config: ActionFactoryBaseConfig | null ) => void; } export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { // eslint-disable-next-line prefer-const let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); // auto pick action factory if there is only 1 available @@ -105,8 +98,8 @@ export const ActionWizard: React.FC = ({ actionFactories, onC ); }; -interface SelectedActionFactoryProps { - actionFactory: ActionFactory; +interface SelectedActionFactoryProps { + actionFactory: ActionFactory; onConfigChange: (config: Config | null) => void; showDeselect: boolean; onDeselect: () => void; @@ -148,7 +141,6 @@ const SelectedActionFactory: React.FC
{actionFactory.wizard({ - context: actionFactory.context, config, onConfig: newConfig => { setConfig(newConfig); @@ -161,8 +153,8 @@ const SelectedActionFactory: React.FC>; - onActionFactorySelected: (actionFactory: ActionFactory) => void; + actionFactories: Array>; + onActionFactorySelected: (actionFactory: ActionFactory) => void; } export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; const ActionFactorySelector: React.FC = ({ diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx index d0222aa483105..8a8d678897625 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx @@ -13,16 +13,11 @@ export const dashboards = [ { id: 'dashboard2', title: 'Dashboard 2' }, ]; -export const dashboardDrilldownActionFactory: ActionFactory< - { - dashboardId: string; - useCurrentDashboardFilters: boolean; - useCurrentDashboardDataRange: boolean; - }, - { - dashboards: Array<{ id: string; title: string }>; - } -> = { +export const dashboardDrilldownActionFactory: ActionFactory<{ + dashboardId: string; + useCurrentDashboardFilters: boolean; + useCurrentDashboardDataRange: boolean; +}> = { type: 'Dashboard', displayName: 'Go to Dashboard', iconType: 'dashboardApp', @@ -57,7 +52,7 @@ export const dashboardDrilldownActionFactory: ActionFactory< ({ value: id, text: title }))} + options={dashboards.map(({ id, title }) => ({ value: id, text: title }))} value={config.dashboardId} onChange={e => { setAndSubmit({ @@ -96,9 +91,6 @@ export const dashboardDrilldownActionFactory: ActionFactory< ); }, - context: { - dashboards, - }, }; export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTab: boolean }> = { @@ -141,10 +133,9 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa ); }, - context: null, }; -export const ACTION_FACTORIES = [ +export const ACTION_FACTORIES = ([ dashboardDrilldownActionFactory, urlDrilldownActionFactory, -] as Array>; +] as unknown) as Array>; From 439bdea3b8b86428f5fa7e9fbadc8f9f419b4642 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Wed, 4 Mar 2020 22:20:51 +0100 Subject: [PATCH 17/32] fix --- .../public/components/action_wizard/action_wizard.test.tsx | 7 ++++++- .../public/components/action_wizard/action_wizard.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index b38036d478c26..85b41d5cc493b 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -9,6 +9,7 @@ import { render, cleanup, fireEvent } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { ActionFactory, + ActionFactoryBaseConfig, ActionWizard, TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY, @@ -28,7 +29,11 @@ test('Pick and configure action', () => { const screen = render( + > + } onChange={wizardChangeFn} /> ); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 91a4e55b656c0..7f7d2224f9604 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -22,7 +22,7 @@ import './action_wizard.scss'; // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ActionFactoryBaseConfig = {}; export interface ActionFactory { - type: string; + type: string; // TODO: type should be tied to Action and ActionByType displayName: string; iconType?: string; wizard: React.FC>; From 1c37f653d7d017dab16086985a7930f6eeaa029d Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 12:49:18 +0100 Subject: [PATCH 18/32] improve. make stateless. Simplify interfaces. --- .../action_wizard/action_wizard.story.tsx | 51 +++---- .../action_wizard/action_wizard.test.tsx | 61 +------- .../action_wizard/action_wizard.tsx | 90 ++++++------ .../components/action_wizard/test_data.tsx | 136 +++++++++++------- 4 files changed, 158 insertions(+), 180 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx index 9921028c7cb0b..62f16890cade2 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.story.tsx @@ -4,45 +4,30 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; - +import React from 'react'; import { storiesOf } from '@storybook/react'; -import { ActionWizard } from './action_wizard'; -import { ACTION_FACTORIES } from './test_data'; - -function Demo() { - const [state, setState] = useState(); - - return ( - <> - { - setState({ - factory, - config, - }); - }} - /> -
-
-
Action Factory Type: {state?.factory?.type}
-
Action Factory Config: {JSON.stringify(state?.config)}
- - ); -} +import { dashboardDrilldownActionFactory, Demo, urlDrilldownActionFactory } from './test_data'; storiesOf('components/ActionWizard', module) - .add('default', () => ) + .add('default', () => ( + + )) + .add('Only one factory is available', () => ( + // to make sure layout doesn't break + + )) .add('Long list of action factories', () => ( // to make sure layout doesn't break - {}} /> )); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index 85b41d5cc493b..cd990e7a41b82 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -5,19 +5,14 @@ */ import React from 'react'; -import { render, cleanup, fireEvent } from '@testing-library/react'; +import { cleanup, fireEvent, render } from '@testing-library/react'; import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global -import { - ActionFactory, - ActionFactoryBaseConfig, - ActionWizard, - TEST_SUBJ_ACTION_FACTORY_ITEM, - TEST_SUBJ_SELECTED_ACTION_FACTORY, -} from './action_wizard'; +import { TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY } from './action_wizard'; import { dashboardDrilldownActionFactory, - urlDrilldownActionFactory, dashboards, + Demo, + urlDrilldownActionFactory, } from './test_data'; // TODO: for some reason global cleanup from RTL doesn't work @@ -25,17 +20,8 @@ import { afterEach(cleanup); test('Pick and configure action', () => { - const wizardChangeFn = jest.fn(); - const screen = render( - - > - } - onChange={wizardChangeFn} - /> + ); // check that all factories are displayed to pick @@ -44,51 +30,24 @@ test('Pick and configure action', () => { // select URL one fireEvent.click(screen.getByText(/Go to URL/i)); - // check that wizard emitted change event. null means config is invalid. this is because URL is empty string yet - expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, null); - // Input url const URL = 'https://elastic.co'; fireEvent.change(screen.getByLabelText(/url/i), { target: { value: URL }, }); - // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, { - url: URL, - openInNewTab: false, - }); - // change to dashboard fireEvent.click(screen.getByText(/change/i)); fireEvent.click(screen.getByText(/Go to Dashboard/i)); - // check that wizard emitted change event - // null config means it is invalid. This is because no dashboard selected yet - expect(wizardChangeFn).lastCalledWith(dashboardDrilldownActionFactory, null); - // Select dashboard fireEvent.change(screen.getByLabelText(/Choose destination dashboard/i), { target: { value: dashboards[1].id }, }); - - // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(dashboardDrilldownActionFactory, { - dashboardId: dashboards[1].id, - useCurrentDashboardDataRange: false, - useCurrentDashboardFilters: false, - }); }); -test('If only one actions factory is available, then no selection step is rendered and no change button displayed', () => { - const wizardChangeFn = jest.fn(); - - const screen = render( - >} - onChange={wizardChangeFn} - /> - ); +test('If only one actions factory is available then actionFactory selection is emitted without user input', () => { + const screen = render(); // check that no factories are displayed to pick from expect(screen.queryByTestId(TEST_SUBJ_ACTION_FACTORY_ITEM)).not.toBeInTheDocument(); @@ -100,12 +59,6 @@ test('If only one actions factory is available, then no selection step is render target: { value: URL }, }); - // check that wizard emitted change event - expect(wizardChangeFn).lastCalledWith(urlDrilldownActionFactory, { - url: URL, - openInNewTab: false, - }); - // check that can't change to action factory type expect(screen.queryByTestId(/change/i)).not.toBeInTheDocument(); }); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 7f7d2224f9604..689a6ed549d41 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React from 'react'; import { EuiButtonEmpty, EuiFlexGroup, @@ -21,67 +21,74 @@ import './action_wizard.scss'; // and it will be imported from the ../ui_actions when implemented properly // eslint-disable-next-line @typescript-eslint/consistent-type-definitions export type ActionFactoryBaseConfig = {}; -export interface ActionFactory { +export interface ActionFactory { type: string; // TODO: type should be tied to Action and ActionByType displayName: string; iconType?: string; wizard: React.FC>; + createConfig: () => Config; + isValid: (name: string, config: Config) => boolean; } export interface ActionFactoryWizardProps { - /** - * Current (latest) config of the item. (state) - */ - config: Config | null; + config?: Config; /** * Callback called when user updates the config in UI. - * ActionFactory's wizard should do validations to the user's input - * In case input is complete and valid - config: Config object should be emitted - * In case input has changed to the invalid state: null should be emitted */ - onConfig: (config: Config | null) => void; + onConfig: (config: Config) => void; } export interface ActionWizardProps { /** * List of available action factories */ - actionFactories: Array>; + actionFactories: Array>; // any here to be able to pass array of ActionFactory with different configs /** - * Notifies when wizard's state changes because of user's interaction - * - * @param actionFactory - current selected action factory. null if none is selected - * @param config - current config for current action factory. null if no action factory or if wizard's inputs are invalid or incomplete + * Currently selected action factory + * undefined - is allowed and means that non is selected */ - onChange: ( - actionFactory: ActionFactory | null, - config: ActionFactoryBaseConfig | null - ) => void; -} -export const ActionWizard: React.FC = ({ actionFactories, onChange }) => { - // eslint-disable-next-line prefer-const - let [selectedActionFactory, setSelectedActionFactory] = useState | null>(null); + currentActionFactory?: ActionFactory; + /** + * Action factory selected changed + * null - means user click "change" and removed action factory selection + */ + onActionFactoryChange: (actionFactory: ActionFactory | null) => void; + + /** + * current config for currently selected action factory + */ + config?: ActionFactoryBaseConfig; + /** + * config changed + */ + onConfigChange: (config: ActionFactoryBaseConfig) => void; +} +export const ActionWizard: React.FC = ({ + currentActionFactory, + actionFactories, + onActionFactoryChange, + onConfigChange, + config, +}) => { // auto pick action factory if there is only 1 available - if (!selectedActionFactory && actionFactories.length === 1) { - selectedActionFactory = actionFactories[0]; + if (!currentActionFactory && actionFactories.length === 1) { + onActionFactoryChange(actionFactories[0]); } - if (selectedActionFactory) { + if (currentActionFactory && config) { return ( 1} onDeselect={() => { - setSelectedActionFactory(null); - onChange(null, null); + onActionFactoryChange(null); }} + config={config} onConfigChange={newConfig => { - onChange(selectedActionFactory, newConfig); + onConfigChange(newConfig); }} /> ); @@ -91,27 +98,29 @@ export const ActionWizard: React.FC = ({ actionFactories, onC { - setSelectedActionFactory(actionFactory); - onChange(actionFactory, null); + onActionFactoryChange(actionFactory); }} /> ); }; -interface SelectedActionFactoryProps { +interface SelectedActionFactoryProps< + Config extends ActionFactoryBaseConfig = ActionFactoryBaseConfig +> { actionFactory: ActionFactory; - onConfigChange: (config: Config | null) => void; + config: Config; + onConfigChange: (config: Config) => void; showDeselect: boolean; onDeselect: () => void; } export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory'; -const SelectedActionFactory: React.FC> = ({ +const SelectedActionFactory: React.FC = ({ actionFactory, onDeselect, showDeselect, onConfigChange, + config, }) => { - const [config, setConfig] = useState(null); return (
{ - setConfig(newConfig); onConfigChange(newConfig); }, })} @@ -153,8 +161,8 @@ const SelectedActionFactory: React.FC>; - onActionFactorySelected: (actionFactory: ActionFactory) => void; + actionFactories: ActionFactory[]; + onActionFactorySelected: (actionFactory: ActionFactory) => void; } export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; const ActionFactorySelector: React.FC = ({ diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx index 8a8d678897625..b103c76657d87 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx @@ -6,7 +6,7 @@ import React, { useState } from 'react'; import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; -import { ActionFactory, ActionFactoryBaseConfig } from './action_wizard'; +import { ActionFactory, ActionFactoryBaseConfig, ActionWizard } from './action_wizard'; export const dashboards = [ { id: 'dashboard1', title: 'Dashboard 1' }, @@ -14,38 +14,31 @@ export const dashboards = [ ]; export const dashboardDrilldownActionFactory: ActionFactory<{ - dashboardId: string; + dashboardId?: string; useCurrentDashboardFilters: boolean; useCurrentDashboardDataRange: boolean; }> = { type: 'Dashboard', displayName: 'Go to Dashboard', iconType: 'dashboardApp', + createConfig: () => { + return { + dashboardId: undefined, + useCurrentDashboardDataRange: true, + useCurrentDashboardFilters: true, + }; + }, + isValid: (name, config) => { + if (!name) return false; + if (!config.dashboardId) return false; + return true; + }, wizard: props => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const [config, setConfig] = useState( - props.config || { - dashboardId: undefined, - useCurrentDashboardDataRange: false, - useCurrentDashboardFilters: false, - } - ); - - function setAndSubmit(newConfig: { - dashboardId: string | undefined; - useCurrentDashboardFilters: boolean; - useCurrentDashboardDataRange: boolean; - }) { - // validate - if (newConfig.dashboardId) { - props.onConfig({ ...newConfig, dashboardId: newConfig.dashboardId }); - } else { - props.onConfig(null); - } - - setConfig(newConfig); - } - + const config = props.config ?? { + dashboardId: undefined, + useCurrentDashboardDataRange: true, + useCurrentDashboardFilters: true, + }; return ( <> @@ -55,10 +48,7 @@ export const dashboardDrilldownActionFactory: ActionFactory<{ options={dashboards.map(({ id, title }) => ({ value: id, text: title }))} value={config.dashboardId} onChange={e => { - setAndSubmit({ - ...config, - dashboardId: e.target.value, - }); + props.onConfig({ ...config, dashboardId: e.target.value }); }} /> @@ -68,7 +58,7 @@ export const dashboardDrilldownActionFactory: ActionFactory<{ label="Use current dashboard's filters" checked={config.useCurrentDashboardFilters} onChange={() => - setAndSubmit({ + props.onConfig({ ...config, useCurrentDashboardFilters: !config.useCurrentDashboardFilters, }) @@ -81,7 +71,7 @@ export const dashboardDrilldownActionFactory: ActionFactory<{ label="Use current dashboard's date range" checked={config.useCurrentDashboardDataRange} onChange={() => - setAndSubmit({ + props.onConfig({ ...config, useCurrentDashboardDataRange: !config.useCurrentDashboardDataRange, }) @@ -97,21 +87,22 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa type: 'Url', displayName: 'Go to URL', iconType: 'link', + createConfig: () => { + return { + url: '', + openInNewTab: false, + }; + }, + isValid: (name, config) => { + if (!name) return false; + if (!config.url) return false; + return true; + }, wizard: props => { - // eslint-disable-next-line react-hooks/rules-of-hooks - const [config, setConfig] = useState(props.config || { url: '', openInNewTab: false }); - - function setAndSubmit(newConfig: { url: string; openInNewTab: boolean }) { - // validate - if (newConfig.url) { - props.onConfig(newConfig); - } else { - props.onConfig(null); - } - - setConfig(newConfig); - } - + const config = props.config ?? { + url: '', + openInNewTab: false, + }; return ( <> @@ -119,7 +110,7 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa placeholder="Enter URL" name="url" value={config.url} - onChange={event => setAndSubmit({ ...config, url: event.target.value })} + onChange={event => props.onConfig({ ...config, url: event.target.value })} /> @@ -127,7 +118,7 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa name="openInNewTab" label="Open in new tab?" checked={config.openInNewTab} - onChange={() => setAndSubmit({ ...config, openInNewTab: !config.openInNewTab })} + onChange={() => props.onConfig({ ...config, openInNewTab: !config.openInNewTab })} /> @@ -135,7 +126,48 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa }, }; -export const ACTION_FACTORIES = ([ - dashboardDrilldownActionFactory, - urlDrilldownActionFactory, -] as unknown) as Array>; +export function Demo({ actionFactories }: { actionFactories: Array> }) { + const [state, setState] = useState<{ + currentActionFactory?: ActionFactory; + config?: ActionFactoryBaseConfig; + }>({}); + + function changeActionFactory(newActionFactory: ActionFactory | null) { + if (!newActionFactory) { + // removing action factory + return setState({}); + } + + setState({ + currentActionFactory: newActionFactory, + config: newActionFactory.createConfig(), + }); + } + + return ( + <> + { + setState({ + ...state, + config: newConfig, + }); + }} + onActionFactoryChange={newActionFactory => { + changeActionFactory(newActionFactory); + }} + currentActionFactory={state.currentActionFactory} + /> +
+
+
Action Factory Type: {state.currentActionFactory?.type}
+
Action Factory Config: {JSON.stringify(state.config)}
+
+ Is config valid:{' '} + {JSON.stringify(state.currentActionFactory?.isValid('fake name', state.config!) ?? false)} +
+ + ); +} From 8501087ed06db8eb5dc625b716a01de182ef32b1 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 13:37:34 +0100 Subject: [PATCH 19/32] wip --- .../flyout_drilldown_wizard.story.tsx | 14 +-- .../flyout_drilldown_wizard.tsx | 100 ++++++++---------- .../form_drilldown_wizard.story.tsx | 4 +- .../form_drilldown_wizard.test.tsx | 8 +- .../form_drilldown_wizard.tsx | 62 ++++------- 5 files changed, 81 insertions(+), 107 deletions(-) diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx index b114a35debb03..8a67c793361cd 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx @@ -11,11 +11,7 @@ import { EuiFlyout } from '@elastic/eui'; import { storiesOf } from '@storybook/react'; import { FlyoutDrilldownWizard } from '.'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { UrlDrilldownActionFactory } from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; -import { - ActionFactoryBaseConfig, - ActionFactory, -} from '../../../../../../src/plugins/ui_actions/public/'; +import { urlDrilldownActionFactory } from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; storiesOf('components/FlyoutDrilldownWizard', module) .add('default', () => { @@ -36,12 +32,10 @@ storiesOf('components/FlyoutDrilldownWizard', module) onClose={() => {}} initialDrilldownWizardConfig={{ name: 'My fancy drilldown', + actionFactory: urlDrilldownActionFactory, actionConfig: { - actionFactory: (UrlDrilldownActionFactory as unknown) as ActionFactory, - config: { - url: 'https://elastic.co', - openInNewTab: true, - } as ActionFactoryBaseConfig, + url: 'https://elastic.co', + openInNewTab: true, }, }} mode={'edit'} diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 0f75a1e0aaa7c..401eeb8cca0ab 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -17,85 +17,65 @@ import { } from './i18n'; import { FlyoutCreateDrilldownActionContext } from '../../actions'; import { ActionFactory, ActionFactoryBaseConfig } from '../../../../advanced_ui_actions/public'; +import { + dashboardDrilldownActionFactory, + urlDrilldownActionFactory, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; -export interface DrilldownWizardConfig { - name: string; - actionConfig: { - actionFactory: ActionFactory; - config: ActionFactoryBaseConfig; - }; -} - -/** - * Represent current wizard's form state in invalid or incomplete shape - */ -export interface PartialDrilldownWizardConfig { +export interface DrilldownWizardConfig< + ActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig +> { name: string; - actionConfig: { - actionFactory: ActionFactory | null; - config: ActionFactoryBaseConfig | null; - }; + actionFactory?: ActionFactory; + actionConfig?: ActionFactoryConfig; } -export interface FlyoutDrilldownWizardProps { +export interface FlyoutDrilldownWizardProps< + CurrentActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig +> { context: FlyoutCreateDrilldownActionContext; onSubmit?: (drilldownWizardConfig: DrilldownWizardConfig) => void; onDelete?: () => void; onClose?: () => void; mode?: 'create' | 'edit'; - initialDrilldownWizardConfig?: DrilldownWizardConfig; + initialDrilldownWizardConfig?: DrilldownWizardConfig; } -export const FlyoutDrilldownWizard: React.FC = ({ +export function FlyoutDrilldownWizard< + CurrentActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig +>({ context, onClose, onSubmit = () => {}, initialDrilldownWizardConfig, mode = 'create', onDelete = () => {}, -}) => { - const [wizardConfig, setWizardConfig] = useState< - DrilldownWizardConfig | PartialDrilldownWizardConfig - >( +}: FlyoutDrilldownWizardProps) { + const [wizardConfig, setWizardConfig] = useState( () => initialDrilldownWizardConfig ?? { name: '', - actionConfig: { - actionFactory: null, - config: null, - }, } ); - const isFormValid = ( - currentWizardConfig: PartialDrilldownWizardConfig | DrilldownWizardConfig - ): currentWizardConfig is DrilldownWizardConfig => { - if (!currentWizardConfig.name) { - // name is required - return false; - } - - if ( - !currentWizardConfig.actionConfig.actionFactory || - !currentWizardConfig.actionConfig.config - ) { - // action factory has to be selected and config has to be present - return false; - } + const isActionValid = (): boolean => { + if (!wizardConfig.actionFactory) return false; + if (!wizardConfig.actionConfig) return false; - return true; + return wizardConfig.actionFactory.isValid(wizardConfig.name, wizardConfig.actionConfig); }; const footer = ( { - if (isFormValid(wizardConfig)) { + if (isActionValid()) { onSubmit(wizardConfig); } }} fill - isDisabled={!isFormValid(wizardConfig)} + isDisabled={!isActionValid()} > {mode === 'edit' ? txtEditDrilldownButtonLabel : txtCreateDrilldownButtonLabel} @@ -108,23 +88,37 @@ export const FlyoutDrilldownWizard: React.FC = ({ onClose={onClose} > { setWizardConfig({ ...wizardConfig, name: newName, }); }} - initialActionConfig={wizardConfig.actionConfig} - onActionConfigChange={(actionFactory, config) => { + actionConfig={wizardConfig.actionConfig} + onActionConfigChange={newActionConfig => { setWizardConfig({ ...wizardConfig, - actionConfig: { - actionFactory, - config, - }, + actionConfig: newActionConfig, }); }} + currentActionFactory={wizardConfig.actionFactory} + onActionFactoryChange={actionFactory => { + if (!actionFactory) { + setWizardConfig({ + ...wizardConfig, + actionFactory: undefined, + actionConfig: undefined, + }); + } else { + setWizardConfig({ + ...wizardConfig, + actionFactory, + actionConfig: actionFactory.createConfig(), + }); + } + }} + actionFactories={[dashboardDrilldownActionFactory, urlDrilldownActionFactory]} /> {mode === 'edit' && ( <> @@ -136,4 +130,4 @@ export const FlyoutDrilldownWizard: React.FC = ({ )} ); -}; +} diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx index f158c46c15fef..dbf7d4c35769c 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.story.tsx @@ -13,7 +13,7 @@ const DemoEditName: React.FC = () => { return ( <> -
name: {name}
+
name: {name}
); }; @@ -23,6 +23,6 @@ storiesOf('components/FormDrilldownWizard', module) return ; }) .add('[name=foobar]', () => { - return ; + return ; }) .add('can edit name', () => ); diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx index 1b6df3cba37aa..9807d0d6b55ca 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx @@ -32,19 +32,23 @@ describe('', () => { test('can set initial name input field value', () => { const div = document.createElement('div'); - render(, div); + render(, div); const input = div.querySelector( '[data-test-subj="dynamicActionNameInput"]' ) as HTMLInputElement; expect(input?.value).toBe('foo'); + + render(, div); + + expect(input?.value).toBe('bar'); }); test('fires onNameChange callback on name change', () => { const onNameChange = jest.fn(); const utils = renderTestingLibrary( - + ); const input = utils.getByLabelText(txtNameOfDrilldown); diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx index cef375746d4fa..69ffb59aa0a2a 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx @@ -4,57 +4,43 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useState } from 'react'; +import React from 'react'; import './form_drilldown_wizard.scss'; -import { EuiForm, EuiFormRow, EuiFieldText, EuiSpacer } from '@elastic/eui'; +import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui'; import { DrilldownHelloBar } from '../drilldown_hello_bar'; -import { txtNameOfDrilldown, txtUntitledDrilldown, txtDrilldownAction } from './i18n'; +import { txtDrilldownAction, txtNameOfDrilldown, txtUntitledDrilldown } from './i18n'; import { - ActionWizard, ActionFactory, ActionFactoryBaseConfig, + ActionWizard, } from '../../../../advanced_ui_actions/public'; - // TODO: this should be actual input to the component and should not be using test data // eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { ACTION_FACTORIES } from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; const noop = () => {}; export interface FormDrilldownWizardProps { - /** - * Initial name - to be used during editing flow - */ - initialName?: string; - + name?: string; onNameChange?: (name: string) => void; - /** - * Initial action config - to be used during edit flow - */ - initialActionConfig?: { - actionFactory: ActionFactory | null; - config: ActionFactoryBaseConfig | null; - }; + currentActionFactory?: ActionFactory; + onActionFactoryChange?: (actionFactory: ActionFactory | null) => void; + + actionConfig?: ActionFactoryBaseConfig; + onActionConfigChange?: (config: ActionFactoryBaseConfig) => void; - /** - * onActionConfigChange - Action's configuration changed - * @param actionFactory - currently selected action factory. Null if non is selected - * @param config - current corresponding config. Null if current config is invalid or incomplete - */ - onActionConfigChange?: ( - actionFactory: ActionFactory | null, - config: ActionFactoryBaseConfig | null - ) => void; + actionFactories?: Array>; } export const FormDrilldownWizard: React.FC = ({ - initialName = '', - initialActionConfig = { actionFactory: null, config: null }, + name = '', + actionConfig, + currentActionFactory, onNameChange = noop, onActionConfigChange = noop, + onActionFactoryChange = noop, + actionFactories = [], }) => { - const [name, setName] = useState(initialName); const nameFragment = ( = ({ placeholder={txtUntitledDrilldown} value={name} disabled={onNameChange === noop} - onChange={event => { - const newName = event.target.value; - onNameChange(newName); - setName(newName); - }} + onChange={event => onNameChange(event.target.value)} data-test-subj="dynamicActionNameInput" /> @@ -79,11 +61,11 @@ export const FormDrilldownWizard: React.FC = ({ className="drdFormDrilldownWizard__formRow" > { - onActionConfigChange(actionFactory, config); - }} + actionFactories={actionFactories} + currentActionFactory={currentActionFactory} + config={actionConfig} + onActionFactoryChange={actionFactory => onActionFactoryChange(actionFactory)} + onConfigChange={config => onActionConfigChange(config)} /> ); From ae07675ec24d0bb5a68e2d2a2c3e1f946778acbe Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 13:42:00 +0100 Subject: [PATCH 20/32] rename --- .../components/action_wizard/action_wizard.tsx | 12 ++++++------ .../public/components/action_wizard/test_data.tsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 689a6ed549d41..be321eb71ccad 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -20,8 +20,8 @@ import './action_wizard.scss'; // TODO: this interface is temporary for just moving forward with the component // and it will be imported from the ../ui_actions when implemented properly // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type ActionFactoryBaseConfig = {}; -export interface ActionFactory { +export type ActionBaseConfig = {}; +export interface ActionFactory { type: string; // TODO: type should be tied to Action and ActionByType displayName: string; iconType?: string; @@ -30,7 +30,7 @@ export interface ActionFactory boolean; } -export interface ActionFactoryWizardProps { +export interface ActionFactoryWizardProps { config?: Config; /** @@ -59,12 +59,12 @@ export interface ActionWizardProps { /** * current config for currently selected action factory */ - config?: ActionFactoryBaseConfig; + config?: ActionBaseConfig; /** * config changed */ - onConfigChange: (config: ActionFactoryBaseConfig) => void; + onConfigChange: (config: ActionBaseConfig) => void; } export const ActionWizard: React.FC = ({ currentActionFactory, @@ -105,7 +105,7 @@ export const ActionWizard: React.FC = ({ }; interface SelectedActionFactoryProps< - Config extends ActionFactoryBaseConfig = ActionFactoryBaseConfig + Config extends ActionBaseConfig = ActionBaseConfig > { actionFactory: ActionFactory; config: Config; diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx index b103c76657d87..fc186b113915d 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx @@ -6,7 +6,7 @@ import React, { useState } from 'react'; import { EuiFieldText, EuiFormRow, EuiSelect, EuiSwitch } from '@elastic/eui'; -import { ActionFactory, ActionFactoryBaseConfig, ActionWizard } from './action_wizard'; +import { ActionFactory, ActionBaseConfig, ActionWizard } from './action_wizard'; export const dashboards = [ { id: 'dashboard1', title: 'Dashboard 1' }, @@ -129,7 +129,7 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa export function Demo({ actionFactories }: { actionFactories: Array> }) { const [state, setState] = useState<{ currentActionFactory?: ActionFactory; - config?: ActionFactoryBaseConfig; + config?: ActionBaseConfig; }>({}); function changeActionFactory(newActionFactory: ActionFactory | null) { From 6d05a1726a4bdac07ad2f97245cf2f37d2cea208 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 13:42:11 +0100 Subject: [PATCH 21/32] fix --- .../public/components/action_wizard/action_wizard.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index be321eb71ccad..88ab792d2ff7e 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -104,9 +104,7 @@ export const ActionWizard: React.FC = ({ ); }; -interface SelectedActionFactoryProps< - Config extends ActionBaseConfig = ActionBaseConfig -> { +interface SelectedActionFactoryProps { actionFactory: ActionFactory; config: Config; onConfigChange: (config: Config) => void; From 5e71093254bae1198ebf2c00b6513e4b32e8063e Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 13:45:19 +0100 Subject: [PATCH 22/32] rename --- .../public/components/action_wizard/index.ts | 2 +- .../flyout_drilldown_wizard.tsx | 18 ++++++++---------- .../form_drilldown_wizard.tsx | 6 +++--- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/index.ts b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/index.ts index cb9a48eba6b07..3e7d0bf79bdc3 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/index.ts +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ActionFactory, ActionWizard, ActionFactoryBaseConfig } from './action_wizard'; +export { ActionFactory, ActionWizard, ActionBaseConfig } from './action_wizard'; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 401eeb8cca0ab..de5042879bf1f 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -16,23 +16,21 @@ import { txtEditDrilldownTitle, } from './i18n'; import { FlyoutCreateDrilldownActionContext } from '../../actions'; -import { ActionFactory, ActionFactoryBaseConfig } from '../../../../advanced_ui_actions/public'; +import { ActionFactory, ActionBaseConfig } from '../../../../advanced_ui_actions/public'; import { dashboardDrilldownActionFactory, urlDrilldownActionFactory, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; -export interface DrilldownWizardConfig< - ActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig -> { +export interface DrilldownWizardConfig { name: string; - actionFactory?: ActionFactory; - actionConfig?: ActionFactoryConfig; + actionFactory?: ActionFactory; + actionConfig?: ActionConfig; } export interface FlyoutDrilldownWizardProps< - CurrentActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig + CurrentActionConfig extends ActionBaseConfig = ActionBaseConfig > { context: FlyoutCreateDrilldownActionContext; onSubmit?: (drilldownWizardConfig: DrilldownWizardConfig) => void; @@ -40,11 +38,11 @@ export interface FlyoutDrilldownWizardProps< onClose?: () => void; mode?: 'create' | 'edit'; - initialDrilldownWizardConfig?: DrilldownWizardConfig; + initialDrilldownWizardConfig?: DrilldownWizardConfig; } export function FlyoutDrilldownWizard< - CurrentActionFactoryConfig extends ActionFactoryBaseConfig = ActionFactoryBaseConfig + CurrentActionConfig extends ActionBaseConfig = ActionBaseConfig >({ context, onClose, @@ -52,7 +50,7 @@ export function FlyoutDrilldownWizard< initialDrilldownWizardConfig, mode = 'create', onDelete = () => {}, -}: FlyoutDrilldownWizardProps) { +}: FlyoutDrilldownWizardProps) { const [wizardConfig, setWizardConfig] = useState( () => initialDrilldownWizardConfig ?? { diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx index 69ffb59aa0a2a..48bb14f5c4ad6 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx @@ -11,7 +11,7 @@ import { DrilldownHelloBar } from '../drilldown_hello_bar'; import { txtDrilldownAction, txtNameOfDrilldown, txtUntitledDrilldown } from './i18n'; import { ActionFactory, - ActionFactoryBaseConfig, + ActionBaseConfig, ActionWizard, } from '../../../../advanced_ui_actions/public'; // TODO: this should be actual input to the component and should not be using test data @@ -26,8 +26,8 @@ export interface FormDrilldownWizardProps { currentActionFactory?: ActionFactory; onActionFactoryChange?: (actionFactory: ActionFactory | null) => void; - actionConfig?: ActionFactoryBaseConfig; - onActionConfigChange?: (config: ActionFactoryBaseConfig) => void; + actionConfig?: ActionBaseConfig; + onActionConfigChange?: (config: ActionBaseConfig) => void; actionFactories?: Array>; } From 765f96271a6f0306628c2b907b720fb84b21188d Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 16:53:24 +0100 Subject: [PATCH 23/32] --- .../components/list_manage_drilldowns/i18n.ts | 36 +++++++ .../list_manage_drilldowns/index.tsx | 7 ++ .../list_manage_drilldowns.story.tsx | 14 +++ .../list_manage_drilldowns.test.tsx | 61 ++++++++++++ .../list_manage_drilldowns.tsx | 97 +++++++++++++++++++ .../list_manage_drilldowns/test_data.ts | 11 +++ 6 files changed, 226 insertions(+) create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/i18n.ts create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/index.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.story.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/test_data.ts diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/i18n.ts b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/i18n.ts new file mode 100644 index 0000000000000..fbc7c9dcfb4a1 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/i18n.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const txtCreateDrilldown = i18n.translate( + 'xpack.drilldowns.components.ListManageDrilldowns.createDrilldownButtonLabel', + { + defaultMessage: 'Create new', + } +); + +export const txtEditDrilldown = i18n.translate( + 'xpack.drilldowns.components.ListManageDrilldowns.editDrilldownButtonLabel', + { + defaultMessage: 'Edit', + } +); + +export const txtDeleteDrilldowns = (count: number) => + i18n.translate('xpack.drilldowns.components.ListManageDrilldowns.deleteDrilldownsButtonLabel', { + defaultMessage: 'Delete ({count})', + values: { + count, + }, + }); + +export const txtSelectDrilldown = i18n.translate( + 'xpack.drilldowns.components.ListManageDrilldowns.selectThisDrilldownCheckboxLabel', + { + defaultMessage: 'Select this drilldown', + } +); diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/index.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/index.tsx new file mode 100644 index 0000000000000..82b6ce27af6d4 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/index.tsx @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './list_manage_drilldowns'; diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.story.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.story.tsx new file mode 100644 index 0000000000000..ae1b063449928 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.story.tsx @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { storiesOf } from '@storybook/react'; +import { ListManageDrilldowns } from './list_manage_drilldowns'; +import { drilldowns } from './test_data'; + +storiesOf('components/ListManageDrilldowns', module).add('default', () => ( + +)); diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx new file mode 100644 index 0000000000000..2ad83f684b339 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { cleanup, fireEvent, render } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global +import { drilldowns } from './test_data'; +import { ListManageDrilldowns, TEST_SUBJ_DRILLDOWN_ITEM } from './list_manage_drilldowns'; + +// TODO: for some reason global cleanup from RTL doesn't work +// afterEach is not available for it globally during setup +afterEach(cleanup); + +test('Render list of drilldowns', () => { + const screen = render(); + expect(screen.getAllByTestId(TEST_SUBJ_DRILLDOWN_ITEM)).toHaveLength(drilldowns.length); +}); + +test('Emit onEdit() when clicking on edit drilldown', () => { + const fn = jest.fn(); + const screen = render(); + + const editButtons = screen.getAllByText('Edit'); + expect(editButtons).toHaveLength(drilldowns.length); + fireEvent.click(editButtons[1]); + expect(fn).toBeCalledWith(drilldowns[1].id); +}); + +test('Emit onCreate() when clicking on create drilldown', () => { + const fn = jest.fn(); + const screen = render(); + fireEvent.click(screen.getByText('Create new')); + expect(fn).toBeCalled(); +}); + +test('Delete button is not visible when non is selected', () => { + const fn = jest.fn(); + const screen = render(); + expect(screen.queryByText(/Delete/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/Create/i)).toBeInTheDocument(); +}); + +test('Can delete drilldowns', () => { + const fn = jest.fn(); + const screen = render(); + + const checkboxes = screen.getAllByLabelText(/Select this drilldown/i); + expect(checkboxes).toHaveLength(3); + + fireEvent.click(checkboxes[1]); + fireEvent.click(checkboxes[2]); + + expect(screen.queryByText(/Create/i)).not.toBeInTheDocument(); + + fireEvent.click(screen.getByText(/Delete \(2\)/i)); + + expect(fn).toBeCalledWith([drilldowns[1].id, drilldowns[2].id]); +}); diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx new file mode 100644 index 0000000000000..71e566d1020f5 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiBasicTable, + EuiBasicTableColumn, + EuiButton, + EuiButtonEmpty, + EuiSpacer, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { + txtEditDrilldown, + txtCreateDrilldown, + txtDeleteDrilldowns, + txtSelectDrilldown, +} from './i18n'; + +// TODO: interface is temporary +export interface DrilldownListItem { + id: string; + actionTypeDisplayName: string; + name: string; +} + +export interface ListManageDrilldownsProps { + drilldowns: DrilldownListItem[]; + + onEdit?: (id: string) => void; + onCreate?: () => void; + onDelete?: (ids: string[]) => void; +} + +const noop = () => {}; + +export const TEST_SUBJ_DRILLDOWN_ITEM = 'list-manage-drilldowns-item'; + +export function ListManageDrilldowns({ + drilldowns, + onEdit = noop, + onCreate = noop, + onDelete = noop, +}: ListManageDrilldownsProps) { + const [selectedDrilldowns, setSelectedDrilldowns] = useState([]); + + const columns: Array> = [ + { + field: 'name', + name: 'Name', + truncateText: true, + }, + { + field: 'actionTypeDisplayName', + name: 'Action', + truncateText: true, + }, + { + render: (drilldown: DrilldownListItem) => ( + onEdit(drilldown.id)}> + {txtEditDrilldown} + + ), + }, + ]; + + return ( + <> + { + setSelectedDrilldowns(selection.map(drilldown => drilldown.id)); + }, + selectableMessage: () => txtSelectDrilldown, + }} + rowProps={{ 'data-test-subj': TEST_SUBJ_DRILLDOWN_ITEM }} + hasActions={true} + /> + + {selectedDrilldowns.length === 0 ? ( + onCreate()}> + {txtCreateDrilldown} + + ) : ( + onDelete(selectedDrilldowns)}> + {txtDeleteDrilldowns(selectedDrilldowns.length)} + + )} + + ); +} diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/test_data.ts b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/test_data.ts new file mode 100644 index 0000000000000..862c3efafa189 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/test_data.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export const drilldowns = [ + { id: '1', actionTypeDisplayName: 'Dashboard', name: 'Drilldown 1' }, + { id: '2', actionTypeDisplayName: 'Dashboard', name: 'Drilldown 2' }, + { id: '3', actionTypeDisplayName: 'Dashboard', name: 'Drilldown 3' }, +]; From 9f0da1cc01c50f84973e92793cd7da278650933c Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 18:55:18 +0100 Subject: [PATCH 24/32] --- .../actions/flyout_create_drilldown/index.tsx | 2 +- .../flyout_drilldown_wizard.story.tsx | 5 +- .../flyout_drilldown_wizard.tsx | 8 +-- .../flyout_frame/flyout_frame.story.tsx | 7 ++ .../components/flyout_frame/flyout_frame.tsx | 37 ++++++++++- .../public/components/flyout_frame/i18n.ts | 6 +- .../flyout_manage_drilldowns.story.tsx | 17 +++++ .../flyout_manage_drilldowns.tsx | 64 +++++++++++++++++++ .../flyout_manage_drilldowns/i18n.ts | 14 ++++ 9 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.story.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/i18n.ts diff --git a/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx b/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx index b0615b3ee9c01..0d534d8b7e95c 100644 --- a/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx +++ b/x-pack/plugins/drilldowns/public/actions/flyout_create_drilldown/index.tsx @@ -46,7 +46,7 @@ export class FlyoutCreateDrilldownAction implements ActionByType handle.close()} />) + toMountPoint( handle.close()} />) ); } } diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx index 8a67c793361cd..a6395db54560c 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.story.tsx @@ -15,12 +15,12 @@ import { urlDrilldownActionFactory } from '../../../../advanced_ui_actions/publi storiesOf('components/FlyoutDrilldownWizard', module) .add('default', () => { - return ; + return ; }) .add('open in flyout - create', () => { return ( {}}> - {}} /> + {}} /> ); }) @@ -28,7 +28,6 @@ storiesOf('components/FlyoutDrilldownWizard', module) return ( {}}> {}} initialDrilldownWizardConfig={{ name: 'My fancy drilldown', diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index de5042879bf1f..8f64a69f42227 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -15,8 +15,7 @@ import { txtEditDrilldownButtonLabel, txtEditDrilldownTitle, } from './i18n'; -import { FlyoutCreateDrilldownActionContext } from '../../actions'; -import { ActionFactory, ActionBaseConfig } from '../../../../advanced_ui_actions/public'; +import { ActionBaseConfig, ActionFactory } from '../../../../advanced_ui_actions/public'; import { dashboardDrilldownActionFactory, urlDrilldownActionFactory, @@ -32,10 +31,10 @@ export interface DrilldownWizardConfig { - context: FlyoutCreateDrilldownActionContext; onSubmit?: (drilldownWizardConfig: DrilldownWizardConfig) => void; onDelete?: () => void; onClose?: () => void; + onBack?: () => void; mode?: 'create' | 'edit'; initialDrilldownWizardConfig?: DrilldownWizardConfig; @@ -44,8 +43,8 @@ export interface FlyoutDrilldownWizardProps< export function FlyoutDrilldownWizard< CurrentActionConfig extends ActionBaseConfig = ActionBaseConfig >({ - context, onClose, + onBack, onSubmit = () => {}, initialDrilldownWizardConfig, mode = 'create', @@ -84,6 +83,7 @@ export function FlyoutDrilldownWizard< title={mode === 'edit' ? txtEditDrilldownTitle : txtCreateDrilldownTitle} footer={footer} onClose={onClose} + onBack={onBack} > { return console.log('onClose')}>test; }) + .add('with onBack', () => { + return ( + console.log('onClose')} title={'Title'}> + test + + ); + }) .add('custom footer', () => { return click me!}>test; }) diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx index 9c1c51d3a0d9d..cd9916bf0dbe4 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.tsx @@ -13,13 +13,15 @@ import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, + EuiButtonIcon, } from '@elastic/eui'; -import { txtClose } from './i18n'; +import { txtClose, txtBack } from './i18n'; export interface FlyoutFrameProps { title?: React.ReactNode; footer?: React.ReactNode; onClose?: () => void; + onBack?: () => void; } /** @@ -30,11 +32,40 @@ export const FlyoutFrame: React.FC = ({ footer, onClose, children, + onBack, }) => { - const headerFragment = title && ( + const headerFragment = (title || onBack) && ( -

{title}

+ <> + {/* just title */} + {title && !onBack &&

{title}

} + {/* just back button */} + {!title && onBack && ( + + )} + {/* back button && title */} + {title && onBack && ( + + + + + +

{title}

+
+
+ )} +
); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts index 257d7d36dbee1..23af89ebf9bc7 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/i18n.ts @@ -6,6 +6,10 @@ import { i18n } from '@kbn/i18n'; -export const txtClose = i18n.translate('xpack.drilldowns.components.FlyoutFrame.Close', { +export const txtClose = i18n.translate('xpack.drilldowns.components.FlyoutFrame.CloseButtonLabel', { defaultMessage: 'Close', }); + +export const txtBack = i18n.translate('xpack.drilldowns.components.FlyoutFrame.BackButtonLabel', { + defaultMessage: 'Back', +}); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.story.tsx b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.story.tsx new file mode 100644 index 0000000000000..33feea11c4f29 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.story.tsx @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import * as React from 'react'; +import { EuiFlyout } from '@elastic/eui'; +import { storiesOf } from '@storybook/react'; +import { FlyoutManageDrilldowns } from './flyout_manage_drilldowns'; +import { drilldowns } from '../list_manage_drilldowns/test_data'; + +storiesOf('components/FlyoutManageDrilldowns', module).add('default', () => ( + {}}> + + +)); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx new file mode 100644 index 0000000000000..6ac5ce7bc2b5a --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx @@ -0,0 +1,64 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useState } from 'react'; +import { FlyoutFrame } from '../flyout_frame'; +import { DrilldownListItem, ListManageDrilldowns } from '../list_manage_drilldowns'; +import { FlyoutDrilldownWizard } from '../flyout_drilldown_wizard'; +import { txtManageDrilldowns } from './i18n'; + +export interface FlyoutManageDrilldownsProps { + drilldowns: DrilldownListItem[]; + onClose?: () => void; +} + +enum ViewState { + List, + Create, + Edit, +} + +export function FlyoutManageDrilldowns({ + drilldowns, + onClose = () => {}, +}: FlyoutManageDrilldownsProps) { + const [viewState, setViewState] = useState(ViewState.List); + + switch (viewState) { + case ViewState.Create: + case ViewState.Edit: + return ( + setViewState(ViewState.List)} + onDelete={() => { + setViewState(ViewState.List); + }} + onClose={() => { + onClose(); + }} + onBack={() => { + setViewState(ViewState.List); + }} + /> + ); + case ViewState.List: + default: + return ( + + { + setViewState(ViewState.Create); + }} + onEdit={() => { + setViewState(ViewState.Edit); + }} + /> + + ); + } +} diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/i18n.ts new file mode 100644 index 0000000000000..460fcf2e06c0e --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/i18n.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const txtManageDrilldowns = i18n.translate( + 'xpack.drilldowns.components.FlyoutManageDrilldowns.manageDrilldownsTitle', + { + defaultMessage: 'Manage Drilldowns', + } +); From ea0d5c1500915b3fe6cc3b69ef7a2dab34a0db40 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 19:07:59 +0100 Subject: [PATCH 25/32] nit fixes --- .../public/components/action_wizard/action_wizard.tsx | 11 +++++++---- .../public/components/action_wizard/test_data.tsx | 8 +++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 88ab792d2ff7e..a1d6d26f01812 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -27,7 +27,7 @@ export interface ActionFactory>; createConfig: () => Config; - isValid: (name: string, config: Config) => boolean; + isValid: (config: Config) => boolean; } export interface ActionFactoryWizardProps { @@ -66,6 +66,7 @@ export interface ActionWizardProps { */ onConfigChange: (config: ActionBaseConfig) => void; } + export const ActionWizard: React.FC = ({ currentActionFactory, actionFactories, @@ -111,7 +112,9 @@ interface SelectedActionFactoryProps void; } + export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory'; + const SelectedActionFactory: React.FC = ({ actionFactory, onDeselect, @@ -149,9 +152,7 @@ const SelectedActionFactory: React.FC = ({
{actionFactory.wizard({ config, - onConfig: newConfig => { - onConfigChange(newConfig); - }, + onConfig: onConfigChange, })}
@@ -162,7 +163,9 @@ interface ActionFactorySelectorProps { actionFactories: ActionFactory[]; onActionFactorySelected: (actionFactory: ActionFactory) => void; } + export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item'; + const ActionFactorySelector: React.FC = ({ actionFactories, onActionFactorySelected, diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx index fc186b113915d..8ecdde681069e 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/test_data.tsx @@ -28,8 +28,7 @@ export const dashboardDrilldownActionFactory: ActionFactory<{ useCurrentDashboardFilters: true, }; }, - isValid: (name, config) => { - if (!name) return false; + isValid: config => { if (!config.dashboardId) return false; return true; }, @@ -93,8 +92,7 @@ export const urlDrilldownActionFactory: ActionFactory<{ url: string; openInNewTa openInNewTab: false, }; }, - isValid: (name, config) => { - if (!name) return false; + isValid: config => { if (!config.url) return false; return true; }, @@ -166,7 +164,7 @@ export function Demo({ actionFactories }: { actionFactories: ArrayAction Factory Config: {JSON.stringify(state.config)}
Is config valid:{' '} - {JSON.stringify(state.currentActionFactory?.isValid('fake name', state.config!) ?? false)} + {JSON.stringify(state.currentActionFactory?.isValid(state.config!) ?? false)}
); From 16e0bc080cc11325eab9cd1e127fb0efb4226080 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Thu, 5 Mar 2020 19:09:39 +0100 Subject: [PATCH 26/32] merge --- .../flyout_drilldown_wizard/flyout_drilldown_wizard.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 8f64a69f42227..8aff0af872e21 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -58,10 +58,11 @@ export function FlyoutDrilldownWizard< ); const isActionValid = (): boolean => { + if (!wizardConfig.name) return false; if (!wizardConfig.actionFactory) return false; if (!wizardConfig.actionConfig) return false; - return wizardConfig.actionFactory.isValid(wizardConfig.name, wizardConfig.actionConfig); + return wizardConfig.actionFactory.isValid(wizardConfig.actionConfig); }; const footer = ( From 2f100c83debbcaf505cdcf07b82458bd2b79ccec Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 07:01:23 +0100 Subject: [PATCH 27/32] revert react testing library configuration extracted to separate pr --- src/dev/jest/config.js | 1 - src/dev/jest/setup/react_testing_library.js | 23 ------------------- x-pack/dev-tools/jest/setup/setup_test.js | 4 ---- .../action_wizard/action_wizard.test.tsx | 10 ++++---- 4 files changed, 6 insertions(+), 32 deletions(-) delete mode 100644 src/dev/jest/setup/react_testing_library.js diff --git a/src/dev/jest/config.js b/src/dev/jest/config.js index 3ac044ce888df..807a3fbf4782b 100644 --- a/src/dev/jest/config.js +++ b/src/dev/jest/config.js @@ -67,7 +67,6 @@ export default { '/src/dev/jest/setup/babel_polyfill.js', '/src/dev/jest/setup/polyfills.js', '/src/dev/jest/setup/enzyme.js', - '/src/dev/jest/setup/react_testing_library.js', ], setupFilesAfterEnv: ['/src/dev/jest/setup/mocks.js'], coverageDirectory: '/target/kibana-coverage/jest', diff --git a/src/dev/jest/setup/react_testing_library.js b/src/dev/jest/setup/react_testing_library.js deleted file mode 100644 index 14a064acdc54f..0000000000000 --- a/src/dev/jest/setup/react_testing_library.js +++ /dev/null @@ -1,23 +0,0 @@ -/* - * 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 { configure } from '@testing-library/react'; - -// instead of default 'data-test-id', use kibana's 'data-test-subj' -configure({ testIdAttribute: 'data-test-subj' }); diff --git a/x-pack/dev-tools/jest/setup/setup_test.js b/x-pack/dev-tools/jest/setup/setup_test.js index 4df2564aad01f..f54be89f30955 100644 --- a/x-pack/dev-tools/jest/setup/setup_test.js +++ b/x-pack/dev-tools/jest/setup/setup_test.js @@ -11,7 +11,3 @@ import 'jest-styled-components'; import '@testing-library/jest-dom/extend-expect'; -import { configure } from '@testing-library/react'; - -// instead of default 'data-test-id', use kibana's 'data-test-subj' -configure({ testIdAttribute: 'data-test-subj' }); diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index cd990e7a41b82..9d8e9d48e281c 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -15,11 +15,12 @@ import { urlDrilldownActionFactory, } from './test_data'; -// TODO: for some reason global cleanup from RTL doesn't work -// afterEach is not available for it globally during setup +// TODO: afterEach is not available for it globally during setup +// https://github.com/elastic/kibana/issues/59469 afterEach(cleanup); -test('Pick and configure action', () => { +// TEMP until https://github.com/elastic/kibana/pull/59445 is merged +test.skip('Pick and configure action', () => { const screen = render( ); @@ -46,7 +47,8 @@ test('Pick and configure action', () => { }); }); -test('If only one actions factory is available then actionFactory selection is emitted without user input', () => { +// TEMP until https://github.com/elastic/kibana/pull/59445 is merged +test.skip('If only one actions factory is available then actionFactory selection is emitted without user input', () => { const screen = render(); // check that no factories are displayed to pick from From 16c31e14b1ded15ec99b18edc4d652da19304f7b Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 10:09:56 +0100 Subject: [PATCH 28/32] improve --- .../components/action_wizard/action_wizard.tsx | 5 +++-- .../drilldown_hello_bar.tsx | 3 --- .../flyout_drilldown_wizard.tsx | 13 +++++++++++++ .../components/flyout_frame/flyout_frame.tsx | 4 +++- .../flyout_manage_drilldowns.tsx | 18 +++++++++++++++++- .../form_drilldown_wizard.tsx | 6 +----- .../list_manage_drilldowns.tsx | 5 ++++- 7 files changed, 41 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index a1d6d26f01812..19bfdb1d50ca3 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -13,6 +13,7 @@ import { EuiSpacer, EuiText, EuiKeyPadMenuItem, + EuiKeyPadMenuItemButton, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; import './action_wizard.scss'; @@ -179,7 +180,7 @@ const ActionFactorySelector: React.FC = ({ return ( {actionFactories.map(actionFactory => ( - = ({ onClick={() => onActionFactorySelected(actionFactory)} > {actionFactory.iconType && } - + ))} ); diff --git a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx index 3961315c2e98a..bec9d0145c2b3 100644 --- a/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx +++ b/x-pack/plugins/drilldowns/public/components/drilldown_hello_bar/drilldown_hello_bar.tsx @@ -22,9 +22,6 @@ export interface DrilldownHelloBarProps { onHideClick?: () => void; } -/** - * @todo improve with https://github.com/elastic/eui/pull/2837 when newer eui is merged into kibana - */ export const DrilldownHelloBar: React.FC = ({ docsLink, onHideClick = () => {}, diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index 8aff0af872e21..b3ee0f5473d9a 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -21,6 +21,7 @@ import { urlDrilldownActionFactory, // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../advanced_ui_actions/public/components/action_wizard/test_data'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; export interface DrilldownWizardConfig { name: string; @@ -38,6 +39,8 @@ export interface FlyoutDrilldownWizardProps< mode?: 'create' | 'edit'; initialDrilldownWizardConfig?: DrilldownWizardConfig; + + showWelcomeMessage?: boolean; } export function FlyoutDrilldownWizard< @@ -49,6 +52,7 @@ export function FlyoutDrilldownWizard< initialDrilldownWizardConfig, mode = 'create', onDelete = () => {}, + showWelcomeMessage = false, }: FlyoutDrilldownWizardProps) { const [wizardConfig, setWizardConfig] = useState( () => @@ -85,6 +89,15 @@ export function FlyoutDrilldownWizard< footer={footer} onClose={onClose} onBack={onBack} + banner={ + showWelcomeMessage && ( + { + // TODO: + }} + /> + ) + } > void; onBack?: () => void; } @@ -33,6 +34,7 @@ export const FlyoutFrame: React.FC = ({ onClose, children, onBack, + banner, }) => { const headerFragment = (title || onBack) && ( @@ -95,7 +97,7 @@ export const FlyoutFrame: React.FC = ({ return ( <> {headerFragment} - {children} + {children} {footerFragment} ); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx index 6ac5ce7bc2b5a..da74509071194 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx @@ -9,10 +9,12 @@ import { FlyoutFrame } from '../flyout_frame'; import { DrilldownListItem, ListManageDrilldowns } from '../list_manage_drilldowns'; import { FlyoutDrilldownWizard } from '../flyout_drilldown_wizard'; import { txtManageDrilldowns } from './i18n'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; export interface FlyoutManageDrilldownsProps { drilldowns: DrilldownListItem[]; onClose?: () => void; + showWelcomeMessage?: boolean; } enum ViewState { @@ -24,6 +26,7 @@ enum ViewState { export function FlyoutManageDrilldowns({ drilldowns, onClose = () => {}, + showWelcomeMessage = true, }: FlyoutManageDrilldownsProps) { const [viewState, setViewState] = useState(ViewState.List); @@ -43,12 +46,25 @@ export function FlyoutManageDrilldowns({ onBack={() => { setViewState(ViewState.List); }} + showWelcomeMessage={showWelcomeMessage} /> ); case ViewState.List: default: return ( - + { + // TODO: + }} + /> + ) + } + > { diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx index 48bb14f5c4ad6..19ec5b7506c1b 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.tsx @@ -7,15 +7,12 @@ import React from 'react'; import './form_drilldown_wizard.scss'; import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui'; -import { DrilldownHelloBar } from '../drilldown_hello_bar'; import { txtDrilldownAction, txtNameOfDrilldown, txtUntitledDrilldown } from './i18n'; import { - ActionFactory, ActionBaseConfig, + ActionFactory, ActionWizard, } from '../../../../advanced_ui_actions/public'; -// TODO: this should be actual input to the component and should not be using test data -// eslint-disable-next-line @kbn/eslint/no-restricted-paths const noop = () => {}; @@ -72,7 +69,6 @@ export const FormDrilldownWizard: React.FC = ({ return ( <> - {nameFragment} diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx index 71e566d1020f5..a9895444558cd 100644 --- a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.tsx @@ -79,7 +79,10 @@ export function ListManageDrilldowns({ }, selectableMessage: () => txtSelectDrilldown, }} - rowProps={{ 'data-test-subj': TEST_SUBJ_DRILLDOWN_ITEM }} + rowProps={{ + 'data-test-subj': TEST_SUBJ_DRILLDOWN_ITEM, + 'data-testid': TEST_SUBJ_DRILLDOWN_ITEM, + }} hasActions={true} /> From f315299ecea56aeaa2757a379007aca973865efb Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 10:10:11 +0100 Subject: [PATCH 29/32] fix --- .../public/components/action_wizard/action_wizard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index 19bfdb1d50ca3..c287ef114a727 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -12,7 +12,6 @@ import { EuiIcon, EuiSpacer, EuiText, - EuiKeyPadMenuItem, EuiKeyPadMenuItemButton, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; From 4641454d0f41ec6055d1a444edc14000e41d4a87 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 11:30:27 +0100 Subject: [PATCH 30/32] fix tests and use buttons instead of links --- .../components/action_wizard/action_wizard.test.tsx | 8 +++----- .../public/components/action_wizard/action_wizard.tsx | 8 +++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx index 9d8e9d48e281c..aea47be693b8f 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { cleanup, fireEvent, render } from '@testing-library/react'; +import { cleanup, fireEvent, render } from '@testing-library/react/pure'; import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { TEST_SUBJ_ACTION_FACTORY_ITEM, TEST_SUBJ_SELECTED_ACTION_FACTORY } from './action_wizard'; import { @@ -19,8 +19,7 @@ import { // https://github.com/elastic/kibana/issues/59469 afterEach(cleanup); -// TEMP until https://github.com/elastic/kibana/pull/59445 is merged -test.skip('Pick and configure action', () => { +test('Pick and configure action', () => { const screen = render( ); @@ -47,8 +46,7 @@ test.skip('Pick and configure action', () => { }); }); -// TEMP until https://github.com/elastic/kibana/pull/59445 is merged -test.skip('If only one actions factory is available then actionFactory selection is emitted without user input', () => { +test('If only one actions factory is available then actionFactory selection is emitted without user input', () => { const screen = render(); // check that no factories are displayed to pick from diff --git a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx index a1d6d26f01812..41ef863c00e44 100644 --- a/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx +++ b/x-pack/plugins/advanced_ui_actions/public/components/action_wizard/action_wizard.tsx @@ -12,7 +12,7 @@ import { EuiIcon, EuiSpacer, EuiText, - EuiKeyPadMenuItem, + EuiKeyPadMenuItemButton, } from '@elastic/eui'; import { txtChangeButton } from './i18n'; import './action_wizard.scss'; @@ -126,6 +126,7 @@ const SelectedActionFactory: React.FC = ({
@@ -179,15 +180,16 @@ const ActionFactorySelector: React.FC = ({ return ( {actionFactories.map(actionFactory => ( - onActionFactorySelected(actionFactory)} > {actionFactory.iconType && } - + ))} ); From 722740702b6428cd4fe10fd0b8bcac10fa0c77b4 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 11:40:13 +0100 Subject: [PATCH 31/32] no new testing-library side effects --- .../public/components/flyout_frame/flyout_frame.test.tsx | 4 +++- .../form_drilldown_wizard/form_drilldown_wizard.test.tsx | 4 +++- .../list_manage_drilldowns/list_manage_drilldowns.test.tsx | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx index b5fb52fcf5c18..0a3989487745f 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_frame/flyout_frame.test.tsx @@ -6,9 +6,11 @@ import React from 'react'; import { render } from 'react-dom'; -import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; +import { render as renderTestingLibrary, fireEvent, cleanup } from '@testing-library/react/pure'; import { FlyoutFrame } from '.'; +afterEach(cleanup); + describe('', () => { test('renders without crashing', () => { const div = document.createElement('div'); diff --git a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx index 9807d0d6b55ca..b4707eef79a68 100644 --- a/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx +++ b/x-pack/plugins/drilldowns/public/components/form_drilldown_wizard/form_drilldown_wizard.test.tsx @@ -7,9 +7,11 @@ import React from 'react'; import { render } from 'react-dom'; import { FormDrilldownWizard } from './form_drilldown_wizard'; -import { render as renderTestingLibrary, fireEvent } from '@testing-library/react'; +import { render as renderTestingLibrary, fireEvent, cleanup } from '@testing-library/react/pure'; import { txtNameOfDrilldown } from './i18n'; +afterEach(cleanup); + describe('', () => { test('renders without crashing', () => { const div = document.createElement('div'); diff --git a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx index 2ad83f684b339..2d8a3ef8440cb 100644 --- a/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx +++ b/x-pack/plugins/drilldowns/public/components/list_manage_drilldowns/list_manage_drilldowns.test.tsx @@ -5,7 +5,7 @@ */ import React from 'react'; -import { cleanup, fireEvent, render } from '@testing-library/react'; +import { cleanup, fireEvent, render } from '@testing-library/react/pure'; import '@testing-library/jest-dom/extend-expect'; // TODO: this should be global import { drilldowns } from './test_data'; import { ListManageDrilldowns, TEST_SUBJ_DRILLDOWN_ITEM } from './list_manage_drilldowns'; From 39f55482b11027e2046c4d78560b2b0a6b0ff443 Mon Sep 17 00:00:00 2001 From: Anton Dosov Date: Fri, 6 Mar 2020 15:10:42 +0100 Subject: [PATCH 32/32] improve --- .../flyout_drilldown_wizard.tsx | 12 ++--- .../flyout_list_manage_drilldowns.story.tsx | 17 +++++++ .../flyout_list_manage_drilldowns.tsx | 46 +++++++++++++++++ .../flyout_list_manage_drilldowns/i18n.ts | 14 +++++ .../flyout_list_manage_drilldowns/index.ts | 7 +++ .../flyout_manage_drilldowns.tsx | 51 ++++++++----------- .../flyout_manage_drilldowns/index.ts | 7 +++ 7 files changed, 116 insertions(+), 38 deletions(-) create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/flyout_list_manage_drilldowns.story.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/flyout_list_manage_drilldowns.tsx create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/i18n.ts create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/index.ts create mode 100644 x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/index.ts diff --git a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx index b3ee0f5473d9a..fcc5bcf2fa8b8 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_drilldown_wizard/flyout_drilldown_wizard.tsx @@ -41,6 +41,7 @@ export interface FlyoutDrilldownWizardProps< initialDrilldownWizardConfig?: DrilldownWizardConfig; showWelcomeMessage?: boolean; + onWelcomeHideClick?: () => void; } export function FlyoutDrilldownWizard< @@ -53,6 +54,7 @@ export function FlyoutDrilldownWizard< mode = 'create', onDelete = () => {}, showWelcomeMessage = false, + onWelcomeHideClick, }: FlyoutDrilldownWizardProps) { const [wizardConfig, setWizardConfig] = useState( () => @@ -89,15 +91,7 @@ export function FlyoutDrilldownWizard< footer={footer} onClose={onClose} onBack={onBack} - banner={ - showWelcomeMessage && ( - { - // TODO: - }} - /> - ) - } + banner={showWelcomeMessage && } > ( + {}}> + + +)); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/flyout_list_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/flyout_list_manage_drilldowns.tsx new file mode 100644 index 0000000000000..a44a7ccccb4dc --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/flyout_list_manage_drilldowns.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { FlyoutFrame } from '../flyout_frame'; +import { DrilldownListItem, ListManageDrilldowns } from '../list_manage_drilldowns'; +import { txtManageDrilldowns } from './i18n'; +import { DrilldownHelloBar } from '../drilldown_hello_bar'; + +export interface FlyoutListManageDrilldownsProps { + drilldowns: DrilldownListItem[]; + onClose?: () => void; + onCreate?: () => void; + onEdit?: (drilldownId: string) => void; + onDelete?: (drilldownIds: string[]) => void; + showWelcomeMessage?: boolean; + onWelcomeHideClick?: () => void; +} + +export function FlyoutListManageDrilldowns({ + drilldowns, + onClose = () => {}, + onCreate, + onDelete, + onEdit, + showWelcomeMessage = true, + onWelcomeHideClick, +}: FlyoutListManageDrilldownsProps) { + return ( + } + > + + + ); +} diff --git a/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/i18n.ts b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/i18n.ts new file mode 100644 index 0000000000000..0dd4e37d4dddd --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/i18n.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const txtManageDrilldowns = i18n.translate( + 'xpack.drilldowns.components.FlyoutListManageDrilldowns.manageDrilldownsTitle', + { + defaultMessage: 'Manage Drilldowns', + } +); diff --git a/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/index.ts b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/index.ts new file mode 100644 index 0000000000000..f8c9d224fb292 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_list_manage_drilldowns/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './flyout_list_manage_drilldowns'; diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx index da74509071194..207cb3e42d2b3 100644 --- a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/flyout_manage_drilldowns.tsx @@ -5,31 +5,33 @@ */ import React, { useState } from 'react'; -import { FlyoutFrame } from '../flyout_frame'; -import { DrilldownListItem, ListManageDrilldowns } from '../list_manage_drilldowns'; +import { DrilldownListItem } from '../list_manage_drilldowns'; import { FlyoutDrilldownWizard } from '../flyout_drilldown_wizard'; -import { txtManageDrilldowns } from './i18n'; -import { DrilldownHelloBar } from '../drilldown_hello_bar'; +import { FlyoutListManageDrilldowns } from '../flyout_list_manage_drilldowns'; export interface FlyoutManageDrilldownsProps { drilldowns: DrilldownListItem[]; onClose?: () => void; showWelcomeMessage?: boolean; + onHideWelcomeMessage?: () => void; } enum ViewState { - List, - Create, - Edit, + List = 'list', + Create = 'create', + Edit = 'edit', } export function FlyoutManageDrilldowns({ drilldowns, onClose = () => {}, showWelcomeMessage = true, + onHideWelcomeMessage, }: FlyoutManageDrilldownsProps) { const [viewState, setViewState] = useState(ViewState.List); + // TODO: apparently this will be the component with all the state management and data fetching + switch (viewState) { case ViewState.Create: case ViewState.Edit: @@ -47,34 +49,25 @@ export function FlyoutManageDrilldowns({ setViewState(ViewState.List); }} showWelcomeMessage={showWelcomeMessage} + onWelcomeHideClick={onHideWelcomeMessage} /> ); case ViewState.List: default: return ( - { - // TODO: - }} - /> - ) - } - > - { - setViewState(ViewState.Create); - }} - onEdit={() => { - setViewState(ViewState.Edit); - }} - /> - + showWelcomeMessage={showWelcomeMessage} + onWelcomeHideClick={onHideWelcomeMessage} + onCreate={() => { + setViewState(ViewState.Create); + }} + onEdit={() => { + setViewState(ViewState.Edit); + }} + onDelete={() => {}} + /> ); } } diff --git a/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/index.ts b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/index.ts new file mode 100644 index 0000000000000..c1c530977a122 --- /dev/null +++ b/x-pack/plugins/drilldowns/public/components/flyout_manage_drilldowns/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export * from './flyout_manage_drilldowns';