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

Add html component #237

Merged
merged 1 commit into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions demo/html_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mesop as me


@me.page(path="/html_demo")
def app():
me.html(
"""
Custom HTML
<a href="https://google.github.io/mesop/" target="_blank">mesop</a>
"""
)
2 changes: 2 additions & 0 deletions demo/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import code_demo as code_demo # cannot call it code due to python library naming conflict
import divider as divider
import embed as embed
import html_demo as html_demo
import icon as icon
import image as image
import input as input
Expand Down Expand Up @@ -146,6 +147,7 @@ class Section:
name="Advanced",
examples=[
Example(name="embed"),
Example(name="html_demo"),
Example(name="plot"),
],
),
Expand Down
17 changes: 17 additions & 0 deletions docs/components/html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
## Overview

The HTML component allows you to add custom HTML to your Mesop app.

> Note: the HTML is [sanitized by Angular](https://angular.dev/best-practices/security#sanitization-example) for web security reasons so potentially unsafe code like JavaScript is removed.

## Examples

<iframe class="component-demo" src="https://mesop-y677hytkra-uc.a.run.app/html"></iframe>

```python
--8<-- "demo/html_demo.py"
```

## API

::: mesop.components.html.html.html
1 change: 1 addition & 0 deletions mesop/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ py_library(
deps = [
":version",
# REF(//scripts/scaffold_component.py):insert_component_import
"//mesop/components/html:py",
"//mesop/components/uploader:py",
"//mesop/components/code:py",
"//mesop/components/embed:py",
Expand Down
1 change: 1 addition & 0 deletions mesop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
from mesop.components.code.code import code as code
from mesop.components.divider.divider import divider as divider
from mesop.components.embed.embed import embed as embed
from mesop.components.html.html import html as html
from mesop.components.icon.icon import icon as icon
from mesop.components.image.image import image as image
from mesop.components.input.input import EnterEvent as EnterEvent
Expand Down
12 changes: 12 additions & 0 deletions mesop/components/html/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("//mesop/components:defs.bzl", "mesop_component")

package(
default_visibility = ["//build_defs:mesop_internal"],
)

mesop_component(
name = "html",
ng_deps = [
"//mesop/web/src/safe_iframe",
],
)
Empty file.
13 changes: 13 additions & 0 deletions mesop/components/html/e2e/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("//build_defs:defaults.bzl", "py_library")

package(
default_visibility = ["//build_defs:mesop_examples"],
)

py_library(
name = "e2e",
srcs = glob(["*.py"]),
deps = [
"//mesop",
],
)
1 change: 1 addition & 0 deletions mesop/components/html/e2e/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import html_app as html_app
11 changes: 11 additions & 0 deletions mesop/components/html/e2e/html_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mesop as me


@me.page(path="/components/html/e2e/html_app")
def app():
me.html(
"""
Custom HTML
<a href="https://google.github.io/mesop/" target="_blank">mesoplink</a>
"""
)
9 changes: 9 additions & 0 deletions mesop/components/html/e2e/html_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import {test, expect} from '@playwright/test';

test('test', async ({page}) => {
await page.goto('/components/html/e2e/html_app');
// mesop is the HTML link so we're checking that it's rendered.
expect(await page.getByText('Custom HTML').textContent()).toContain(
'mesoplink',
);
});
1 change: 1 addition & 0 deletions mesop/components/html/html.ng.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div [innerHTML]="config().getHtml()" [style]="getStyle()"></div>
wwwillchen marked this conversation as resolved.
Show resolved Hide resolved
7 changes: 7 additions & 0 deletions mesop/components/html/html.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
syntax = "proto2";

package mesop.components.html;

message HtmlType {
optional string html = 1;
}
41 changes: 41 additions & 0 deletions mesop/components/html/html.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import mesop.components.html.html_pb2 as html_pb
from mesop.component_helpers import (
Border,
BorderSide,
Style,
insert_component,
register_native_component,
)


@register_native_component
def html(
html: str = "",
*,
style: Style | None = None,
key: str | None = None,
):
"""
This function renders custom HTML inside an iframe for web security isolation.

Args:
html: The HTML content to be rendered.
style: The style to apply to the embed, such as width and height.
key: The component [key](../guides/components.md#component-key).
"""
if style is None:
style = Style()
if style.border is None:
style.border = Border.all(
BorderSide(
width=0,
)
)
insert_component(
key=key,
type_name="html",
proto=html_pb.HtmlType(
html=html,
),
style=style,
)
34 changes: 34 additions & 0 deletions mesop/components/html/html.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Component, Input} from '@angular/core';
import {
Key,
Style,
Type,
} from 'mesop/mesop/protos/ui_jspb_proto_pb/mesop/protos/ui_pb';
import {HtmlType} from 'mesop/mesop/components/html/html_jspb_proto_pb/mesop/components/html/html_pb';
import {formatStyle} from '../../web/src/utils/styles';

@Component({
selector: 'mesop-html',
templateUrl: 'html.ng.html',
standalone: true,
})
export class HtmlComponent {
@Input({required: true}) type!: Type;
@Input() key!: Key;
@Input() style!: Style;
private _config!: HtmlType;

ngOnChanges() {
this._config = HtmlType.deserializeBinary(
this.type.getValue() as unknown as Uint8Array,
);
}

config(): HtmlType {
return this._config;
}

getStyle(): string {
return formatStyle(this.style);
}
}
1 change: 1 addition & 0 deletions mesop/example_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@
import mesop.components.table.e2e as table_e2e
import mesop.components.embed.e2e as embed_e2e
import mesop.components.uploader.e2e as uploader_e2e
import mesop.components.html.e2e as html_e2e
# REF(//scripts/scaffold_component.py):insert_component_e2e_import_export
1 change: 1 addition & 0 deletions mesop/examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ py_library(
deps = [
"//demo",
# REF(//scripts/scaffold_component.py):insert_component_e2e_import
"//mesop/components/html/e2e",
"//mesop/components/uploader/e2e",
"//mesop/components/embed/e2e",
"//mesop/components/table/e2e",
Expand Down
1 change: 1 addition & 0 deletions mesop/web/src/component_renderer/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ ng_module(
]) + ["component_renderer.css"],
deps = [
# REF(//scripts/scaffold_component.py):insert_component_import
"//mesop/components/html:ng",
"//mesop/components/uploader:ng",
"//mesop/components/embed:ng",
"//mesop/components/table:ng",
Expand Down
2 changes: 2 additions & 0 deletions mesop/web/src/component_renderer/type_to_component.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {HtmlComponent} from '../../../components/html/html';
import {UploaderComponent} from '../../../components/uploader/uploader';
import {EmbedComponent} from '../../../components/embed/embed';
import {TableComponent} from '../../../components/table/table';
Expand Down Expand Up @@ -53,6 +54,7 @@ export class UserDefinedComponent implements BaseComponent {
}

export const typeToComponent = {
'html': HtmlComponent,
'uploader': UploaderComponent,
'embed': EmbedComponent,
'table': TableComponent,
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ nav:
- Tooltip: components/tooltip.md
- Advanced:
- Embed: components/embed.md
- HTML: components/html.md
- Plot: components/plot.md
- API:
- Page: api/page.md
Expand Down
Loading