diff --git a/types/react-dom/index.d.ts b/types/react-dom/index.d.ts index 13e672e812a166..f1550655fc4d20 100644 --- a/types/react-dom/index.d.ts +++ b/types/react-dom/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for React (react-dom) 16.9 +// Type definitions for React (react-dom) 17.0 // Project: https://reactjs.org // Definitions by: Asana // AssureSign diff --git a/types/react-dom/v16/index.d.ts b/types/react-dom/v16/index.d.ts new file mode 100644 index 00000000000000..13e672e812a166 --- /dev/null +++ b/types/react-dom/v16/index.d.ts @@ -0,0 +1,98 @@ +// Type definitions for React (react-dom) 16.9 +// Project: https://reactjs.org +// Definitions by: Asana +// AssureSign +// Microsoft +// MartynasZilinskas +// Josh Rutherford +// Jessica Franco +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +// NOTE: Users of the `experimental` builds of React should add a reference +// to 'react-dom/experimental' in their project. See experimental.d.ts's top comment +// for reference and documentation on how exactly to do it. + +export as namespace ReactDOM; + +import { + ReactInstance, Component, ComponentState, + ReactElement, SFCElement, CElement, + DOMAttributes, DOMElement, ReactNode, ReactPortal +} from 'react'; + +export function findDOMNode(instance: ReactInstance | null | undefined): Element | null | Text; +export function unmountComponentAtNode(container: Element | DocumentFragment): boolean; + +export function createPortal(children: ReactNode, container: Element, key?: null | string): ReactPortal; + +export const version: string; +export const render: Renderer; +export const hydrate: Renderer; + +export function unstable_batchedUpdates(callback: (a: A, b: B) => any, a: A, b: B): void; +export function unstable_batchedUpdates(callback: (a: A) => any, a: A): void; +export function unstable_batchedUpdates(callback: () => any): void; + +export function unstable_renderSubtreeIntoContainer( + parentComponent: Component, + element: DOMElement, T>, + container: Element, + callback?: (element: T) => any): T; +export function unstable_renderSubtreeIntoContainer>( + parentComponent: Component, + element: CElement, + container: Element, + callback?: (component: T) => any): T; +export function unstable_renderSubtreeIntoContainer

