forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Embeddables rebuild] Add new registry (elastic#176018)
Creates a new registry for `React Embeddables` and allows the Dashboard to render them.
- Loading branch information
1 parent
a09bd99
commit 4f2d2d4
Showing
69 changed files
with
1,871 additions
and
463 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
143 changes: 143 additions & 0 deletions
143
examples/embeddable_examples/public/react_embeddables/eui_markdown_react_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,143 @@ | ||
/* | ||
* 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 { EuiMarkdownEditor, EuiMarkdownFormat } from '@elastic/eui'; | ||
import { css } from '@emotion/react'; | ||
import { euiThemeVars } from '@kbn/ui-theme'; | ||
import { | ||
ReactEmbeddableFactory, | ||
RegisterReactEmbeddable, | ||
registerReactEmbeddableFactory, | ||
useReactEmbeddableApiHandle, | ||
initializeReactEmbeddableUuid, | ||
initializeReactEmbeddableTitles, | ||
SerializedReactEmbeddableTitles, | ||
DefaultEmbeddableApi, | ||
useReactEmbeddableUnsavedChanges, | ||
} from '@kbn/embeddable-plugin/public'; | ||
import { i18n } from '@kbn/i18n'; | ||
import { useInheritedViewMode, useStateFromPublishingSubject } from '@kbn/presentation-publishing'; | ||
import React from 'react'; | ||
import { BehaviorSubject } from 'rxjs'; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Types for this embeddable | ||
// ----------------------------------------------------------------------------- | ||
type MarkdownEditorSerializedState = SerializedReactEmbeddableTitles & { | ||
content: string; | ||
}; | ||
|
||
type MarkdownEditorApi = DefaultEmbeddableApi; | ||
|
||
const type = 'euiMarkdown'; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Define the Embeddable Factory | ||
// ----------------------------------------------------------------------------- | ||
const markdownEmbeddableFactory: ReactEmbeddableFactory< | ||
MarkdownEditorSerializedState, | ||
MarkdownEditorApi | ||
> = { | ||
// ----------------------------------------------------------------------------- | ||
// Deserialize function | ||
// ----------------------------------------------------------------------------- | ||
deserializeState: (state) => { | ||
// We could run migrations here. | ||
// We should inject references here. References are given as state.references | ||
|
||
return state.rawState as MarkdownEditorSerializedState; | ||
}, | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Register the Embeddable component | ||
// ----------------------------------------------------------------------------- | ||
getComponent: async (state, maybeId) => { | ||
/** | ||
* initialize state (source of truth) | ||
*/ | ||
const uuid = initializeReactEmbeddableUuid(maybeId); | ||
const { titlesApi, titleComparators, serializeTitles } = initializeReactEmbeddableTitles(state); | ||
const contentSubject = new BehaviorSubject(state.content); | ||
|
||
/** | ||
* getComponent is async so you can async import the component or load a saved object here. | ||
* the loading will be handed gracefully by the Presentation Container. | ||
*/ | ||
|
||
return RegisterReactEmbeddable((apiRef) => { | ||
/** | ||
* Unsaved changes logic is handled automatically by this hook. You only need to provide | ||
* a subject, setter, and optional state comparator for each key in your state type. | ||
*/ | ||
const { unsavedChanges, resetUnsavedChanges } = useReactEmbeddableUnsavedChanges( | ||
uuid, | ||
markdownEmbeddableFactory, | ||
{ | ||
content: [contentSubject, (value) => contentSubject.next(value)], | ||
...titleComparators, | ||
} | ||
); | ||
|
||
/** | ||
* Publish the API. This is what gets forwarded to the Actions framework, and to whatever the | ||
* parent of this embeddable is. | ||
*/ | ||
const thisApi = useReactEmbeddableApiHandle( | ||
{ | ||
...titlesApi, | ||
unsavedChanges, | ||
resetUnsavedChanges, | ||
serializeState: async () => { | ||
return { | ||
rawState: { | ||
...serializeTitles(), | ||
content: contentSubject.getValue(), | ||
}, | ||
}; | ||
}, | ||
}, | ||
apiRef, | ||
uuid | ||
); | ||
|
||
// get state for rendering | ||
const content = useStateFromPublishingSubject(contentSubject); | ||
const viewMode = useInheritedViewMode(thisApi) ?? 'view'; | ||
|
||
return viewMode === 'edit' ? ( | ||
<EuiMarkdownEditor | ||
css={css` | ||
width: 100%; | ||
`} | ||
value={content ?? ''} | ||
onChange={(value) => contentSubject.next(value)} | ||
aria-label={i18n.translate('dashboard.test.markdownEditor.ariaLabel', { | ||
defaultMessage: 'Dashboard markdown editor', | ||
})} | ||
height="full" | ||
/> | ||
) : ( | ||
<EuiMarkdownFormat | ||
css={css` | ||
padding: ${euiThemeVars.euiSizeS}; | ||
`} | ||
> | ||
{content ?? ''} | ||
</EuiMarkdownFormat> | ||
); | ||
}); | ||
}, | ||
}; | ||
|
||
// ----------------------------------------------------------------------------- | ||
// Register the defined Embeddable Factory - notice that this isn't defined | ||
// on the plugin. Instead, it's a simple imported function. I.E to register an | ||
// Embeddable, you only need the embeddable plugin in your requiredBundles | ||
// ----------------------------------------------------------------------------- | ||
export const registerMarkdownEditorEmbeddable = () => | ||
registerReactEmbeddableFactory(type, markdownEmbeddableFactory); |
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
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
51 changes: 51 additions & 0 deletions
51
packages/presentation/presentation_containers/interfaces/last_saved_state.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,51 @@ | ||
/* | ||
* 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 { PublishingSubject } from '@kbn/presentation-publishing'; | ||
import { BehaviorSubject, Subject } from 'rxjs'; | ||
import { filter, map } from 'rxjs/operators'; | ||
import { SerializedPanelState } from './serialized_state'; | ||
|
||
export interface PublishesLastSavedState { | ||
lastSavedState: Subject<void>; // a notification that the last saved state has changed | ||
getLastSavedStateForChild: (childId: string) => SerializedPanelState | undefined; | ||
} | ||
|
||
export const apiPublishesLastSavedState = (api: unknown): api is PublishesLastSavedState => { | ||
return Boolean( | ||
api && | ||
(api as PublishesLastSavedState).lastSavedState && | ||
(api as PublishesLastSavedState).getLastSavedStateForChild | ||
); | ||
}; | ||
|
||
export const getLastSavedStateSubjectForChild = <StateType extends unknown = unknown>( | ||
parentApi: unknown, | ||
childId: string, | ||
deserializer?: (state: SerializedPanelState) => StateType | ||
): PublishingSubject<StateType | undefined> | undefined => { | ||
if (!parentApi) return; | ||
const fetchUnsavedChanges = (): StateType | undefined => { | ||
if (!apiPublishesLastSavedState(parentApi)) return; | ||
const rawLastSavedState = parentApi.getLastSavedStateForChild(childId); | ||
if (rawLastSavedState === undefined) return; | ||
return deserializer | ||
? deserializer(rawLastSavedState) | ||
: (rawLastSavedState.rawState as StateType); | ||
}; | ||
|
||
const lastSavedStateForChild = new BehaviorSubject<StateType | undefined>(fetchUnsavedChanges()); | ||
if (!apiPublishesLastSavedState(parentApi)) return; | ||
parentApi.lastSavedState | ||
.pipe( | ||
map(() => fetchUnsavedChanges()), | ||
filter((rawLastSavedState) => rawLastSavedState !== undefined) | ||
) | ||
.subscribe(lastSavedStateForChild); | ||
return lastSavedStateForChild; | ||
}; |
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
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
19 changes: 19 additions & 0 deletions
19
packages/presentation/presentation_containers/interfaces/serialized_state.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,19 @@ | ||
/* | ||
* 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 type { SavedObjectReference } from '@kbn/core-saved-objects-api-server'; | ||
|
||
/** | ||
* A package containing the serialized Embeddable state, with references extracted. When saving Embeddables using any | ||
* strategy, this is the format that should be used. | ||
*/ | ||
export interface SerializedPanelState<RawStateType extends object = object> { | ||
references?: SavedObjectReference[]; | ||
rawState: RawStateType; | ||
version?: string; | ||
} |
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 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 { Subject } from 'rxjs'; | ||
import { PresentationContainer } from './interfaces/presentation_container'; | ||
|
||
export const getMockPresentationContainer = (): PresentationContainer => { | ||
return { | ||
registerPanelApi: jest.fn(), | ||
removePanel: jest.fn(), | ||
replacePanel: jest.fn(), | ||
lastSavedState: new Subject<void>(), | ||
getLastSavedStateForChild: jest.fn(), | ||
}; | ||
}; |
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
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
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
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
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
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
Oops, something went wrong.