Skip to content

Commit

Permalink
Convert tests to typescript (#12)
Browse files Browse the repository at this point in the history
* Convert tests to TypeScript

* Remove some `@ts-expect-error` comments
  • Loading branch information
koddsson authored Apr 8, 2024
1 parent bc6abf4 commit 6e4a897
Show file tree
Hide file tree
Showing 19 changed files with 828 additions and 300 deletions.
25 changes: 12 additions & 13 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ name: Node.js CI

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

jobs:
build:

runs-on: ubuntu-latest

strategy:
Expand All @@ -20,13 +19,13 @@ jobs:
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npx playwright install
- run: npm run build --if-present
- run: npm test
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run build --if-present
- run: npm test
70 changes: 53 additions & 17 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ObservableMap } from "./observable-map.js";
import { createContext, ContextEvent, UnknownContext } from "./context-protocol.js";
import {
createContext,
ContextEvent,
UnknownContext,
} from "./context-protocol.js";

interface CustomElement extends HTMLElement {
new (...args: any[]): CustomElement;
observedAttributes: string[];
export interface CustomElement extends Element {
connectedCallback?(): void;
attributeChangedCallback?(
name: string,
Expand All @@ -12,26 +14,47 @@ interface CustomElement extends HTMLElement {
): void;
disconnectedCallback?(): void;
adoptedCallback?(): void;
formAssociatedCallback?(form: HTMLFormElement): void;
formDisabledCallback?(disabled: boolean): void;
formResetCallback?(): void;
formStateRestoreCallback?(
state: unknown,
reason: "autocomplete" | "restore",
): void;
}

export function ProviderMixin(Class: CustomElement): CustomElement {
export declare type Constructor<T> = new (...args: any[]) => T;

type ProviderElement = CustomElement & {
contexts?: Record<PropertyKey, () => unknown>;
updateContext?(name: string, value: unknown): void;
};

type ConsumerElement = CustomElement & {
contexts?: Record<PropertyKey, (data: any) => void>;
};

export function ProviderMixin<T extends Constructor<ProviderElement>>(
Class: T,
): T & Constructor<ProviderElement> {
return class extends Class {
#dataStore = new ObservableMap();

connectedCallback() {
super.connectedCallback?.();

// @ts-expect-error todo
for (const [key, value] of Object.entries(this.contexts || {})) {
// @ts-expect-error todo
const contexts = "contexts" in this ? this.contexts : {};

for (const [key, value] of Object.entries(contexts || {})) {
this.#dataStore.set(key, value());
}

this.addEventListener('context-request', this);
this.addEventListener("context-request", this);
}

disconnectedCallback(): void {
this.#dataStore = new ObservableMap();
this.removeEventListener("context-request", this);
}

handleEvent(event: Event) {
Expand Down Expand Up @@ -72,15 +95,29 @@ export function ProviderMixin(Class: CustomElement): CustomElement {
};
}

export function ConsumerMixin(Class: CustomElement): CustomElement {
export function ConsumerMixin<T extends Constructor<ConsumerElement>>(
Class: T,
): T & Constructor<ConsumerElement> {
return class extends Class {
unsubscribes: Array<() => void> = [];
#unsubscribes: Array<() => void> = [];

getContext(contextName: string) {
let result;

this.dispatchEvent(
new ContextEvent(createContext(contextName), (data) => {
result = data;
}),
);

return result;
}

connectedCallback() {
super.connectedCallback?.();

// @ts-expect-error don't worry about it babe
for (const [contextName, callback] of Object.entries(this.contexts)) {
const contexts = "contexts" in this ? this.contexts : {};
for (const [contextName, callback] of Object.entries(contexts || {})) {
const context = createContext(contextName);

// We dispatch a event with that context. The event will bubble up the tree until it
Expand All @@ -90,10 +127,9 @@ export function ConsumerMixin(Class: CustomElement): CustomElement {
new ContextEvent(
context,
(data, unsubscribe) => {
// @ts-expect-error
callback(data);
if (unsubscribe) {
this.unsubscribes.push(unsubscribe);
this.#unsubscribes.push(unsubscribe);
}
},
// Always subscribe. Consumers can ignore updates if they'd like.
Expand All @@ -105,12 +141,12 @@ export function ConsumerMixin(Class: CustomElement): CustomElement {

// Unsubscribe from all callbacks when disconnecting
disconnectedCallback() {
for (const unsubscribe of this.unsubscribes) {
for (const unsubscribe of this.#unsubscribes) {
unsubscribe?.();
}
// Empty out the array in case this element is still stored in memory but just not connected
// to the DOM.
this.unsubscribes = [];
this.#unsubscribes = [];
}
};
}
1 change: 1 addition & 0 deletions observable-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export class ObservableMap {
set(key: string, value: unknown, subscribers = new Set<Subscriber<unknown>>()) {
const data = this.#store.get(key);
subscribers = new Set([...subscribers, ...(data?.subscribers || new Set())]);

this.#store.set(key, {value, subscribers});
for (const subscriber of subscribers) {
subscriber(value);
Expand Down
Loading

0 comments on commit 6e4a897

Please sign in to comment.