Skip to content

Commit

Permalink
New core plugin for dynamic content rendering(#7201)
Browse files Browse the repository at this point in the history
Signed-off-by: SuZhou-Joe <[email protected]>
  • Loading branch information
SuZhou-Joe committed Jul 17, 2024
1 parent 7cbbca2 commit 601317a
Show file tree
Hide file tree
Showing 27 changed files with 1,229 additions and 8 deletions.
9 changes: 9 additions & 0 deletions src/plugins/content_management/opensearch_dashboards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "contentManagement",
"version": "opensearchDashboards",
"server": false,
"ui": true,
"requiredPlugins": ["embeddable"],
"optionalPlugins": [],
"requiredBundles": ["embeddable"]
}
64 changes: 64 additions & 0 deletions src/plugins/content_management/public/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { Route, Router, Switch } from 'react-router-dom';
import { I18nProvider } from '@osd/i18n/react';

import {
AppMountParameters,
CoreStart,
SavedObjectsClientContract,
} from 'opensearch-dashboards/public';
import { PageRender } from './components/page_render';
import { Page } from './services';
import { ContentManagementPluginStartDependencies } from './types';
import { EmbeddableStart } from '../../embeddable/public';

interface Props {
params: AppMountParameters;
pages: Page[];
coreStart: CoreStart;
depsStart: ContentManagementPluginStartDependencies;
}

export const renderPage = ({
page,
embeddable,
savedObjectsClient,
}: {
page: Page;
embeddable: EmbeddableStart;
savedObjectsClient: SavedObjectsClientContract;
}) => {
return <PageRender page={page} embeddable={embeddable} savedObjectsClient={savedObjectsClient} />;

Check warning on line 37 in src/plugins/content_management/public/app.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/app.tsx#L37

Added line #L37 was not covered by tests
};

