Skip to content

Commit

Permalink
[Index patterns] Field editor example app (elastic#100524)
Browse files Browse the repository at this point in the history
* add example app
  • Loading branch information
mattkime committed Jun 12, 2021
1 parent 1f52dd0 commit 89b53d1
Show file tree
Hide file tree
Showing 9 changed files with 310 additions and 0 deletions.
7 changes: 7 additions & 0 deletions examples/index_pattern_field_editor_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
## index pattern field editor example

This example index pattern field editor app shows how to:
- Edit index pattern fields via flyout
- Delete index pattern runtime fields with modal confirm prompt

To run this example, use the command `yarn start --run-examples`.
10 changes: 10 additions & 0 deletions examples/index_pattern_field_editor_example/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"id": "indexPatternFieldEditorExample",
"version": "0.0.1",
"kibanaVersion": "kibana",
"server": false,
"ui": true,
"requiredPlugins": ["data", "indexPatternFieldEditor", "developerExamples"],
"optionalPlugins": [],
"requiredBundles": []
}
145 changes: 145 additions & 0 deletions examples/index_pattern_field_editor_example/public/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import {
EuiPage,
EuiPageHeader,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiButton,
EuiInMemoryTable,
EuiText,
DefaultItemAction,
} from '@elastic/eui';
import { AppMountParameters } from '../../../src/core/public';
import {
DataPublicPluginStart,
IndexPattern,
IndexPatternField,
} from '../../../src/plugins/data/public';
import { IndexPatternFieldEditorStart } from '../../../src/plugins/index_pattern_field_editor/public';

interface Props {
indexPattern?: IndexPattern;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
}

const IndexPatternFieldEditorExample = ({ indexPattern, indexPatternFieldEditor }: Props) => {
const [fields, setFields] = useState<IndexPatternField[]>(
indexPattern?.getNonScriptedFields() || []
);
const refreshFields = () => setFields(indexPattern?.getNonScriptedFields() || []);
const columns = [
{
field: 'name',
name: 'Field name',
},
{
name: 'Actions',
actions: [
{
name: 'Edit',
description: 'Edit this field',
icon: 'pencil',
type: 'icon',
'data-test-subj': 'editField',
onClick: (fld: IndexPatternField) =>
indexPatternFieldEditor.openEditor({
ctx: { indexPattern: indexPattern! },
fieldName: fld.name,
onSave: refreshFields,
}),
},
{
name: 'Delete',
description: 'Delete this field',
icon: 'trash',
type: 'icon',
'data-test-subj': 'deleteField',
available: (fld) => !!fld.runtimeField,
onClick: (fld: IndexPatternField) =>
indexPatternFieldEditor.openDeleteModal({
fieldName: fld.name,
ctx: {
indexPattern: indexPattern!,
},
onDelete: refreshFields,
}),
},
] as Array<DefaultItemAction<IndexPatternField>>,
},
];

const content = indexPattern ? (
<>
<EuiText data-test-subj="indexPatternTitle">Index pattern: {indexPattern?.title}</EuiText>
<div>
<EuiButton
onClick={() =>
indexPatternFieldEditor.openEditor({
ctx: { indexPattern: indexPattern! },
onSave: refreshFields,
})
}
data-test-subj="addField"
>
Add field
</EuiButton>
</div>
<EuiInMemoryTable<IndexPatternField>
items={fields}
columns={columns}
pagination={true}
hasActions={true}
sorting={{
sort: {
field: 'name',
direction: 'asc',
},
}}
/>
</>
) : (
<p>Please create an index pattern</p>
);

return (
<EuiPage>
<EuiPageBody>
<EuiPageHeader>Index pattern field editor demo</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>{content}</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
);
};

interface RenderAppDependencies {
data: DataPublicPluginStart;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
}

export const renderApp = async (
{ data, indexPatternFieldEditor }: RenderAppDependencies,
{ element }: AppMountParameters
) => {
const indexPattern = (await data.indexPatterns.getDefault()) || undefined;
ReactDOM.render(
<IndexPatternFieldEditorExample
indexPattern={indexPattern}
indexPatternFieldEditor={indexPatternFieldEditor}
/>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
11 changes: 11 additions & 0 deletions examples/index_pattern_field_editor_example/public/index.ts
Original file line number Diff line number Diff line change
@@ -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
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { IndexPatternFieldEditorPlugin } from './plugin';

export const plugin = () => new IndexPatternFieldEditorPlugin();
56 changes: 56 additions & 0 deletions examples/index_pattern_field_editor_example/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { Plugin, CoreSetup, AppMountParameters, AppNavLinkStatus } from '../../../src/core/public';
import { DeveloperExamplesSetup } from '../../developer_examples/public';
import { DataPublicPluginStart } from '../../../src/plugins/data/public';
import { IndexPatternFieldEditorStart } from '../../../src/plugins/index_pattern_field_editor/public';

interface StartDeps {
data: DataPublicPluginStart;
indexPatternFieldEditor: IndexPatternFieldEditorStart;
}

interface SetupDeps {
developerExamples: DeveloperExamplesSetup;
}

export class IndexPatternFieldEditorPlugin implements Plugin<void, void, SetupDeps, StartDeps> {
public setup(core: CoreSetup<StartDeps>, deps: SetupDeps) {
core.application.register({
id: 'indexPatternFieldEditorExample',
title: 'Index pattern field editor example',
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const [, depsStart] = await core.getStartServices();
const { renderApp } = await import('./app');
return renderApp(depsStart, params);
},
});

deps.developerExamples.register({
appId: 'indexPatternFieldEditorExample',
title: 'Index pattern field editor',
description: `IndexPatternFieldEditor provides a UI for editing index pattern fields directly from Kibana apps. This example plugin demonstrates integration.`,
links: [
{
label: 'README',
href:
'https://github.com/elastic/kibana/blob/master/src/plugins/index_pattern_field_editor/README.md',
iconType: 'logoGithub',
size: 's',
target: '_blank',
},
],
});
}

public start() {}

public stop() {}
}
18 changes: 18 additions & 0 deletions examples/index_pattern_field_editor_example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target",
"skipLibCheck": true
},
"include": [
"index.ts",
"public/**/*.ts",
"public/**/*.tsx",
"../../typings/**/*",
],
"exclude": [],
"references": [
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../../src/plugins/kibana_react/tsconfig.json" },
]
}
1 change: 1 addition & 0 deletions test/examples/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default async function ({ readConfigFile }) {
require.resolve('./state_sync'),
require.resolve('./routing'),
require.resolve('./expressions_explorer'),
require.resolve('./index_pattern_field_editor_example'),
],
services: {
...functionalConfig.get('services'),
Expand Down
38 changes: 38 additions & 0 deletions test/examples/index_pattern_field_editor_example/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';

// eslint-disable-next-line import/no-default-export
export default function ({
getService,
getPageObjects,
loadTestFile,
}: PluginFunctionalProviderContext) {
const browser = getService('browser');
const es = getService('es');
const PageObjects = getPageObjects(['common', 'header', 'settings']);

describe('index pattern field editor example', function () {
this.tags('ciGroup2');
before(async () => {
await browser.setWindowSize(1300, 900);
await es.transport.request({
path: '/blogs/_doc',
method: 'POST',
body: { user: 'matt', message: 20 },
});

await PageObjects.settings.navigateTo();
await PageObjects.settings.createIndexPattern('blogs', null);
await PageObjects.common.navigateToApp('indexPatternFieldEditorExample');
});

loadTestFile(require.resolve('./index_pattern_field_editor_example'));
});
}
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
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';

// eslint-disable-next-line import/no-default-export
export default function ({ getService }: PluginFunctionalProviderContext) {
const testSubjects = getService('testSubjects');

describe('', () => {
it('finds an index pattern', async () => {
await testSubjects.existOrFail('indexPatternTitle');
});
it('opens the field editor', async () => {
await testSubjects.click('addField');
await testSubjects.existOrFail('flyoutTitle');
});
});
}

0 comments on commit 89b53d1

Please sign in to comment.