Skip to content

Commit

Permalink
[Doc] UI actions explorer (#3614) (#3630)
Browse files Browse the repository at this point in the history
* adds ui actions explorer

Signed-off-by: Ashwin P Chandran <[email protected]>

* updates UI actions readme

Signed-off-by: Ashwin P Chandran <[email protected]>

* updates UI actions readme again

Signed-off-by: Ashwin P Chandran <[email protected]>

* cleans up developer examples view

Signed-off-by: Ashwin P Chandran <[email protected]>

* adds changelog

Signed-off-by: Ashwin P Chandran <[email protected]>

---------

Signed-off-by: Ashwin P Chandran <[email protected]>
(cherry picked from commit c9211b9)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>

# Conflicts:
#	CHANGELOG.md

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent 67de375 commit b4fa703
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 146 deletions.
44 changes: 26 additions & 18 deletions examples/developer_examples/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,12 @@ import {
EuiPageContent,
EuiCard,
EuiPageContentHeader,
EuiFlexGroup,
EuiFlexItem,
EuiFieldSearch,
EuiListGroup,
EuiHighlight,
EuiLink,
EuiButtonIcon,
EuiPage,
} from '@elastic/eui';
import { AppMountParameters } from '../../../src/core/public';
import { ExampleDefinition } from './types';
Expand All @@ -66,26 +65,33 @@ function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) {
});

