Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip][Drilldowns] Drilldown Wizard Components #59139

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
de86fbd
wip
Dosant Feb 28, 2020
40ea973
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 2, 2020
21d8232
wip
Dosant Mar 2, 2020
bd601c6
lint
Dosant Mar 2, 2020
9d181d3
improve
Dosant Mar 2, 2020
9e994a1
fix
Dosant Mar 2, 2020
caefe6e
Merge branch 'master' into dev/drilldowns/action-factory-picker
elasticmachine Mar 3, 2020
a9144b7
wip
Dosant Mar 3, 2020
64283e9
Merge branch 'master' into dev/drilldowns/create-drilldown-form
elasticmachine Mar 3, 2020
a08ea95
@cchaos review
Dosant Mar 3, 2020
642514c
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 3, 2020
eff3167
improve storybook setup to autoimport scss mixins
Dosant Mar 3, 2020
34ee67d
using keypadmenuitem
andreadelrio Mar 3, 2020
e40204c
Merge pull request #3 from andreadelrio/design-action-factory-picker
Dosant Mar 4, 2020
56565dc
fix ts
Dosant Mar 4, 2020
70c6d1c
Merge branch 'master' into dev/drilldowns/action-factory-picker
elasticmachine Mar 4, 2020
5c8176a
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 4, 2020
b88ec04
wip
Dosant Mar 4, 2020
20992f8
move to x-pack
Dosant Mar 4, 2020
68e17ad
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 4, 2020
b77d7b5
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 4, 2020
4b4f9a7
fix i18n x-pack
Dosant Mar 4, 2020
ed19d4c
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 4, 2020
8d7eccf
fix
Dosant Mar 4, 2020
d7b70d3
Merge branch 'dev/drilldowns/create-drilldown-form' of github.com:Dos…
Dosant Mar 4, 2020
068b485
fix capitalization
Dosant Mar 4, 2020
8456731
simplify - don’t expose context on action factory
Dosant Mar 4, 2020
439bdea
fix
Dosant Mar 4, 2020
0c6d5e9
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 4, 2020
e7a608c
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 5, 2020
1c37f65
improve. make <ActionWizard/> stateless. Simplify interfaces.
Dosant Mar 5, 2020
f66922d
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 5, 2020
f6175b9
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 5, 2020
8501087
wip
Dosant Mar 5, 2020
ae07675
rename
Dosant Mar 5, 2020
6d05a17
fix
Dosant Mar 5, 2020
2c4ceed
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 5, 2020
5e71093
rename
Dosant Mar 5, 2020
e80538b
Merge branch 'master' into dev/drilldowns/action-factory-picker
elasticmachine Mar 5, 2020
765f962
<ListManageDrilldowns/>
Dosant Mar 5, 2020
9f0da1c
<FlyoutDrilldownWizard>
Dosant Mar 5, 2020
ea0d5c1
nit fixes
Dosant Mar 5, 2020
ecd6a1d
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 5, 2020
16e0bc0
merge
Dosant Mar 5, 2020
2f100c8
revert react testing library configuration
Dosant Mar 6, 2020
9e040c1
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 6, 2020
2c87c36
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 6, 2020
16c31e1
improve
Dosant Mar 6, 2020
f315299
fix
Dosant Mar 6, 2020
05367ec
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 6, 2020
4641454
fix tests and use buttons instead of links
Dosant Mar 6, 2020
fc947ac
Merge branch 'dev/drilldowns/action-factory-picker' of github.com:Dos…
Dosant Mar 6, 2020
7227407
no new testing-library side effects
Dosant Mar 6, 2020
fe91657
Merge branch 'master' of github.com:elastic/kibana into dev/drilldown…
Dosant Mar 6, 2020
39f5548
improve
Dosant Mar 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion packages/kbn-storybook/storybook_config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,6 +73,38 @@ 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: {
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')],
},
},
},
],
});

