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

Add TypeScript for extending JSX types with custom elements #2581

Merged
merged 5 commits into from
Jun 14, 2020
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
12 changes: 9 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
"prop-types": "^15.7.2",
"sinon": "^6.1.3",
"sinon-chai": "^3.0.0",
"typescript": "^3.0.1",
"typescript": "3.5.3",
"webpack": "^4.3.0"
}
}
2 changes: 1 addition & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ declare namespace preact {
props: P & { children: ComponentChildren };
key: Key;
/**
* ref is not guaranteed by React.ReactElement, for compatiblity reasons
* ref is not guaranteed by React.ReactElement, for compatibility reasons
* with popular react libs we define it as optional too
*/
ref?: Ref<any> | null;
Expand Down
7 changes: 1 addition & 6 deletions test/ts/Component-test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import 'mocha';
import { expect } from 'chai';
import {
createElement,
Component,
RenderableProps,
Fragment
} from '../../src/';
import { createElement, Component, RenderableProps, Fragment } from '../../';

// Test `this` binding on event handlers
function onHandler(this: HTMLInputElement, event: any) {
Expand Down
2 changes: 1 addition & 1 deletion test/ts/VNode-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
VNode,
ComponentChildren,
cloneElement
} from '../../src';
} from '../../';

function getDisplayType(vnode: VNode | string | number) {
if (typeof vnode === 'string' || typeof vnode == 'number') {
Expand Down
85 changes: 85 additions & 0 deletions test/ts/custom-elements.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { createElement, Component, createContext } from '../../';

declare module '../../' {
namespace createElement.JSX {
interface IntrinsicElements {
// Custom element can use JSX EventHandler definitions
'clickable-ce': {
optionalAttr?: string;
onClick?: MouseEventHandler<HTMLElement>;
};

// Custom Element that extends HTML attributes
'color-picker': HTMLAttributes & {
// Required attribute
space: 'rgb' | 'hsl' | 'hsv';
// Optional attribute
alpha?: boolean;
};

// Custom Element with custom interface definition
'custom-whatever': WhateveElAttributes;
}
}
}

// Whatever Element definition

interface WhateverElement {
instanceProp: string;
}

interface WhateverElementEvent {
eventProp: number;
}

// preact.JSX.HTMLAttributes also appears to work here but for consistency,
// let's use createElement.JSX
interface WhateveElAttributes extends createElement.JSX.HTMLAttributes {
someattribute?: string;
onsomeevent?: (this: WhateverElement, ev: WhateverElementEvent) => void;
}

// Ensure context still works
const { Provider, Consumer } = createContext({ contextValue: '' });

// Sample component that uses custom elements

class SimpleComponent extends Component {
componentProp = 'componentProp';
render() {
// Render inside div to ensure standard JSX elements still work
return (
<Provider value={{ contextValue: 'value' }}>
<div>
<clickable-ce
onClick={e => {
// `this` should be instance of SimpleComponent since this is an
// arrow function
console.log(this.componentProp);

// Validate `currentTarget` is HTMLElement
console.log('clicked ', e.currentTarget.style.display);
}}
></clickable-ce>
<color-picker space="rgb" dir="rtl"></color-picker>
<custom-whatever
dir="auto" // Inherited prop from HTMLAttributes
someattribute="string"
onsomeevent={function(e) {
// Validate `this` and `e` are the right type
console.log('clicked', this.instanceProp, e.eventProp);
}}
></custom-whatever>

{/* Ensure context still works */}
<Consumer>
{({ contextValue }) => contextValue.toLowerCase()}
</Consumer>
</div>
</Provider>
);
}
}

const component = <SimpleComponent />;
2 changes: 1 addition & 1 deletion test/ts/hoc-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
ComponentFactory,
ComponentConstructor,
Component
} from '../../src';
} from '../../';
import { SimpleComponent, SimpleComponentProps } from './Component-test';

export interface highlightedProps {
Expand Down
2 changes: 1 addition & 1 deletion test/ts/jsx-namespacce-test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createElement, Component } from '../../src';
import { createElement, Component } from '../../';

// declare global JSX types that should not be mixed with preact's internal types
declare global {
Expand Down
5 changes: 3 additions & 2 deletions test/ts/preact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
FunctionalComponent,
AnyComponent,
h
} from '../../src';
} from '../../';

interface DummyProps {
initialInput: string;
Expand Down Expand Up @@ -207,7 +207,8 @@ class DefaultPropsWithUnion extends Component<
| {
type: 'number';
num: number;
})
}
)
> {
static defaultProps = {
default: true
Expand Down
2 changes: 1 addition & 1 deletion test/ts/refs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
Fragment,
RefObject,
RefCallback
} from '../../src';
} from '../../';

// Test Fixtures
const Foo: FunctionalComponent = () => <span>Foo</span>;
Expand Down