Deprecate py-button, py-inputbox, py-box and py-title (#931)

This commit is contained in:
Fábio Rosado
2022-11-14 16:29:28 +00:00
committed by GitHub
parent be9b9f66d3
commit adfa9a9b05
15 changed files with 179 additions and 24 deletions

View File

@@ -417,11 +417,11 @@ The following tags can be used to add visual attributes to your HTML page.
| Tag | Description | | Tag | Description |
| --- | ----------- | | --- | ----------- |
| `<py-inputbox>` | Adds an input box that can be used to prompt users to enter input values. | | `<py-inputbox>` | (Deprecated) Adds an input box that can be used to prompt users to enter input values. |
| `<py-box>` | Creates a container object that can be used to host one or more visual components that define how elements of `<py-box>` should align and show on the page. | | `<py-box>` | (Deprecated) Creates a container object that can be used to host one or more visual components that define how elements of `<py-box>` should align and show on the page. |
| `<py-button>` | Adds a button to which authors can add labels and event handlers for actions on the button, such as `on_focus` or `on_click`. | | `<py-button>` | (Deprecated) Adds a button to which authors can add labels and event handlers for actions on the button, such as `on_focus` or `on_click`. |
| `<py-title>` | Adds a static text title component that styles the text inside the tag as a page title. | | `<py-title>` | (Deprecated) Adds a static text title component that styles the text inside the tag as a page title. |
```{note} ```{note}
All the elements above are experimental and not implemented at their full functionality. Use them with the understanding that the APIs or full support might change or be removed until the visual components are more mature. These elements have been deprecated, we suggest that you use native elements and attach the respective `py-` attribute. For example for `<py-button>` you can write `<button py-click="function()">`
``` ```

View File

@@ -24,9 +24,9 @@
[[fetch]] [[fetch]]
files = ["./utils.py", "./antigravity.py"] files = ["./utils.py", "./antigravity.py"]
</py-config> </py-config>
<py-box widths="2/3;1/3"> <div>
<py-repl id="my-repl" auto-generate="true"> </py-repl> <py-repl id="my-repl" auto-generate="true"> </py-repl>
<div id="output" class="p-4"></div> <div id="output" class="p-4"></div>
</py-box> </div>
</body> </body>
</html> </html>

View File

@@ -17,8 +17,12 @@
</py-config> </py-config>
<py-script> <py-script>
from js import document
from pyodide.ffi.wrappers import add_event_listener
def add_task(*ags, **kws): def add_task(*ags, **kws):
# create a new dictionary representing the new task # create a new dictionary representing the new task
new_task_content = Element("new-task-content")
task = { "content": new_task_content.value, "done": False, "created_at": dt.now() } task = { "content": new_task_content.value, "done": False, "created_at": dt.now() }
# add a new task to the list and tell it to use the `content` key to show in the UI # add a new task to the list and tell it to use the `content` key to show in the UI
@@ -28,22 +32,28 @@
# clear the inputbox element used to create the new task # clear the inputbox element used to create the new task
new_task_content.clear() new_task_content.clear()
def on_click(evt):
add_task()
def handle_keypress(evt):
if evt.key == "Enter":
add_task()
add_event_listener(
document.getElementById("new-task-content"),
"keypress",
handle_keypress
)
</py-script> </py-script>
</head> </head>
<body> <body>
<py-title>To Do List</py-title> <h1>To Do List</h1>
<py-box widths="4/5;1/5"> <div class="py-box">
<py-inputbox id="new-task-content"> <input id="new-task-content" />
def on_keypress(e): <button py-click="add_task()" id="new-task-btn" class="py-button">Add Task!</button>
if (e.code == "Enter"): </div>
add_task()
</py-inputbox>
<py-button id="new-task-btn" label="Add Task!">
def on_click(evt):
add_task()
</py-button>
</py-box>
<py-list id="myList"></py-list> <py-list id="myList"></py-list>
<py-repl id="my-repl" auto-generate="true"> </py-repl> <py-repl id="my-repl" auto-generate="true"> </py-repl>

View File

@@ -1,4 +1,4 @@
import { getAttribute, addClasses } from '../utils'; import { getAttribute, addClasses, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger'; import { getLogger } from '../logger';
const logger = getLogger('py-box'); const logger = getLogger('py-box');
@@ -20,6 +20,11 @@ export class PyBox extends HTMLElement {
} }
connectedCallback() { connectedCallback() {
const deprecationMessage = (
'<p>The element &lt;py-box&gt; is deprecated, you should create a ' +
'div with "py-box" class name instead. For example: &lt;div class="py-box"&gt; '
)
createDeprecationWarning(deprecationMessage, "py-box")
const mainDiv = document.createElement('div'); const mainDiv = document.createElement('div');
addClasses(mainDiv, ['py-box']); addClasses(mainDiv, ['py-box']);

View File

@@ -1,4 +1,4 @@
import { getAttribute, addClasses, htmlDecode, ensureUniqueId } from '../utils'; import { getAttribute, addClasses, htmlDecode, ensureUniqueId, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger'; import { getLogger } from '../logger';
import type { Runtime } from '../runtime'; import type { Runtime } from '../runtime';
@@ -42,6 +42,12 @@ export function make_PyButton(runtime: Runtime) {
} }
async connectedCallback() { async connectedCallback() {
const deprecationMessage = (
'<p>The element &lt;py-button&gt; is deprecated, create a function with your ' +
'inline code and use &lt;button py-click="function()" class="py-button"&gt; instead.</p>'
)
createDeprecationWarning(deprecationMessage, "py-button")
ensureUniqueId(this); ensureUniqueId(this);
this.code = htmlDecode(this.innerHTML) || ''; this.code = htmlDecode(this.innerHTML) || '';
this.mount_name = this.id.split('-').join('_'); this.mount_name = this.id.split('-').join('_');

View File

@@ -1,4 +1,4 @@
import { getAttribute, addClasses, htmlDecode, ensureUniqueId } from '../utils'; import { getAttribute, addClasses, htmlDecode, ensureUniqueId, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger'; import { getLogger } from '../logger';
import type { Runtime } from '../runtime'; import type { Runtime } from '../runtime';
@@ -21,6 +21,11 @@ export function make_PyInputBox(runtime: Runtime) {
} }
async connectedCallback() { async connectedCallback() {
const deprecationMessage = (
'<p>The element &lt;py-input&gt; is deprecated, ' +
'use &lt;input class="py-input"&gt; instead.</p>'
)
createDeprecationWarning(deprecationMessage, "py-input")
ensureUniqueId(this); ensureUniqueId(this);
this.code = htmlDecode(this.innerHTML); this.code = htmlDecode(this.innerHTML);
this.mount_name = this.id.split('-').join('_'); this.mount_name = this.id.split('-').join('_');

View File

@@ -1,4 +1,4 @@
import { addClasses, htmlDecode } from '../utils'; import { addClasses, htmlDecode, createDeprecationWarning } from '../utils';
export class PyTitle extends HTMLElement { export class PyTitle extends HTMLElement {
widths: string[]; widths: string[];
@@ -9,6 +9,10 @@ export class PyTitle extends HTMLElement {
} }
connectedCallback() { connectedCallback() {
const deprecationMessage = (
'<p>The element &lt;py-title&gt; is deprecated, please use an &lt;h1&gt; tag instead.</p>'
)
createDeprecationWarning(deprecationMessage, "py-title")
this.label = htmlDecode(this.innerHTML); this.label = htmlDecode(this.innerHTML);
this.mount_name = this.id.split('-').join('_'); this.mount_name = this.id.split('-').join('_');
this.innerHTML = ''; this.innerHTML = '';

View File

@@ -293,6 +293,7 @@ input {
border-color: rgba(37, 99, 235, var(--tw-border-opacity)); border-color: rgba(37, 99, 235, var(--tw-border-opacity));
border-width: 1px; border-width: 1px;
border-radius: 0.25rem; border-radius: 0.25rem;
cursor: pointer;
} }
.py-li-element p { .py-li-element p {

View File

@@ -111,3 +111,16 @@ export function joinPaths(parts: string[], separator = '/') {
} }
return res; return res;
} }
export function createDeprecationWarning(msg: string, elementName: string): void {
const banners = document.getElementsByClassName('alert-banner py-warning');
let bannerCount = 0;
for (const banner of banners) {
if (banner.innerHTML.includes(elementName)) {
bannerCount++;
}
}
if (bannerCount == 0) {
_createAlertBanner(msg, "warning");
}
}

View File

@@ -16,3 +16,19 @@ class TestPyButton(PyScriptTest):
assert len(pybox_element) == 2 assert len(pybox_element) == 2
assert pybox_element[1].get_attribute("class") == "py-box-child" assert pybox_element[1].get_attribute("class") == "py-box-child"
def test_deprecated_element(self):
self.pyscript_run(
"""
<py-box>
</py-box>
"""
)
banner = self.page.locator(".py-warning")
banner_content = banner.inner_text()
expected = (
"The element <py-box> is deprecated, you should create a div "
'with "py-box" class name instead. For example: <div class="py-box">'
)
assert banner_content == expected

View File

@@ -16,3 +16,44 @@ class TestPyButton(PyScriptTest):
self.page.locator("text=my button").click() self.page.locator("text=my button").click()
self.page.locator("text=my button").click() self.page.locator("text=my button").click()
assert self.console.log.lines == [self.PY_COMPLETE, "clicked!", "clicked!"] assert self.console.log.lines == [self.PY_COMPLETE, "clicked!", "clicked!"]
def test_deprecated_element(self):
self.pyscript_run(
"""
<py-button label="my button">
import js
def on_click(evt):
js.console.log('clicked!')
</py-button>
"""
)
banner = self.page.locator(".py-warning")
banner_content = banner.inner_text()
expected = (
"The element <py-button> is deprecated, create a function with your "
'inline code and use <button py-click="function()" class="py-button"> instead.'
)
assert banner_content == expected
def test_creates_single_deprecation_banner(self):
self.pyscript_run(
"""
<py-button label="my button">
import js
def on_click(evt):
js.console.log('clicked!')
</py-button>
<py-button label="another button!">
</py-button>
"""
)
banner = self.page.query_selector_all(".py-warning")
assert len(banner) == 1
banner_content = banner[0].inner_text()
expected = (
"The element <py-button> is deprecated, create a function with your "
'inline code and use <button py-click="function()" class="py-button"> instead.'
)
assert banner_content == expected

View File

@@ -20,3 +20,23 @@ class TestPyInputBox(PyScriptTest):
input.press("Enter") input.press("Enter")
assert self.console.log.lines == [self.PY_COMPLETE, "Hello"] assert self.console.log.lines == [self.PY_COMPLETE, "Hello"]
def test_deprecated_element(self):
self.pyscript_run(
"""
<py-inputbox label="my input">
import js
def on_keypress(evt):
if evt.key == "Enter":
js.console.log(evt.target.value)
</py-inputbox>
"""
)
banner = self.page.locator(".py-warning")
banner_content = banner.inner_text()
expected = (
"The element <py-input> is deprecated, "
'use <input class="py-input"> instead.'
)
assert banner_content == expected

View File

@@ -14,3 +14,17 @@ class TestPyTitle(PyScriptTest):
# py_title will be none # py_title will be none
assert py_title assert py_title
assert py_title.text_content() == "Hello, World!" assert py_title.text_content() == "Hello, World!"
def test_deprecated_element(self):
self.pyscript_run(
"""
<py-title>Hello, world!</py-title>
"""
)
banner = self.page.locator(".py-warning")
banner_content = banner.inner_text()
expected = (
"The element <py-title> is deprecated, please use an <h1> tag instead."
)
assert banner_content == expected

View File

@@ -282,7 +282,7 @@ class TestExamples(PyScriptTest):
self.page.locator("py-repl").type("import utils\ndisplay(utils.now())") self.page.locator("py-repl").type("import utils\ndisplay(utils.now())")
self.page.locator("button").click() self.page.locator("button").click()
# Make sure the output is in the page # Make sure the output is in the page
self.page.wait_for_selector("#output") self.page.wait_for_selector("#my-repl-1-1")
# utils.now returns current date time # utils.now returns current date time
content = self.page.content() content = self.page.content()
pattern = "\\d+/\\d+/\\d+, \\d+:\\d+:\\d+" # e.g. 08/09/2022 15:57:32 pattern = "\\d+/\\d+/\\d+, \\d+:\\d+:\\d+" # e.g. 08/09/2022 15:57:32

View File

@@ -11,6 +11,11 @@ describe('PyButton', () => {
let instance; let instance;
beforeEach(() => { beforeEach(() => {
instance = new PyButton(); instance = new PyButton();
// Remove all the alert banners created when calling `connectedCallback`
const banners = document.getElementsByClassName("alert-banner")
for (const banner of banners) {
banner.remove()
}
}); });
it('should get the Button to just instantiate', async () => { it('should get the Button to just instantiate', async () => {
@@ -65,4 +70,19 @@ describe('PyButton', () => {
expect(instanceId).toMatch(/py-(\w+-){1,5}container/); expect(instanceId).toMatch(/py-(\w+-){1,5}container/);
expect(instance.mount_name).toBe(instanceId.replace('-container', '').split('-').join('_')); expect(instance.mount_name).toBe(instanceId.replace('-container', '').split('-').join('_'));
}); });
it('should create a single deprecation banner', async () => {
document.body.innerHTML = ""
let alertBanners = document.getElementsByClassName('alert-banner');
expect(alertBanners.length).toBe(0);
instance.connectedCallback();
expect(alertBanners.length).toBe(1);
expect(alertBanners[0].innerHTML).toContain("&lt;py-button&gt; is deprecated");
// Calling `connectedCallback` again should not create a new banner
instance.connectedCallback();
alertBanners = document.getElementsByClassName('alert-banner');
expect(alertBanners.length).toBe(1);
})
}); });