Remove deprecated elements and adds deprecation banner to pys-on (#1084)

* Show deprecation banner

* Add test for deprecation warning

* Remove deprecated elements

* Add entry in changelog

* Update test_style

* Remove random color rule

* Add PR link to changelog
This commit is contained in:
Fábio Rosado
2023-01-03 13:14:20 +00:00
committed by GitHub
parent 5c67384fbf
commit dbdcd0b3d0
18 changed files with 30 additions and 663 deletions

View File

@@ -1,24 +1,14 @@
import type { Runtime } from '../runtime';
import { make_PyRepl } from './pyrepl';
import { PyBox } from './pybox';
import { make_PyButton } from './pybutton';
import { PyTitle } from './pytitle';
import { make_PyInputBox } from './pyinputbox';
import { make_PyWidget } from './pywidget';
function createCustomElements(runtime: Runtime) {
const PyInputBox = make_PyInputBox(runtime);
const PyButton = make_PyButton(runtime);
const PyWidget = make_PyWidget(runtime);
const PyRepl = make_PyRepl(runtime);
/* eslint-disable @typescript-eslint/no-unused-vars */
const xPyRepl = customElements.define('py-repl', PyRepl);
const xPyBox = customElements.define('py-box', PyBox);
const xPyTitle = customElements.define('py-title', PyTitle);
const xPyWidget = customElements.define('py-register-widget', PyWidget);
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
const xPyButton = customElements.define('py-button', PyButton);
/* eslint-enable @typescript-eslint/no-unused-vars */
}

View File

@@ -1,76 +0,0 @@
import { getAttribute, addClasses, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger';
const logger = getLogger('py-box');
export class PyBox extends HTMLElement {
shadow: ShadowRoot;
wrapper: HTMLElement;
theme: string;
widths: string[];
constructor() {
super();
// attach shadow so we can preserve the element original innerHtml content
this.shadow = this.attachShadow({ mode: 'open' });
this.wrapper = document.createElement('slot');
this.shadow.appendChild(this.wrapper);
}
connectedCallback() {
const deprecationMessage =
'The element <py-box> is deprecated, you should create a ' +
'div with "py-box" class name instead. For example: <div class="py-box">';
createDeprecationWarning(deprecationMessage, 'py-box');
const mainDiv = document.createElement('div');
addClasses(mainDiv, ['py-box']);
// Hack: for some reason when moving children, the editor box duplicates children
// meaning that we end up with 2 editors, if there's a <py-repl> inside the <py-box>
// so, if we have more than 2 children with the cm-editor class, we remove one of them
while (this.childNodes.length > 0) {
if (this.firstChild.nodeName == 'PY-REPL') {
// in this case we need to remove the child and create a new one from scratch
const replDiv = document.createElement('div');
// we need to put the new repl inside a div so that if the repl has auto-generate true
// it can replicate itself inside that constrained div
replDiv.appendChild(this.firstChild.cloneNode());
mainDiv.appendChild(replDiv);
this.firstChild.remove();
} else {
if (this.firstChild.nodeName != '#text') {
mainDiv.appendChild(this.firstChild);
} else {
this.firstChild.remove();
}
}
}
// now we need to set widths
this.widths = [];
const widthsAttr = getAttribute(this, 'widths');
if (widthsAttr) {
for (const w of widthsAttr.split(';')) {
if (w.includes('/')) {
this.widths.push(w.split('/')[0]);
} else {
this.widths.push(w);
}
}
} else {
this.widths = Array<string>(mainDiv.children.length).fill('1 1 0');
}
this.widths.forEach((width, index) => {
const node: ChildNode = mainDiv.childNodes[index];
(<HTMLElement>node).style.flex = width;
addClasses(<HTMLElement>node, ['py-box-child']);
});
this.appendChild(mainDiv);
logger.info('py-box connected');
}
}

View File

@@ -1,85 +0,0 @@
import { getAttribute, addClasses, htmlDecode, ensureUniqueId, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger';
import type { Runtime } from '../runtime';
const logger = getLogger('py-button');
export function make_PyButton(runtime: Runtime) {
class PyButton extends HTMLElement {
widths: string[] = [];
label: string | undefined = undefined;
class: string[];
defaultClass: string[];
mount_name: string | undefined = undefined;
code: string;
constructor() {
super();
this.defaultClass = ['py-button'];
const label = getAttribute(this, 'label');
if (label) {
this.label = label;
}
// Styling does the same thing as class in normal HTML. Using the name "class" makes the style to malfunction
const styling = getAttribute(this, 'styling');
if (styling) {
const klass = styling.trim();
if (klass === '') {
this.class = this.defaultClass;
} else {
// trim each element to remove unnecessary spaces which makes the button style to malfunction
this.class = klass
.split(' ')
.map(x => x.trim())
.filter(x => x !== '');
}
} else {
this.class = this.defaultClass;
}
}
connectedCallback() {
const deprecationMessage =
'The element <py-button> is deprecated, create a function with your ' +
'inline code and use <button py-click="function()" class="py-button"> instead.';
createDeprecationWarning(deprecationMessage, 'py-button');
ensureUniqueId(this);
this.code = htmlDecode(this.innerHTML) || '';
this.mount_name = this.id.split('-').join('_');
this.innerHTML = '';
const mainDiv = document.createElement('button');
mainDiv.innerHTML = this.label;
addClasses(mainDiv, this.class);
mainDiv.id = this.id;
this.id = `${this.id}-container`;
this.appendChild(mainDiv);
this.code = this.code.split('self').join(this.mount_name);
let registrationCode = `from pyodide.ffi import create_proxy`;
registrationCode += `\n${this.mount_name} = Element("${mainDiv.id}")`;
if (this.code.includes('def on_focus')) {
this.code = this.code.replace('def on_focus', `def on_focus_${this.mount_name}`);
registrationCode += `\n${this.mount_name}.element.addEventListener('focus', create_proxy(on_focus_${this.mount_name}))`;
}
if (this.code.includes('def on_click')) {
this.code = this.code.replace('def on_click', `def on_click_${this.mount_name}`);
registrationCode += `\n${this.mount_name}.element.addEventListener('click', create_proxy(on_click_${this.mount_name}))`;
}
// now that we appended and the element is attached, lets connect with the event handlers
// defined for this widget
runtime.runButDontRaise(this.code);
runtime.runButDontRaise(registrationCode);
logger.debug('py-button connected');
}
}
return PyButton;
}

View File

@@ -1,58 +0,0 @@
import { getAttribute, addClasses, htmlDecode, ensureUniqueId, createDeprecationWarning } from '../utils';
import { getLogger } from '../logger';
import type { Runtime } from '../runtime';
const logger = getLogger('py-inputbox');
export function make_PyInputBox(runtime: Runtime) {
class PyInputBox extends HTMLElement {
widths: string[] = [];
label: string | undefined = undefined;
mount_name: string | undefined = undefined;
code: string;
constructor() {
super();
const label = getAttribute(this, 'label');
if (label) {
this.label = label;
}
}
connectedCallback() {
const deprecationMessage =
'The element <py-input> is deprecated, ' + 'use <input class="py-input"> instead.';
createDeprecationWarning(deprecationMessage, 'py-input');
ensureUniqueId(this);
this.code = htmlDecode(this.innerHTML);
this.mount_name = this.id.split('-').join('_');
this.innerHTML = '';
const mainDiv = document.createElement('input');
mainDiv.type = 'text';
addClasses(mainDiv, ['py-input']);
mainDiv.id = this.id;
this.id = `${this.id}-container`;
this.appendChild(mainDiv);
// now that we appended and the element is attached, lets connect with the event handlers
// defined for this widget
this.appendChild(mainDiv);
this.code = this.code.split('self').join(this.mount_name);
let registrationCode = `from pyodide.ffi import create_proxy`;
registrationCode += `\n${this.mount_name} = Element("${mainDiv.id}")`;
if (this.code.includes('def on_keypress')) {
this.code = this.code.replace('def on_keypress', `def on_keypress_${this.mount_name}`);
registrationCode += `\n${this.mount_name}.element.addEventListener('keypress', create_proxy(on_keypress_${this.mount_name}))`;
}
runtime.runButDontRaise(this.code);
runtime.runButDontRaise(registrationCode);
logger.debug('py-inputbox connected');
}
}
return PyInputBox;
}

View File

@@ -1,4 +1,4 @@
import { htmlDecode, ensureUniqueId, showWarning } from '../utils';
import { htmlDecode, ensureUniqueId, showWarning, createDeprecationWarning } from '../utils';
import type { Runtime } from '../runtime';
import { getLogger } from '../logger';
import { pyExec } from '../pyexec';
@@ -165,9 +165,10 @@ function createElementsWithEventListeners(runtime: Runtime, pyAttribute: string)
const event = pyAttributeToEvent.get(pyAttribute);
if (pyAttribute === 'pys-onClick' || pyAttribute === 'pys-onKeyDown') {
console.warn(
'Use of pys-onClick and pys-onKeyDown attributes is deprecated in favor of py-onClick() and py-onKeyDown(). pys-on* attributes will be deprecated in a future version of PyScript.',
);
const msg =
`The attribute 'pys-onClick' and 'pys-onKeyDown' are deprecated. Please 'py-click="myFunction()"' ` +
` or 'py-keydown="myFunction()"' instead.`;
createDeprecationWarning(msg, msg);
const source = `
from pyodide.ffi import create_proxy
Element("${el.id}").element.addEventListener("${event}", create_proxy(${handlerCode}))

View File

@@ -1,29 +0,0 @@
import { addClasses, htmlDecode, createDeprecationWarning } from '../utils';
export class PyTitle extends HTMLElement {
widths: string[];
label: string;
mount_name: string;
constructor() {
super();
}
connectedCallback() {
const deprecationMessage = 'The element <py-title> is deprecated, please use an <h1> tag instead.';
createDeprecationWarning(deprecationMessage, 'py-title');
this.label = htmlDecode(this.innerHTML);
this.mount_name = this.id.split('-').join('_');
this.innerHTML = '';
const mainDiv = document.createElement('div');
const divContent = document.createElement('h1');
addClasses(mainDiv, ['py-title']);
divContent.innerHTML = this.label;
mainDiv.id = this.id;
this.id = `${this.id}-container`;
mainDiv.appendChild(divContent);
this.appendChild(mainDiv);
}
}