mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Example] Embeddable by Reference and Value (#68719)
Added an attribute service to embeddable start contract which provides a higher level abstraction for embeddables that can be by reference OR by value. Added an example that uses this service.
This commit is contained in:
parent
b25b690530
commit
33fd5cf457
21 changed files with 781 additions and 8 deletions
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectAttributes } from '../../../src/core/types';
|
||||
|
||||
export const BOOK_SAVED_OBJECT = 'book';
|
||||
|
||||
export interface BookSavedObjectAttributes extends SavedObjectAttributes {
|
||||
title: string;
|
||||
author?: string;
|
||||
readIt?: boolean;
|
||||
}
|
|
@ -18,3 +18,4 @@
|
|||
*/
|
||||
|
||||
export { TodoSavedObjectAttributes } from './todo_saved_object_attributes';
|
||||
export { BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from './book_saved_object_attributes';
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"kibanaVersion": "kibana",
|
||||
"server": true,
|
||||
"ui": true,
|
||||
"requiredPlugins": ["embeddable"],
|
||||
"requiredPlugins": ["embeddable", "uiActions"],
|
||||
"optionalPlugins": [],
|
||||
"extraPublicDirs": ["public/todo", "public/hello_world", "public/todo/todo_ref_embeddable"]
|
||||
}
|
||||
|
|
90
examples/embeddable_examples/public/book/book_component.tsx
Normal file
90
examples/embeddable_examples/public/book/book_component.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { EuiFlexItem, EuiFlexGroup, EuiIcon } from '@elastic/eui';
|
||||
|
||||
import { EuiText } from '@elastic/eui';
|
||||
import { EuiFlexGrid } from '@elastic/eui';
|
||||
import { withEmbeddableSubscription } from '../../../../src/plugins/embeddable/public';
|
||||
import { BookEmbeddableInput, BookEmbeddableOutput, BookEmbeddable } from './book_embeddable';
|
||||
|
||||
interface Props {
|
||||
input: BookEmbeddableInput;
|
||||
output: BookEmbeddableOutput;
|
||||
embeddable: BookEmbeddable;
|
||||
}
|
||||
|
||||
function wrapSearchTerms(task?: string, search?: string) {
|
||||
if (!search || !task) return task;
|
||||
const parts = task.split(new RegExp(`(${search})`, 'g'));
|
||||
return parts.map((part, i) =>
|
||||
part === search ? (
|
||||
<span key={i} style={{ backgroundColor: 'yellow' }}>
|
||||
{part}
|
||||
</span>
|
||||
) : (
|
||||
part
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function BookEmbeddableComponentInner({ input: { search }, output: { attributes } }: Props) {
|
||||
const title = attributes?.title;
|
||||
const author = attributes?.author;
|
||||
const readIt = attributes?.readIt;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<EuiFlexGrid columns={1} gutterSize="none">
|
||||
{title ? (
|
||||
<EuiFlexItem>
|
||||
<EuiText data-test-subj="bookEmbeddableTitle">
|
||||
<h3>{wrapSearchTerms(title, search)},</h3>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{author ? (
|
||||
<EuiFlexItem>
|
||||
<EuiText data-test-subj="bookEmbeddableAuthor">
|
||||
<h5>-{wrapSearchTerms(author, search)}</h5>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
{readIt ? (
|
||||
<EuiFlexItem>
|
||||
<EuiIcon type="check" />
|
||||
</EuiFlexItem>
|
||||
) : (
|
||||
<EuiFlexItem>
|
||||
<EuiIcon type="cross" />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGrid>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
export const BookEmbeddableComponent = withEmbeddableSubscription<
|
||||
BookEmbeddableInput,
|
||||
BookEmbeddableOutput,
|
||||
BookEmbeddable,
|
||||
{}
|
||||
>(BookEmbeddableComponentInner);
|
123
examples/embeddable_examples/public/book/book_embeddable.tsx
Normal file
123
examples/embeddable_examples/public/book/book_embeddable.tsx
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Subscription } from 'rxjs';
|
||||
import {
|
||||
Embeddable,
|
||||
EmbeddableInput,
|
||||
IContainer,
|
||||
EmbeddableOutput,
|
||||
SavedObjectEmbeddableInput,
|
||||
AttributeService,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
import { BookSavedObjectAttributes } from '../../common';
|
||||
import { BookEmbeddableComponent } from './book_component';
|
||||
|
||||
export const BOOK_EMBEDDABLE = 'book';
|
||||
export type BookEmbeddableInput = BookByValueInput | BookByReferenceInput;
|
||||
export interface BookEmbeddableOutput extends EmbeddableOutput {
|
||||
hasMatch: boolean;
|
||||
attributes: BookSavedObjectAttributes;
|
||||
}
|
||||
|
||||
interface BookInheritedInput extends EmbeddableInput {
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export type BookByValueInput = { attributes: BookSavedObjectAttributes } & BookInheritedInput;
|
||||
export type BookByReferenceInput = SavedObjectEmbeddableInput & BookInheritedInput;
|
||||
|
||||
/**
|
||||
* Returns whether any attributes contain the search string. If search is empty, true is returned. If
|
||||
* there are no savedAttributes, false is returned.
|
||||
* @param search - the search string
|
||||
* @param savedAttributes - the saved object attributes for the saved object with id `input.savedObjectId`
|
||||
*/
|
||||
function getHasMatch(search?: string, savedAttributes?: BookSavedObjectAttributes): boolean {
|
||||
if (!search) return true;
|
||||
if (!savedAttributes) return false;
|
||||
return Boolean(
|
||||
(savedAttributes.author && savedAttributes.author.match(search)) ||
|
||||
(savedAttributes.title && savedAttributes.title.match(search))
|
||||
);
|
||||
}
|
||||
|
||||
export class BookEmbeddable extends Embeddable<BookEmbeddableInput, BookEmbeddableOutput> {
|
||||
public readonly type = BOOK_EMBEDDABLE;
|
||||
private subscription: Subscription;
|
||||
private node?: HTMLElement;
|
||||
private savedObjectId?: string;
|
||||
private attributes?: BookSavedObjectAttributes;
|
||||
|
||||
constructor(
|
||||
initialInput: BookEmbeddableInput,
|
||||
private attributeService: AttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>,
|
||||
{
|
||||
parent,
|
||||
}: {
|
||||
parent?: IContainer;
|
||||
}
|
||||
) {
|
||||
super(initialInput, {} as BookEmbeddableOutput, parent);
|
||||
|
||||
this.subscription = this.getInput$().subscribe(async () => {
|
||||
const savedObjectId = (this.getInput() as BookByReferenceInput).savedObjectId;
|
||||
const attributes = (this.getInput() as BookByValueInput).attributes;
|
||||
if (this.attributes !== attributes || this.savedObjectId !== savedObjectId) {
|
||||
this.savedObjectId = savedObjectId;
|
||||
this.reload();
|
||||
} else {
|
||||
this.updateOutput({
|
||||
attributes: this.attributes,
|
||||
hasMatch: getHasMatch(this.input.search, this.attributes),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public render(node: HTMLElement) {
|
||||
if (this.node) {
|
||||
ReactDOM.unmountComponentAtNode(this.node);
|
||||
}
|
||||
this.node = node;
|
||||
ReactDOM.render(<BookEmbeddableComponent embeddable={this} />, node);
|
||||
}
|
||||
|
||||
public async reload() {
|
||||
this.attributes = await this.attributeService.unwrapAttributes(this.input);
|
||||
|
||||
this.updateOutput({
|
||||
attributes: this.attributes,
|
||||
hasMatch: getHasMatch(this.input.search, this.attributes),
|
||||
});
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
super.destroy();
|
||||
this.subscription.unsubscribe();
|
||||
if (this.node) {
|
||||
ReactDOM.unmountComponentAtNode(this.node);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from '../../common';
|
||||
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
EmbeddableFactoryDefinition,
|
||||
EmbeddableStart,
|
||||
IContainer,
|
||||
AttributeService,
|
||||
EmbeddableFactory,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
BookEmbeddable,
|
||||
BOOK_EMBEDDABLE,
|
||||
BookEmbeddableInput,
|
||||
BookEmbeddableOutput,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput,
|
||||
} from './book_embeddable';
|
||||
import { CreateEditBookComponent } from './create_edit_book_component';
|
||||
import { OverlayStart } from '../../../../src/core/public';
|
||||
|
||||
interface StartServices {
|
||||
getAttributeService: EmbeddableStart['getAttributeService'];
|
||||
openModal: OverlayStart['openModal'];
|
||||
}
|
||||
|
||||
export type BookEmbeddableFactory = EmbeddableFactory<
|
||||
BookEmbeddableInput,
|
||||
BookEmbeddableOutput,
|
||||
BookEmbeddable,
|
||||
BookSavedObjectAttributes
|
||||
>;
|
||||
|
||||
export class BookEmbeddableFactoryDefinition
|
||||
implements
|
||||
EmbeddableFactoryDefinition<
|
||||
BookEmbeddableInput,
|
||||
BookEmbeddableOutput,
|
||||
BookEmbeddable,
|
||||
BookSavedObjectAttributes
|
||||
> {
|
||||
public readonly type = BOOK_EMBEDDABLE;
|
||||
public savedObjectMetaData = {
|
||||
name: 'Book',
|
||||
includeFields: ['title', 'author', 'readIt'],
|
||||
type: BOOK_SAVED_OBJECT,
|
||||
getIconForSavedObject: () => 'pencil',
|
||||
};
|
||||
|
||||
private attributeService?: AttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>;
|
||||
|
||||
constructor(private getStartServices: () => Promise<StartServices>) {}
|
||||
|
||||
public async isEditable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async create(input: BookEmbeddableInput, parent?: IContainer) {
|
||||
return new BookEmbeddable(input, await this.getAttributeService(), {
|
||||
parent,
|
||||
});
|
||||
}
|
||||
|
||||
public getDisplayName() {
|
||||
return i18n.translate('embeddableExamples.book.displayName', {
|
||||
defaultMessage: 'Book',
|
||||
});
|
||||
}
|
||||
|
||||
public async getExplicitInput(): Promise<Omit<BookEmbeddableInput, 'id'>> {
|
||||
const { openModal } = await this.getStartServices();
|
||||
return new Promise<Omit<BookEmbeddableInput, 'id'>>((resolve) => {
|
||||
const onSave = async (attributes: BookSavedObjectAttributes, useRefType: boolean) => {
|
||||
const wrappedAttributes = (await this.getAttributeService()).wrapAttributes(
|
||||
attributes,
|
||||
useRefType
|
||||
);
|
||||
resolve(wrappedAttributes);
|
||||
};
|
||||
const overlay = openModal(
|
||||
toMountPoint(
|
||||
<CreateEditBookComponent
|
||||
onSave={(attributes: BookSavedObjectAttributes, useRefType: boolean) => {
|
||||
onSave(attributes, useRefType);
|
||||
overlay.close();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private async getAttributeService() {
|
||||
if (!this.attributeService) {
|
||||
this.attributeService = await (await this.getStartServices()).getAttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>(this.type);
|
||||
}
|
||||
return this.attributeService;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useState } from 'react';
|
||||
import { EuiModalBody, EuiCheckbox } from '@elastic/eui';
|
||||
import { EuiFieldText } from '@elastic/eui';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { EuiModalFooter } from '@elastic/eui';
|
||||
import { EuiModalHeader } from '@elastic/eui';
|
||||
import { EuiFormRow } from '@elastic/eui';
|
||||
import { BookSavedObjectAttributes } from '../../common';
|
||||
|
||||
export function CreateEditBookComponent({
|
||||
savedObjectId,
|
||||
attributes,
|
||||
onSave,
|
||||
}: {
|
||||
savedObjectId?: string;
|
||||
attributes?: BookSavedObjectAttributes;
|
||||
onSave: (attributes: BookSavedObjectAttributes, useRefType: boolean) => void;
|
||||
}) {
|
||||
const [title, setTitle] = useState(attributes?.title ?? '');
|
||||
const [author, setAuthor] = useState(attributes?.author ?? '');
|
||||
const [readIt, setReadIt] = useState(attributes?.readIt ?? false);
|
||||
return (
|
||||
<EuiModalBody>
|
||||
<EuiModalHeader>
|
||||
<h1>{`${savedObjectId ? 'Create new ' : 'Edit '}`}</h1>
|
||||
</EuiModalHeader>
|
||||
<EuiModalBody>
|
||||
<EuiFormRow label="Title">
|
||||
<EuiFieldText
|
||||
data-test-subj="titleInputField"
|
||||
value={title}
|
||||
placeholder="Title"
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label="Author">
|
||||
<EuiFieldText
|
||||
data-test-subj="authorInputField"
|
||||
value={author}
|
||||
placeholder="Author"
|
||||
onChange={(e) => setAuthor(e.target.value)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFormRow label="Read It">
|
||||
<EuiCheckbox
|
||||
id="ReadIt"
|
||||
checked={readIt}
|
||||
onChange={(event) => setReadIt(event.target.checked)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiModalBody>
|
||||
<EuiModalFooter>
|
||||
<EuiButton
|
||||
data-test-subj="saveBookEmbeddableByValue"
|
||||
disabled={title === ''}
|
||||
onClick={() => onSave({ title, author, readIt }, false)}
|
||||
>
|
||||
{savedObjectId ? 'Unlink from library item' : 'Save and Return'}
|
||||
</EuiButton>
|
||||
<EuiButton
|
||||
data-test-subj="saveBookEmbeddableByRef"
|
||||
disabled={title === ''}
|
||||
onClick={() => onSave({ title, author, readIt }, true)}
|
||||
>
|
||||
{savedObjectId ? 'Update library item' : 'Save to library'}
|
||||
</EuiButton>
|
||||
</EuiModalFooter>
|
||||
</EuiModalBody>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { OverlayStart } from 'kibana/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from '../../common';
|
||||
import { createAction } from '../../../../src/plugins/ui_actions/public';
|
||||
import { toMountPoint } from '../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
ViewMode,
|
||||
EmbeddableStart,
|
||||
SavedObjectEmbeddableInput,
|
||||
} from '../../../../src/plugins/embeddable/public';
|
||||
import {
|
||||
BookEmbeddable,
|
||||
BOOK_EMBEDDABLE,
|
||||
BookByReferenceInput,
|
||||
BookByValueInput,
|
||||
} from './book_embeddable';
|
||||
import { CreateEditBookComponent } from './create_edit_book_component';
|
||||
|
||||
interface StartServices {
|
||||
openModal: OverlayStart['openModal'];
|
||||
getAttributeService: EmbeddableStart['getAttributeService'];
|
||||
}
|
||||
|
||||
interface ActionContext {
|
||||
embeddable: BookEmbeddable;
|
||||
}
|
||||
|
||||
export const ACTION_EDIT_BOOK = 'ACTION_EDIT_BOOK';
|
||||
|
||||
export const createEditBookAction = (getStartServices: () => Promise<StartServices>) =>
|
||||
createAction({
|
||||
getDisplayName: () =>
|
||||
i18n.translate('embeddableExamples.book.edit', { defaultMessage: 'Edit Book' }),
|
||||
type: ACTION_EDIT_BOOK,
|
||||
order: 100,
|
||||
getIconType: () => 'documents',
|
||||
isCompatible: async ({ embeddable }: ActionContext) => {
|
||||
return (
|
||||
embeddable.type === BOOK_EMBEDDABLE && embeddable.getInput().viewMode === ViewMode.EDIT
|
||||
);
|
||||
},
|
||||
execute: async ({ embeddable }: ActionContext) => {
|
||||
const { openModal, getAttributeService } = await getStartServices();
|
||||
const attributeService = getAttributeService<
|
||||
BookSavedObjectAttributes,
|
||||
BookByValueInput,
|
||||
BookByReferenceInput
|
||||
>(BOOK_SAVED_OBJECT);
|
||||
const onSave = async (attributes: BookSavedObjectAttributes, useRefType: boolean) => {
|
||||
const newInput = await attributeService.wrapAttributes(attributes, useRefType, embeddable);
|
||||
if (!useRefType && (embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId) {
|
||||
// Remove the savedObejctId when un-linking
|
||||
newInput.savedObjectId = null;
|
||||
}
|
||||
embeddable.updateInput(newInput);
|
||||
if (useRefType) {
|
||||
// Ensures that any duplicate embeddables also register the changes. This mirrors the behavior of going back and forth between apps
|
||||
embeddable.getRoot().reload();
|
||||
}
|
||||
};
|
||||
const overlay = openModal(
|
||||
toMountPoint(
|
||||
<CreateEditBookComponent
|
||||
savedObjectId={(embeddable.getInput() as BookByReferenceInput).savedObjectId}
|
||||
attributes={embeddable.getOutput().attributes}
|
||||
onSave={(attributes: BookSavedObjectAttributes, useRefType: boolean) => {
|
||||
overlay.close();
|
||||
onSave(attributes, useRefType);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
21
examples/embeddable_examples/public/book/index.ts
Normal file
21
examples/embeddable_examples/public/book/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
export * from './book_embeddable';
|
||||
export * from './book_embeddable_factory';
|
|
@ -18,9 +18,9 @@
|
|||
*/
|
||||
|
||||
import { SavedObjectsClientContract } from 'kibana/public';
|
||||
import { TodoSavedObjectAttributes } from '../common';
|
||||
import { TodoSavedObjectAttributes, BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from '../common';
|
||||
|
||||
export async function createSampleData(client: SavedObjectsClientContract) {
|
||||
export async function createSampleData(client: SavedObjectsClientContract, overwrite = true) {
|
||||
await client.create<TodoSavedObjectAttributes>(
|
||||
'todo',
|
||||
{
|
||||
|
@ -30,7 +30,20 @@ export async function createSampleData(client: SavedObjectsClientContract) {
|
|||
},
|
||||
{
|
||||
id: 'sample-todo-saved-object',
|
||||
overwrite: true,
|
||||
overwrite,
|
||||
}
|
||||
);
|
||||
|
||||
await client.create<BookSavedObjectAttributes>(
|
||||
BOOK_SAVED_OBJECT,
|
||||
{
|
||||
title: 'Pillars of the Earth',
|
||||
author: 'Ken Follett',
|
||||
readIt: true,
|
||||
},
|
||||
{
|
||||
id: 'sample-book-saved-object',
|
||||
overwrite,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ export {
|
|||
export { ListContainer, LIST_CONTAINER, ListContainerFactory } from './list_container';
|
||||
export { TODO_EMBEDDABLE, TodoEmbeddableFactory } from './todo';
|
||||
|
||||
export { BOOK_EMBEDDABLE } from './book';
|
||||
|
||||
import { EmbeddableExamplesPlugin } from './plugin';
|
||||
|
||||
export {
|
||||
|
|
|
@ -17,14 +17,19 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
import { EmbeddableSetup, EmbeddableStart } from '../../../src/plugins/embeddable/public';
|
||||
import { CoreSetup, CoreStart, Plugin } from '../../../src/core/public';
|
||||
import {
|
||||
EmbeddableSetup,
|
||||
EmbeddableStart,
|
||||
CONTEXT_MENU_TRIGGER,
|
||||
} from '../../../src/plugins/embeddable/public';
|
||||
import { Plugin, CoreSetup, CoreStart } from '../../../src/core/public';
|
||||
import {
|
||||
HelloWorldEmbeddableFactory,
|
||||
HELLO_WORLD_EMBEDDABLE,
|
||||
HelloWorldEmbeddableFactoryDefinition,
|
||||
HelloWorldEmbeddableFactory,
|
||||
} from './hello_world';
|
||||
import { TODO_EMBEDDABLE, TodoEmbeddableFactory, TodoEmbeddableFactoryDefinition } from './todo';
|
||||
|
||||
import {
|
||||
MULTI_TASK_TODO_EMBEDDABLE,
|
||||
MultiTaskTodoEmbeddableFactory,
|
||||
|
@ -46,9 +51,17 @@ import {
|
|||
TodoRefEmbeddableFactory,
|
||||
TodoRefEmbeddableFactoryDefinition,
|
||||
} from './todo/todo_ref_embeddable_factory';
|
||||
import { ACTION_EDIT_BOOK, createEditBookAction } from './book/edit_book_action';
|
||||
import { BookEmbeddable, BOOK_EMBEDDABLE } from './book/book_embeddable';
|
||||
import {
|
||||
BookEmbeddableFactory,
|
||||
BookEmbeddableFactoryDefinition,
|
||||
} from './book/book_embeddable_factory';
|
||||
import { UiActionsStart } from '../../../src/plugins/ui_actions/public';
|
||||
|
||||
export interface EmbeddableExamplesSetupDependencies {
|
||||
embeddable: EmbeddableSetup;
|
||||
uiActions: UiActionsStart;
|
||||
}
|
||||
|
||||
export interface EmbeddableExamplesStartDependencies {
|
||||
|
@ -62,6 +75,7 @@ interface ExampleEmbeddableFactories {
|
|||
getListContainerEmbeddableFactory: () => ListContainerFactory;
|
||||
getTodoEmbeddableFactory: () => TodoEmbeddableFactory;
|
||||
getTodoRefEmbeddableFactory: () => TodoRefEmbeddableFactory;
|
||||
getBookEmbeddableFactory: () => BookEmbeddableFactory;
|
||||
}
|
||||
|
||||
export interface EmbeddableExamplesStart {
|
||||
|
@ -69,6 +83,12 @@ export interface EmbeddableExamplesStart {
|
|||
factories: ExampleEmbeddableFactories;
|
||||
}
|
||||
|
||||
declare module '../../../src/plugins/ui_actions/public' {
|
||||
export interface ActionContextMapping {
|
||||
[ACTION_EDIT_BOOK]: { embeddable: BookEmbeddable };
|
||||
}
|
||||
}
|
||||
|
||||
export class EmbeddableExamplesPlugin
|
||||
implements
|
||||
Plugin<
|
||||
|
@ -121,6 +141,20 @@ export class EmbeddableExamplesPlugin
|
|||
getEmbeddableFactory: (await core.getStartServices())[1].embeddable.getEmbeddableFactory,
|
||||
}))
|
||||
);
|
||||
this.exampleEmbeddableFactories.getBookEmbeddableFactory = deps.embeddable.registerEmbeddableFactory(
|
||||
BOOK_EMBEDDABLE,
|
||||
new BookEmbeddableFactoryDefinition(async () => ({
|
||||
getAttributeService: (await core.getStartServices())[1].embeddable.getAttributeService,
|
||||
openModal: (await core.getStartServices())[0].overlays.openModal,
|
||||
}))
|
||||
);
|
||||
|
||||
const editBookAction = createEditBookAction(async () => ({
|
||||
getAttributeService: (await core.getStartServices())[1].embeddable.getAttributeService,
|
||||
openModal: (await core.getStartServices())[0].overlays.openModal,
|
||||
}));
|
||||
deps.uiActions.registerAction(editBookAction);
|
||||
deps.uiActions.attachAction(CONTEXT_MENU_TRIGGER, editBookAction.id);
|
||||
}
|
||||
|
||||
public start(
|
||||
|
|
40
examples/embeddable_examples/server/book_saved_object.ts
Normal file
40
examples/embeddable_examples/server/book_saved_object.ts
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsType } from 'kibana/server';
|
||||
|
||||
export const bookSavedObject: SavedObjectsType = {
|
||||
name: 'book',
|
||||
hidden: false,
|
||||
namespaceType: 'agnostic',
|
||||
mappings: {
|
||||
properties: {
|
||||
title: {
|
||||
type: 'keyword',
|
||||
},
|
||||
author: {
|
||||
type: 'keyword',
|
||||
},
|
||||
readIt: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
migrations: {},
|
||||
};
|
|
@ -19,10 +19,12 @@
|
|||
|
||||
import { Plugin, CoreSetup, CoreStart } from 'kibana/server';
|
||||
import { todoSavedObject } from './todo_saved_object';
|
||||
import { bookSavedObject } from './book_saved_object';
|
||||
|
||||
export class EmbeddableExamplesPlugin implements Plugin {
|
||||
public setup(core: CoreSetup) {
|
||||
core.savedObjects.registerType(todoSavedObject);
|
||||
core.savedObjects.registerType(bookSavedObject);
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {}
|
||||
|
|
|
@ -33,6 +33,7 @@ import { EmbeddableStart, IEmbeddable } from '../../../src/plugins/embeddable/pu
|
|||
import {
|
||||
HELLO_WORLD_EMBEDDABLE,
|
||||
TODO_EMBEDDABLE,
|
||||
BOOK_EMBEDDABLE,
|
||||
MULTI_TASK_TODO_EMBEDDABLE,
|
||||
SearchableListContainerFactory,
|
||||
} from '../../embeddable_examples/public';
|
||||
|
@ -72,6 +73,35 @@ export function EmbeddablePanelExample({ embeddableServices, searchListContainer
|
|||
tasks: ['Go to school', 'Watch planet earth', 'Read the encyclopedia'],
|
||||
},
|
||||
},
|
||||
'4': {
|
||||
type: BOOK_EMBEDDABLE,
|
||||
explicitInput: {
|
||||
id: '4',
|
||||
savedObjectId: 'sample-book-saved-object',
|
||||
},
|
||||
},
|
||||
'5': {
|
||||
type: BOOK_EMBEDDABLE,
|
||||
explicitInput: {
|
||||
id: '5',
|
||||
attributes: {
|
||||
title: 'The Sympathizer',
|
||||
author: 'Viet Thanh Nguyen',
|
||||
readIt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
'6': {
|
||||
type: BOOK_EMBEDDABLE,
|
||||
explicitInput: {
|
||||
id: '6',
|
||||
attributes: {
|
||||
title: 'The Hobbit',
|
||||
author: 'J.R.R. Tolkien',
|
||||
readIt: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ export {
|
|||
ACTION_EDIT_PANEL,
|
||||
Adapters,
|
||||
AddPanelAction,
|
||||
AttributeService,
|
||||
ChartActionContext,
|
||||
Container,
|
||||
ContainerInput,
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { SavedObjectsClientContract } from '../../../../../core/public';
|
||||
import {
|
||||
SavedObjectEmbeddableInput,
|
||||
isSavedObjectEmbeddableInput,
|
||||
EmbeddableInput,
|
||||
IEmbeddable,
|
||||
} from '.';
|
||||
import { SimpleSavedObject } from '../../../../../core/public';
|
||||
|
||||
export class AttributeService<
|
||||
SavedObjectAttributes,
|
||||
ValType extends EmbeddableInput & { attributes: SavedObjectAttributes },
|
||||
RefType extends SavedObjectEmbeddableInput
|
||||
> {
|
||||
constructor(private type: string, private savedObjectsClient: SavedObjectsClientContract) {}
|
||||
|
||||
public async unwrapAttributes(input: RefType | ValType): Promise<SavedObjectAttributes> {
|
||||
if (isSavedObjectEmbeddableInput(input)) {
|
||||
const savedObject: SimpleSavedObject<SavedObjectAttributes> = await this.savedObjectsClient.get<
|
||||
SavedObjectAttributes
|
||||
>(this.type, input.savedObjectId);
|
||||
return savedObject.attributes;
|
||||
}
|
||||
return input.attributes;
|
||||
}
|
||||
|
||||
public async wrapAttributes(
|
||||
newAttributes: SavedObjectAttributes,
|
||||
useRefType: boolean,
|
||||
embeddable?: IEmbeddable
|
||||
): Promise<Omit<ValType | RefType, 'id'>> {
|
||||
const savedObjectId =
|
||||
embeddable && isSavedObjectEmbeddableInput(embeddable.getInput())
|
||||
? (embeddable.getInput() as SavedObjectEmbeddableInput).savedObjectId
|
||||
: undefined;
|
||||
|
||||
if (useRefType) {
|
||||
if (savedObjectId) {
|
||||
await this.savedObjectsClient.update(this.type, savedObjectId, newAttributes);
|
||||
return { savedObjectId } as RefType;
|
||||
} else {
|
||||
const savedItem = await this.savedObjectsClient.create(this.type, newAttributes);
|
||||
return { savedObjectId: savedItem.id } as RefType;
|
||||
}
|
||||
} else {
|
||||
return { attributes: newAttributes } as ValType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,4 +25,5 @@ export { ErrorEmbeddable, isErrorEmbeddable } from './error_embeddable';
|
|||
export { withEmbeddableSubscription } from './with_subscription';
|
||||
export { EmbeddableRoot } from './embeddable_root';
|
||||
export * from './saved_object_embeddable';
|
||||
export { AttributeService } from './attribute_service';
|
||||
export { EmbeddableRenderer, EmbeddableRendererProps } from './embeddable_renderer';
|
||||
|
|
|
@ -26,5 +26,5 @@ export interface SavedObjectEmbeddableInput extends EmbeddableInput {
|
|||
export function isSavedObjectEmbeddableInput(
|
||||
input: EmbeddableInput | SavedObjectEmbeddableInput
|
||||
): input is SavedObjectEmbeddableInput {
|
||||
return (input as SavedObjectEmbeddableInput).savedObjectId !== undefined;
|
||||
return Boolean((input as SavedObjectEmbeddableInput).savedObjectId);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ const createStartContract = (): Start => {
|
|||
getEmbeddableFactories: jest.fn(),
|
||||
getEmbeddableFactory: jest.fn(),
|
||||
EmbeddablePanel: jest.fn(),
|
||||
getAttributeService: jest.fn(),
|
||||
getEmbeddablePanel: jest.fn(),
|
||||
getStateTransfer: jest.fn(() => createEmbeddableStateTransferMock() as EmbeddableStateTransfer),
|
||||
filtersAndTimeRangeFromContext: jest.fn(),
|
||||
|
|
|
@ -43,11 +43,13 @@ import {
|
|||
defaultEmbeddableFactoryProvider,
|
||||
IEmbeddable,
|
||||
EmbeddablePanel,
|
||||
SavedObjectEmbeddableInput,
|
||||
ChartActionContext,
|
||||
isRangeSelectTriggerContext,
|
||||
isValueClickTriggerContext,
|
||||
} from './lib';
|
||||
import { EmbeddableFactoryDefinition } from './lib/embeddables/embeddable_factory_definition';
|
||||
import { AttributeService } from './lib/embeddables/attribute_service';
|
||||
import { EmbeddableStateTransfer } from './lib/state_transfer';
|
||||
|
||||
export interface EmbeddableSetupDependencies {
|
||||
|
@ -82,6 +84,13 @@ export interface EmbeddableStart {
|
|||
embeddableFactoryId: string
|
||||
) => EmbeddableFactory<I, O, E> | undefined;
|
||||
getEmbeddableFactories: () => IterableIterator<EmbeddableFactory>;
|
||||
getAttributeService: <
|
||||
A,
|
||||
V extends EmbeddableInput & { attributes: A },
|
||||
R extends SavedObjectEmbeddableInput
|
||||
>(
|
||||
type: string
|
||||
) => AttributeService<A, V, R>;
|
||||
|
||||
/**
|
||||
* Given {@link ChartActionContext} returns a list of `data` plugin {@link Filter} entries.
|
||||
|
@ -206,6 +215,7 @@ export class EmbeddablePublicPlugin implements Plugin<EmbeddableSetup, Embeddabl
|
|||
return {
|
||||
getEmbeddableFactory: this.getEmbeddableFactory,
|
||||
getEmbeddableFactories: this.getEmbeddableFactories,
|
||||
getAttributeService: (type: string) => new AttributeService(type, core.savedObjects.client),
|
||||
filtersFromContext,
|
||||
filtersAndTimeRangeFromContext,
|
||||
getStateTransfer: (history?: ScopedHistory) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue