Skip to content

Commit

Permalink
fix #1275 (#1308)
Browse files Browse the repository at this point in the history
* fix #1275

* chore: stylish fixes

* chore: fixed naming

* fix: fixes after refactoring

* chore: added new tests

* Sync test for teleports (#1356)

* tests(p-v4-components-demo): accept search param for goto

* chore(i-static-page): remove console.log

* fix(core/component/watch): fix flush post watchers

* tests(i-block): fix sync className test for teleport

---------

Co-authored-by: kobezzza <kobezzza@gmail.com>
Co-authored-by: Artem Shinkaruk <46344555+shining-mind@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 26, 2024
1 parent 1176200 commit e616ee9
Show file tree
Hide file tree
Showing 27 changed files with 505 additions and 104 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ Changelog
_Note: Gaps between patch versions are faulty, broken or test releases._

## v4.0.0-beta.?? (2024-07-??)

#### :bug: Bug Fix

* `core/component/init`:
* Fixed a typo in the event name `hookChange` which is responsible for processing activation and deactivation in the component
* Amended the deactivation sequence within the component to ensure that children are deactivated first

* Fixed an issue to prevent the `hookChange` event from bubbling up `bDynamicPage`

## v4.0.0-beta.114 (2024-07-24)

#### :house: Internal
Expand Down
76 changes: 75 additions & 1 deletion components-lock.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"hash": "23d17036073396142d81f3c91143966489f3337fbfb97bd1a3362b9cfb87c889",
"hash": "5ccda0c56681f6df98bcce9b5a48a424b09f104f6e403b75c7dd9ac02a30e93d",
"data": {
"%data": "%data:Map",
"%data:Map": [
Expand Down Expand Up @@ -1387,6 +1387,44 @@
"etpl": null
}
],
[
"b-super-i-block-deactivation-dummy",
{
"index": "src/components/super/i-block/test/b-super-i-block-deactivation-dummy/index.js",
"declaration": {
"name": "b-super-i-block-deactivation-dummy",
"parent": "i-block",
"dependencies": [
"b-button",
"b-bottom-slide"
],
"libs": []
},
"name": "b-super-i-block-deactivation-dummy",
"parent": "i-block",
"dependencies": [
"b-button",
"b-bottom-slide"
],
"libs": [],
"resolvedLibs": {
"%data": "%data:Set",
"%data:Set": []
},
"resolvedOwnLibs": {
"%data": "%data:Set",
"%data:Set": []
},
"type": "block",
"mixin": false,
"logic": "src/components/super/i-block/test/b-super-i-block-deactivation-dummy/b-super-i-block-deactivation-dummy.ts",
"styles": [
"src/components/super/i-block/test/b-super-i-block-deactivation-dummy/b-super-i-block-deactivation-dummy.styl"
],
"tpl": "src/components/super/i-block/test/b-super-i-block-deactivation-dummy/b-super-i-block-deactivation-dummy.ss",
"etpl": null
}
],
[
"b-super-i-block-decorators-dummy",
{
Expand Down Expand Up @@ -1515,6 +1553,42 @@
"etpl": null
}
],
[
"b-super-i-block-teleport-dummy",
{
"index": "src/components/super/i-block/test/b-super-i-block-teleport-dummy/index.js",
"declaration": {
"name": "b-super-i-block-teleport-dummy",
"parent": "i-block",
"dependencies": [
"b-bottom-slide"
],
"libs": []
},
"name": "b-super-i-block-teleport-dummy",
"parent": "i-block",
"dependencies": [
"b-bottom-slide"
],
"libs": [],
"resolvedLibs": {
"%data": "%data:Set",
"%data:Set": []
},
"resolvedOwnLibs": {
"%data": "%data:Set",
"%data:Set": []
},
"type": "block",
"mixin": false,
"logic": "src/components/super/i-block/test/b-super-i-block-teleport-dummy/b-super-i-block-teleport-dummy.ts",
"styles": [
"src/components/super/i-block/test/b-super-i-block-teleport-dummy/b-super-i-block-teleport-dummy.styl"
],
"tpl": "src/components/super/i-block/test/b-super-i-block-teleport-dummy/b-super-i-block-teleport-dummy.ss",
"etpl": null
}
],
[
"b-super-i-block-watch-dummy",
{
Expand Down
6 changes: 6 additions & 0 deletions src/components/base/b-dynamic-page/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ Changelog
> - :house: [Internal]
> - :nail_care: [Polish]
## v4.0.0-beta.?? (2024-07-??)

#### :bug: Bug Fix

* Fixed an issue to prevent the `hookChange` event from bubbling up

## v4.0.0-beta.112 (2024-07-22)

#### :bug: Bug Fix
Expand Down
2 changes: 1 addition & 1 deletion src/components/base/b-dynamic-page/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ to its inner page component.

## Catching Events of the Inner Page Component

By default, `bDynamicPage` dispatches all events from the inner page component.
By default, `bDynamicPage` dispatches all events from the inner page component, except `hookChange` and `hook:*` events.

```
/// `initLoad` is caught from the inner page component
Expand Down
4 changes: 2 additions & 2 deletions src/components/base/b-dynamic-page/b-dynamic-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,8 @@ export default class bDynamicPage extends iDynamicPage {
return component.reload(params);
}

override canSelfDispatchEvent(_: string): boolean {
return true;
override canSelfDispatchEvent(event: string): boolean {
return !/^hook(?::\w+(-\w+)*|-change)$/.test(event.dasherize());
}

/**
Expand Down
63 changes: 0 additions & 63 deletions src/components/friends/block/class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,69 +81,6 @@ class Block extends Friend {
Object.entries(component.mods).forEach(([name, val]) => {
this.setMod(name, val, 'initSetMod');
});

const {
node,
ctx: {
$el: originalNode,
$async: $a
}
} = this;

const
mountedAttrs = new Set<string>(),
mountedAttrsGroup = {group: 'mountedAttrs'};

if (originalNode != null && node != null && originalNode !== node) {
Object.defineProperty(this.ctx, '$el', {
configurable: true,
get: () => node
});

node.component = component;
mountAttrs(this.ctx.$attrs);

this.ctx.watch('$attrs', {deep: true}, (attrs) => {
$a.terminateWorker(mountedAttrsGroup);
mountAttrs(attrs);
});
}

function mountAttrs(attrs: Dictionary<string>) {
if (node == null || originalNode == null) {
return;
}

Object.entries(attrs).forEach(([name, attr]) => {
if (attr == null) {
return;
}

if (name === 'class') {
attr.split(/\s+/).forEach((val) => {
node.classList.add(val);
mountedAttrs.add(`class.${val}`);
});

} else if (originalNode.hasAttribute(name)) {
node.setAttribute(name, attr);
mountedAttrs.add(name);
}
});

$a.worker(() => {
mountedAttrs.forEach((attr) => {
if (attr.startsWith('class.')) {
node.classList.remove(attr.split('.')[1]);

} else {
node.removeAttribute(attr);
}
});

mountedAttrs.clear();
}, mountedAttrsGroup);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@
- include 'components/super/i-static-page/i-static-page.component.ss'|b as placeholder

- template index() extends ['i-static-page.component'].index
- block body
< template v-if = stage === 'teleports'
< b-bottom-slide
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @packageDocumentation
*/

import iStaticPage, { component, prop, field, system } from 'components/super/i-static-page/i-static-page';
import iStaticPage, { component, prop, field, system, hook } from 'components/super/i-static-page/i-static-page';
import VDOM, * as VDOMAPI from 'components/friends/vdom';

export * from 'components/super/i-static-page/i-static-page';
Expand Down Expand Up @@ -42,4 +42,13 @@ export default class pV4ComponentsDemo extends iStaticPage {
*/
@field()
someField: unknown = 'foo';

@hook('beforeCreate')
setStageFromLocation(): void {
const matches = /stage=(.*)/.exec(globalThis.location.search);

if (matches != null) {
this.stage = decodeURIComponent(matches[1]);
}
}
}
12 changes: 10 additions & 2 deletions src/components/pages/p-v4-components-demo/test/api/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,30 @@ export default class DemoPage {
return '';
}

/**
* Name of the HTML file
*/
protected pageFileName: string;

/**
* @param page
* @param baseUrl
*/
constructor(page: Page, baseUrl: string) {
this.page = page;
this.baseUrl = baseUrl;
this.pageFileName = build.demoPage();
}

/**
* Opens a demo page
* @param [query] - query parameters for the URL, i.e. `a=1&b=1`
*/
async goto(): Promise<DemoPage> {
async goto(query: string = ''): Promise<DemoPage> {
const
root = this.page.locator('#root-component');

await this.page.goto(concatURLs(this.baseUrl, `${build.demoPage()}.html`), {waitUntil: 'networkidle'});
await this.page.goto(concatURLs(this.baseUrl, `${this.pageFileName}.html`) + (query.length > 0 ? `?${query}` : ''), {waitUntil: 'networkidle'});
await root.waitFor({state: 'attached'});

this.component = await root.evaluateHandle((ctx) => ctx.component);
Expand Down
8 changes: 4 additions & 4 deletions src/components/super/i-block/base/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,16 +264,16 @@ export default class bExample extends iBlock {
Activates the component.
The deactivated component won't load data from its providers during initializing.

Basically, you don't need to think about the component activation,
because it's automatically synchronized with `keep-alive` or the component prop.
Essentially, you don't need to worry about component activation,
as it automatically synchronizes with the `keep-alive` mode or a specific component prop.

#### deactivate

Deactivates the component.
The deactivated component won't load data from its providers during initializing.

Basically, you don't need to think about the component activation,
because it's automatically synchronized with `keep-alive` or the component prop.
Essentially, you don't need to worry about component activation,
as it automatically synchronizes with the `keep-alive` mode or a specific component prop.

#### watch

Expand Down
82 changes: 81 additions & 1 deletion src/components/super/i-block/i-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* @packageDocumentation
*/

import { component, UnsafeGetter } from 'core/component';
import { component, hook, watch, UnsafeGetter } from 'core/component';
import type { Classes } from 'components/friends/provide';

import type { ModVal, ModsDecl, ModsProp, ModsDict } from 'components/super/i-block/modules/mods';
Expand All @@ -24,6 +24,8 @@ import('components/super/i-block/test/b-super-i-block-dummy');
import('components/super/i-block/test/b-super-i-block-watch-dummy');
import('components/super/i-block/test/b-super-i-block-lfc-dummy');
import('components/super/i-block/test/b-super-i-block-destructor-dummy');
import('components/super/i-block/test/b-super-i-block-deactivation-dummy');
import('components/super/i-block/test/b-super-i-block-teleport-dummy');
//#endif

export * from 'core/component';
Expand Down Expand Up @@ -82,4 +84,82 @@ export default abstract class iBlock extends iBlockProviders {
isComponent<T extends iBlock>(obj: unknown, constructor?: {new(): T} | Function): obj is T {
return Object.isTruly(obj) && (<Dictionary>obj).instance instanceof (constructor ?? iBlock);
}

/**
* Handler: fixes the issue where the teleported component
* and its DOM nodes were rendered before the teleport container was ready
*/
@watch({
path: 'r.shouldMountTeleports',
flush: 'post'
})

@hook('before:mounted')
protected onMountTeleports(): void {
const getNode = () => this.$refs[this.$resolveRef('$el')] ?? this.$el;

const {
$el: originalNode,
$async: $a
} = this;

const
node = getNode(),
mountedAttrs = new Set<string>(),
mountedAttrsGroup = {group: 'mountedAttrs'};

if (originalNode != null && node != null && originalNode !== node) {
// Fix the DOM element link to the component
originalNode.component = this;

// Fix the teleported DOM element link to the component
node.component = this;

Object.defineProperty(this.unsafe, '$el', {
configurable: true,
get: () => node
});

mountAttrs(this.$attrs);
this.watch('$attrs', {deep: true}, mountAttrs);
}

function mountAttrs(attrs: Dictionary<string>) {
$a.terminateWorker(mountedAttrsGroup);

if (node == null || originalNode == null) {
return;
}

Object.entries(attrs).forEach(([name, attr]) => {
if (attr == null) {
return;
}

if (name === 'class') {
attr.split(/\s+/).forEach((val) => {
node.classList.add(val);
mountedAttrs.add(`class.${val}`);
});

} else if (originalNode.hasAttribute(name)) {
node.setAttribute(name, attr);
mountedAttrs.add(name);
}
});

$a.worker(() => {
mountedAttrs.forEach((attr) => {
if (attr.startsWith('class.')) {
node.classList.remove(attr.split('.')[1]);

} else {
node.removeAttribute(attr);
}
});

mountedAttrs.clear();
}, mountedAttrsGroup);
}
}
}
Loading

0 comments on commit e616ee9

Please sign in to comment.