-
Notifications
You must be signed in to change notification settings - Fork 870
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New core plugin for dynamic content rendering(#7201)
Signed-off-by: SuZhou-Joe <suzhou@amazon.com>
- Loading branch information
1 parent
dd6c922
commit eaf5fea
Showing
39 changed files
with
2,025 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} />; | ||
}; | ||
|
||
export const renderApp = ( | ||
{ params, pages, coreStart, depsStart }: Props, | ||
element: AppMountParameters['element'] | ||
) => { | ||
ReactDOM.render( | ||
<I18nProvider> | ||
<Router history={params.history}> | ||
<Switch> | ||
{pages.map((page) => ( | ||
<Route path={[`/${page.config.id}`]}> | ||
{renderPage({ | ||
page, | ||
embeddable: depsStart.embeddable, | ||
savedObjectsClient: coreStart.savedObjects.client, | ||
})} | ||
</Route> | ||
))} | ||
</Switch> | ||
</Router> | ||
</I18nProvider>, | ||
element | ||
); | ||
|
||
return () => ReactDOM.unmountComponentAtNode(element); | ||
}; |
27 changes: 27 additions & 0 deletions
27
src/plugins/content_management/public/components/card_container/card_container.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; | ||
import { CardContainer } from './card_container'; | ||
|
||
jest.mock('./card_list', () => { | ||
return { | ||
CardList: jest.fn().mockReturnValue(<span id="mockCardList"></span>), | ||
}; | ||
}); | ||
|
||
test('CardContainer should render CardList', () => { | ||
const container = new CardContainer( | ||
{ id: 'container-id', panels: {} }, | ||
embeddablePluginMock.createStartContract() | ||
); | ||
const node = document.createElement('div'); | ||
container.render(node); | ||
expect(node.querySelector('#mockCardList')).toBeTruthy(); | ||
|
||
container.destroy(); | ||
expect(node.querySelector('#mockCardList')).toBeFalsy(); | ||
}); |
46 changes: 46 additions & 0 deletions
46
src/plugins/content_management/public/components/card_container/card_container.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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<{}, CardContainerInput> { | ||
public readonly type = CARD_CONTAINER; | ||
private node?: HTMLElement; | ||
|
||
constructor(input: CardContainerInput, private embeddableServices: EmbeddableStart) { | ||
super(input, { embeddableLoaded: {} }, embeddableServices.getEmbeddableFactory); | ||
} | ||
|
||
getInheritedInput() { | ||
return { | ||
viewMode: this.input.viewMode, | ||
}; | ||
} | ||
|
||
public render(node: HTMLElement) { | ||
if (this.node) { | ||
ReactDOM.unmountComponentAtNode(this.node); | ||
} | ||
this.node = node; | ||
ReactDOM.render( | ||
<CardList embeddable={this} embeddableServices={this.embeddableServices} />, | ||
node | ||
); | ||
} | ||
|
||
public destroy() { | ||
super.destroy(); | ||
if (this.node) { | ||
ReactDOM.unmountComponentAtNode(this.node); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...lugins/content_management/public/components/card_container/card_container_factory.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; | ||
import { CARD_CONTAINER, CardContainer } from './card_container'; | ||
import { CardContainerFactoryDefinition } from './card_container_factory'; | ||
|
||
test('CardContainerFactoryDefinition', async () => { | ||
const getStartServices = jest | ||
.fn() | ||
.mockResolvedValue({ embeddableServices: embeddablePluginMock.createStartContract() }); | ||
const factory = new CardContainerFactoryDefinition(getStartServices); | ||
expect(factory.type).toBe(CARD_CONTAINER); | ||
expect(factory.isContainerType).toBe(true); | ||
expect(await factory.isEditable()).toBe(false); | ||
expect(factory.getDisplayName()).toBe('Card container'); | ||
expect(await factory.create({ id: 'card-id', panels: {} })).toBeInstanceOf(CardContainer); | ||
}); |
42 changes: 42 additions & 0 deletions
42
src/plugins/content_management/public/components/card_container/card_container_factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { i18n } from '@osd/i18n'; | ||
|
||
import { | ||
EmbeddableFactoryDefinition, | ||
EmbeddableStart, | ||
EmbeddableFactory, | ||
ContainerOutput, | ||
} from '../../../../embeddable/public'; | ||
import { CARD_CONTAINER, CardContainer, CardContainerInput } from './card_container'; | ||
|
||
interface StartServices { | ||
embeddableServices: EmbeddableStart; | ||
} | ||
|
||
export type CardContainerFactory = EmbeddableFactory<CardContainerInput, ContainerOutput>; | ||
export class CardContainerFactoryDefinition | ||
implements EmbeddableFactoryDefinition<CardContainerInput, ContainerOutput> { | ||
public readonly type = CARD_CONTAINER; | ||
public readonly isContainerType = true; | ||
|
||
constructor(private getStartServices: () => Promise<StartServices>) {} | ||
|
||
public async isEditable() { | ||
return false; | ||
} | ||
|
||
public create = async (initialInput: CardContainerInput) => { | ||
const { embeddableServices } = await this.getStartServices(); | ||
return new CardContainer(initialInput, embeddableServices); | ||
}; | ||
|
||
public getDisplayName() { | ||
return i18n.translate('contentManagement.cardContainer.displayName', { | ||
defaultMessage: 'Card container', | ||
}); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/plugins/content_management/public/components/card_container/card_embeddable.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { CardEmbeddable } from './card_embeddable'; | ||
|
||
test('CardEmbeddable should render a card with the title', () => { | ||
const embeddable = new CardEmbeddable({ id: 'card-id', title: 'card title', description: '' }); | ||
|
||
const node = document.createElement('div'); | ||
embeddable.render(node); | ||
|
||
// it should render the card with title specified | ||
expect( | ||
Array.from(node.querySelectorAll('*')).find((ele) => ele.textContent?.trim() === 'card title') | ||
).toBeTruthy(); | ||
|
||
embeddable.destroy(); | ||
expect( | ||
Array.from(node.querySelectorAll('*')).find((ele) => ele.textContent?.trim() === 'card title') | ||
).toBeFalsy(); | ||
}); |
46 changes: 46 additions & 0 deletions
46
src/plugins/content_management/public/components/card_container/card_embeddable.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'; | ||
|
||
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; | ||
|
||
constructor(initialInput: CardEmbeddableInput, parent?: IContainer) { | ||
super(initialInput, {}, parent); | ||
} | ||
|
||
public render(node: HTMLElement) { | ||
if (this.node) { | ||
ReactDOM.unmountComponentAtNode(this.node); | ||
} | ||
this.node = node; | ||
ReactDOM.render( | ||
<EuiCard | ||
title={this.input.title ?? ''} | ||
description={this.input.description} | ||
display="plain" | ||
onClick={this.input.onClick} | ||
/>, | ||
node | ||
); | ||
} | ||
|
||
public destroy() { | ||
super.destroy(); | ||
if (this.node) { | ||
ReactDOM.unmountComponentAtNode(this.node); | ||
} | ||
} | ||
|
||
public reload() {} | ||
} |
17 changes: 17 additions & 0 deletions
17
...ugins/content_management/public/components/card_container/card_embeddable_factory.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { CARD_EMBEDDABLE, CardEmbeddable } from './card_embeddable'; | ||
import { CardEmbeddableFactoryDefinition } from './card_embeddable_factory'; | ||
|
||
test('create CardEmbeddableFactoryDefinition', async () => { | ||
const factory = new CardEmbeddableFactoryDefinition(); | ||
expect(factory.type).toBe(CARD_EMBEDDABLE); | ||
expect(factory.getDisplayName()).toBe('Card'); | ||
expect(await factory.isEditable()).toBe(false); | ||
expect(await factory.create({ id: 'card-id', title: 'title', description: '' })).toBeInstanceOf( | ||
CardEmbeddable | ||
); | ||
}); |
26 changes: 26 additions & 0 deletions
26
src/plugins/content_management/public/components/card_container/card_embeddable_factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
public async isEditable() { | ||
return false; | ||
} | ||
|
||
public async create(initialInput: CardEmbeddableInput, parent?: IContainer) { | ||
return new CardEmbeddable(initialInput, parent); | ||
} | ||
|
||
public getDisplayName() { | ||
return i18n.translate('contentManagement.embeddable.card', { | ||
defaultMessage: 'Card', | ||
}); | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
src/plugins/content_management/public/components/card_container/card_list.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
|
||
import { CardList } from './card_list'; | ||
import { CardContainer } from './card_container'; | ||
import { embeddablePluginMock } from '../../../../embeddable/public/mocks'; | ||
import { CARD_EMBEDDABLE } from './card_embeddable'; | ||
|
||
beforeEach(() => { | ||
jest.restoreAllMocks(); | ||
}); | ||
|
||
test('render list of cards', () => { | ||
const embeddableStart = embeddablePluginMock.createStartContract(); | ||
jest | ||
.spyOn(embeddableStart, 'EmbeddablePanel') | ||
.mockImplementation(() => <span>CardEmbeddablePanel</span>); | ||
render( | ||
<CardList | ||
embeddableServices={embeddableStart} | ||
embeddable={ | ||
new CardContainer( | ||
{ | ||
id: 'card', | ||
panels: { | ||
'card-id-1': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-1' } }, | ||
'card-id-2': { type: CARD_EMBEDDABLE, explicitInput: { id: 'card-id-2' } }, | ||
}, | ||
}, | ||
embeddableStart | ||
) | ||
} | ||
/> | ||
); | ||
expect(screen.queryAllByText('CardEmbeddablePanel')).toHaveLength(2); | ||
}); |
Oops, something went wrong.