( + parentComponent: Component, + element: ReactElement

, + container: Element, + callback?: (component?: Component | Element) => any): Component | Element | void; + +export interface Renderer { + // Deprecated(render): The return value is deprecated. + // In future releases the render function's return type will be void. + + ( + element: DOMElement, T>, + container: Element | DocumentFragment | null, + callback?: () => void + ): T; + + ( + element: Array, any>>, + container: Element | DocumentFragment | null, + callback?: () => void + ): Element; + + ( + element: SFCElement | Array>, + container: Element | DocumentFragment | null, + callback?: () => void + ): void; + + >( + element: CElement, + container: Element | DocumentFragment | null, + callback?: () => void + ): T; + + ( + element: Array>>, + container: Element | DocumentFragment | null, + callback?: () => void + ): Component; + +

( + element: ReactElement

, + container: Element | DocumentFragment | null, + callback?: () => void + ): Component | Element | void; + + ( + element: ReactElement[], + container: Element | DocumentFragment | null, + callback?: () => void + ): Component | Element | void; +} diff --git a/types/react-dom/v16/node-stream/index.d.ts b/types/react-dom/v16/node-stream/index.d.ts new file mode 100644 index 00000000000000..480392aaa4c83a --- /dev/null +++ b/types/react-dom/v16/node-stream/index.d.ts @@ -0,0 +1,18 @@ +import { ReactElement } from 'react'; + +/** + * Render a ReactElement to its initial HTML. This should only be used on the + * server. + * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostream + */ +export function renderToStream(element: ReactElement): any; + +/** + * Similar to renderToStream, except this doesn't create extra DOM attributes + * such as data-react-id that React uses internally. + * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostaticstream + */ +export function renderToStaticStream(element: ReactElement): any; +export const version: string; + +export as namespace ReactDOMNodeStream; diff --git a/types/react-dom/v16/server/index.d.ts b/types/react-dom/v16/server/index.d.ts new file mode 100644 index 00000000000000..97747165a8f381 --- /dev/null +++ b/types/react-dom/v16/server/index.d.ts @@ -0,0 +1,47 @@ +// forward declarations +declare global { + namespace NodeJS { + // tslint:disable-next-line:no-empty-interface + interface ReadableStream {} + } +} + +import { ReactElement } from 'react'; + +/** + * Render a React element to its initial HTML. This should only be used on the server. + * React will return an HTML string. You can use this method to generate HTML on the server + * and send the markup down on the initial request for faster page loads and to allow search + * engines to crawl your pages for SEO purposes. + * + * If you call `ReactDOM.hydrate()` on a node that already has this server-rendered markup, + * React will preserve it and only attach event handlers, allowing you + * to have a very performant first-load experience. + */ +export function renderToString(element: ReactElement): string; + +/** + * Render a React element to its initial HTML. Returns a Readable stream that outputs + * an HTML string. The HTML output by this stream is exactly equal to what + * `ReactDOMServer.renderToString()` would return. + */ +export function renderToNodeStream(element: ReactElement): NodeJS.ReadableStream; + +/** + * Similar to `renderToString`, except this doesn't create extra DOM attributes + * such as `data-reactid`, that React uses internally. This is useful if you want + * to use React as a simple static page generator, as stripping away the extra + * attributes can save lots of bytes. + */ +export function renderToStaticMarkup(element: ReactElement): string; + +/** + * Similar to `renderToNodeStream`, except this doesn't create extra DOM attributes + * such as `data-reactid`, that React uses internally. The HTML output by this stream + * is exactly equal to what `ReactDOMServer.renderToStaticMarkup()` would return. + */ +export function renderToStaticNodeStream(element: ReactElement): NodeJS.ReadableStream; + +export const version: string; + +export as namespace ReactDOMServer; diff --git a/types/react-dom/v16/test-utils/index.d.ts b/types/react-dom/v16/test-utils/index.d.ts new file mode 100644 index 00000000000000..66946a85ae6829 --- /dev/null +++ b/types/react-dom/v16/test-utils/index.d.ts @@ -0,0 +1,303 @@ +import { + AbstractView, Component, ComponentClass, + ReactElement, ReactInstance, ClassType, + DOMElement, SFCElement, CElement, + ReactHTMLElement, DOMAttributes, SFC +} from 'react'; + +import * as ReactTestUtils from "."; + +export interface OptionalEventProperties { + bubbles?: boolean; + cancelable?: boolean; + currentTarget?: EventTarget; + defaultPrevented?: boolean; + eventPhase?: number; + isTrusted?: boolean; + nativeEvent?: Event; + preventDefault?(): void; + stopPropagation?(): void; + target?: EventTarget; + timeStamp?: Date; + type?: string; +} + +export interface SyntheticEventData extends OptionalEventProperties { + altKey?: boolean; + button?: number; + buttons?: number; + clientX?: number; + clientY?: number; + changedTouches?: TouchList; + charCode?: number; + clipboardData?: DataTransfer; + ctrlKey?: boolean; + deltaMode?: number; + deltaX?: number; + deltaY?: number; + deltaZ?: number; + detail?: number; + getModifierState?(key: string): boolean; + key?: string; + keyCode?: number; + locale?: string; + location?: number; + metaKey?: boolean; + pageX?: number; + pageY?: number; + relatedTarget?: EventTarget; + repeat?: boolean; + screenX?: number; + screenY?: number; + shiftKey?: boolean; + targetTouches?: TouchList; + touches?: TouchList; + view?: AbstractView; + which?: number; +} + +export type EventSimulator = (element: Element | Component, eventData?: SyntheticEventData) => void; + +export interface MockedComponentClass { + new (props: any): any; +} + +export interface ShallowRenderer { + /** + * After `shallowRenderer.render()` has been called, returns shallowly rendered output. + */ + getRenderOutput(): E; + /** + * Similar to `ReactDOM.render` but it doesn't require DOM and only renders a single level deep. + */ + render(element: ReactElement, context?: any): void; + unmount(): void; +} + +/** + * Simulate an event dispatch on a DOM node with optional `eventData` event data. + * `Simulate` has a method for every event that React understands. + */ +export namespace Simulate { + const abort: EventSimulator; + const animationEnd: EventSimulator; + const animationIteration: EventSimulator; + const animationStart: EventSimulator; + const blur: EventSimulator; + const canPlay: EventSimulator; + const canPlayThrough: EventSimulator; + const change: EventSimulator; + const click: EventSimulator; + const compositionEnd: EventSimulator; + const compositionStart: EventSimulator; + const compositionUpdate: EventSimulator; + const contextMenu: EventSimulator; + const copy: EventSimulator; + const cut: EventSimulator; + const doubleClick: EventSimulator; + const drag: EventSimulator; + const dragEnd: EventSimulator; + const dragEnter: EventSimulator; + const dragExit: EventSimulator; + const dragLeave: EventSimulator; + const dragOver: EventSimulator; + const dragStart: EventSimulator; + const drop: EventSimulator; + const durationChange: EventSimulator; + const emptied: EventSimulator; + const encrypted: EventSimulator; + const ended: EventSimulator; + const error: EventSimulator; + const focus: EventSimulator; + const input: EventSimulator; + const invalid: EventSimulator; + const keyDown: EventSimulator; + const keyPress: EventSimulator; + const keyUp: EventSimulator; + const load: EventSimulator; + const loadStart: EventSimulator; + const loadedData: EventSimulator; + const loadedMetadata: EventSimulator; + const mouseDown: EventSimulator; + const mouseEnter: EventSimulator; + const mouseLeave: EventSimulator; + const mouseMove: EventSimulator; + const mouseOut: EventSimulator; + const mouseOver: EventSimulator; + const mouseUp: EventSimulator; + const paste: EventSimulator; + const pause: EventSimulator; + const play: EventSimulator; + const playing: EventSimulator; + const progress: EventSimulator; + const rateChange: EventSimulator; + const scroll: EventSimulator; + const seeked: EventSimulator; + const seeking: EventSimulator; + const select: EventSimulator; + const stalled: EventSimulator; + const submit: EventSimulator; + const suspend: EventSimulator; + const timeUpdate: EventSimulator; + const touchCancel: EventSimulator; + const touchEnd: EventSimulator; + const touchMove: EventSimulator; + const touchStart: EventSimulator; + const transitionEnd: EventSimulator; + const volumeChange: EventSimulator; + const waiting: EventSimulator; + const wheel: EventSimulator; +} + +/** + * Render a React element into a detached DOM node in the document. __This function requires a DOM__. + */ +export function renderIntoDocument( + element: DOMElement): T; +export function renderIntoDocument( + element: SFCElement): void; +// If we replace `P` with `any` in this overload, then some tests fail because +// calls to `renderIntoDocument` choose the last overload on the +// subtype-relation pass and get an undesirably broad return type. Using `P` +// allows this overload to match on the subtype-relation pass. +export function renderIntoDocument>( + element: CElement): T; +export function renderIntoDocument

