From a3da42076e18f856b7ddd3c54a1f86fe2b825417 Mon Sep 17 00:00:00 2001 From: Alex Plex Date: Thu, 29 Aug 2024 12:28:49 +0500 Subject: [PATCH 1/2] fixes events on links --- examples/.gitignore | 2 +- packages/platform-browser/src/constants.ts | 1 + packages/platform-browser/src/dom/dom.ts | 7 +++++++ packages/platform-browser/src/events/events.ts | 9 +++++---- packages/platform-server/src/dom/dom.ts | 10 ++++++++-- packages/web-router/src/link/link.tsx | 6 ++++-- 6 files changed, 26 insertions(+), 9 deletions(-) diff --git a/examples/.gitignore b/examples/.gitignore index 2e5b866b..73897bbf 100644 --- a/examples/.gitignore +++ b/examples/.gitignore @@ -1,4 +1,4 @@ /dev +/dev-ssr /dev-native -/dev-server /dev-desktop diff --git a/packages/platform-browser/src/constants.ts b/packages/platform-browser/src/constants.ts index 74631deb..e74e1ec8 100644 --- a/packages/platform-browser/src/constants.ts +++ b/packages/platform-browser/src/constants.ts @@ -14,3 +14,4 @@ export const AS_ATTR = '_as'; export const DANGER_HTML_ATTR = '__danger'; export const EXCLUDE_ATTR_MARK = '$'; export const DASH_MARK = '-'; +export const PREVENT = '__prevent'; diff --git a/packages/platform-browser/src/dom/dom.ts b/packages/platform-browser/src/dom/dom.ts index fbcf79d4..ad686cde 100644 --- a/packages/platform-browser/src/dom/dom.ts +++ b/packages/platform-browser/src/dom/dom.ts @@ -48,6 +48,7 @@ import { EXCLUDE_ATTR_MARK, DANGER_HTML_ATTR, DASH_MARK, + PREVENT, } from '../constants'; import type { NativeElement, @@ -163,6 +164,11 @@ function performAttribute( return null; } + if (attrName === PREVENT) { + tagElement[PREVENT] = true; + return null; + } + if (attrName === AS_ATTR) { attrName = attrName.slice(1, AS_ATTR.length); } @@ -282,6 +288,7 @@ function commitCreation(fiber: Fiber) { } detectIsTagVirtualNode(fiber.inst) && addAttributes(fiber.element, fiber.inst, isHydration); + fiber.element.parentElement?.[PREVENT] && (fiber.element[PREVENT] = true); } function commitUpdate(fiber: Fiber) { diff --git a/packages/platform-browser/src/events/events.ts b/packages/platform-browser/src/events/events.ts index 6ab25488..5e765c4d 100644 --- a/packages/platform-browser/src/events/events.ts +++ b/packages/platform-browser/src/events/events.ts @@ -1,12 +1,13 @@ import { detectIsFunction, $$scope, detectIsArray } from '@dark-engine/core'; import type { TagNativeElement } from '../native-element'; +import { PREVENT } from '../constants'; export type EventHandler = | ((e: SyntheticEvent) => void) | [(...args: Array) => void, ...args: Array]; -type BrowserEventConstructor = (type: string, event: Event) => void; +type BrowserEvent = (type: string, event: Event) => void; class SyntheticEvent { type = ''; @@ -46,6 +47,8 @@ function delegateEvent(target: Element, eventName: string, handler: EventHandler const target = event.target as TagNativeElement; let $event: SyntheticEvent = null; + target[PREVENT] && event.preventDefault(); + if (detectIsFunction(handler)) { $event = new SyntheticEvent({ sourceEvent: event, target }); @@ -58,9 +61,7 @@ function delegateEvent(target: Element, eventName: string, handler: EventHandler const shouldPropagate = $event ? $event.getPropagation() : true; if (shouldPropagate) { - const constructor = event.constructor as BrowserEventConstructor; - - target.parentElement.dispatchEvent(new constructor(event.type, event)); + target.parentElement.dispatchEvent(new (event.constructor as BrowserEvent)(event.type, event)); } } }; diff --git a/packages/platform-server/src/dom/dom.ts b/packages/platform-server/src/dom/dom.ts index 3d75156f..d98d62b2 100644 --- a/packages/platform-server/src/dom/dom.ts +++ b/packages/platform-server/src/dom/dom.ts @@ -20,7 +20,13 @@ import { createReplacer, detectIsTextBased, } from '@dark-engine/core'; -import { type AttributeValue, VALUE_ATTR, TEXTAREA_TAG, detectIsVoidElement } from '@dark-engine/platform-browser'; +import { + type AttributeValue, + VALUE_ATTR, + TEXTAREA_TAG, + PREVENT, + detectIsVoidElement, +} from '@dark-engine/platform-browser'; import { NativeElement, TagNativeElement, TextNativeElement, CommentNativeElement } from '../native-element'; @@ -43,7 +49,7 @@ function addAttributes(element: NativeElement, vNode: TagVirtualNode) { for (const attrName in vNode.attrs) { const attrValue = vNode.attrs[attrName]; - if (attrName === REF_ATTR || detectIsFunction(attrValue)) { + if (attrName === REF_ATTR || attrName === PREVENT || detectIsFunction(attrValue)) { continue; } else if (!detectIsUndefined(attrValue) && !ATTR_BLACK_LIST[attrName]) { !patchAttributes(tagElement, attrName, attrValue) && tagElement.setAttribute(attrName, attrValue); diff --git a/packages/web-router/src/link/link.tsx b/packages/web-router/src/link/link.tsx index 65e250b0..6c35f64b 100644 --- a/packages/web-router/src/link/link.tsx +++ b/packages/web-router/src/link/link.tsx @@ -1,5 +1,5 @@ import { type DarkElement, type Ref, component, useEvent, detectIsFunction } from '@dark-engine/core'; -import { type SyntheticEvent, type DarkJSX } from '@dark-engine/platform-browser'; +import { type SyntheticEvent, type DarkJSX, PREVENT } from '@dark-engine/platform-browser'; import { useHistory } from '../use-history'; @@ -21,7 +21,7 @@ const Link = component( }); return ( - + {slot} ); @@ -31,4 +31,6 @@ const Link = component( }, ); +const prevent = { [PREVENT]: true }; + export { Link }; From 801c48e668c0222f454dc0c7f23920014175b258 Mon Sep 17 00:00:00 2001 From: Alex Plex Date: Thu, 29 Aug 2024 19:42:47 +0500 Subject: [PATCH 2/2] adds test for link --- packages/web-router/src/link/link.spec.tsx | 59 +++++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/packages/web-router/src/link/link.spec.tsx b/packages/web-router/src/link/link.spec.tsx index eb8c1db8..b9cf770a 100644 --- a/packages/web-router/src/link/link.spec.tsx +++ b/packages/web-router/src/link/link.spec.tsx @@ -112,7 +112,8 @@ describe('@web-router/link', () => { expect(host.innerHTML).toMatchInlineSnapshot(`"first"`); }); - test('prevent default click event', () => { + test('prevents default click event #1', () => { + const spy = jest.fn(); const routes: Routes = [ { path: '', @@ -120,12 +121,8 @@ describe('@web-router/link', () => { }, ]; - let defaultPrevented = false; - const App = component(() => { - const handleClick = (e: SyntheticEvent) => { - defaultPrevented = e.sourceEvent.defaultPrevented; - }; + const handleClick = (e: SyntheticEvent) => spy(e.sourceEvent.defaultPrevented); return ( @@ -144,6 +141,54 @@ describe('@web-router/link', () => { expect(host.innerHTML).toMatchInlineSnapshot(`"first"`); click(host.querySelector('a')); - expect(defaultPrevented).toBe(true); + expect(spy).toHaveBeenCalledWith(true); + }); + + test('prevents default click event #2', () => { + // https://github.com/atellmer/dark/issues/77 + const spy = jest.fn(); + const routes: Routes = [ + { + path: '', + component: component(() => ), + }, + { + path: 'page', + component: component(() => ), + }, + ]; + const App = component(() => { + const handleClick = (e: SyntheticEvent) => spy(e.sourceEvent.defaultPrevented); + + return ( + + {slot => { + return ( + <> + +
+
+ page +
+
+ + {slot} + + ); + }} +
+ ); + }); + + render(); + expect(host.innerHTML).toMatchInlineSnapshot( + `"
page
"`, + ); + + click(host.querySelector('[data-link]')); + expect(host.innerHTML).toMatchInlineSnapshot( + `"
page
"`, + ); + expect(spy).toHaveBeenCalledWith(true); }); });