return (
<EuiPageContent>
<EuiPageContentHeader>
<EuiText>
<h1>Developer examples</h1>
<p>
The following examples showcase services and APIs that are available to developers.
<EuiPage restrictWidth="1500px">
<EuiPageContent>
<EuiPageContentHeader>
<EuiText>
<h1>Developer examples</h1>
<p>
The following examples showcase services and APIs that are available to developers.
</p>
<EuiFieldSearch
placeholder="Search"
value={search}
onChange={(e) => setSearch(e.target.value)}
isClearable={true}
aria-label="Search developer examples"
/>
</p>
</EuiText>
</EuiPageContentHeader>
<EuiFlexGroup wrap>
{filteredExamples.map((def) => (
<EuiFlexItem style={{ minWidth: 300, maxWidth: 500 }} key={def.appId}>
</EuiText>
</EuiPageContentHeader>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: 16, // $euiSize
}}
>
{filteredExamples.map((def) => (
<EuiCard
key={def.appId}
description={
<EuiHighlight search={search} highlightAll={true}>
{def.description}
Expand Down Expand Up @@ -114,11 +120,13 @@ function DeveloperExamples({ examples, navigateToApp, getUrlForApp }: Props) {
}
image={def.image}
footer={def.links ? <EuiListGroup size={'s'} listItems={def.links} /> : undefined}
titleSize="xs"
textAlign="left"
/>
</EuiFlexItem>
))}
</EuiFlexGroup>
</EuiPageContent>
))}
</div>
</EuiPageContent>
</EuiPage>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export function ExplorerTab() {
const allTypes = new Set(Object.values(functions).map((fn) => fn.type));

// Catch all filter and remove
allTypes.delete(undefined);
allTypes.add('all');

return [...allTypes].map((type) => ({ text: type }));
Expand Down
6 changes: 3 additions & 3 deletions examples/state_containers_examples/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class StateContainersExamplesPlugin implements Plugin {

developerExamples.register({
appId: 'stateContainersExampleBrowserHistory',
title: 'State containers using browser history',
title: 'State containers: browser history',
description: `An example todo app that uses browser history and state container utilities like createStateContainerReactHelpers,
createStateContainer, createOsdUrlStateStorage, createSessionStorageStateStorage,
syncStates and getStateFromOsdUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the
Expand All @@ -101,7 +101,7 @@ export class StateContainersExamplesPlugin implements Plugin {

developerExamples.register({
appId: 'stateContainersExampleHashHistory',
title: 'State containers using hash history',
title: 'State containers: hash history',
description: `An example todo app that uses hash history and state container utilities like createStateContainerReactHelpers,
createStateContainer, createOsdUrlStateStorage, createSessionStorageStateStorage,
syncStates and getStateFromOsdUrl to keep state in sync with the URL. Change some parameters, navigate away and then back, and the
Expand All @@ -120,7 +120,7 @@ export class StateContainersExamplesPlugin implements Plugin {

developerExamples.register({
appId: PLUGIN_ID,
title: 'Sync state from a query bar with the url',
title: 'State containers: Sync with the url',
description: `Shows how to use data.syncQueryStateWitUrl in combination with state container utilities from opensearch_dashboards_utils to
show a query bar that stores state in the url and is kept in sync.
`,
Expand Down
180 changes: 81 additions & 99 deletions examples/ui_actions_explorer/public/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,114 +28,96 @@
* under the License.
*/

import React, { useState } from 'react';
import React, { useMemo } from 'react';
import ReactDOM from 'react-dom';
import { FormattedMessage, I18nProvider } from '@osd/i18n/react';

import { EuiPage } from '@elastic/eui';
import {
EuiPage,
EuiTitle,
EuiPageBody,
EuiPageContent,
EuiPageContentBody,
EuiPageHeader,
EuiTabbedContent,
} from '@elastic/eui';
import { AppMountParameters, CoreStart } from '../../../src/core/public';
import { UiActionsExplorerServices, UiActionsExplorerStartDependencies } from './types';
import { OpenSearchDashboardsContextProvider } from '../../../src/plugins/opensearch_dashboards_react/public';

import { EuiButton } from '@elastic/eui';
import { EuiPageBody } from '@elastic/eui';
import { EuiPageContent } from '@elastic/eui';
import { EuiPageContentBody } from '@elastic/eui';
import { EuiSpacer } from '@elastic/eui';
import { EuiText } from '@elastic/eui';
import { EuiFieldText } from '@elastic/eui';
import { EuiCallOut } from '@elastic/eui';
import { EuiPageHeader } from '@elastic/eui';
import { EuiModalBody } from '@elastic/eui';
import { toMountPoint } from '../../../src/plugins/opensearch_dashboards_react/public';
import { UiActionsStart, createAction } from '../../../src/plugins/ui_actions/public';
import { AppMountParameters, OverlayStart } from '../../../src/core/public';
import { HELLO_WORLD_TRIGGER_ID, ACTION_HELLO_WORLD } from '../../ui_action_examples/public';
import { TriggerContextExample } from './trigger_context_example';
import { ContextMenuExamples } from './context_menu_examples';
import { BasicTab } from './basic_tab';
import { ExplorerTab } from './explorer_tab';

interface Props {
uiActionsApi: UiActionsStart;
openModal: OverlayStart['openModal'];
}
const ActionsExplorer = () => {
const tabs = useMemo(
() => [
{
id: 'demo-basic',
name: (
<FormattedMessage
id="uiActionsExplorer.demoBasic.TabTitle"
defaultMessage="{name}"
values={{ name: 'Basic' }}
/>
),
content: <BasicTab />,
},
{
id: 'demo-explorer',
name: (
<FormattedMessage
id="uiActionsExplorer.demoExplorer.TabTitle"
defaultMessage="{name}"
values={{ name: 'Explorer' }}
/>
),
content: <ExplorerTab />,
},
],
[]
);

const ActionsExplorer = ({ uiActionsApi, openModal }: Props) => {
const [name, setName] = useState('Waldo');
const [confirmationText, setConfirmationText] = useState('');
return (
<EuiPage>
<EuiPageBody component="main">
<EuiPageHeader>Ui Actions Explorer</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
<EuiText>
<p>
By default there is a single action attached to the `HELLO_WORLD_TRIGGER`. Clicking
this button will cause it to be executed immediately.
</p>
</EuiText>
<EuiButton
data-test-subj="emitHelloWorldTrigger"
onClick={() => uiActionsApi.executeTriggerActions(HELLO_WORLD_TRIGGER_ID, {})}
>
Say hello world!
</EuiButton>

<EuiText>
<p>
Lets dynamically add new actions to this trigger. After you click this button, click
the above button again. This time it should offer you multiple options to choose
from. Using the UI Action and Trigger API makes your plugin extensible by other
plugins. Any actions attached to the `HELLO_WORLD_TRIGGER_ID` will show up here!
</p>
<EuiFieldText prepend="Name" value={name} onChange={(e) => setName(e.target.value)} />
<EuiButton
data-test-subj="addDynamicAction"
onClick={() => {
const dynamicAction = createAction<typeof ACTION_HELLO_WORLD>({
id: `${ACTION_HELLO_WORLD}-${name}`,
type: ACTION_HELLO_WORLD,
getDisplayName: () => `Say hello to ${name}`,
execute: async () => {
const overlay = openModal(
toMountPoint(
<EuiModalBody>
<EuiText data-test-subj="dynamicHelloWorldActionText">
{`Hello ${name}`}
</EuiText>{' '}
<EuiButton data-test-subj="closeModal" onClick={() => overlay.close()}>
Close
</EuiButton>
</EuiModalBody>
)
);
},
});
uiActionsApi.addTriggerAction(HELLO_WORLD_TRIGGER_ID, dynamicAction);
setConfirmationText(
`You've successfully added a new action: ${dynamicAction.getDisplayName({
trigger: uiActionsApi.getTrigger(HELLO_WORLD_TRIGGER_ID),
})}. Refresh the page to reset state. It's up to the user of the system to persist state like this.`
);
}}
>
Say hello to me!
</EuiButton>
{confirmationText !== '' ? <EuiCallOut>{confirmationText}</EuiCallOut> : undefined}
</EuiText>

<EuiSpacer />

<TriggerContextExample uiActionsApi={uiActionsApi} />

<EuiSpacer />

<ContextMenuExamples />
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
<I18nProvider>
<EuiPage restrictWidth="1500px">
<EuiPageBody component="main">
<EuiPageHeader>
<EuiTitle size="l">
<h1>
<FormattedMessage
id="uiAsctionsExample.appTitle"
defaultMessage="{name}"
values={{ name: 'UI Actions' }}
/>
</h1>
</EuiTitle>
</EuiPageHeader>
<EuiPageContent>
<EuiPageContentBody>
<EuiTabbedContent tabs={tabs} initialSelectedTab={tabs[0]} />
</EuiPageContentBody>
</EuiPageContent>
</EuiPageBody>
</EuiPage>
</I18nProvider>
);
};

export const renderApp = (props: Props, { element }: AppMountParameters) => {
ReactDOM.render(<ActionsExplorer {...props} />, element);
export const renderApp = (
coreStart: CoreStart,
{ uiActions }: UiActionsExplorerStartDependencies,
{ element }: AppMountParameters
) => {
const services: UiActionsExplorerServices = {
...coreStart,
uiActions,
};
ReactDOM.render(
<OpenSearchDashboardsContextProvider services={services}>
<ActionsExplorer />
</OpenSearchDashboardsContextProvider>,
element
);

return () => ReactDOM.unmountComponentAtNode(element);
};
Loading

0 comments on commit b4fa703

Please sign in to comment.