( + element: ReactElement

): Component

| Element | void; + +/** + * Pass a mocked component module to this method to augment it with useful methods that allow it to + * be used as a dummy React component. Instead of rendering as usual, the component will become + * a simple `

` (or other tag if `mockTagName` is provided) containing any provided children. + */ +export function mockComponent( + mocked: MockedComponentClass, mockTagName?: string): typeof ReactTestUtils; + +/** + * Returns `true` if `element` is any React element. + */ +export function isElement(element: any): boolean; + +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType( + element: ReactElement, type: string): element is ReactHTMLElement; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType

, T extends Element>( + element: ReactElement, type: string): element is DOMElement; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType

( + element: ReactElement, type: SFC

): element is SFCElement

; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType, C extends ComponentClass

>( + element: ReactElement, type: ClassType): element is CElement; + +/** + * Returns `true` if `instance` is a DOM component (such as a `

` or ``). + */ +export function isDOMComponent(instance: ReactInstance): instance is Element; +/** + * Returns `true` if `instance` is a user-defined component, such as a class or a function. + */ +export function isCompositeComponent(instance: ReactInstance): instance is Component; +/** + * Returns `true` if `instance` is a component whose type is of a React `componentClass`. + */ +export function isCompositeComponentWithType, C extends ComponentClass>( + instance: ReactInstance, type: ClassType): boolean; + +/** + * Traverse all components in `tree` and accumulate all components where + * `test(component)` is `true`. This is not that useful on its own, but it's used + * as a primitive for other test utils. + */ +export function findAllInRenderedTree( + root: Component, + fn: (i: ReactInstance) => boolean): ReactInstance[]; + +/** + * Finds all DOM elements of components in the rendered tree that are + * DOM components with the class name matching `className`. + */ +export function scryRenderedDOMComponentsWithClass( + root: Component, + className: string): Element[]; +/** + * Like `scryRenderedDOMComponentsWithClass()` but expects there to be one result, + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedDOMComponentWithClass( + root: Component, + className: string): Element; + +/** + * Finds all DOM elements of components in the rendered tree that are + * DOM components with the tag name matching `tagName`. + */ +export function scryRenderedDOMComponentsWithTag( + root: Component, + tagName: string): Element[]; +/** + * Like `scryRenderedDOMComponentsWithTag()` but expects there to be one result, + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedDOMComponentWithTag( + root: Component, + tagName: string): Element; + +/** + * Finds all instances of components with type equal to `componentClass`. + */ +export function scryRenderedComponentsWithType, C extends ComponentClass>( + root: Component, + type: ClassType): T[]; + +/** + * Same as `scryRenderedComponentsWithType()` but expects there to be one result + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedComponentWithType, C extends ComponentClass>( + root: Component, + type: ClassType): T; + +/** + * Call this in your tests to create a shallow renderer. + */ +export function createRenderer(): ShallowRenderer; + +/** + * Wrap any code rendering and triggering updates to your components into `act()` calls. + * + * Ensures that the behavior in your tests matches what happens in the browser + * more closely by executing pending `useEffect`s before returning. This also + * reduces the amount of re-renders done. + * + * @param callback A synchronous, void callback that will execute as a single, complete React commit. + * + * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks + */ +// NOTES +// - the order of these signatures matters - typescript will check the signatures in source order. +// If the `() => void` signature is first, it'll erroneously match a Promise returning function for users with +// `strictNullChecks: false`. +// - the "void | undefined" types are there to forbid any non-void return values for users with `strictNullChecks: true` +// tslint:disable-next-line: void-return +export function act(callback: () => Promise): Promise; +export function act(callback: () => void | undefined): void; + +// Intentionally doesn't extend PromiseLike. +// Ideally this should be as hard to accidentally use as possible. +export interface DebugPromiseLike { + // the actual then() in here is 0-ary, but that doesn't count as a PromiseLike. + then(onfulfilled: (value: never) => never, onrejected: (reason: never) => never): never; +} diff --git a/types/react-dom/v16/test/react-dom-tests.tsx b/types/react-dom/v16/test/react-dom-tests.tsx new file mode 100644 index 00000000000000..033afe3b08bd74 --- /dev/null +++ b/types/react-dom/v16/test/react-dom-tests.tsx @@ -0,0 +1,220 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import * as ReactDOMServer from 'react-dom/server'; +import * as ReactDOMNodeStream from 'react-dom/node-stream'; +import * as ReactTestUtils from 'react-dom/test-utils'; + +declare function describe(desc: string, f: () => void): void; +declare function it(desc: string, f: () => void): void; + +class TestComponent extends React.Component<{x: string}> { } + +describe('ReactDOM', () => { + it('render', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + }); + + it('hydrate', () => { + const rootElement = document.createElement('div'); + ReactDOM.hydrate(React.createElement('div'), rootElement); + }); + + it('unmounts', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + ReactDOM.unmountComponentAtNode(rootElement); + }); + + it('works with document fragments', () => { + const fragment = document.createDocumentFragment(); + ReactDOM.render(React.createElement('div'), fragment); + ReactDOM.unmountComponentAtNode(fragment); + }); + + it('find dom node', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + ReactDOM.findDOMNode(rootElement); + ReactDOM.findDOMNode(null); + ReactDOM.findDOMNode(undefined); + }); + + it('createPortal', () => { + const rootElement = document.createElement('div'); + const portalTarget = document.createElement('div'); + + class ClassComponent extends React.Component { + render() { + return ReactDOM.createPortal(
, portalTarget); + } + } + + ReactDOM.createPortal(
, document.createElement('div')); + ReactDOM.createPortal(
, document.createElement('div'), null); + ReactDOM.createPortal(
, document.createElement('div'), 'key'); + + ReactDOM.createPortal(React.createElement('div'), document.createElement('div')); + ReactDOM.createPortal(React.createElement('div'), document.createElement('div'), null); + ReactDOM.createPortal(React.createElement('div'), document.createElement('div'), 'key'); + + ReactDOM.render(, rootElement); + }); +}); + +describe('ReactDOMServer', () => { + it('renderToString', () => { + const content: string = ReactDOMServer.renderToString(React.createElement('div')); + }); + + it('renderToStaticMarkup', () => { + const content: string = ReactDOMServer.renderToStaticMarkup(React.createElement('div')); + }); +}); + +describe('ReactDOMNodeStream', () => { + it('renderToStream', () => { + const content: any = ReactDOMNodeStream.renderToStream(React.createElement('div')); + }); + + it('renderToStaticStream', () => { + const content: any = ReactDOMNodeStream.renderToStaticStream(React.createElement('div')); + }); +}); + +describe('React dom test utils', () => { + it('Simulate', () => { + const element = document.createElement('div'); + const dom = ReactDOM.render( + React.createElement('input', { type: 'text' }), + element + ) as Element; + const node = ReactDOM.findDOMNode(dom) as HTMLInputElement; + + node.value = 'giraffe'; + ReactTestUtils.Simulate.change(node); + ReactTestUtils.Simulate.keyDown(node, { key: "Enter", keyCode: 13, which: 13 }); + }); + + it('renderIntoDocument', () => { + const element = React.createElement('input', { type: 'text' }); + ReactTestUtils.renderIntoDocument(element); + }); + + it('mockComponent', () => { + ReactTestUtils.mockComponent(TestComponent, 'div'); + }); + + it('isElement', () => { + const element = React.createElement(TestComponent); + const isReactElement: boolean = ReactTestUtils.isElement(element); + }); + + it('isElementOfType', () => { + const element = React.createElement(TestComponent); + const isReactElement: boolean = ReactTestUtils.isElementOfType(element, TestComponent); + }); + + it('isDOMComponent', () => { + const element = React.createElement('div'); + const instance = ReactTestUtils.renderIntoDocument(element) as HTMLDivElement; + const isDOMElement: boolean = ReactTestUtils.isDOMComponent(instance); + }); + + it('isCompositeComponent', () => { + const element = React.createElement(TestComponent); + const instance: TestComponent = ReactTestUtils.renderIntoDocument(element); + const isCompositeComponent: boolean = ReactTestUtils.isCompositeComponent(instance); + }); + + it('isCompositeComponentWithType', () => { + const element = React.createElement(TestComponent); + const instance: TestComponent = ReactTestUtils.renderIntoDocument(element); + const isCompositeComponent: boolean = ReactTestUtils.isCompositeComponentWithType(instance, TestComponent); + }); + + it('findAllInRenderedTree', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findAllInRenderedTree(component, (i: React.ReactInstance) => true); + }); + + it('scryRenderedDOMComponentsWithClass', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedDOMComponentsWithClass(component, 'class'); + }); + + it('findRenderedDOMComponentWithClass', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedDOMComponentWithClass(component, 'class'); + }); + + it('scryRenderedDOMComponentsWithTag', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedDOMComponentsWithTag(component, 'div'); + }); + + it('findRenderedDOMComponentWithTag', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedDOMComponentWithTag(component, 'tag'); + }); + + it('scryRenderedComponentsWithType', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedComponentsWithType(component, TestComponent); + }); + + it('findRenderedComponentWithType', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedComponentWithType(component, TestComponent); + }); + + describe('Shallow Rendering', () => { + it('createRenderer', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + }); + + it('shallowRenderer.render', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + shallowRenderer.render(component); + }); + + it('shallowRenderer.getRenderOutput', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + shallowRenderer.getRenderOutput(); + }); + }); + + describe('act', () => { + describe('with sync callback', () => { + it('accepts a callback that is void', () => { + ReactTestUtils.act(() => {}); + }); + it('rejects a callback that returns null', () => { + // $ExpectError + ReactTestUtils.act(() => null); + }); + it('returns a type that is not Promise-like', () => { + // tslint:disable-next-line no-void-expression + const result = ReactTestUtils.act(() => {}); + // $ExpectError + result.then((x) => {}); + }); + }); + describe('with async callback', () => { + it('accepts a callback that is void', async () => { + await ReactTestUtils.act(async () => {}); + }); + it('rejects a callback that returns a value', async () => { + // $ExpectError + await ReactTestUtils.act(async () => null); + }); + it('returns a Promise-like', () => { + const result = ReactTestUtils.act(async () => {}); + result.then((x) => {}); + }); + }); + }); +}); diff --git a/types/react-dom/v16/tsconfig.json b/types/react-dom/v16/tsconfig.json new file mode 100644 index 00000000000000..0db3098c490d9f --- /dev/null +++ b/types/react-dom/v16/tsconfig.json @@ -0,0 +1,36 @@ +{ + "files": [ + "index.d.ts", + "test/react-dom-tests.tsx" + ], + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "paths": { + "react": [ + "react/v16" + ], + "react-dom": [ + "react-dom/v16" + ], + "react-dom/*": [ + "react-dom/v16/*" + ] + }, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "jsx": "preserve" + } +} diff --git a/types/react-dom/v16/tslint.json b/types/react-dom/v16/tslint.json new file mode 100644 index 00000000000000..c4fd1ce0bb3721 --- /dev/null +++ b/types/react-dom/v16/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "dt-header": false, + "no-unnecessary-generics": false + } +} diff --git a/types/react-is/index.d.ts b/types/react-is/index.d.ts index 3f33f40f9c8f0a..e7953f4e114d65 100644 --- a/types/react-is/index.d.ts +++ b/types/react-is/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for react-is 16.7 +// Type definitions for react-is 17.0 // Project: https://reactjs.org/ // Definitions by: Avi Vahl // Christian Chown diff --git a/types/react-is/v16/index.d.ts b/types/react-is/v16/index.d.ts new file mode 100644 index 00000000000000..3f33f40f9c8f0a --- /dev/null +++ b/types/react-is/v16/index.d.ts @@ -0,0 +1,44 @@ +// Type definitions for react-is 16.7 +// Project: https://reactjs.org/ +// Definitions by: Avi Vahl +// Christian Chown +// Sebastian Silbermann +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +export as namespace ReactIs; + +import { + LazyExoticComponent, + MemoExoticComponent, + ReactElement, + ReactType +} from "react"; + +export function typeOf(value: any): symbol | undefined; +export function isValidElementType(value: any): value is ReactType; +export function isAsyncMode(value: any): value is ReactElement; +export function isContextConsumer(value: any): value is ReactElement; +export function isContextProvider(value: any): value is ReactElement; +export function isElement(value: any): value is ReactElement; +export function isForwardRef(value: any): value is ReactElement; +export function isFragment(value: any): value is ReactElement; +export function isLazy(value: any): value is LazyExoticComponent; +export function isMemo(value: any): value is MemoExoticComponent; +export function isProfiler(value: any): value is ReactElement; +export function isPortal(value: any): value is ReactElement; +export function isStrictMode(value: any): value is ReactElement; +export function isSuspense(value: any): value is ReactElement; + +export const AsyncMode: symbol; +export const ContextConsumer: symbol; +export const ContextProvider: symbol; +export const Element: symbol; +export const ForwardRef: symbol; +export const Fragment: symbol; +export const Lazy: symbol; +export const Memo: symbol; +export const Portal: symbol; +export const Profiler: symbol; +export const StrictMode: symbol; +export const Suspense: symbol; diff --git a/types/react-is/v16/react-is-tests.tsx b/types/react-is/v16/react-is-tests.tsx new file mode 100644 index 00000000000000..d41b2a828bf0f4 --- /dev/null +++ b/types/react-is/v16/react-is-tests.tsx @@ -0,0 +1,98 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import * as ReactIs from 'react-is'; + +// Below is taken from README of react-is +// Determining if a Component is Valid + +interface CompProps { + forwardedRef: React.Ref; + children?: React.ReactNode; +} + +class ClassComponent extends React.Component { + render() { + return React.createElement('div'); + } +} + +const StatelessComponent = () => React.createElement('div'); + +const ForwardRefComponent = React.forwardRef((props, ref) => + React.createElement(ClassComponent, { forwardedRef: ref, ...props }) +); + +const LazyComponent = React.lazy(() => + Promise.resolve({ default: ForwardRefComponent }) +); + +const MemoComponent = React.memo(StatelessComponent); + +const Context = React.createContext(false); + +ReactIs.isValidElementType('div'); // true +ReactIs.isValidElementType(ClassComponent); // true +ReactIs.isValidElementType(StatelessComponent); // true +ReactIs.isValidElementType(ForwardRefComponent); // true +ReactIs.isValidElementType(Context.Provider); // true +ReactIs.isValidElementType(Context.Consumer); // true +ReactIs.isValidElementType(React.createFactory('div')); // true +ReactIs.isValidElementType(LazyComponent); +ReactIs.isValidElementType(MemoComponent); + +// Determining an Element's Type + +// AsyncMode - unstable_AsyncMode is not implemented in @types/react yet +// ReactIs.isAsyncMode(); // true +// ReactIs.typeOf() === ReactIs.AsyncMode; // true + +// Context +const ThemeContext = React.createContext('blue'); + +ReactIs.isContextConsumer(); // true +ReactIs.isContextProvider(); // true +ReactIs.typeOf() === ReactIs.ContextConsumer; // true +ReactIs.typeOf() === ReactIs.ContextProvider; // true + +// Element +ReactIs.isElement(
); // true +ReactIs.typeOf(
) === ReactIs.Element; // true + +// Fragment +ReactIs.isFragment(<>); // true +ReactIs.typeOf(<>) === ReactIs.Fragment; // true + +// Portal +const div = document.createElement('div'); +const portal = ReactDOM.createPortal(
, div); + +ReactIs.isPortal(portal); // true +ReactIs.typeOf(portal) === ReactIs.Portal; // true + +// StrictMode +ReactIs.isStrictMode(); // true +ReactIs.typeOf() === ReactIs.StrictMode; // true + +// Verify typeOf accepts any type of value (taken from tests of react-is) +ReactIs.typeOf('abc') === undefined; +ReactIs.typeOf(true) === undefined; +ReactIs.typeOf(123) === undefined; +ReactIs.typeOf({}) === undefined; +ReactIs.typeOf(null) === undefined; +ReactIs.typeOf(undefined) === undefined; + +// ForwardRef +ReactIs.isForwardRef(); // true +ReactIs.typeOf() === ReactIs.ForwardRef; // true + +// Lazy +ReactIs.isLazy(LazyComponent); // true +ReactIs.typeOf(LazyComponent) === ReactIs.Lazy; // true + +// Memo +ReactIs.isMemo(MemoComponent); // true +ReactIs.typeOf(MemoComponent) === ReactIs.Memo; // true + +// Suspense +ReactIs.isForwardRef(); // true +ReactIs.typeOf() === ReactIs.Suspense; // true diff --git a/types/react-is/v16/tsconfig.json b/types/react-is/v16/tsconfig.json new file mode 100644 index 00000000000000..98c75c4eae1a16 --- /dev/null +++ b/types/react-is/v16/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "paths": { + "react": ["react/v16"], + "react-dom": ["react-dom/v16"], + "react-is": ["react-is/v16"] + }, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "strictFunctionTypes": true, + "jsx": "preserve" + }, + "files": [ + "index.d.ts", + "react-is-tests.tsx" + ] +} diff --git a/types/react-is/v16/tslint.json b/types/react-is/v16/tslint.json new file mode 100644 index 00000000000000..3db14f85eaf7b9 --- /dev/null +++ b/types/react-is/v16/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } diff --git a/types/react-test-renderer/index.d.ts b/types/react-test-renderer/index.d.ts index cac1dde907dc99..f3abd906573344 100644 --- a/types/react-test-renderer/index.d.ts +++ b/types/react-test-renderer/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for react-test-renderer 16.9 +// Type definitions for react-test-renderer 17.0 // Project: https://facebook.github.io/react/ // Definitions by: Arvitaly // Lochbrunner diff --git a/types/react-test-renderer/v16/index.d.ts b/types/react-test-renderer/v16/index.d.ts new file mode 100644 index 00000000000000..cac1dde907dc99 --- /dev/null +++ b/types/react-test-renderer/v16/index.d.ts @@ -0,0 +1,91 @@ +// Type definitions for react-test-renderer 16.9 +// Project: https://facebook.github.io/react/ +// Definitions by: Arvitaly +// Lochbrunner +// John Reilly +// John Gozde +// Jessica Franco +// Dhruv Jain +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +import { ReactElement, ElementType } from 'react'; + +// extracted from: +// - https://github.com/facebook/react/blob/v16.0.0/src/renderers/testing/ReactTestRendererFiberEntry.js +// - https://reactjs.org/docs/test-renderer.html + +export interface ReactTestRendererJSON { + type: string; + props: { [propName: string]: any }; + children: null | ReactTestRendererNode[]; +} +export type ReactTestRendererNode = ReactTestRendererJSON | string; +export interface ReactTestRendererTree extends ReactTestRendererJSON { + nodeType: 'component' | 'host'; + instance: any; + rendered: null | ReactTestRendererTree | ReactTestRendererTree[]; +} +export interface ReactTestInstance { + instance: any; + type: ElementType; + props: { [propName: string]: any }; + parent: null | ReactTestInstance; + children: Array; + + find(predicate: (node: ReactTestInstance) => boolean): ReactTestInstance; + findByType(type: ElementType): ReactTestInstance; + findByProps(props: { [propName: string]: any }): ReactTestInstance; + + findAll(predicate: (node: ReactTestInstance) => boolean, options?: { deep: boolean }): ReactTestInstance[]; + findAllByType(type: ElementType, options?: { deep: boolean }): ReactTestInstance[]; + findAllByProps(props: { [propName: string]: any }, options?: { deep: boolean }): ReactTestInstance[]; +} +export interface ReactTestRenderer { + toJSON(): null | ReactTestRendererJSON | ReactTestRendererJSON[]; + toTree(): null | ReactTestRendererTree; + unmount(nextElement?: ReactElement): void; + update(nextElement: ReactElement): void; + getInstance(): null | ReactTestInstance; + root: ReactTestInstance; +} +export interface TestRendererOptions { + createNodeMock(element: ReactElement): any; +} +export function create(nextElement: ReactElement, options?: TestRendererOptions): ReactTestRenderer; + +/** + * Wrap any code rendering and triggering updates to your components into `act()` calls. + * + * Ensures that the behavior in your tests matches what happens in the browser + * more closely by executing pending `useEffect`s before returning. This also + * reduces the amount of re-renders done. + * + * @param callback An asynchronous, void callback that will execute as a single, complete React commit. + * + * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks + */ +// the "void | undefined" is here to forbid any sneaky return values +// tslint:disable-next-line: void-return +export function act(callback: () => Promise): Promise; +/** + * Wrap any code rendering and triggering updates to your components into `act()` calls. + * + * Ensures that the behavior in your tests matches what happens in the browser + * more closely by executing pending `useEffect`s before returning. This also + * reduces the amount of re-renders done. + * + * @param callback A synchronous, void callback that will execute as a single, complete React commit. + * + * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks + */ +// the "void | undefined" is here to forbid any sneaky "Promise" returns. +// the actual return value is always a "DebugPromiseLike". +export function act(callback: () => void | undefined): DebugPromiseLike; + +// Intentionally doesn't extend PromiseLike. +// Ideally this should be as hard to accidentally use as possible. +export interface DebugPromiseLike { + // the actual then() in here is 1-ary, but that doesn't count as a PromiseLike. + then(onfulfilled: (value: never) => never, onrejected: (reason: never) => never): never; +} diff --git a/types/react-test-renderer/v16/react-test-renderer-tests.ts b/types/react-test-renderer/v16/react-test-renderer-tests.ts new file mode 100644 index 00000000000000..c52a803c7fbd95 --- /dev/null +++ b/types/react-test-renderer/v16/react-test-renderer-tests.ts @@ -0,0 +1,97 @@ +import React = require("react"); +import { act, create, ReactTestInstance } from "react-test-renderer"; +import { createRenderer } from 'react-test-renderer/shallow'; + +class TestComponent extends React.Component { } + +const renderer = create(React.createElement("div"), { + createNodeMock: (el: React.ReactElement) => { + return {}; + } +}); + +const json = renderer.toJSON(); +if (json && !Array.isArray(json)) { + json.type = "t"; + json.props = { + prop1: "p", + }; + json.children = [json]; +} + +if (json && Array.isArray(json)) { + json[json.length - 1].type = "t"; + json[json.length - 1].props = { + prop1: "p", + }; + json[json.length - 1].children = [json[json.length - 1]]; +} + +const tree = renderer.toTree(); +if (tree) { + tree.type = "t"; + tree.props = { + prop1: "p", + }; + tree.children = [tree]; + tree.rendered = tree; + tree.rendered = [tree]; + tree.nodeType = "component"; + tree.nodeType = "host"; +} + +renderer.update(React.createElement(TestComponent)); + +renderer.unmount(); +renderer.unmount(React.createElement(TestComponent)); + +function testInstance(inst: ReactTestInstance) { + inst.children = [inst, "a"]; + inst.parent = instance; + inst.parent = null; + inst.props = { + prop1: "p", + }; + inst.type = "a"; + testInstance(inst.find(n => n.type === "a")); + testInstance(inst.findByProps({ prop1: "p" })); + testInstance(inst.findByType("a")); + testInstance(inst.findByType(TestComponent)); + inst.findAll(n => n.type === "div", { deep: true }).map(testInstance); + inst.findAllByProps({ prop1: "p" }, { deep: true }).map(testInstance); + inst.findAllByType("a", { deep: true }).map(testInstance); + inst.findAllByType(TestComponent, { deep: true }).map(testInstance); +} + +const instance = renderer.getInstance(); +if (instance) { + testInstance(instance); +} + +testInstance(renderer.root); + +const component = React.createElement(TestComponent); +const shallowRenderer = createRenderer(); +shallowRenderer.render(component); +shallowRenderer.getRenderOutput(); +shallowRenderer.getMountedInstance(); + +// Only synchronous, void callbacks are acceptable for act() +act(() => {}); +// $ExpectError +act(() => null); +// $ExpectError +Promise.resolve(act(() => {})); + +// async act is now acceptable in React 16.9, +// but the result must be void or undefined +Promise.resolve(act(async () => {})); + +void (async () => { + act(() => {}); + + await act(async () => {}); + await act(async () => undefined); + // $ExpectError + await act(async () => null); +})(); diff --git a/types/react-test-renderer/v16/shallow/index.d.ts b/types/react-test-renderer/v16/shallow/index.d.ts new file mode 100644 index 00000000000000..83c7e4211ca58e --- /dev/null +++ b/types/react-test-renderer/v16/shallow/index.d.ts @@ -0,0 +1,22 @@ +import { ReactElement, ReactInstance } from 'react'; + +export interface ShallowRenderer { + /** + * After `shallowRenderer.render()` has been called, returns mounted instance. + */ + getMountedInstance(): ReactInstance; + /** + * After `shallowRenderer.render()` has been called, returns shallowly rendered output. + */ + getRenderOutput(): E; + /** + * Similar to `ReactDOM.render` but it doesn't require DOM and only renders a single level deep. + */ + render(element: ReactElement, context?: any): void; + unmount(): void; +} + +/** + * Call this in your tests to create a shallow renderer. + */ +export function createRenderer(): ShallowRenderer; diff --git a/types/react-test-renderer/v16/tsconfig.json b/types/react-test-renderer/v16/tsconfig.json new file mode 100644 index 00000000000000..d62cc5b59771e0 --- /dev/null +++ b/types/react-test-renderer/v16/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ],"paths": { + "react": [ + "react/v16" + ], + "react-dom": [ + "react-dom/v16" + ], + "react-test-renderer": [ + "react-test-renderer/v16" + ], + "react-test-renderer/*": [ + "react-test-renderer/v16/*" + ] + }, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "react-test-renderer-tests.ts" + ] +} diff --git a/types/react-test-renderer/v16/tslint.json b/types/react-test-renderer/v16/tslint.json new file mode 100644 index 00000000000000..71ee04c4e1a210 --- /dev/null +++ b/types/react-test-renderer/v16/tslint.json @@ -0,0 +1,6 @@ +{ + "extends": "dtslint/dt.json", + "rules": { + "no-unnecessary-generics": false + } +} diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 1639abb4e9a95f..4fb1b5fb74ddf2 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for React 16.14 +// Type definitions for React 17.0 // Project: http://facebook.github.io/react/ // Definitions by: Asana // AssureSign @@ -1211,6 +1211,7 @@ declare namespace React { /** @deprecated */ charCode: number; ctrlKey: boolean; + code: string; /** * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. */ @@ -2091,6 +2092,7 @@ declare namespace React { checked?: boolean; crossOrigin?: string; disabled?: boolean; + enterKeyHint?: 'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'; form?: string; formAction?: string; formEncType?: string; @@ -2352,6 +2354,7 @@ declare namespace React { poster?: string; width?: number | string; disablePictureInPicture?: boolean; + disableRemotePlayback?: boolean; } // this list is "complete" in that it contains every SVG attribute diff --git a/types/react/test/elementAttributes.tsx b/types/react/test/elementAttributes.tsx index 13bc62f7b8f66c..0d7cbef483bc77 100644 --- a/types/react/test/elementAttributes.tsx +++ b/types/react/test/elementAttributes.tsx @@ -30,5 +30,9 @@ const testCases = [
{}} />, } />, , - + , + , + // $ExpectError + , +