// Reference the built DLL file of static(ish) dependencies, which are removed
// during kbn:bootstrap and rebuilt if missing.
config.plugins.push(
Expand All @@ -96,7 +129,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) {
Expand Down
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,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: 'x-pack/plugins/advanced_ui_actions/scripts/storybook.js',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.auaActionWizard__selectedActionFactoryContainer {
background-color: $euiColorLightestShade;
padding: $euiSize;
}

.auaActionWizard__actionFactoryItem {
.euiKeyPadMenuItem__label {
height: #{$euiSizeXL};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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 { storiesOf } from '@storybook/react';
import { dashboardDrilldownActionFactory, Demo, urlDrilldownActionFactory } from './test_data';

storiesOf('components/ActionWizard', module)
.add('default', () => (
<Demo actionFactories={[dashboardDrilldownActionFactory, urlDrilldownActionFactory]} />
))
.add('Only one factory is available', () => (
// to make sure layout doesn't break
<Demo actionFactories={[dashboardDrilldownActionFactory]} />
))
.add('Long list of action factories', () => (
// to make sure layout doesn't break
<Demo
actionFactories={[
dashboardDrilldownActionFactory,
urlDrilldownActionFactory,
dashboardDrilldownActionFactory,
urlDrilldownActionFactory,
dashboardDrilldownActionFactory,
urlDrilldownActionFactory,
dashboardDrilldownActionFactory,
urlDrilldownActionFactory,
]}
/>
));
Original file line number Diff line number Diff line change
@@ -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 from '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 {
dashboardDrilldownActionFactory,
dashboards,
Demo,
urlDrilldownActionFactory,
} from './test_data';

// TODO: afterEach is not available for it globally during setup
// https://github.com/elastic/kibana/issues/59469
afterEach(cleanup);

test('Pick and configure action', () => {
const screen = render(
<Demo actionFactories={[dashboardDrilldownActionFactory, urlDrilldownActionFactory]} />
);

// 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));

// Input url
const URL = 'https://elastic.co';
fireEvent.change(screen.getByLabelText(/url/i), {
target: { value: URL },
});

// change to dashboard
fireEvent.click(screen.getByText(/change/i));
fireEvent.click(screen.getByText(/Go to Dashboard/i));

// Select dashboard
fireEvent.change(screen.getByLabelText(/Choose destination dashboard/i), {
target: { value: dashboards[1].id },
});
});

test('If only one actions factory is available then actionFactory selection is emitted without user input', () => {
const screen = render(<Demo actionFactories={[urlDrilldownActionFactory]} />);

// 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 can't change to action factory type
expect(screen.queryByTestId(/change/i)).not.toBeInTheDocument();
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
* 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 {
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
EuiSpacer,
EuiText,
EuiKeyPadMenuItemButton,
} from '@elastic/eui';
import { txtChangeButton } from './i18n';
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 ActionBaseConfig = {};
export interface ActionFactory<Config extends ActionBaseConfig = ActionBaseConfig> {
type: string; // TODO: type should be tied to Action and ActionByType
displayName: string;
iconType?: string;
wizard: React.FC<ActionFactoryWizardProps<Config>>;
createConfig: () => Config;
isValid: (config: Config) => boolean;
}

export interface ActionFactoryWizardProps<Config extends ActionBaseConfig> {
config?: Config;

/**
* Callback called when user updates the config in UI.
*/
onConfig: (config: Config) => void;
}

export interface ActionWizardProps {
/**
* List of available action factories
*/
actionFactories: Array<ActionFactory<any>>; // any here to be able to pass array of ActionFactory<Config> with different configs

/**
* Currently selected action factory
* undefined - is allowed and means that non is selected
*/
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?: ActionBaseConfig;

/**
* config changed
*/
onConfigChange: (config: ActionBaseConfig) => void;
}

export const ActionWizard: React.FC<ActionWizardProps> = ({
currentActionFactory,
actionFactories,
onActionFactoryChange,
onConfigChange,
config,
}) => {
// auto pick action factory if there is only 1 available
if (!currentActionFactory && actionFactories.length === 1) {
onActionFactoryChange(actionFactories[0]);
}

if (currentActionFactory && config) {
return (
<SelectedActionFactory
actionFactory={currentActionFactory}
showDeselect={actionFactories.length > 1}
onDeselect={() => {
onActionFactoryChange(null);
}}
config={config}
onConfigChange={newConfig => {
onConfigChange(newConfig);
}}
/>
);
}

return (
<ActionFactorySelector
actionFactories={actionFactories}
onActionFactorySelected={actionFactory => {
onActionFactoryChange(actionFactory);
}}
/>
);
};

interface SelectedActionFactoryProps<Config extends ActionBaseConfig = ActionBaseConfig> {
actionFactory: ActionFactory<Config>;
config: Config;
onConfigChange: (config: Config) => void;
showDeselect: boolean;
onDeselect: () => void;
}

export const TEST_SUBJ_SELECTED_ACTION_FACTORY = 'selected-action-factory';

const SelectedActionFactory: React.FC<SelectedActionFactoryProps> = ({
actionFactory,
onDeselect,
showDeselect,
onConfigChange,
config,
}) => {
return (
<div
className="auaActionWizard__selectedActionFactoryContainer"
data-test-subj={TEST_SUBJ_SELECTED_ACTION_FACTORY}
data-testid={TEST_SUBJ_SELECTED_ACTION_FACTORY}
>
<header>
<EuiFlexGroup alignItems="center" gutterSize="s">
{actionFactory.iconType && (
<EuiFlexItem grow={false}>
<EuiIcon type={actionFactory.iconType} size="m" />
</EuiFlexItem>
)}
<EuiFlexItem grow={true}>
<EuiText>
<h4>{actionFactory.displayName}</h4>
</EuiText>
</EuiFlexItem>
{showDeselect && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty size="s" onClick={() => onDeselect()}>
{txtChangeButton}
</EuiButtonEmpty>
</EuiFlexItem>
)}
</EuiFlexGroup>
</header>
<EuiSpacer size="m" />
<div>
{actionFactory.wizard({
config,
onConfig: onConfigChange,
})}
</div>
</div>
);
};

interface ActionFactorySelectorProps {
actionFactories: ActionFactory[];
onActionFactorySelected: (actionFactory: ActionFactory) => void;
}

export const TEST_SUBJ_ACTION_FACTORY_ITEM = 'action-factory-item';

const ActionFactorySelector: React.FC<ActionFactorySelectorProps> = ({
actionFactories,
onActionFactorySelected,
}) => {
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 <div>No action factories to pick from</div>;
}

return (
<EuiFlexGroup wrap>
{actionFactories.map(actionFactory => (
<EuiKeyPadMenuItemButton
className="auaActionWizard__actionFactoryItem"
key={actionFactory.type}
label={actionFactory.displayName}
data-testid={TEST_SUBJ_ACTION_FACTORY_ITEM}
data-test-subj={TEST_SUBJ_ACTION_FACTORY_ITEM}
onClick={() => onActionFactorySelected(actionFactory)}
>
{actionFactory.iconType && <EuiIcon type={actionFactory.iconType} size="m" />}
</EuiKeyPadMenuItemButton>
))}
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

import { i18n } from '@kbn/i18n';

export const txtCreateDrilldown = i18n.translate(
'xpack.drilldowns.components.FlyoutCreateDrilldown.CreateDrilldown',
export const txtChangeButton = i18n.translate(
'xpack.advancedUiActions.components.actionWizard.changeButton',
{
defaultMessage: 'Create drilldown',
defaultMessage: 'change',
}
);
Original file line number Diff line number Diff line change
@@ -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 { ActionFactory, ActionWizard, ActionBaseConfig } from './action_wizard';
Loading