From 6ae92fa4b3690c815d3ae28b0f86f4410547b77e Mon Sep 17 00:00:00 2001 From: Bruno Boi Date: Mon, 15 Nov 2021 15:05:44 +0100 Subject: [PATCH] [IMP] hooks: reintroduce useExternalListener --- src/hooks.ts | 29 +++++++++++++++++ src/index.ts | 2 +- .../__snapshots__/hooks.test.ts.snap | 31 +++++++++++++++++++ tests/components/hooks.test.ts | 23 +++++++------- 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/hooks.ts b/src/hooks.ts index 07b34360..79e8f84f 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -90,3 +90,32 @@ export function useEffect(effect: Effect, computeDependencies: () => any[] = () onWillUnmount(() => cleanup()); } + +// ----------------------------------------------------------------------------- +// useExternalListener +// ----------------------------------------------------------------------------- + +/** + * When a component needs to listen to DOM Events on element(s) that are not + * part of his hierarchy, we can use the `useExternalListener` hook. + * It will correctly add and remove the event listener, whenever the + * component is mounted and unmounted. + * + * Example: + * a menu needs to listen to the click on window to be closed automatically + * + * Usage: + * in the constructor of the OWL component that needs to be notified, + * `useExternalListener(window, 'click', this._doSomething);` + * */ +export function useExternalListener( + target: HTMLElement | typeof window, + eventName: string, + handler: EventListener, + eventParams?: AddEventListenerOptions +) { + const node = getCurrent()!; + const boundHandler = handler.bind(node.component); + onMounted(() => target.addEventListener(eventName, boundHandler, eventParams)); + onWillUnmount(() => target.removeEventListener(eventName, boundHandler, eventParams)); +} diff --git a/src/index.ts b/src/index.ts index a7ac8a97..e46bf8ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,7 +57,7 @@ export { Portal } from "./misc/portal"; export { Memo } from "./misc/memo"; export { css, xml } from "./tags"; export { useState } from "./reactivity"; -export { useRef, useEnv, useSubEnv, useEffect } from "./hooks"; +export { useEffect, useEnv, useExternalListener, useRef, useSubEnv } from "./hooks"; export const utils = { EventBus, whenReady, loadFile }; export { diff --git a/tests/components/__snapshots__/hooks.test.ts.snap b/tests/components/__snapshots__/hooks.test.ts.snap index 16f52263..d7cdad79 100644 --- a/tests/components/__snapshots__/hooks.test.ts.snap +++ b/tests/components/__snapshots__/hooks.test.ts.snap @@ -212,6 +212,37 @@ exports[`hooks useEffect hook effect with empty dependency list never reruns 1`] }" `; +exports[`hooks useExternalListener 1`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let d1 = ctx['props'].value; + return block1([d1]); + } +}" +`; + +exports[`hooks useExternalListener 2`] = ` +"function anonymous(bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component } = bdom; + let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; + + return function template(ctx, node, key = \\"\\") { + let b2; + if (ctx['state'].flag) { + b2 = component(\`MyComponent\`, {}, key + \`__1\`, node, ctx); + } + return multi([b2]); + } +}" +`; + exports[`hooks useRef hook: basic use 1`] = ` "function anonymous(bdom, helpers ) { diff --git a/tests/components/hooks.test.ts b/tests/components/hooks.test.ts index 45494d23..c2acef96 100644 --- a/tests/components/hooks.test.ts +++ b/tests/components/hooks.test.ts @@ -2,19 +2,20 @@ import { App, Component, mount, - useRef, - useState, - useComponent, - useEnv, - useSubEnv, - useEffect, onMounted, onPatched, - onWillStart, - onWillUpdateProps, onWillPatch, - xml, + onWillStart, onWillUnmount, + onWillUpdateProps, + useComponent, + useEffect, + useEnv, + useExternalListener, + useRef, + useState, + useSubEnv, + xml, } from "../../src/index"; import { makeTestFixture, nextTick, snapshotEverything } from "../helpers"; @@ -287,13 +288,13 @@ describe("hooks", () => { ]); }); - test.skip("useExternalListener", async () => { + test("useExternalListener", async () => { let n = 0; class MyComponent extends Component { static template = xml``; setup() { - //useExternalListener(window as any, "click", this.increment); + useExternalListener(window, "click", this.increment); } increment() { n++;