Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/plenty-hoops-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/web-elements": patch
---

feat: x-input && x-textarea add attribute input-filter, which can filter input value.
25 changes: 17 additions & 8 deletions packages/web-platform/web-elements/src/XInput/XInputEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { registerEventEnableStatusChangeHandler } from '@lynx-js/web-elements-re
export class XInputEvents
implements InstanceType<AttributeReactiveClass<typeof HTMLElement>>
{
static observedAttributes = ['send-composing-input'];
static observedAttributes = ['send-composing-input', 'input-filter'];
#dom: HTMLElement;

#sendComposingInput = false;
Expand All @@ -29,8 +29,9 @@ export class XInputEvents
'#form',
);

@registerAttributeHandler('input-filter', true)
@registerEventEnableStatusChangeHandler('input')
#handleEnableInputEvent(status: boolean) {
#handleEnableInputEvent(status: boolean | string | null) {
const input = this.#getInputElement();
if (status) {
input.addEventListener(
Expand Down Expand Up @@ -74,16 +75,20 @@ export class XInputEvents

#teleportInput = (event: InputEvent) => {
const input = this.#getInputElement();
const value = input.value;
const inputFilter = this.#dom.getAttribute('input-filter');
const filterValue = inputFilter
? input.value.replace(new RegExp(inputFilter, 'g'), '')
: input.value;
const isComposing = event.isComposing;
input.value = filterValue;
if (isComposing && !this.#sendComposingInput) return;
this.#dom.dispatchEvent(
new CustomEvent('input', {
...commonComponentEventSetting,
detail: {
value,
value: filterValue,
/** @deprecated */
textLength: value.length,
textLength: filterValue.length,
/** @deprecated */
cursor: input.selectionStart,
isComposing,
Expand All @@ -96,16 +101,20 @@ export class XInputEvents

#teleportCompositionendInput = () => {
const input = this.#getInputElement();
const value = input.value;
const inputFilter = this.#dom.getAttribute('input-filter');
const filterValue = inputFilter
? input.value.replace(new RegExp(inputFilter, 'g'), '')
: input.value;
input.value = filterValue;
// if #sendComposingInput set true, #teleportInput will send detail
if (!this.#sendComposingInput) {
this.#dom.dispatchEvent(
new CustomEvent('input', {
...commonComponentEventSetting,
detail: {
value,
value: filterValue,
/** @deprecated */
textLength: value.length,
textLength: filterValue.length,
/** @deprecated */
cursor: input.selectionStart,
isComposing: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { registerEventEnableStatusChangeHandler } from '@lynx-js/web-elements-re
export class XTextareaEvents
implements InstanceType<AttributeReactiveClass<typeof HTMLElement>>
{
static observedAttributes = ['send-composing-input'];
static observedAttributes = ['send-composing-input', 'input-filter'];
#dom: HTMLElement;

#sendComposingInput = false;
Expand All @@ -29,8 +29,9 @@ export class XTextareaEvents
'#form',
);

@registerAttributeHandler('input-filter', true)
@registerEventEnableStatusChangeHandler('input')
#handleEnableConfirmEvent(status: boolean) {
#handleEnableConfirmEvent(status: string | boolean | null) {
const textareaElement = this.#getTextareaElement();
if (status) {
textareaElement.addEventListener(
Expand Down Expand Up @@ -74,16 +75,20 @@ export class XTextareaEvents

#teleportInput = (event: InputEvent) => {
const input = this.#getTextareaElement();
const value = input.value;
const inputFilter = this.#dom.getAttribute('input-filter');
const filterValue = inputFilter
? input.value.replace(new RegExp(inputFilter, 'g'), '')
: input.value;
const isComposing = event.isComposing;
input.value = filterValue;
if (isComposing && !this.#sendComposingInput) return;
this.#dom.dispatchEvent(
new CustomEvent('input', {
...commonComponentEventSetting,
detail: {
value,
value: filterValue,
/** @deprecated */
textLength: value.length,
textLength: filterValue.length,
/** @deprecated */
cursor: input.selectionStart,
isComposing,
Expand All @@ -96,16 +101,20 @@ export class XTextareaEvents

#teleportCompositionendInput = () => {
const input = this.#getTextareaElement();
const value = input.value;
const inputFilter = this.#dom.getAttribute('input-filter');
const filterValue = inputFilter
? input.value.replace(new RegExp(inputFilter, 'g'), '')
: input.value;
input.value = filterValue;
// if #sendComposingInput set true, #teleportInput will send detail
if (!this.#sendComposingInput) {
this.#dom.dispatchEvent(
new CustomEvent('input', {
...commonComponentEventSetting,
detail: {
value,
value: filterValue,
/** @deprecated */
textLength: value.length,
textLength: filterValue.length,
/** @deprecated */
cursor: input.selectionStart,
isComposing: false,
Expand Down
25 changes: 25 additions & 0 deletions packages/web-platform/web-tests/tests/react.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
ReturnType<typeof expect<Page>>['toHaveScreenshot']
>[0],
) => {
await expect(page).toHaveScreenshot([

Check failure on line 24 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux / check

[webkit] › tests/react.spec.ts:3649:7 › reactlynx3 tests › elements › list › basic-element-list-scroll-to-position

6) [webkit] › tests/react.spec.ts:3649:7 › reactlynx3 tests › elements › list › basic-element-list-scroll-to-position Error: expect(page).toHaveScreenshot(expected) 194987 pixels (ratio 0.76 of all image pixels) are different. Expected: /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts-snapshots/list/basic-element-list-scroll-to-position/initial-webkit-linux.png Received: /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/test-results/react-reactlynx3-tests-ele-c6a35-ent-list-scroll-to-position-webkit/list/basic-element-list-scroll-to-position/initial-actual.png Diff: /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/test-results/react-reactlynx3-tests-ele-c6a35-ent-list-scroll-to-position-webkit/list/basic-element-list-scroll-to-position/initial-diff.png Call log: - expect.toHaveScreenshot(list/basic-element-list-scroll-to-position/initial.png) with timeout 5000ms - verifying given screenshot expectation - taking page screenshot - waiting for fonts to load... - fonts loaded - 194987 pixels (ratio 0.76 of all image pixels) are different. - waiting 100ms before taking screenshot - taking page screenshot - waiting for fonts to load... - fonts loaded - captured a stable screenshot - 194987 pixels (ratio 0.76 of all image pixels) are different. 22 | >[0], 23 | ) => { > 24 | await expect(page).toHaveScreenshot([ | ^ 25 | `${caseName}`, 26 | `${subcaseName}`, 27 | `${label}.png`, at diffScreenShot (/home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:24:22) at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:3653:17
`${caseName}`,
`${subcaseName}`,
`${label}.png`,
Expand All @@ -35,7 +35,7 @@

const expectHasText = async (page: Page, text: string) => {
const hasText = (await page.getByText(text).count()) === 1;
await expect(hasText).toBe(true);

Check failure on line 38 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux-all-on-ui / check

[webkit] › tests/react.spec.ts:132:5 › reactlynx3 tests › basic › basic-setsate-with-cb

2) [webkit] › tests/react.spec.ts:132:5 › reactlynx3 tests › basic › basic-setsate-with-cb ─────── Error: expect(received).toBe(expected) // Object.is equality Expected: true Received: false 36 | const expectHasText = async (page: Page, text: string) => { 37 | const hasText = (await page.getByText(text).count()) === 1; > 38 | await expect(hasText).toBe(true); | ^ 39 | }; 40 | 41 | const expectNoText = async (page: Page, text: string) => { at expectHasText (/home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:38:25) at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:135:7
};

const expectNoText = async (page: Page, text: string) => {
Expand Down Expand Up @@ -631,7 +631,7 @@
document.querySelector('lynx-view')!.remove();
});
await wait(50);
expect(message).toContain('fin');

Check failure on line 634 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux-all-on-ui / check

[webkit] › tests/react.spec.ts:622:5 › reactlynx3 tests › apis › api-dispose

3) [webkit] › tests/react.spec.ts:622:5 › reactlynx3 tests › apis › api-dispose ────────────────── Error: expect(received).toContain(expected) // indexOf Expected value: "fin" Received array: [] 632 | }); 633 | await wait(50); > 634 | expect(message).toContain('fin'); | ^ 635 | expect(page.workers().length).toStrictEqual(1); 636 | }); 637 | at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:634:23
expect(page.workers().length).toStrictEqual(1);
});

Expand Down Expand Up @@ -979,7 +979,7 @@
globalThis.lynxView.sendGlobalEvent('event-test', ['change']);
});
await wait(200);
await expect(target).toHaveCSS(

Check failure on line 982 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux / check

[webkit] › tests/react.spec.ts:972:5 › reactlynx3 tests › apis › api-sendGlobalEvent

2) [webkit] › tests/react.spec.ts:972:5 › reactlynx3 tests › apis › api-sendGlobalEvent ────────── Error: Timed out 5000ms waiting for expect(locator).toHaveCSS(expected) Locator: locator('#target') Expected string: "rgb(0, 128, 0)" Received string: "rgb(255, 192, 203)" Call log: - expect.toHaveCSS with timeout 5000ms - waiting for locator('#target') 9 × locator resolved to <x-view l-uid="2" id="target" lynx-tag="view" l-p-comp-uid="1"></x-view> - unexpected value "rgb(255, 192, 203)" 980 | }); 981 | await wait(200); > 982 | await expect(target).toHaveCSS( | ^ 983 | 'background-color', 984 | 'rgb(0, 128, 0)', 985 | ); // green; at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:982:28
'background-color',
'rgb(0, 128, 0)',
); // green;
Expand Down Expand Up @@ -2169,7 +2169,7 @@
await page.locator('.focus').click({ force: true });
await wait(100);
const result = await page.locator('.result').first().innerText();
expect(result).toBe('bindfocus');

Check failure on line 2172 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux / check

[webkit] › tests/react.spec.ts:2163:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-focus

3) [webkit] › tests/react.spec.ts:2163:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-focus Error: expect(received).toBe(expected) // Object.is equality Expected: "bindfocus" Received: "" 2170 | await wait(100); 2171 | const result = await page.locator('.result').first().innerText(); > 2172 | expect(result).toBe('bindfocus'); | ^ 2173 | }, 2174 | ); 2175 | // input/focus test-case end at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:2172:26
},
);
// input/focus test-case end
Expand All @@ -2192,7 +2192,7 @@
await page.locator('input').press('Enter');
await wait(100);
const result = await page.locator('.result').first().innerText();
expect(result).toBe('bindconfirm');

Check failure on line 2195 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux / check

[webkit] › tests/react.spec.ts:2189:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-bindconfirm

4) [webkit] › tests/react.spec.ts:2189:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-bindconfirm Error: expect(received).toBe(expected) // Object.is equality Expected: "bindconfirm" Received: "" 2193 | await wait(100); 2194 | const result = await page.locator('.result').first().innerText(); > 2195 | expect(result).toBe('bindconfirm'); | ^ 2196 | }); 2197 | // input/bindconfirm test-case end 2198 | at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:2195:24
});
// input/bindconfirm test-case end

Expand Down Expand Up @@ -2258,9 +2258,21 @@
inputDom?.setSelectionRange(2, 5);
});
const result = await page.locator('.result').first().innerText();
expect(result).toBe('2-5');

Check failure on line 2261 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux / check

[webkit] › tests/react.spec.ts:2249:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-bindselection

5) [webkit] › tests/react.spec.ts:2249:7 › reactlynx3 tests › elements › x-input › basic-element-x-input-bindselection Error: expect(received).toBe(expected) // Object.is equality Expected: "2-5" Received: "" 2259 | }); 2260 | const result = await page.locator('.result').first().innerText(); > 2261 | expect(result).toBe('2-5'); | ^ 2262 | }, 2263 | ); 2264 | test( at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:2261:26
},
);
test(
'basic-element-x-input-input-filter',
async ({ page }, { title }) => {
await goto(page, title);
await page.locator('input').press('Enter');
await wait(200);
await page.locator('input').fill('foobar!@#)');
await wait(200);
const result = await page.locator('.result').first().innerText();
expect(result).toBe('foobar');
},
);
});
test.describe('x-overlay-ng', () => {
test('basic-element-x-overlay-ng-demo', async ({ page }, { title }) => {
Expand Down Expand Up @@ -3575,9 +3587,22 @@
textareaDom?.setSelectionRange(2, 5);
});
const result = await page.locator('.result').first().innerText();
expect(result).toBe('2-5');

Check failure on line 3590 in packages/web-platform/web-tests/tests/react.spec.ts

View workflow job for this annotation

GitHub Actions / playwright-linux-all-on-ui / check

[webkit] › tests/react.spec.ts:3576:7 › reactlynx3 tests › elements › x-textarea › basic-element-x-textarea-bindselection

4) [webkit] › tests/react.spec.ts:3576:7 › reactlynx3 tests › elements › x-textarea › basic-element-x-textarea-bindselection Error: expect(received).toBe(expected) // Object.is equality Expected: "2-5" Received: "" 3588 | }); 3589 | const result = await page.locator('.result').first().innerText(); > 3590 | expect(result).toBe('2-5'); | ^ 3591 | }, 3592 | ); 3593 | at /home/runner/_work/lynx-stack/lynx-stack/packages/web-platform/web-tests/tests/react.spec.ts:3590:26
},
);