export const renderApp = (
{ params, pages, coreStart, depsStart }: Props,
element: AppMountParameters['element']
) => {
ReactDOM.render(

Check warning on line 44 in src/plugins/content_management/public/app.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/app.tsx#L44

Added line #L44 was not covered by tests
<I18nProvider>
<Router history={params.history}>
<Switch>
{pages.map((page) => (
<Route path={[`/${page.config.id}`]}>

Check warning on line 49 in src/plugins/content_management/public/app.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/app.tsx#L49

Added line #L49 was not covered by tests
{renderPage({
page,
embeddable: depsStart.embeddable,
savedObjectsClient: coreStart.savedObjects.client,
})}
</Route>
))}
</Switch>
</Router>
</I18nProvider>,
element
);

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

Check warning on line 63 in src/plugins/content_management/public/app.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/app.tsx#L63

Added line #L63 was not covered by tests
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { Container, ContainerInput, EmbeddableStart } from '../../../../embeddable/public';
import { CardList } from './card_list';

export const CARD_CONTAINER = 'CARD_CONTAINER';

export type CardContainerInput = ContainerInput<{ description: string; onClick?: () => void }>;

export class CardContainer extends Container<{}, ContainerInput> {
public readonly type = CARD_CONTAINER;

Check warning on line 16 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L16

Added line #L16 was not covered by tests
private node?: HTMLElement;

constructor(input: ContainerInput, private embeddableServices: EmbeddableStart) {
super(input, { embeddableLoaded: {} }, embeddableServices.getEmbeddableFactory);

Check warning on line 20 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L20

Added line #L20 was not covered by tests
}

getInheritedInput() {
return {

Check warning on line 24 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L24

Added line #L24 was not covered by tests
viewMode: this.input.viewMode,
};
}

public render(node: HTMLElement) {
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 31 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L31

Added line #L31 was not covered by tests
}
this.node = node;
ReactDOM.render(

Check warning on line 34 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L33-L34

Added lines #L33 - L34 were not covered by tests
<CardList embeddable={this} embeddableServices={this.embeddableServices} />,
node
);
}

public destroy() {
super.destroy();

Check warning on line 41 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L41

Added line #L41 was not covered by tests
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 43 in src/plugins/content_management/public/components/card_container/card_container.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container.tsx#L43

Added line #L43 was not covered by tests
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

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

import {
EmbeddableFactoryDefinition,
ContainerInput,
EmbeddableStart,
EmbeddableFactory,
ContainerOutput,
} from '../../../../embeddable/public';
import { CARD_CONTAINER, CardContainer } from './card_container';

interface StartServices {
embeddableServices: EmbeddableStart;
}

export type CardContainerFactory = EmbeddableFactory<ContainerInput, ContainerOutput>;
export class CardContainerFactoryDefinition
implements EmbeddableFactoryDefinition<ContainerInput, ContainerOutput> {
public readonly type = CARD_CONTAINER;
public readonly isContainerType = true;

Check warning on line 25 in src/plugins/content_management/public/components/card_container/card_container_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container_factory.ts#L24-L25

Added lines #L24 - L25 were not covered by tests

constructor(private getStartServices: () => Promise<StartServices>) {}

public async isEditable() {
return true;

Check warning on line 30 in src/plugins/content_management/public/components/card_container/card_container_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container_factory.ts#L30

Added line #L30 was not covered by tests
}

public create = async (initialInput: ContainerInput) => {
const { embeddableServices } = await this.getStartServices();
return new CardContainer(initialInput, embeddableServices);

Check warning on line 35 in src/plugins/content_management/public/components/card_container/card_container_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container_factory.ts#L33-L35

Added lines #L33 - L35 were not covered by tests
};

public getDisplayName() {
return i18n.translate('contentManagement.cardContainer.displayName', {

Check warning on line 39 in src/plugins/content_management/public/components/card_container/card_container_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_container_factory.ts#L39

Added line #L39 was not covered by tests
defaultMessage: 'Card container',
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { Embeddable, EmbeddableInput, IContainer } from '../../../../embeddable/public';
import { EuiCard } from '@elastic/eui';

Check failure on line 9 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

`@elastic/eui` import should occur before import of `../../../../embeddable/public`

Check failure on line 9 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

`@elastic/eui` import should occur before import of `../../../../embeddable/public`

export const CARD_EMBEDDABLE = 'card_embeddable';
export type CardEmbeddableInput = EmbeddableInput & { description: string; onClick?: () => void };

export class CardEmbeddable extends Embeddable<CardEmbeddableInput> {
public readonly type = CARD_EMBEDDABLE;
private node: HTMLElement | null = null;

Check warning on line 16 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L15-L16

Added lines #L15 - L16 were not covered by tests

constructor(initialInput: CardEmbeddableInput, parent?: IContainer) {
super(initialInput, {}, parent);

Check warning on line 19 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L19

Added line #L19 was not covered by tests
}

public render(node: HTMLElement) {
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 24 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L24

Added line #L24 was not covered by tests
}
this.node = node;
ReactDOM.render(

Check warning on line 27 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L26-L27

Added lines #L26 - L27 were not covered by tests
<EuiCard
title={this.input.title ?? ''}
description={this.input.description}
display="plain"
onClick={this.input.onClick}
/>,
node
);
}

public destroy() {
super.destroy();

Check warning on line 39 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L39

Added line #L39 was not covered by tests
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 41 in src/plugins/content_management/public/components/card_container/card_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable.tsx#L41

Added line #L41 was not covered by tests
}
}

public reload() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import { EmbeddableFactoryDefinition, IContainer } from '../../../../embeddable/public';
import { CARD_EMBEDDABLE, CardEmbeddable, CardEmbeddableInput } from './card_embeddable';

export class CardEmbeddableFactoryDefinition implements EmbeddableFactoryDefinition {
public readonly type = CARD_EMBEDDABLE;

Check warning on line 11 in src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts#L11

Added line #L11 was not covered by tests

public async isEditable() {
return false;

Check warning on line 14 in src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts#L14

Added line #L14 was not covered by tests
}

public async create(initialInput: CardEmbeddableInput, parent?: IContainer) {
return new CardEmbeddable(initialInput, parent);

Check warning on line 18 in src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts#L18

Added line #L18 was not covered by tests
}

public getDisplayName() {
return i18n.translate('contentManagement.embeddable.card', {

Check warning on line 22 in src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts#L22

Added line #L22 was not covered by tests
defaultMessage: 'Card',
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui';

import {
IContainer,
withEmbeddableSubscription,
ContainerInput,
ContainerOutput,
EmbeddableStart,
} from '../../../../embeddable/public';

interface Props {
embeddable: IContainer;
input: ContainerInput;
embeddableServices: EmbeddableStart;
}

const CardListInner = ({ embeddable, input, embeddableServices }: Props) => {
const cards = Object.values(input.panels).map((panel) => {
const child = embeddable.getChild(panel.explicitInput.id);
return (

Check warning on line 26 in src/plugins/content_management/public/components/card_container/card_list.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_list.tsx#L24-L26

Added lines #L24 - L26 were not covered by tests
<EuiFlexItem key={panel.explicitInput.id}>
<embeddableServices.EmbeddablePanel embeddable={child} />
</EuiFlexItem>
);
});
return (

Check warning on line 32 in src/plugins/content_management/public/components/card_container/card_list.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/card_container/card_list.tsx#L32

Added line #L32 was not covered by tests
<EuiFlexGrid gutterSize="s" columns={4}>
{cards}
</EuiFlexGrid>
);
};

export const CardList = withEmbeddableSubscription<
ContainerInput,
ContainerOutput,
IContainer,
{ embeddableServices: EmbeddableStart }
>(CardListInner);
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import ReactDOM from 'react-dom';

import { Embeddable, EmbeddableInput, IContainer } from '../../../embeddable/public';

export const CUSTOM_CONTENT_EMBEDDABLE = 'custom_content_embeddable';
export type CustomContentEmbeddableInput = EmbeddableInput & { render: () => React.ReactElement };

export class CustomContentEmbeddable extends Embeddable<CustomContentEmbeddableInput> {
public readonly type = CUSTOM_CONTENT_EMBEDDABLE;
private node: HTMLElement | null = null;

Check warning on line 16 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L15-L16

Added lines #L15 - L16 were not covered by tests

constructor(initialInput: CustomContentEmbeddableInput, parent?: IContainer) {
super(initialInput, {}, parent);

Check warning on line 19 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L19

Added line #L19 was not covered by tests
}

public render(node: HTMLElement) {
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 24 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L24

Added line #L24 was not covered by tests
}
this.node = node;
ReactDOM.render(this.input.render(), node);

Check warning on line 27 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L26-L27

Added lines #L26 - L27 were not covered by tests
}

public destroy() {
super.destroy();

Check warning on line 31 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L31

Added line #L31 was not covered by tests
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);

Check warning on line 33 in src/plugins/content_management/public/components/custom_content_embeddable.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable.tsx#L33

Added line #L33 was not covered by tests
}
}

public reload() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

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

import { EmbeddableFactoryDefinition, IContainer } from '../../../embeddable/public';
import {
CUSTOM_CONTENT_EMBEDDABLE,
CustomContentEmbeddable,
CustomContentEmbeddableInput,
} from './custom_content_embeddable';

export class CustomContentEmbeddableFactoryDefinition implements EmbeddableFactoryDefinition {
public readonly type = CUSTOM_CONTENT_EMBEDDABLE;

Check warning on line 16 in src/plugins/content_management/public/components/custom_content_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable_factory.ts#L16

Added line #L16 was not covered by tests

public async isEditable() {
return false;

Check warning on line 19 in src/plugins/content_management/public/components/custom_content_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable_factory.ts#L19

Added line #L19 was not covered by tests
}

public async create(initialInput: CustomContentEmbeddableInput, parent?: IContainer) {
return new CustomContentEmbeddable(initialInput, parent);

Check warning on line 23 in src/plugins/content_management/public/components/custom_content_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable_factory.ts#L23

Added line #L23 was not covered by tests
}

public getDisplayName() {
return i18n.translate('contentManagement.embeddable.customContent', {

Check warning on line 27 in src/plugins/content_management/public/components/custom_content_embeddable_factory.ts

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/custom_content_embeddable_factory.ts#L27

Added line #L27 was not covered by tests
defaultMessage: 'Content',
});
}
}
6 changes: 6 additions & 0 deletions src/plugins/content_management/public/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export * from './page_render';
35 changes: 35 additions & 0 deletions src/plugins/content_management/public/components/page_render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { useObservable } from 'react-use';

import { Page } from '../services';
import { SectionRender } from './section_render';
import { EmbeddableStart } from '../../../embeddable/public';
import { SavedObjectsClientContract } from 'opensearch-dashboards/public';

Check failure on line 12 in src/plugins/content_management/public/components/page_render.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

`opensearch-dashboards/public` import should occur before import of `../services`

Check failure on line 12 in src/plugins/content_management/public/components/page_render.tsx

View workflow job for this annotation

GitHub Actions / Build and Verify on Linux (ciGroup1)

`opensearch-dashboards/public` import should occur before import of `../services`

export interface Props {
page: Page;
embeddable: EmbeddableStart;
savedObjectsClient: SavedObjectsClientContract;
}

export const PageRender = ({ page, embeddable, savedObjectsClient }: Props) => {
const sections = useObservable(page.getSections$()) || [];

return (

Check warning on line 23 in src/plugins/content_management/public/components/page_render.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/page_render.tsx#L23

Added line #L23 was not covered by tests
<div className="contentManagement-page" style={{ margin: '10px 20px' }}>
{sections.map((section) => (
<SectionRender

Check warning on line 26 in src/plugins/content_management/public/components/page_render.tsx

View check run for this annotation

Codecov / codecov/patch

src/plugins/content_management/public/components/page_render.tsx#L26

Added line #L26 was not covered by tests
embeddable={embeddable}
section={section}
savedObjectsClient={savedObjectsClient}
contents$={page.getContents$(section.id)}
/>
))}
</div>
);
};
Loading

0 comments on commit 601317a

Please sign in to comment.