Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(lr-upload-ctx-provider): import event types #563

Merged
merged 1 commit into from
Nov 30, 2023
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: 3 additions & 2 deletions blocks/Config/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class ConfigClass extends Block {

ConfigClass.bindAttributes(attrStateMapping);

export const Config = ConfigClass;
/** @typedef {import('../../utils/mixinClass.js').MixinClass<typeof ConfigClass, import('../../types').ConfigType>} Config */

/** @typedef {typeof ConfigClass & import('../../types').ConfigType} Config */
// This is workaround for jsdoc that allows us to export extended class type along with the class itself
export const Config = /** @type {Config} */ (/** @type {unknown} */ (ConfigClass));
37 changes: 35 additions & 2 deletions blocks/DataOutput/DataOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { applyStyles } from '@symbiotejs/symbiote';
* }} Output}
*/

export class DataOutput extends UploaderBlock {
class DataOutputClass extends UploaderBlock {
processInnerHtml = true;
requireCtxName = true;

Expand Down Expand Up @@ -148,7 +148,7 @@ export class DataOutput extends UploaderBlock {
}
}

DataOutput.dict = Object.freeze({
DataOutputClass.dict = Object.freeze({
SRC_CTX_KEY: '*outputData',
EVENT_NAME: 'lr-data-output',
FIRE_EVENT_ATTR: 'use-event',
Expand All @@ -158,3 +158,36 @@ DataOutput.dict = Object.freeze({
INPUT_NAME_ATTR: 'input-name',
INPUT_REQUIRED: 'input-required',
});

/**
* @typedef {import('../../utils/mixinClass.js').MixinClass<
* typeof DataOutputClass,
* {
* addEventListener(
* type: 'lr-data-output',
* listener: (
* e: CustomEvent<{
* timestamp: number;
* ctxName: string;
* data: Output;
* }>
* ) => void,
* options?: boolean | AddEventListenerOptions
* ): void;
* removeEventListener(
* type: 'lr-data-output',
* listener: (
* e: CustomEvent<{
* timestamp: number;
* ctxName: string;
* data: Output;
* }>
* ) => void,
* options?: boolean | EventListenerOptions
* ): void;
* }
* >}
* DataOutput
*/

export const DataOutput = /** @type {DataOutput} */ (/** @type {unknown} */ (DataOutputClass));
16 changes: 6 additions & 10 deletions blocks/ShadowWrapper/ShadowWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import { waitForAttribute } from '../../utils/waitForAttribute.js';
const CSS_ATTRIBUTE = 'css-src';

/**
* @template T
* @typedef {new (...args: any[]) => T} GConstructor
*/

/**
* @template {GConstructor<import('../../abstract/Block.js').Block>} T
* @template {import('../../utils/mixinClass.js').GConstructor<import('../../abstract/Block.js').Block>} T
* @param {T} Base
* @returns {{
* new (...args: ConstructorParameters<T>): InstanceType<T> & {
* @returns {import('../../utils/mixinClass.js').MixinClass<
* T,
* {
* shadowReadyCallback(): void;
* };
* } & Omit<T, 'new'>}
* }
* >}
*/
export function shadowed(Base) {
// @ts-ignore
Expand Down
40 changes: 21 additions & 19 deletions blocks/UploadCtxProvider/UploadCtxProvider.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// @ts-check

import { UploaderBlock } from '../../abstract/UploaderBlock.js';

class UploadCtxProviderClass extends UploaderBlock {
requireCtxName = true;

Expand All @@ -10,23 +9,26 @@ class UploadCtxProviderClass extends UploaderBlock {
}
}

export const UploadCtxProvider = UploadCtxProviderClass;

/**
* @typedef {typeof UploadCtxProviderClass & {
* addEventListener<
* T extends typeof import('./EventEmitter.js').EventType[keyof typeof import('./EventEmitter.js').EventType]
* >(
* type: T,
* listener: (e: CustomEvent<import('./EventEmitter.js').EventPayload[T]>) => void,
* options?: boolean | AddEventListenerOptions
* ): void;
* removeEventListener<
* T extends typeof import('./EventEmitter.js').EventType[keyof typeof import('./EventEmitter.js').EventType]
* >(
* type: T,
* listener: (e: CustomEvent<import('./EventEmitter.js').EventPayload[T]>) => void,
* options?: boolean | EventListenerOptions
* ): void;
* }} UploadCtxProvider
* @typedef {import('../../utils/mixinClass.js').MixinClass<
* typeof UploadCtxProviderClass,
* {
* addEventListener<
* T extends typeof import('./EventEmitter.js').EventType[keyof typeof import('./EventEmitter.js').EventType]
* >(
* type: T,
* listener: (e: CustomEvent<import('./EventEmitter.js').EventPayload[T]>) => void,
* options?: boolean | AddEventListenerOptions
* ): void;
* removeEventListener<
* T extends typeof import('./EventEmitter.js').EventType[keyof typeof import('./EventEmitter.js').EventType]
* >(
* type: T,
* listener: (e: CustomEvent<import('./EventEmitter.js').EventPayload[T]>) => void,
* options?: boolean | EventListenerOptions
* ): void;
* }
* >} UploadCtxProvider
*/

export const UploadCtxProvider = /** @type {UploadCtxProvider} */ (/** @type {unknown} */ (UploadCtxProviderClass));
17 changes: 11 additions & 6 deletions types/events.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import type { GlobalEventPayload } from '../blocks/UploadCtxProvider/EventEmitter';
import type { GlobalEventPayload, EventPayload } from '../blocks/UploadCtxProvider/EventEmitter';

type CustomEventMap = {
export type GlobalEventMap = {
[T in keyof GlobalEventPayload]: CustomEvent<GlobalEventPayload[T]>;
};

export type EventMap = {
[T in keyof EventPayload]: CustomEvent<EventPayload[T]>;
};

declare global {
interface Window {
addEventListener<T extends keyof CustomEventMap>(
addEventListener<T extends keyof GlobalEventMap>(
type: T,
listener: (e: CustomEventMap[T]) => void,
listener: (e: GlobalEventMap[T]) => void,
options?: boolean | AddEventListenerOptions
): void;
removeEventListener<T extends keyof CustomEventMap>(
removeEventListener<T extends keyof GlobalEventMap>(
type: T,
listener: (e: CustomEventMap[T]) => void,
listener: (e: GlobalEventMap[T]) => void,
options?: boolean | EventListenerOptions
): void;
}
Expand Down
24 changes: 12 additions & 12 deletions types/jsx.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
/// <reference types="react" />

type ConfigPlainType = import('./exported').ConfigPlainType;
type UploadCtxProvider = import('..').UploadCtxProvider;
type ConfigPlainType = import('./exported.js').ConfigPlainType;
type UploadCtxProvider = import('../index.js').UploadCtxProvider;
type Config = import('../index.js').Config;
type FileUploaderInline = import('..').FileUploaderInline;
type FileUploaderRegular = import('..').FileUploaderRegular;
type FileUploaderMinimal = import('..').FileUploaderMinimal;
type DataOutput = import('..').DataOutput;
type CloudImageEditorBlock = import('..').CloudImageEditorBlock;
type FileUploaderInline = import('../index.js').FileUploaderInline;
type FileUploaderRegular = import('../index.js').FileUploaderRegular;
type FileUploaderMinimal = import('../index.js').FileUploaderMinimal;
type DataOutput = import('../index.js').DataOutput;
type CloudImageEditorBlock = import('../index.js').CloudImageEditorBlock;
type CtxAttributes = {
'ctx-name': string;
};
Expand Down Expand Up @@ -56,17 +56,17 @@ declare namespace JSX {
'lr-cloud-image-editor-activity': any;
'lr-cloud-image-editor-block': CustomElement<
CloudImageEditorBlock,
CtxAttributes & { uuid: string; 'cdn-url': string }
CtxAttributes & ({ uuid: string } | { 'cdn-url': string }) & Partial<{ tabs: string; 'crop-preset': string }>
>;
'lr-cloud-image-editor': CustomElement<
CloudImageEditorBlock,
CtxAttributes & ShadowWrapperAttributes & { uuid: string; 'cdn-url': string }
JSX.IntrinsicElements['lr-cloud-image-editor-block'] & ShadowWrapperAttributes
>;
'lr-data-output': CustomElement<DataOutput, CtxAttributes>;
'lr-data-output': CustomElement<InstanceType<DataOutput>, CtxAttributes>;
'lr-file-uploader-regular': CustomElement<FileUploaderRegular, CtxAttributes & ShadowWrapperAttributes>;
'lr-file-uploader-minimal': CustomElement<FileUploaderMinimal, CtxAttributes & ShadowWrapperAttributes>;
'lr-file-uploader-inline': CustomElement<FileUploaderInline, CtxAttributes & ShadowWrapperAttributes>;
'lr-upload-ctx-provider': CustomElement<UploadCtxProvider, CtxAttributes>;
'lr-config': CustomElement<Config, CtxAttributes & Partial<ConfigPlainType>>;
'lr-upload-ctx-provider': CustomElement<InstanceType<UploadCtxProvider>, CtxAttributes>;
'lr-config': CustomElement<InstanceType<Config>, CtxAttributes & Partial<ConfigPlainType>>;
}
}
15 changes: 0 additions & 15 deletions types/test/events.test-d.ts

This file was deleted.

10 changes: 10 additions & 0 deletions types/test/global-events.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { expectType } from 'tsd';
import { GlobalEventPayload } from '../index.js';

window.addEventListener(
'LR_DATA_OUTPUT',
(e) => {
expectType<CustomEvent<GlobalEventPayload['LR_DATA_OUTPUT']>>(e);
},
{ once: true }
);
12 changes: 12 additions & 0 deletions types/test/lr-cloud-image-editor.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @ts-expect-error - no props
() => <lr-cloud-image-editor />;

// @ts-expect-error - no css-url
() => <lr-cloud-image-editor ctx-name="my-uploader" />;

// @ts-expect-error - no css-src
() => <lr-cloud-image-editor css-src="url" />;

() => <lr-cloud-image-editor ctx-name="my-editor" css-src="url" uuid="123124" />;
() => <lr-cloud-image-editor ctx-name="my-editor" css-src="url" uuid="123124" tabs="tab" crop-preset="preset" />;
() => <lr-cloud-image-editor ctx-name="my-editor" css-src="url" cdn-url="url" />;
35 changes: 17 additions & 18 deletions types/test/lr-config.test-d.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { expectType } from 'tsd';
import '../jsx';
import { OutputFileEntry } from '..';
import '../jsx.js';
import { OutputFileEntry } from '../index.js';

// @ts-expect-error untyped props
() => <lr-config ctx-name="1" something="wrong"></lr-config>;
Expand All @@ -17,8 +17,8 @@ import { OutputFileEntry } from '..';

// allow useRef hook
() => {
const ref = React.useRef<Config | null>(null);
expectType<Config | null>(ref.current);
const ref = React.useRef<InstanceType<Config> | null>(null);
expectType<InstanceType<Config> | null>(ref.current);
<lr-config ctx-name="1" ref={ref}></lr-config>;
};

Expand All @@ -27,15 +27,15 @@ import { OutputFileEntry } from '..';
<lr-config
ctx-name="1"
ref={(el) => {
expectType<Config | null>(el);
expectType<InstanceType<Config> | null>(el);
}}
></lr-config>;
};

// allow createRef
() => {
const ref = React.createRef<Config>();
expectType<Config | null>(ref.current);
const ref = React.createRef<InstanceType<Config>>();
expectType<InstanceType<Config> | null>(ref.current);
<lr-config ctx-name="1" ref={ref}></lr-config>;
};

Expand All @@ -44,26 +44,25 @@ import { OutputFileEntry } from '..';

// allow to use DOM properties
() => {
const ref = React.useRef<Config | null>(null);
const ref = React.useRef<InstanceType<Config> | null>(null);
if (ref.current) {
const config = ref.current;
config.metadata = {foo: 'bar'}
config.secureSignature = '1231'
config.multiple = true
config.metadata = { foo: 'bar' };
config.secureSignature = '1231';
config.multiple = true;
}
};


// allow to pass metadata
() => {
const ref = React.useRef<Config | null>(null);
const ref = React.useRef<InstanceType<Config> | null>(null);
if (ref.current) {
const config = ref.current;
config.metadata = {foo: 'bar'}
config.metadata = () => ({foo: 'bar'})
config.metadata = { foo: 'bar' };
config.metadata = () => ({ foo: 'bar' });
config.metadata = async (entry) => {
expectType<OutputFileEntry>(entry)
return {foo: 'bar'}
}
expectType<OutputFileEntry>(entry);
return { foo: 'bar' };
};
}
};
18 changes: 18 additions & 0 deletions types/test/lr-data-output.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { expectType } from 'tsd';
import { DataOutput } from '../../index.js';
import { Output } from '../../blocks/DataOutput/DataOutput.js';

() => <lr-data-output ctx-name="my-uploader" />;

const dataOutput = new DataOutput();
dataOutput.addEventListener('lr-data-output', (e) => {
expectType<
CustomEvent<{
timestamp: number;
ctxName: string;
data: Output;
}>
>(e);
});

dataOutput.validationInput;
27 changes: 27 additions & 0 deletions types/test/lr-upload-ctx-provider.test-d.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expectType } from 'tsd';
import { EventMap, UploadCtxProvider } from '../../index.js';
import { useRef } from 'react';

const instance = new UploadCtxProvider();

instance.addFileFromUrl('https://example.com/image.png');
instance.uploadCollection.size;
instance.setOrAddState('fileId', 'uploading');

instance.addEventListener('data-output', (e) => {
expectType<EventMap['data-output']>(e);

// @ts-expect-error - wrong event type
expectType<EventMap['init-flow']>(e);
});

const onDataOutput = (e: EventMap['data-output']) => {
// noop
};

instance.addEventListener('data-output', onDataOutput);

() => {
const ref = useRef<InstanceType<UploadCtxProvider>>(null);
return <lr-upload-ctx-provider ctx-name="ctx" ref={ref}></lr-upload-ctx-provider>;
};
18 changes: 18 additions & 0 deletions utils/mixinClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @template T
* @typedef {new (...args: any[]) => T} GConstructor
*/

/**
* This is a helper to create a class type extended with the provided set of instance properties. It's useful when there
* are some dynamic generated properties or native overrides in the class. We're use it to define dynamic access
* properties and events to subscribe to.
*
* @template {GConstructor<HTMLElement>} Base
* @template {Record<string, any>} [InstanceProperties={}] Default is `{}`
* @typedef {{
* new (...args: ConstructorParameters<Base>): InstanceProperties & InstanceType<Base>;
* } & Omit<Base, 'new'>} MixinClass
*/

export {};
Loading