test(
'basic-element-x-textarea-input-filter',
async ({ page }, { title }) => {
await goto(page, title);
await page.locator('textarea').press('Enter');
await wait(200);
await page.locator('textarea').fill('foobar!@#)');
await wait(200);
const result = await page.locator('.result').first().innerText();
expect(result).toBe('foobar');
},
);
});
test.describe('x-audio-tt', () => {
test('basic-element-x-audio-tt-play', async ({ page }, { title }) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2023 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { root, useState } from '@lynx-js/react';

function App() {
const value = 'bindinput';
const [result, setResult] = useState();

const onInput = ({ detail }) => {
const { value, cursor, textLength } = detail;

if (value.length !== textLength) {
throw new Error(
`input length not match. expect ${textLength}, got ${value.length}`,
);
}

setResult(value);
};

return (
<view>
<x-input
bindinput={onInput}
value={value}
style='border: 1px solid;width: 300px;height:40px'
input-filter='[^a-zA-Z0-9]'
/>
<view class='result'>
<text>{result}</text>
</view>
</view>
);
}

root.render(<App></App>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2023 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { useState, root, useEffect } from '@lynx-js/react';

function App() {
const value = 'bindinput';
const [result, setResult] = useState();

const onInput = ({ detail }) => {
const { value, cursor, textLength } = detail;

if (value.length !== textLength) {
throw new Error(
`input length not match. expect ${textLength}, got ${value.length}`,
);
}

setResult(value);
};

return (
<view class='page'>
<x-textarea
bindinput={onInput}
value={value}
style='border: 1px solid;width: 300px;height:40px'
input-filter='[^a-zA-Z0-9]'
/>
<view class='result'>
<text>{result}</text>
</view>
</view>
);
}

root.render(<App />);
Loading