Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Example] Embeddable by Reference and Value #68719

Merged
merged 25 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f8a21a8
Started example
ThomThomson Jun 1, 2020
74b101a
Created book embeddable, started work on an unwrapper function for re…
ThomThomson Jun 4, 2020
f8670a1
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 8, 2020
c057648
finished value and reference embeddable
ThomThomson Jun 9, 2020
16d0fc5
Changed the default books :)
ThomThomson Jun 9, 2020
45da976
type changes
ThomThomson Jun 10, 2020
ba62f65
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 11, 2020
a4bf199
type fix
ThomThomson Jun 11, 2020
2cf06b1
Update fields
ThomThomson Jun 11, 2020
576e2f9
added attribute service to embeddable start contract. added a wrap me…
ThomThomson Jun 12, 2020
48bddd0
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 12, 2020
f49d8e1
small fixes
ThomThomson Jun 12, 2020
58d89ef
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 17, 2020
e620bcb
fixed typo in node order
ThomThomson Jun 18, 2020
5dc4fb1
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 18, 2020
c1ced25
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 23, 2020
5115a3b
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 23, 2020
7f9d8a0
Merge branch 'master' into example/refToValEmbeddable
elasticmachine Jun 25, 2020
215d6be
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 26, 2020
c44775b
merge
ThomThomson Jun 26, 2020
6de27c0
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jun 29, 2020
a653ff2
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jul 6, 2020
d3af35e
Merge branch 'master' of github.com:elastic/kibana into example/refTo…
ThomThomson Jul 7, 2020
b0b8745
Removed create from saved object
ThomThomson Jul 7, 2020
7c826af
Removed unused import
ThomThomson Jul 7, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
}
1 change: 1 addition & 0 deletions examples/embeddable_examples/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
*/

export { TodoSavedObjectAttributes } from './todo_saved_object_attributes';
export { BookSavedObjectAttributes, BOOK_SAVED_OBJECT } from './book_saved_object_attributes';
2 changes: 1 addition & 1 deletion examples/embeddable_examples/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
"kibanaVersion": "kibana",
"server": true,
"ui": true,
"requiredPlugins": ["embeddable"],
"requiredPlugins": ["embeddable", "uiActions"],
"optionalPlugins": []
}
90 changes: 90 additions & 0 deletions examples/embeddable_examples/public/book/book_component.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 123 additions & 0 deletions examples/embeddable_examples/public/book/book_embeddable.tsx
Original file line number Diff line number Diff line change
@@ -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;
ThomThomson marked this conversation as resolved.
Show resolved Hide resolved
this.reload();
} else {
this.updateOutput({
attributes: this.attributes,
hasMatch: getHasMatch(this.input.search, this.attributes),
});
}
});
}

public render(node: HTMLElement) {
this.node = node;
if (this.node) {
ReactDOM.unmountComponentAtNode(this.node);
ThomThomson marked this conversation as resolved.
Show resolved Hide resolved
}
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);
}
}
}
128 changes: 128 additions & 0 deletions examples/embeddable_examples/public/book/book_embeddable_factory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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,
ErrorEmbeddable,
IContainer,
AttributeService,
} 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 class BookEmbeddableFactory
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 createFromSavedObject = async (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this still ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't notice that it was optional! I've removed this method.

savedObjectId: string,
input: BookEmbeddableInput,
parent?: IContainer
): Promise<BookEmbeddable | ErrorEmbeddable> => {
return this.create(input, parent);
};

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, includeInLibrary: boolean) => {
ThomThomson marked this conversation as resolved.
Show resolved Hide resolved
const wrappedAttributes = (await this.getAttributeService()).wrapAttributes(
attributes,
includeInLibrary
);
resolve(wrappedAttributes);
};
const overlay = openModal(
toMountPoint(
<CreateEditBookComponent
onSave={(attributes: BookSavedObjectAttributes, includeInLibrary: boolean) => {
onSave(attributes, includeInLibrary);
overlay.close();
}}
/>
)
);
});
}

private async getAttributeService() {
if (!this.attributeService) {
this.attributeService = await (await this.getStartServices()).getAttributeService<
BookSavedObjectAttributes,
BookByValueInput,
BookByReferenceInput
>(this.type);
}
return this.attributeService;
}
}
Loading