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

[Controls] [Portable Dashboards] Add control group renderer example plugin #146189

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions examples/controls_example/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "controlsExample",
"owner": {
"name": "Kibana Presentation",
"githubTeam": "kibana-presentation"
},
"version": "1.0.0",
"kibanaVersion": "kibana",
"ui": true,
"requiredPlugins": ["data", "developerExamples", "presentationUtil", "controls"]
}
42 changes: 42 additions & 0 deletions examples/controls_example/public/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* 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 from 'react';
import ReactDOM from 'react-dom';

import type { DataView } from '@kbn/data-views-plugin/public';
import { AppMountParameters } from '@kbn/core/public';
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
import { ControlsExampleStartDeps } from './plugin';
import { BasicReduxExample } from './basic_redux_example';

interface Props {
dataView: DataView;
}

const ControlsExamples = ({ dataView }: Props) => {
return (
<KibanaPageTemplate>
<KibanaPageTemplate.Header pageTitle="Controls as a Building Block" />
<KibanaPageTemplate.Section>
<BasicReduxExample dataView={dataView} />
</KibanaPageTemplate.Section>
</KibanaPageTemplate>
);
};

export const renderApp = async (
{ data }: ControlsExampleStartDeps,
{ element }: AppMountParameters
) => {
const dataViews = await data.dataViews.find('kibana_sample_data_ecommerce');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required for this PR - as this one is mostly about upgrading the API, but food for thought - maybe for all of our examples, we should align behind the sample web logs data set, as it has more relevance to solution use cases.

Additionally, maybe we should show some sort of warning asking developers who want to use the examples to install this data set if it doesn't exist.

if (dataViews.length > 0) {
ReactDOM.render(<ControlsExamples dataView={dataViews[0]} />, element);
}
return () => ReactDOM.unmountComponentAtNode(element);
};
133 changes: 133 additions & 0 deletions examples/controls_example/public/basic_redux_example.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* 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, { useMemo, useState } from 'react';

import {
LazyControlGroupRenderer,
ControlGroupContainer,
ControlGroupInput,
useControlGroupContainerContext,
ControlStyle,
} from '@kbn/controls-plugin/public';
import { withSuspense } from '@kbn/presentation-util-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import {
EuiButtonGroup,
EuiFlexGroup,
EuiFlexItem,
EuiPanel,
EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui';
import { getDefaultControlGroupInput } from '@kbn/controls-plugin/common';

interface Props {
dataView: DataView;
}
const ControlGroupRenderer = withSuspense(LazyControlGroupRenderer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, not for right now, but I've been thinking about our usages of withSuspense, and how we actually export the LazyControlGroupRenderer. I'm thinking that maybe we should write our own version of withSuspense that only works with the Control Group (with a better / centered loading indicator), and export that instead so that implementors don't need to also use presentation_util.


export const BasicReduxExample = ({ dataView }: Props) => {
const [myControlGroup, setControlGroup] = useState<ControlGroupContainer>();
const [currentControlStyle, setCurrentControlStyle] = useState<ControlStyle>('oneLine');

const ControlGroupReduxWrapper = useMemo(() => {
if (myControlGroup) return myControlGroup.getReduxEmbeddableTools().Wrapper;
}, [myControlGroup]);

const ButtonControls = () => {
const {
useEmbeddableDispatch,
actions: { setControlStyle },
} = useControlGroupContainerContext();
const dispatch = useEmbeddableDispatch();

return (
<>
<EuiFlexGroup alignItems="center">
<EuiFlexItem grow={false}>
<EuiText>
<p>Choose a style for your control group:</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiButtonGroup
legend="Text style"
options={[
{
id: `oneLine`,
label: 'One line',
value: 'oneLine' as ControlStyle,
},
{
id: `twoLine`,
label: 'Two lines',
value: 'twoLine' as ControlStyle,
},
]}
idSelected={currentControlStyle}
onChange={(id, value) => {
setCurrentControlStyle(value);
dispatch(setControlStyle(value));
}}
type="single"
/>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="m" />
</>
);
};

return (
<>
<EuiTitle>
<h2>Basic Redux Example</h2>
</EuiTitle>
<EuiText>
<p>
This example uses the redux context from the control group container in order to
dynamically change the style of the control group.
</p>
</EuiText>
<EuiSpacer size="m" />
<EuiPanel hasBorder={true}>
{ControlGroupReduxWrapper && (
<ControlGroupReduxWrapper>
<ButtonControls />
</ControlGroupReduxWrapper>
)}

<ControlGroupRenderer
onEmbeddableLoad={async (controlGroup) => {
setControlGroup(controlGroup);
}}
getCreationOptions={async (controlGroupInputBuilder) => {
const initialInput: Partial<ControlGroupInput> = {
...getDefaultControlGroupInput(),
defaultControlWidth: 'small',
};
await controlGroupInputBuilder.addDataControlFromField(initialInput, {
dataViewId: dataView.id ?? 'kibana_sample_data_ecommerce',
fieldName: 'customer_first_name.keyword',
});
await controlGroupInputBuilder.addDataControlFromField(initialInput, {
dataViewId: dataView.id ?? 'kibana_sample_data_ecommerce',
fieldName: 'customer_last_name.keyword',
width: 'medium',
grow: false,
title: 'Last Name',
});
return initialInput;
}}
/>
</EuiPanel>
</>
);
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions examples/controls_example/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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 { ControlsExamplePlugin } from './plugin';

export function plugin() {
return new ControlsExamplePlugin();
}
54 changes: 54 additions & 0 deletions examples/controls_example/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 {
AppMountParameters,
AppNavLinkStatus,
CoreSetup,
CoreStart,
Plugin,
} from '@kbn/core/public';
import { DataPublicPluginStart } from '@kbn/data-plugin/public';
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
import img from './control_group_image.png';

interface SetupDeps {
developerExamples: DeveloperExamplesSetup;
}

export interface ControlsExampleStartDeps {
data: DataPublicPluginStart;
}

export class ControlsExamplePlugin
implements Plugin<void, void, SetupDeps, ControlsExampleStartDeps>
{
public setup(core: CoreSetup<ControlsExampleStartDeps>, { developerExamples }: SetupDeps) {
core.application.register({
id: 'controlsExamples',
title: 'Controls examples',
navLinkStatus: AppNavLinkStatus.hidden,
async mount(params: AppMountParameters) {
const [, depsStart] = await core.getStartServices();
const { renderApp } = await import('./app');
return renderApp(depsStart, params);
},
});

developerExamples.register({
appId: 'controlsExamples',
title: 'Controls as a Building Block',
description: `Showcases different ways to embed a control group into your app`,
image: img,
});
}

public start(core: CoreStart) {}

public stop() {}
}
22 changes: 22 additions & 0 deletions examples/controls_example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target/types"
},
"include": [
"index.ts",
"common/**/*.ts",
"public/**/*.ts",
"public/**/*.tsx",
"server/**/*.ts",
"../../typings/**/*"
],
"exclude": [],
"kbn_references": [
{ "path": "../../src/core/tsconfig.json" },
{ "path": "../developer_examples/tsconfig.json" },
{ "path": "../../src/plugins/data/tsconfig.json" },
{ "path": "../../src/plugins/controls/tsconfig.json" },
{ "path": "../../src/plugins/presentation_util/tsconfig.json" }
]
}
Loading