Skip to content

Commit

Permalink
Add <FormCreateDrilldown> component (#58335) (#58502)
Browse files Browse the repository at this point in the history
* refactor: 💡 use .story for Storybook and standartize dir struct

* feat: 🎸 add <FlyoutFrame> component

* feat: 🎸 add <FlyoutCreateDrilldown> component

* refactor: 💡 improve FlyoutCreateDrilldownAction

* test: 💍 add <FlyoutFrame> tests

* docs: ✏️ add @todo for <DrilldownHelloBar>

* feat: 🎸 make name editable in <FormCreateDrilldown>

* test: 💍 add <FormCreateDrilldown> name field tests

* chore: 🤖 change drilldown translation keys
  • Loading branch information
streamich authored Feb 26, 2020
1 parent b3b8dbb commit c85ae4f
Show file tree
Hide file tree
Showing 24 changed files with 528 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,43 @@ import { CoreStart } from 'src/core/public';
import { Action } from '../../../../../../src/plugins/ui_actions/public';
import { toMountPoint } from '../../../../../../src/plugins/kibana_react/public';
import { IEmbeddable } from '../../../../../../src/plugins/embeddable/public';
import { FormCreateDrilldown } from '../../components/form_create_drilldown';
import { FlyoutCreateDrilldown } from '../../components/flyout_create_drilldown';

export const OPEN_FLYOUT_ADD_DRILLDOWN = 'OPEN_FLYOUT_ADD_DRILLDOWN';

interface ActionContext {
export interface FlyoutCreateDrilldownActionContext {
embeddable: IEmbeddable;
}

export interface OpenFlyoutAddDrilldownParams {
overlays: () => Promise<CoreStart['overlays']>;
}

export class OpenFlyoutAddDrilldown implements Action<ActionContext> {
export class FlyoutCreateDrilldownAction implements Action<FlyoutCreateDrilldownActionContext> {
public readonly type = OPEN_FLYOUT_ADD_DRILLDOWN;
public readonly id = OPEN_FLYOUT_ADD_DRILLDOWN;
public order = 100;
public order = 5;

constructor(protected readonly params: OpenFlyoutAddDrilldownParams) {}

public getDisplayName() {
return i18n.translate('xpack.drilldowns.panel.openFlyoutAddDrilldown.displayName', {
defaultMessage: 'Add drilldown',
return i18n.translate('xpack.drilldowns.FlyoutCreateDrilldownAction.displayName', {
defaultMessage: 'Create Drilldown',
});
}

public getIconType() {
return 'empty';
return 'plusInCircle';
}

public async isCompatible({ embeddable }: ActionContext) {
public async isCompatible({ embeddable }: FlyoutCreateDrilldownActionContext) {
return true;
}

public async execute({ embeddable }: ActionContext) {
public async execute(context: FlyoutCreateDrilldownActionContext) {
const overlays = await this.params.overlays();
overlays.openFlyout(toMountPoint(<FormCreateDrilldown />));
const handle = overlays.openFlyout(
toMountPoint(<FlyoutCreateDrilldown context={context} onClose={() => handle.close()} />)
);
}
}
2 changes: 1 addition & 1 deletion x-pack/plugins/drilldowns/public/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export * from './open_flyout_add_drilldown';
export * from './flyout_create_drilldown';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { DrilldownHelloBar } from '..';
import { DrilldownHelloBar } from '.';

storiesOf('components/DrilldownHelloBar', module).add('default', () => {
return <DrilldownHelloBar />;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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';

export interface DrilldownHelloBarProps {
docsLink?: string;
}

/**
* @todo https://github.com/elastic/kibana/issues/55311
*/
export const DrilldownHelloBar: React.FC<DrilldownHelloBarProps> = ({ docsLink }) => {
return (
<div>
<p>
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.
</p>
<a href={docsLink}>View docs</a>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';

export interface DrilldownHelloBarProps {
docsLink?: string;
}

export const DrilldownHelloBar: React.FC<DrilldownHelloBarProps> = ({ docsLink }) => {
return (
<div>
<p>
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.
</p>
<a href={docsLink}>View docs</a>
<img
src="https://user-images.githubusercontent.com/9773803/72729009-e5803180-3b8e-11ea-8330-b86089bf5f0a.png"
alt=""
/>
</div>
);
};
export * from './drilldown_hello_bar';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import * as React from 'react';
import { storiesOf } from '@storybook/react';
import { DrilldownPicker } from '..';
import { DrilldownPicker } from '.';

storiesOf('components/DrilldownPicker', module).add('default', () => {
return <DrilldownPicker />;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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<DrilldownPickerProps> = () => {
return (
<img
src={
'https://user-images.githubusercontent.com/9773803/72725665-9e8e3e00-3b86-11ea-9314-8724c521b41f.png'
}
alt=""
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,4 @@
* 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<DrilldownPickerProps> = () => {
return (
<img
src={
'https://user-images.githubusercontent.com/9773803/72725665-9e8e3e00-3b86-11ea-9314-8724c521b41f.png'
}
alt=""
/>
);
};
export * from './drilldown_picker';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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.
*/

/* eslint-disable no-console */

import * as React from 'react';
import { EuiFlyout } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import { FlyoutCreateDrilldown } from '.';

storiesOf('components/FlyoutCreateDrilldown', module)
.add('default', () => {
return <FlyoutCreateDrilldown context={{} as any} />;
})
.add('open in flyout', () => {
return (
<EuiFlyout>
<FlyoutCreateDrilldown context={{} as any} />
</EuiFlyout>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { EuiButton } from '@elastic/eui';
import { FormCreateDrilldown } from '../form_create_drilldown';
import { FlyoutFrame } from '../flyout_frame';
import { txtCreateDrilldown } from './i18n';
import { FlyoutCreateDrilldownActionContext } from '../../actions';

export interface FlyoutCreateDrilldownProps {
context: FlyoutCreateDrilldownActionContext;
onClose?: () => void;
}

export const FlyoutCreateDrilldown: React.FC<FlyoutCreateDrilldownProps> = ({
context,
onClose,
}) => {
const footer = (
<EuiButton onClick={() => {}} fill>
{txtCreateDrilldown}
</EuiButton>
);

return (
<FlyoutFrame title={txtCreateDrilldown} footer={footer} onClose={onClose}>
<FormCreateDrilldown />
</FlyoutFrame>
);
};
Original file line number Diff line number Diff line change
@@ -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 txtCreateDrilldown = i18n.translate(
'xpack.drilldowns.components.FlyoutCreateDrilldown.CreateDrilldown',
{
defaultMessage: 'Create drilldown',
}
);
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 * from './flyout_create_drilldown';
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.
*/

/* eslint-disable no-console */

import * as React from 'react';
import { EuiFlyout, EuiButton } from '@elastic/eui';
import { storiesOf } from '@storybook/react';
import { FlyoutFrame } from '.';

storiesOf('components/FlyoutFrame', module)
.add('default', () => {
return <FlyoutFrame>test</FlyoutFrame>;
})
.add('with title', () => {
return <FlyoutFrame title="Hello world">test</FlyoutFrame>;
})
.add('with onClose', () => {
return <FlyoutFrame onClose={() => console.log('onClose')}>test</FlyoutFrame>;
})
.add('custom footer', () => {
return <FlyoutFrame footer={<button>click me!</button>}>test</FlyoutFrame>;
})
.add('open in flyout', () => {
return (
<EuiFlyout>
<FlyoutFrame
title="Create drilldown"
footer={<EuiButton>Save</EuiButton>}
onClose={() => console.log('onClose')}
>
test
</FlyoutFrame>
</EuiFlyout>
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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 { render } from 'react-dom';
import { render as renderTestingLibrary, fireEvent } from '@testing-library/react';
import { FlyoutFrame } from '.';

describe('<FlyoutFrame>', () => {
test('renders without crashing', () => {
const div = document.createElement('div');
render(<FlyoutFrame />, div);
});

describe('[title=]', () => {
test('renders title in <h1> tag', () => {
const div = document.createElement('div');
render(<FlyoutFrame title={'foobar'} />, div);

const title = div.querySelector('h1');
expect(title?.textContent).toBe('foobar');
});

test('title can be any react node', () => {
const div = document.createElement('div');
render(
<FlyoutFrame
title={
<>
foo <strong>bar</strong>
</>
}
/>,
div
);

const title = div.querySelector('h1');
expect(title?.innerHTML).toBe('foo <strong>bar</strong>');
});
});

describe('[footer=]', () => {
test('if [footer=] prop not provided, does not render footer', () => {
const div = document.createElement('div');
render(<FlyoutFrame />, div);

const footer = div.querySelector('[data-test-subj="flyoutFooter"]');
expect(footer).toBe(null);
});

test('can render anything in footer', () => {
const div = document.createElement('div');
render(
<FlyoutFrame
footer={
<>
a <em>b</em>
</>
}
/>,
div
);

const footer = div.querySelector('[data-test-subj="flyoutFooter"]');
expect(footer?.innerHTML).toBe('a <em>b</em>');
});
});

describe('[onClose=]', () => {
test('does not render close button if "onClose" prop is missing', () => {
const div = document.createElement('div');
render(<FlyoutFrame />, div);

const closeButton = div.querySelector('[data-test-subj="flyoutCloseButton"]');
expect(closeButton).toBe(null);
});

test('renders close button if "onClose" prop is provided', () => {
const div = document.createElement('div');
render(<FlyoutFrame onClose={() => {}} />, div);

const closeButton = div.querySelector('[data-test-subj="flyoutCloseButton"]');
expect(closeButton).not.toBe(null);
});

test('calls onClose prop when close button clicked', async () => {
const onClose = jest.fn();
const el = renderTestingLibrary(<FlyoutFrame onClose={onClose} />);

const closeButton = el.queryByText('Close');

expect(onClose).toHaveBeenCalledTimes(0);

fireEvent.click(closeButton!);

expect(onClose).toHaveBeenCalledTimes(1);
});
});
});
Loading

0 comments on commit c85ae4f

Please sign in to comment.