diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9417395e042..2c7f4f5a60e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)
+- Convert observer utility components to TypeScript ([#2009](https://github.com/elastic/eui/pull/2009))
+
**Bug fixes**
- Fixed `EuiCallOut` header icon alignment ([#2006](https://github.com/elastic/eui/pull/2006))
diff --git a/package.json b/package.json
index 22c3f79f70a..6faec23319f 100644
--- a/package.json
+++ b/package.json
@@ -82,6 +82,7 @@
"@types/react-dom": "^16.8.2",
"@types/react-is": "~16.3.0",
"@types/react-virtualized": "^9.18.6",
+ "@types/resize-observer-browser": "^0.1.1",
"@types/uuid": "^3.4.4",
"@typescript-eslint/eslint-plugin": "^1.9.0",
"@typescript-eslint/parser": "^1.9.0",
diff --git a/scripts/babel/proptypes-from-ts-props/index.js b/scripts/babel/proptypes-from-ts-props/index.js
index 826b1428597..0f56ca34cd8 100644
--- a/scripts/babel/proptypes-from-ts-props/index.js
+++ b/scripts/babel/proptypes-from-ts-props/index.js
@@ -1184,8 +1184,14 @@ module.exports = function propTypesFromTypeScript({ types }) {
const typeName = idTypeAnnotation.typeAnnotation.typeName.name;
if (typeName === 'SFC' || typeName === 'FunctionComponent') {
if (state.get('importsFromReact').has(typeName)) {
- processComponentDeclaration(idTypeAnnotation.typeAnnotation.typeParameters.params[0], nodePath, state);
- fileCodeNeedsUpdating = true;
+ // Declarations like `const Foo: FunctionComponent`
+ // don't have type parameters. It's a valid declaration
+ // because the generic argument has a default of empty
+ // props.
+ if (idTypeAnnotation.typeAnnotation.typeParameters) {
+ processComponentDeclaration(idTypeAnnotation.typeAnnotation.typeParameters.params[0], nodePath, state);
+ fileCodeNeedsUpdating = true;
+ }
}
} else {
// reprocess this variable declaration but use the identifier lookup
diff --git a/src/components/index.d.ts b/src/components/index.d.ts
index bb7dccd9c7f..e4ecf1ad4d9 100644
--- a/src/components/index.d.ts
+++ b/src/components/index.d.ts
@@ -20,8 +20,6 @@
///
///
///
-///
-///
///
///
///
diff --git a/src/components/observer/mutation_observer/index.d.ts b/src/components/observer/mutation_observer/index.d.ts
deleted file mode 100644
index a3167f80ea6..00000000000
--- a/src/components/observer/mutation_observer/index.d.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { CommonProps } from '../../common';
-
-import { FunctionComponent } from 'react';
-
-declare module '@elastic/eui' {
- /**
- * MutationObserver type defs
- *
- * @see './mutation_observer.js'
- */
- export interface EuiMutationObserverProps {
- observerOptions: MutationObserverInit; // [MutationObserverInit](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit)
- onMutation: MutationCallback;
- }
-
- export const EuiMutationObserver: FunctionComponent<
- CommonProps & EuiMutationObserverProps
- >;
-}
diff --git a/src/components/observer/mutation_observer/index.js b/src/components/observer/mutation_observer/index.ts
similarity index 100%
rename from src/components/observer/mutation_observer/index.js
rename to src/components/observer/mutation_observer/index.ts
diff --git a/src/components/observer/mutation_observer/mutation_observer.js b/src/components/observer/mutation_observer/mutation_observer.js
deleted file mode 100644
index 1538578e2c7..00000000000
--- a/src/components/observer/mutation_observer/mutation_observer.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import PropTypes from 'prop-types';
-
-import { EuiObserver } from '../observer';
-
-class EuiMutationObserver extends EuiObserver {
- constructor(...args) {
- super(...args);
- this.name = 'EuiMutationObserver';
- }
- beginObserve = () => {
- this.observer = new MutationObserver(this.props.onMutation);
- this.observer.observe(this.childNode, this.props.observerOptions);
- };
-}
-
-EuiMutationObserver.propTypes = {
- children: PropTypes.func.isRequired,
- observerOptions: PropTypes.shape({
- // matches a [MutationObserverInit](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit)
- attributeFilter: PropTypes.arrayOf(PropTypes.string),
- attributeOldValue: PropTypes.bool,
- attributes: PropTypes.bool,
- characterData: PropTypes.bool,
- characterDataOldValue: PropTypes.bool,
- childList: PropTypes.bool,
- subtree: PropTypes.bool,
- }).isRequired,
- onMutation: PropTypes.func.isRequired,
-};
-
-export { EuiMutationObserver };
diff --git a/src/components/observer/mutation_observer/mutation_observer.test.js b/src/components/observer/mutation_observer/mutation_observer.test.tsx
similarity index 85%
rename from src/components/observer/mutation_observer/mutation_observer.test.js
rename to src/components/observer/mutation_observer/mutation_observer.test.tsx
index 6cd74f666f3..1b00147742c 100644
--- a/src/components/observer/mutation_observer/mutation_observer.test.js
+++ b/src/components/observer/mutation_observer/mutation_observer.test.tsx
@@ -1,8 +1,8 @@
-import React from 'react';
+import React, { FunctionComponent } from 'react';
import { mount } from 'enzyme';
import { EuiMutationObserver } from './mutation_observer';
-async function sleep(duration) {
+async function sleep(duration: number) {
return new Promise(resolve => {
setTimeout(resolve, duration);
});
@@ -18,7 +18,7 @@ describe('EuiMutationObserver', () => {
expect.assertions(1);
const onMutation = jest.fn();
- function Wrapper({ value }) {
+ const Wrapper: FunctionComponent<{ value: number }> = ({ value }) => {
return (
{
)}
);
- }
+ };
const component = mount();
diff --git a/src/components/observer/mutation_observer/mutation_observer.ts b/src/components/observer/mutation_observer/mutation_observer.ts
new file mode 100644
index 00000000000..9d64c5f0609
--- /dev/null
+++ b/src/components/observer/mutation_observer/mutation_observer.ts
@@ -0,0 +1,18 @@
+import { ReactNode } from 'react';
+
+import { EuiObserver } from '../observer';
+
+interface Props {
+ children: (ref: (e: HTMLElement | null) => void) => ReactNode;
+ onMutation: MutationCallback;
+ observerOptions?: MutationObserverInit;
+}
+
+export class EuiMutationObserver extends EuiObserver {
+ name = 'EuiMutationObserver';
+
+ beginObserve = () => {
+ this.observer = new MutationObserver(this.props.onMutation);
+ this.observer.observe(this.childNode!, this.props.observerOptions);
+ };
+}
diff --git a/src/components/observer/observer.js b/src/components/observer/observer.ts
similarity index 57%
rename from src/components/observer/observer.js
rename to src/components/observer/observer.ts
index fe58506c955..07e3caa7784 100644
--- a/src/components/observer/observer.js
+++ b/src/components/observer/observer.ts
@@ -1,13 +1,18 @@
-import { Component } from 'react';
-import PropTypes from 'prop-types';
-
-class EuiObserver extends Component {
- constructor(...args) {
- super(...args);
- this.name = 'EuiObserver';
- this.childNode = null;
- this.observer = null;
- }
+import { Component, ReactNode } from 'react';
+
+interface BaseProps {
+ children: (ref: any) => ReactNode;
+}
+
+interface Observer {
+ disconnect: () => void;
+ observe: (element: Element, options?: { [key: string]: any }) => void;
+}
+
+export class EuiObserver extends Component {
+ protected name: string = 'EuiObserver';
+ protected childNode: null | Element = null;
+ protected observer: null | Observer = null;
componentDidMount() {
if (this.childNode == null) {
@@ -21,7 +26,7 @@ class EuiObserver extends Component {
}
}
- updateChildNode = ref => {
+ updateChildNode = (ref: Element) => {
if (this.childNode === ref) return; // node hasn't changed
// if there's an existing observer disconnect it
@@ -37,7 +42,7 @@ class EuiObserver extends Component {
}
};
- beginObserve = () => {
+ beginObserve: () => void = () => {
throw new Error('EuiObserver has no default observation method');
};
@@ -45,9 +50,3 @@ class EuiObserver extends Component {
return this.props.children(this.updateChildNode);
}
}
-
-EuiObserver.propTypes = {
- children: PropTypes.func.isRequired,
-};
-
-export { EuiObserver };
diff --git a/src/components/observer/resize_observer/index.d.ts b/src/components/observer/resize_observer/index.d.ts
deleted file mode 100644
index 662f5bd1598..00000000000
--- a/src/components/observer/resize_observer/index.d.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { CommonProps } from '../../common';
-
-import { FunctionComponent } from 'react';
-
-declare module '@elastic/eui' {
- /**
- * ResizeObserver type defs
- *
- * @see './resize_observer.js'
- */
- export interface EuiResizeObserverProps {
- onResize: (dimensions: { width: number; height: number }) => void;
- }
-
- export const EuiResizeObserver: FunctionComponent<
- CommonProps & EuiResizeObserverProps
- >;
-}
diff --git a/src/components/observer/resize_observer/index.js b/src/components/observer/resize_observer/index.ts
similarity index 100%
rename from src/components/observer/resize_observer/index.js
rename to src/components/observer/resize_observer/index.ts
diff --git a/src/components/observer/resize_observer/resize_observer.test.js b/src/components/observer/resize_observer/resize_observer.test.tsx
similarity index 77%
rename from src/components/observer/resize_observer/resize_observer.test.js
rename to src/components/observer/resize_observer/resize_observer.test.tsx
index 09343caeac9..089845c0854 100644
--- a/src/components/observer/resize_observer/resize_observer.test.js
+++ b/src/components/observer/resize_observer/resize_observer.test.tsx
@@ -1,8 +1,8 @@
-import React from 'react';
+import React, { FunctionComponent } from 'react';
import { mount } from 'enzyme';
import { EuiResizeObserver } from './resize_observer';
-async function sleep(duration) {
+async function sleep(duration: number) {
return new Promise(resolve => {
setTimeout(resolve, duration);
});
@@ -18,13 +18,15 @@ describe('EuiResizeObserver', () => {
expect.assertions(1);
const onResize = jest.fn();
- function Wrapper({ children }) {
+ const Wrapper: FunctionComponent<{}> = ({ children }) => {
return (
- {resizeRef => {children}
}
+ {(resizeRef: (e: HTMLElement | null) => void) => (
+ {children}
+ )}
);
- }
+ };
const component = mount(Hello World} />);
diff --git a/src/components/observer/resize_observer/resize_observer.js b/src/components/observer/resize_observer/resize_observer.tsx
similarity index 62%
rename from src/components/observer/resize_observer/resize_observer.js
rename to src/components/observer/resize_observer/resize_observer.tsx
index 80f6c4318b0..73aca10c58a 100644
--- a/src/components/observer/resize_observer/resize_observer.js
+++ b/src/components/observer/resize_observer/resize_observer.tsx
@@ -1,14 +1,17 @@
-import PropTypes from 'prop-types';
+import { ReactNode } from 'react';
import { EuiObserver } from '../observer';
-class EuiResizeObserver extends EuiObserver {
- constructor(...args) {
- super(...args);
- this.name = 'EuiResizeObserver';
- // Only Chrome and Opera support the `ResizeObserver` API at the time of writing
- this.hasResizeObserver = typeof ResizeObserver !== 'undefined';
- }
+interface Props {
+ children: (ref: (e: HTMLElement | null) => void) => ReactNode;
+ onResize: (dimensions: { height: number; width: number }) => void;
+}
+
+export class EuiResizeObserver extends EuiObserver {
+ name = 'EuiResizeObserver';
+
+ // Only Chrome and Opera support the `ResizeObserver` API at the time of writing
+ hasResizeObserver = typeof window.ResizeObserver !== 'undefined';
onResize = () => {
if (this.childNode != null) {
@@ -24,7 +27,7 @@ class EuiResizeObserver extends EuiObserver {
beginObserve = () => {
let observerOptions;
if (this.hasResizeObserver) {
- this.observer = new ResizeObserver(this.onResize);
+ this.observer = new window.ResizeObserver(this.onResize);
} else {
// MutationObserver fallback
observerOptions = {
@@ -37,13 +40,8 @@ class EuiResizeObserver extends EuiObserver {
this.observer = new MutationObserver(this.onResize);
requestAnimationFrame(this.onResize); // Mimic ResizeObserver behavior of triggering a resize event on init
}
- this.observer.observe(this.childNode, observerOptions);
+ // The superclass checks that childNode is not null before invoking
+ // beginObserve()
+ this.observer.observe(this.childNode!, observerOptions);
};
}
-
-EuiResizeObserver.propTypes = {
- children: PropTypes.func.isRequired,
- onResize: PropTypes.func.isRequired,
-};
-
-export { EuiResizeObserver };
diff --git a/yarn.lock b/yarn.lock
index 06dfa25cda7..49a28ec9720 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1151,6 +1151,11 @@
"@types/prop-types" "*"
csstype "^2.2.0"
+"@types/resize-observer-browser@^0.1.1":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.1.tgz#9b7cdae9cdc8b1a7020ca7588018dac64c770866"
+ integrity sha512-5/bJS/uGB5kmpRrrAWXQnmyKlv+4TlPn4f+A2NBa93p+mt6Ht+YcNGkQKf8HMx28a9hox49ZXShtbGqZkk41Sw==
+
"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
version "2.0.3"
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e"