diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4b44a60f2e3..ceb0dd26c6d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)
- Fixed `EuiSuperDatePicker` to update `asyncInterval.isStopped` on a `isPaused` prop change. ([#2250](https://github.com/elastic/eui/pull/2250))
+- Converted table, popover, buttons, pagination, outside click detector, focus trap, context menu, and panel to TypeScript ([#2212](https://github.com/elastic/eui/pull/2212))
## [`13.5.0`](https://github.com/elastic/eui/tree/v13.5.0)
diff --git a/package.json b/package.json
index f4602d36a38..cacbc48843c 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
"@types/react-is": "~16.3.0",
"@types/react-virtualized": "^9.18.6",
"@types/resize-observer-browser": "^0.1.1",
+ "@types/tabbable": "^3.1.0",
"@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 eaf2d1d53f3..99c0c9dfd32 100644
--- a/scripts/babel/proptypes-from-ts-props/index.js
+++ b/scripts/babel/proptypes-from-ts-props/index.js
@@ -5,6 +5,8 @@ const path = require('path');
const babelTemplate = require('babel-template');
const babelCore = require('@babel/core');
+const importedDefinitionsCache = new Map();
+
// react-docgen does not understand typescript annotations
function stripTypeScript(filename, ast) {
return babelCore.transform(
@@ -809,6 +811,16 @@ const typeDefinitionExtractors = {
return [];
}
+ if (importedDefinitionsCache.has(resolvedPath)) {
+ return importedDefinitionsCache.get(resolvedPath);
+ }
+
+ // to support circular dependencies, create & pre-cache the array of imported dependencies
+ // this array is directly mutated after parsing the subsequent files, supporting
+ // the circular nature as values settle into the correct locations
+ const importedDefinitions = [];
+ importedDefinitionsCache.set(resolvedPath, importedDefinitions);
+
// load & parse the imported file
const ast = parse(fs.readFileSync(resolvedPath).toString());
@@ -840,18 +852,15 @@ const typeDefinitionExtractors = {
);
// for each importedTypeName, fully resolve the type information
- const importedDefinitions = definitions.reduce(
- (importedDefinitions, { name, definition }) => {
+ definitions.forEach(
+ ({ name, definition }) => {
if (importedTypeNames.includes(name)) {
// this type declaration is imported by the parent script
const propTypes = getPropTypesForNode(definition, true, state);
propTypes.isAlreadyResolved = true; // when getPropTypesForNode is called on this node later, tell it to skip processing
importedDefinitions.push({ name, definition: propTypes });
}
-
- return importedDefinitions;
- },
- []
+ }
);
// reset typeDefinitions and continue processing the original file
@@ -1026,7 +1035,7 @@ function processComponentDeclaration(typeDefinition, path, state) {
// import PropTypes library if it isn't already
const proptypesBinding = getVariableBinding(path, 'PropTypes');
- if (proptypesBinding == null) {
+ if (proptypesBinding == null && state.get('hasInjectedPropTypes') !== true) {
let targetNode;
// find the first statement in the program and import PropTypes there
targetNode = path;
@@ -1043,6 +1052,7 @@ function processComponentDeclaration(typeDefinition, path, state) {
types.stringLiteral('prop-types')
)
);
+ state.set('hasInjectedPropTypes', true);
}
}
@@ -1268,3 +1278,7 @@ module.exports = function propTypesFromTypeScript({ types }) {
},
};
};
+
+module.exports.clearImportCache = function clearImportCache() {
+ importedDefinitionsCache.clear();
+}
diff --git a/scripts/babel/proptypes-from-ts-props/index.test.js b/scripts/babel/proptypes-from-ts-props/index.test.js
index 7edfa6061d8..e204af325bc 100644
--- a/scripts/babel/proptypes-from-ts-props/index.test.js
+++ b/scripts/babel/proptypes-from-ts-props/index.test.js
@@ -10,6 +10,9 @@ const babelOptions = {
],
filename: 'somefile.tsx',
};
+const babelPlugin = require('./index');
+
+beforeEach(() => babelPlugin.clearImportCache());
describe('proptypes-from-ts-props', () => {
diff --git a/src-docs/src/i18ntokens.json b/src-docs/src/i18ntokens.json
index bb50ffa9ce5..f5cc28ea59b 100644
--- a/src-docs/src/i18ntokens.json
+++ b/src-docs/src/i18ntokens.json
@@ -421,11 +421,11 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 329,
+ "line": 337,
"column": 14
},
"end": {
- "line": 332,
+ "line": 340,
"column": 16
}
},
@@ -661,15 +661,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 37,
+ "line": 61,
"column": 6
},
"end": {
- "line": 41,
+ "line": 65,
"column": 57
}
},
- "filepath": "src/components/pagination/pagination.js"
+ "filepath": "src/components/pagination/pagination.tsx"
},
{
"token": "euiPagination.previousPage",
@@ -677,15 +677,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 57,
+ "line": 81,
"column": 4
},
"end": {
- "line": 57,
+ "line": 81,
"column": 72
}
},
- "filepath": "src/components/pagination/pagination.js"
+ "filepath": "src/components/pagination/pagination.tsx"
},
{
"token": "euiPagination.pageOfTotal",
@@ -693,15 +693,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 75,
+ "line": 99,
"column": 6
},
"end": {
- "line": 79,
+ "line": 103,
"column": 53
}
},
- "filepath": "src/components/pagination/pagination.js"
+ "filepath": "src/components/pagination/pagination.tsx"
},
{
"token": "euiPagination.jumpToLastPage",
@@ -709,15 +709,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 120,
+ "line": 144,
"column": 6
},
"end": {
- "line": 124,
+ "line": 148,
"column": 31
}
},
- "filepath": "src/components/pagination/pagination.js"
+ "filepath": "src/components/pagination/pagination.tsx"
},
{
"token": "euiPagination.nextPage",
@@ -725,15 +725,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 138,
+ "line": 162,
"column": 4
},
"end": {
- "line": 138,
+ "line": 162,
"column": 64
}
},
- "filepath": "src/components/pagination/pagination.js"
+ "filepath": "src/components/pagination/pagination.tsx"
},
{
"token": "euiPopover.screenReaderAnnouncement",
@@ -741,15 +741,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 442,
+ "line": 589,
"column": 14
},
"end": {
- "line": 445,
+ "line": 592,
"column": 16
}
},
- "filepath": "src/components/popover/popover.js"
+ "filepath": "src/components/popover/popover.tsx"
},
{
"token": "euiSelectable.loadingOptions",
@@ -917,15 +917,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 50,
+ "line": 49,
"column": 8
},
"end": {
- "line": 50,
+ "line": 49,
"column": 72
}
},
- "filepath": "src/components/table/mobile/table_sort_mobile.js"
+ "filepath": "src/components/table/mobile/table_sort_mobile.tsx"
},
{
"token": "euiTablePagination.rowsPerPage",
@@ -933,15 +933,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 50,
+ "line": 62,
"column": 8
},
"end": {
- "line": 53,
+ "line": 65,
"column": 10
}
},
- "filepath": "src/components/table/table_pagination/table_pagination.js"
+ "filepath": "src/components/table/table_pagination/table_pagination.tsx"
},
{
"token": "euiTablePagination.rowsPerPageOption",
@@ -949,15 +949,15 @@
"highlighting": "string",
"loc": {
"start": {
- "line": 66,
+ "line": 78,
"column": 8
},
"end": {
- "line": 70,
+ "line": 82,
"column": 10
}
},
- "filepath": "src/components/table/table_pagination/table_pagination.js"
+ "filepath": "src/components/table/table_pagination/table_pagination.tsx"
},
{
"token": "euiToast.dismissToast",
diff --git a/src/components/basic_table/__snapshots__/basic_table.test.js.snap b/src/components/basic_table/__snapshots__/basic_table.test.js.snap
index 0fb11ef045d..054b85bca2e 100644
--- a/src/components/basic_table/__snapshots__/basic_table.test.js.snap
+++ b/src/components/basic_table/__snapshots__/basic_table.test.js.snap
@@ -48,12 +48,6 @@ exports[`EuiBasicTable cellProps renders cells with custom props from a callback
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -173,12 +167,6 @@ exports[`EuiBasicTable cellProps renders rows with custom props from an object 1
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -300,12 +288,6 @@ exports[`EuiBasicTable empty is rendered 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -316,12 +298,6 @@ exports[`EuiBasicTable empty is rendered 1`] = `
align="center"
colSpan={1}
isMobileFullWidth={true}
- mobileOptions={
- Object {
- "show": true,
- }
- }
- textOnly={true}
>
No items found
@@ -380,12 +356,6 @@ exports[`EuiBasicTable empty renders a node as a custom message 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -396,12 +366,6 @@ exports[`EuiBasicTable empty renders a node as a custom message 1`] = `
align="center"
colSpan={1}
isMobileFullWidth={true}
- mobileOptions={
- Object {
- "show": true,
- }
- }
- textOnly={true}
>
no items, click
@@ -468,12 +432,6 @@ exports[`EuiBasicTable empty renders a string as a custom message 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -484,12 +442,6 @@ exports[`EuiBasicTable empty renders a string as a custom message 1`] = `
align="center"
colSpan={1}
isMobileFullWidth={true}
- mobileOptions={
- Object {
- "show": true,
- }
- }
- textOnly={true}
>
where my items at?
@@ -548,12 +500,6 @@ exports[`EuiBasicTable footers do not render without a column footer definition
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -561,12 +507,6 @@ exports[`EuiBasicTable footers do not render without a column footer definition
align="left"
data-test-subj="tableHeaderCell_id_1"
key="_data_h_id_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
ID
@@ -574,12 +514,6 @@ exports[`EuiBasicTable footers do not render without a column footer definition
align="left"
data-test-subj="tableHeaderCell_age_2"
key="_data_h_age_2"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Age
@@ -802,13 +736,7 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
@@ -816,12 +744,6 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
align="left"
data-test-subj="tableHeaderCell_id_1"
key="_data_h_id_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
ID
@@ -829,12 +751,6 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
align="left"
data-test-subj="tableHeaderCell_age_2"
key="_data_h_age_2"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Age
@@ -1005,11 +921,9 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
@@ -1017,13 +931,11 @@ exports[`EuiBasicTable footers render with pagination, selection, sorting, and f
ID
@@ -1099,12 +1011,6 @@ exports[`EuiBasicTable itemIdToExpandedRowMap renders an expanded row 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1133,14 +1039,7 @@ exports[`EuiBasicTable itemIdToExpandedRowMap renders an expanded row 1`] = `
isExpandedRow={true}
>
Expanded row
@@ -1235,12 +1134,6 @@ exports[`EuiBasicTable rowProps renders rows with custom props from a callback 1
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1366,12 +1259,6 @@ exports[`EuiBasicTable rowProps renders rows with custom props from an object 1`
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1497,12 +1384,6 @@ exports[`EuiBasicTable with pagination - 2nd page 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1607,12 +1488,6 @@ exports[`EuiBasicTable with pagination 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1734,12 +1609,6 @@ exports[`EuiBasicTable with pagination and error 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -1750,12 +1619,6 @@ exports[`EuiBasicTable with pagination and error 1`] = `
align="center"
colSpan={1}
isMobileFullWidth={true}
- mobileOptions={
- Object {
- "show": true,
- }
- }
- textOnly={true}
>
Name
@@ -1997,12 +1854,6 @@ exports[`EuiBasicTable with pagination, hiding the per page options 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -2159,13 +2010,7 @@ exports[`EuiBasicTable with pagination, selection and sorting 1`] = `
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
@@ -2354,25 +2199,13 @@ exports[`EuiBasicTable with pagination, selection, sorting and a single record a
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
Actions
@@ -2410,11 +2243,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and a single record a
align="right"
hasActions={true}
key="record_actions_1_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -2472,11 +2300,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and a single record a
align="right"
hasActions={true}
key="record_actions_2_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -2534,11 +2357,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and a single record a
align="right"
hasActions={true}
key="record_actions_3_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -2663,13 +2481,7 @@ exports[`EuiBasicTable with pagination, selection, sorting and column dataType 1
isSortAscending={true}
isSorted={true}
key="_data_h_count_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Count
@@ -2858,13 +2670,7 @@ exports[`EuiBasicTable with pagination, selection, sorting and column renderer 1
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
@@ -3053,25 +2859,13 @@ exports[`EuiBasicTable with pagination, selection, sorting and multiple record a
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
Actions
@@ -3109,11 +2903,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and multiple record a
align="right"
hasActions={true}
key="record_actions_1_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -3177,11 +2966,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and multiple record a
align="right"
hasActions={true}
key="record_actions_2_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -3245,11 +3029,6 @@ exports[`EuiBasicTable with pagination, selection, sorting and multiple record a
align="right"
hasActions={true}
key="record_actions_3_1"
- mobileOptions={
- Object {
- "show": true,
- }
- }
showOnHover={true}
textOnly={false}
>
@@ -3380,13 +3159,7 @@ exports[`EuiBasicTable with pagination, selection, sorting, column renderer and
isSortAscending={true}
isSorted={true}
key="_data_h_count_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Count
@@ -3541,12 +3314,6 @@ exports[`EuiBasicTable with sortable columns and sorting disabled 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
Name
@@ -3673,13 +3440,7 @@ exports[`EuiBasicTable with sorting 1`] = `
isSortAscending={true}
isSorted={true}
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
onSort={[Function]}
- scope="col"
>
Name
diff --git a/src/components/basic_table/__snapshots__/default_item_action.test.js.snap b/src/components/basic_table/__snapshots__/default_item_action.test.js.snap
index e07983baee7..ba136011d28 100644
--- a/src/components/basic_table/__snapshots__/default_item_action.test.js.snap
+++ b/src/components/basic_table/__snapshots__/default_item_action.test.js.snap
@@ -9,11 +9,9 @@ exports[`DefaultItemAction render - button 1`] = `
action1
@@ -29,11 +27,9 @@ exports[`DefaultItemAction render - icon 1`] = `
`;
diff --git a/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap b/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
index afecec86f3c..fd3b2b209d8 100644
--- a/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
+++ b/src/components/basic_table/__snapshots__/in_memory_table.test.js.snap
@@ -150,12 +150,6 @@ exports[`EuiInMemoryTable behavior pagination 1`] = `
align="left"
data-test-subj="tableHeaderCell_name_0"
key="_data_h_name_0"
- mobileOptions={
- Object {
- "show": true,
- }
- }
- scope="col"
>
{
- // If in the loading state, force disabled to true
- isDisabled = isLoading ? true : isDisabled;
-
- const classes = classNames(
- 'euiButton',
- colorToClassNameMap[color],
- sizeToClassNameMap[size],
- iconSideToClassNameMap[iconSide],
- className,
- {
- 'euiButton--fill': fill,
- 'euiButton--fullWidth': fullWidth,
- }
- );
-
- const contentClassNames = classNames(
- 'euiButton__content',
- contentProps && contentProps.className
- );
-
- const textClassNames = classNames(
- 'euiButton__text',
- textProps && textProps.className
- );
-
- // Add an icon to the button if one exists.
- let buttonIcon;
-
- if (isLoading) {
- buttonIcon = ;
- } else if (iconType) {
- buttonIcon = (
-
- );
- }
-
- const innerNode = (
-
- {buttonIcon}
-
- {children}
-
-
- );
-
- // elements don't respect the `disabled` attribute. So if we're disabled, we'll just pretend
- // this is a button and piggyback off its disabled styles.
- if (href && !isDisabled) {
- const secureRel = getSecureRelForTarget({ href, target, rel });
-
- return (
-
- {innerNode}
-
- );
- } else {
- return (
-
- {innerNode}
-
- );
- }
-};
-
-EuiButton.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
-
- /**
- * See EuiIcon
- */
- iconType: IconPropType,
- iconSide: PropTypes.oneOf(ICON_SIDES),
-
- /**
- * Add more focus to an action
- */
- fill: PropTypes.bool,
-
- /**
- * Define the color of the button
- */
- color: PropTypes.oneOf(COLORS),
- size: PropTypes.oneOf(SIZES),
-
- /**
- * Expands button to fill the width of the parent
- */
- fullWidth: PropTypes.bool,
- isDisabled: PropTypes.bool,
- href: PropTypes.string,
- target: PropTypes.string,
- rel: PropTypes.string,
- onClick: PropTypes.func,
-
- /**
- * Adds/swaps for loading spinner & disables
- */
- isLoading: PropTypes.bool,
-
- /**
- * Standard HTML attribute
- */
- type: PropTypes.string,
- buttonRef: PropTypes.func,
-
- /**
- * Passes props to `euiButton__content` span
- */
- contentProps: PropTypes.object,
-
- /**
- * Passes props to `euiButton__text` span
- */
- textProps: PropTypes.object,
-};
-
-EuiButton.defaultProps = {
- size: 'm',
- type: 'button',
- iconSide: 'left',
- color: 'primary',
- fill: false,
-};
diff --git a/src/components/button/button.test.js b/src/components/button/button.test.tsx
similarity index 100%
rename from src/components/button/button.test.js
rename to src/components/button/button.test.tsx
diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx
new file mode 100644
index 00000000000..3fac06d3d08
--- /dev/null
+++ b/src/components/button/button.tsx
@@ -0,0 +1,184 @@
+import React, {
+ AnchorHTMLAttributes,
+ ButtonHTMLAttributes,
+ FunctionComponent,
+ HTMLAttributes,
+ MouseEventHandler,
+ Ref,
+} from 'react';
+import classNames from 'classnames';
+
+import { CommonProps, ExclusiveUnion, keysOf } from '../common';
+import { EuiLoadingSpinner } from '../loading';
+
+import { getSecureRelForTarget } from '../../services';
+
+import { IconType, EuiIcon } from '../icon';
+
+export type ButtonIconSide = 'left' | 'right';
+
+export type ButtonColor =
+ | 'primary'
+ | 'secondary'
+ | 'warning'
+ | 'danger'
+ | 'ghost'
+ | 'text';
+
+export type ButtonSize = 's' | 'm';
+
+const colorToClassNameMap: { [color in ButtonColor]: string } = {
+ primary: 'euiButton--primary',
+ secondary: 'euiButton--secondary',
+ warning: 'euiButton--warning',
+ danger: 'euiButton--danger',
+ ghost: 'euiButton--ghost',
+ text: 'euiButton--text',
+};
+
+export const COLORS = keysOf(colorToClassNameMap);
+
+const sizeToClassNameMap: { [size in ButtonSize]: string | null } = {
+ s: 'euiButton--small',
+ m: null,
+};
+
+export const SIZES = keysOf(sizeToClassNameMap);
+
+const iconSideToClassNameMap: { [side in ButtonIconSide]: string | null } = {
+ left: null,
+ right: 'euiButton--iconRight',
+};
+
+export const ICON_SIDES = keysOf(iconSideToClassNameMap);
+
+export interface EuiButtonProps extends CommonProps {
+ iconType?: IconType;
+ iconSide?: ButtonIconSide;
+ fill?: boolean;
+ color?: ButtonColor;
+ size?: ButtonSize;
+ isLoading?: boolean;
+ isDisabled?: boolean;
+ fullWidth?: boolean;
+ contentProps?: HTMLAttributes;
+ textProps?: HTMLAttributes;
+}
+
+type EuiButtonPropsForAnchor = EuiButtonProps &
+ AnchorHTMLAttributes & {
+ href?: string;
+ onClick?: MouseEventHandler;
+ buttonRef?: Ref;
+ };
+
+type EuiButtonPropsForButton = EuiButtonProps &
+ ButtonHTMLAttributes & {
+ onClick?: MouseEventHandler;
+ buttonRef?: Ref;
+ };
+
+export type Props = ExclusiveUnion<
+ EuiButtonPropsForAnchor,
+ EuiButtonPropsForButton
+>;
+
+export const EuiButton: FunctionComponent = ({
+ children,
+ className,
+ iconType,
+ iconSide = 'left',
+ color = 'primary',
+ size = 'm',
+ fill = false,
+ isDisabled,
+ isLoading,
+ href,
+ target,
+ rel,
+ type = 'button',
+ buttonRef,
+ contentProps,
+ textProps,
+ fullWidth,
+ ...rest
+}) => {
+ // If in the loading state, force disabled to true
+ isDisabled = isLoading ? true : isDisabled;
+
+ const classes = classNames(
+ 'euiButton',
+ color ? colorToClassNameMap[color] : null,
+ size ? sizeToClassNameMap[size] : null,
+ iconSide ? iconSideToClassNameMap[iconSide] : null,
+ className,
+ {
+ 'euiButton--fill': fill,
+ 'euiButton--fullWidth': fullWidth,
+ }
+ );
+
+ const contentClassNames = classNames(
+ 'euiButton__content',
+ contentProps && contentProps.className
+ );
+
+ const textClassNames = classNames(
+ 'euiButton__text',
+ textProps && textProps.className
+ );
+
+ // Add an icon to the button if one exists.
+ let buttonIcon;
+
+ if (isLoading) {
+ buttonIcon = ;
+ } else if (iconType) {
+ buttonIcon = (
+
+ );
+ }
+
+ const innerNode = (
+
+ {buttonIcon}
+
+ {children}
+
+
+ );
+
+ // elements don't respect the `disabled` attribute. So if we're disabled, we'll just pretend
+ // this is a button and piggyback off its disabled styles.
+ if (href && !isDisabled) {
+ const secureRel = getSecureRelForTarget({ href, target, rel });
+
+ return (
+ }
+ {...rest as AnchorHTMLAttributes}>
+ {innerNode}
+
+ );
+ }
+
+ return (
+ }
+ {...rest as ButtonHTMLAttributes}>
+ {innerNode}
+
+ );
+};
diff --git a/src/components/button/button_empty/__snapshots__/button_empty.test.js.snap b/src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap
similarity index 100%
rename from src/components/button/button_empty/__snapshots__/button_empty.test.js.snap
rename to src/components/button/button_empty/__snapshots__/button_empty.test.tsx.snap
diff --git a/src/components/button/button_empty/button_empty.test.js b/src/components/button/button_empty/button_empty.test.tsx
similarity index 100%
rename from src/components/button/button_empty/button_empty.test.js
rename to src/components/button/button_empty/button_empty.test.tsx
diff --git a/src/components/button/button_empty/button_empty.js b/src/components/button/button_empty/button_empty.tsx
similarity index 64%
rename from src/components/button/button_empty/button_empty.js
rename to src/components/button/button_empty/button_empty.tsx
index 55b583efbbd..bd5615c7103 100644
--- a/src/components/button/button_empty/button_empty.js
+++ b/src/components/button/button_empty/button_empty.tsx
@@ -1,12 +1,10 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent, HTMLAttributes } from 'react';
import classNames from 'classnames';
+import { CommonProps, keysOf, NoArgCallback } from '../../common';
import { EuiLoadingSpinner } from '../../loading';
-
import { getSecureRelForTarget } from '../../../services';
-
-import { IconPropType, EuiIcon } from '../../icon';
+import { IconType, EuiIcon } from '../../icon';
const colorToClassNameMap = {
primary: 'euiButtonEmpty--primary',
@@ -16,7 +14,7 @@ const colorToClassNameMap = {
ghost: 'euiButtonEmpty--ghost',
};
-export const COLORS = Object.keys(colorToClassNameMap);
+export const COLORS = keysOf(colorToClassNameMap);
const sizeToClassNameMap = {
xs: 'euiButtonEmpty--xSmall',
@@ -24,28 +22,60 @@ const sizeToClassNameMap = {
l: 'euiButtonEmpty--large',
};
-export const SIZES = Object.keys(sizeToClassNameMap);
+export const SIZES = keysOf(sizeToClassNameMap);
const iconSideToClassNameMap = {
left: '',
right: 'euiButtonEmpty--iconRight',
};
-export const ICON_SIDES = Object.keys(iconSideToClassNameMap);
+export const ICON_SIDES = keysOf(iconSideToClassNameMap);
const flushTypeToClassNameMap = {
left: 'euiButtonEmpty--flushLeft',
right: 'euiButtonEmpty--flushRight',
};
-export const FLUSH_TYPES = Object.keys(flushTypeToClassNameMap);
+export const FLUSH_TYPES = keysOf(flushTypeToClassNameMap);
+
+export interface EuiButtonEmptyProps {
+ iconType?: IconType;
+ iconSide?: keyof typeof iconSideToClassNameMap;
+ color?: keyof typeof colorToClassNameMap;
+ size?: keyof typeof sizeToClassNameMap;
+ flush?: keyof typeof flushTypeToClassNameMap;
+ isDisabled?: boolean;
+ href?: string;
+ target?: string;
+ rel?: string;
+ onClick?: NoArgCallback;
+
+ /**
+ * Adds/swaps for loading spinner & disables
+ */
+ isLoading?: boolean;
+
+ type?: 'button' | 'submit';
+ buttonRef?: () => void;
+ /**
+ * Passes props to `euiButtonEmpty__content` span
+ */
+ contentProps?: Partial>;
+
+ /**
+ * Passes props to `euiButtonEmpty__text` span
+ */
+ textProps?: Partial>;
+}
+
+type Props = CommonProps & EuiButtonEmptyProps;
-export const EuiButtonEmpty = ({
+export const EuiButtonEmpty: FunctionComponent = ({
children,
className,
iconType,
- iconSide,
- color,
+ iconSide = 'left',
+ color = 'primary',
size,
flush,
isDisabled,
@@ -53,7 +83,7 @@ export const EuiButtonEmpty = ({
href,
target,
rel,
- type,
+ type = 'button',
buttonRef,
contentProps,
textProps,
@@ -65,9 +95,9 @@ export const EuiButtonEmpty = ({
const classes = classNames(
'euiButtonEmpty',
colorToClassNameMap[color],
- sizeToClassNameMap[size],
+ size ? sizeToClassNameMap[size] : null,
iconSideToClassNameMap[iconSide],
- flushTypeToClassNameMap[flush],
+ flush ? flushTypeToClassNameMap[flush] : null,
className
);
@@ -122,55 +152,16 @@ export const EuiButtonEmpty = ({
{innerNode}
);
- } else {
- return (
-
- {innerNode}
-
- );
}
-};
-
-EuiButtonEmpty.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- iconType: IconPropType,
- iconSide: PropTypes.oneOf(ICON_SIDES),
- color: PropTypes.oneOf(COLORS),
- size: PropTypes.oneOf(SIZES),
- flush: PropTypes.oneOf(FLUSH_TYPES),
- isDisabled: PropTypes.bool,
- href: PropTypes.string,
- target: PropTypes.string,
- rel: PropTypes.string,
- onClick: PropTypes.func,
-
- /**
- * Adds/swaps for loading spinner & disables
- */
- isLoading: PropTypes.bool,
- type: PropTypes.string,
- buttonRef: PropTypes.func,
-
- /**
- * Passes props to `euiButtonEmpty__content` span
- */
- contentProps: PropTypes.object,
-
- /**
- * Passes props to `euiButtonEmpty__text` span
- */
- textProps: PropTypes.object,
-};
-
-EuiButtonEmpty.defaultProps = {
- type: 'button',
- iconSide: 'left',
- color: 'primary',
+ return (
+
+ {innerNode}
+
+ );
};
diff --git a/src/components/button/button_empty/index.js b/src/components/button/button_empty/index.js
deleted file mode 100644
index 0fed95c145a..00000000000
--- a/src/components/button/button_empty/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { COLORS, ICON_SIDES, EuiButtonEmpty } from './button_empty';
diff --git a/src/components/button/button_empty/index.ts b/src/components/button/button_empty/index.ts
new file mode 100644
index 00000000000..338a6ad895e
--- /dev/null
+++ b/src/components/button/button_empty/index.ts
@@ -0,0 +1,6 @@
+export {
+ COLORS,
+ ICON_SIDES,
+ EuiButtonEmpty,
+ EuiButtonEmptyProps,
+} from './button_empty';
diff --git a/src/components/button/button_group/__snapshots__/button_group.test.js.snap b/src/components/button/button_group/__snapshots__/button_group.test.tsx.snap
similarity index 100%
rename from src/components/button/button_group/__snapshots__/button_group.test.js.snap
rename to src/components/button/button_group/__snapshots__/button_group.test.tsx.snap
diff --git a/src/components/button/button_group/button_group.test.js b/src/components/button/button_group/button_group.test.tsx
similarity index 100%
rename from src/components/button/button_group/button_group.test.js
rename to src/components/button/button_group/button_group.test.tsx
diff --git a/src/components/button/button_group/button_group.js b/src/components/button/button_group/button_group.tsx
similarity index 53%
rename from src/components/button/button_group/button_group.js
rename to src/components/button/button_group/button_group.tsx
index fd87a2bb311..120315ac205 100644
--- a/src/components/button/button_group/button_group.js
+++ b/src/components/button/button_group/button_group.tsx
@@ -1,25 +1,63 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent, HTMLAttributes } from 'react';
import classNames from 'classnames';
import { EuiScreenReaderOnly } from '../../accessibility';
-import { EuiButtonToggle } from '../button_toggle';
-import { TOGGLE_TYPES } from '../../toggle';
+import { ToggleType } from '../../toggle';
+import { IconType } from '../../icon';
-export const EuiButtonGroup = ({
+import { EuiButtonToggle } from '../button_toggle';
+import { Omit } from '../../common';
+
+import { ButtonColor } from '../button';
+
+export interface EuiButtonGroupIdToSelectedMap {
+ [id: string]: boolean;
+}
+
+export type GroupButtonSize = 's' | 'm';
+
+export interface EuiButtonGroupOption {
+ id: string;
+ label: string;
+ isDisabled?: boolean;
+ name?: string;
+ value?: any;
+ iconSide?: 'left' | 'right';
+ iconType?: IconType;
+}
+
+export interface EuiButtonGroupProps {
+ options?: EuiButtonGroupOption[];
+ onChange: (id: string, value: any) => void;
+ buttonSize?: GroupButtonSize;
+ isDisabled?: boolean;
+ isFullWidth?: boolean;
+ isIconOnly?: boolean;
+ idSelected?: string;
+ idToSelectedMap?: EuiButtonGroupIdToSelectedMap;
+ legend?: string;
+ color?: ButtonColor;
+ type?: ToggleType;
+ name?: string;
+}
+
+type Props = Omit, 'onChange'> &
+ EuiButtonGroupProps;
+
+export const EuiButtonGroup: FunctionComponent = ({
className,
- buttonSize,
- color,
+ buttonSize = 's',
+ color = 'text',
idSelected,
- idToSelectedMap,
+ idToSelectedMap = {},
isDisabled,
isFullWidth,
isIconOnly,
name,
legend,
onChange,
- options,
- type,
+ options = [],
+ type = 'single',
...rest
}) => {
const classes = classNames(
@@ -78,64 +116,3 @@ export const EuiButtonGroup = ({
);
};
-
-EuiButtonGroup.propTypes = {
- options: PropTypes.arrayOf(
- PropTypes.shape({
- id: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired,
- isDisabled: PropTypes.bool,
- })
- ).isRequired,
- onChange: PropTypes.func.isRequired,
-
- /**
- * See `EuiButton`
- */
- color: PropTypes.string,
-
- /**
- * Most button groups should be the small button size,
- * but if you NEED to bump it to regular, change this to 'm'
- */
- buttonSize: PropTypes.string,
-
- /**
- * Hides the label from the button content and only displays the icon
- */
- isIconOnly: PropTypes.bool,
- isDisabled: PropTypes.bool,
-
- /**
- * Makes the whole group 100% of its parent
- */
- isFullWidth: PropTypes.bool,
-
- /**
- * Can only a "single" option be selected or "multi"ple?
- */
- type: PropTypes.oneOf(TOGGLE_TYPES),
-
- /**
- * Id of selected option for `type="single"`
- */
- idSelected: PropTypes.string,
-
- /**
- * Map of ids of selected options for `type="multi"`
- */
- idToSelectedMap: PropTypes.objectOf(PropTypes.bool),
-
- /**
- * Adds a hidden legend to the group for accessiblity
- */
- legend: PropTypes.string,
-};
-
-EuiButtonGroup.defaultProps = {
- buttonSize: 's',
- color: 'text',
- idToSelectedMap: {},
- options: [],
- type: 'single',
-};
diff --git a/src/components/button/button_group/index.js b/src/components/button/button_group/index.js
deleted file mode 100644
index 2bfb7bd81a6..00000000000
--- a/src/components/button/button_group/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiButtonGroup } from './button_group';
diff --git a/src/components/button/button_group/index.ts b/src/components/button/button_group/index.ts
new file mode 100644
index 00000000000..240e4091125
--- /dev/null
+++ b/src/components/button/button_group/index.ts
@@ -0,0 +1,5 @@
+export {
+ EuiButtonGroup,
+ EuiButtonGroupOption,
+ EuiButtonGroupProps,
+} from './button_group';
diff --git a/src/components/button/button_icon/__snapshots__/button_icon.test.js.snap b/src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap
similarity index 100%
rename from src/components/button/button_icon/__snapshots__/button_icon.test.js.snap
rename to src/components/button/button_icon/__snapshots__/button_icon.test.tsx.snap
diff --git a/src/components/button/button_icon/button_icon.js b/src/components/button/button_icon/button_icon.js
deleted file mode 100644
index 7f287876328..00000000000
--- a/src/components/button/button_icon/button_icon.js
+++ /dev/null
@@ -1,120 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
-
-import { getSecureRelForTarget } from '../../../services';
-
-import { ICON_SIZES, IconPropType, EuiIcon } from '../../icon';
-
-const accessibleButtonIcon = (props, propName, componentName) => {
- if (props['aria-label']) {
- return;
- }
-
- if (props['aria-labelledby']) {
- return;
- }
-
- throw new Error(
- `${componentName} requires aria-label or aria-labelledby to be specified because icon-only
- buttons are screen-reader-inaccessible without them.`
- );
-};
-
-const colorToClassNameMap = {
- danger: 'euiButtonIcon--danger',
- disabled: 'euiButtonIcon--disabled',
- ghost: 'euiButtonIcon--ghost',
- primary: 'euiButtonIcon--primary',
- subdued: 'euiButtonIcon--subdued',
- success: 'euiButtonIcon--success',
- text: 'euiButtonIcon--text',
- warning: 'euiButtonIcon--warning',
-};
-
-export const COLORS = Object.keys(colorToClassNameMap);
-
-export const EuiButtonIcon = ({
- className,
- iconType,
- iconSize,
- color,
- isDisabled,
- href,
- type,
- target,
- rel,
- buttonRef,
- ...rest
-}) => {
- const classes = classNames(
- 'euiButtonIcon',
- colorToClassNameMap[color],
- className
- );
-
- // Add an icon to the button if one exists.
- let buttonIcon;
-
- if (iconType) {
- buttonIcon = (
-
- );
- }
-
- // elements don't respect the `disabled` attribute. So if we're disabled, we'll just pretend
- // this is a button and piggyback off its disabled styles.
- if (href && !isDisabled) {
- const secureRel = getSecureRelForTarget({ href, target, rel });
-
- return (
-
- {buttonIcon}
-
- );
- } else {
- return (
-
- {buttonIcon}
-
- );
- }
-};
-
-EuiButtonIcon.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- iconType: IconPropType,
- iconSize: PropTypes.oneOf(ICON_SIZES),
- color: PropTypes.oneOf(COLORS),
- isDisabled: PropTypes.bool,
- 'aria-label': accessibleButtonIcon,
- href: PropTypes.string,
- target: PropTypes.string,
- rel: PropTypes.string,
- onClick: PropTypes.func,
- type: PropTypes.string,
- buttonRef: PropTypes.func,
-};
-
-EuiButtonIcon.defaultProps = {
- type: 'button',
- color: 'primary',
- iconSize: 'm',
-};
diff --git a/src/components/button/button_icon/button_icon.test.js b/src/components/button/button_icon/button_icon.test.tsx
similarity index 100%
rename from src/components/button/button_icon/button_icon.test.js
rename to src/components/button/button_icon/button_icon.test.tsx
diff --git a/src/components/button/button_icon/button_icon.tsx b/src/components/button/button_icon/button_icon.tsx
new file mode 100644
index 00000000000..ec7379c6cdc
--- /dev/null
+++ b/src/components/button/button_icon/button_icon.tsx
@@ -0,0 +1,135 @@
+import React, {
+ AnchorHTMLAttributes,
+ ButtonHTMLAttributes,
+ FunctionComponent,
+ MouseEventHandler,
+ Ref,
+} from 'react';
+import classNames from 'classnames';
+
+import { getSecureRelForTarget } from '../../../services';
+import { CommonProps, ExclusiveUnion, keysOf } from '../../common';
+
+import { IconType, IconSize, EuiIcon } from '../../icon';
+
+import { ButtonSize } from '../button';
+
+export type ButtonIconColor =
+ | 'danger'
+ | 'disabled'
+ | 'ghost'
+ | 'primary'
+ | 'subdued'
+ | 'success'
+ | 'text'
+ | 'warning';
+
+export interface EuiButtonIconProps extends CommonProps {
+ iconType?: IconType;
+ color?: ButtonIconColor;
+ 'aria-label'?: string;
+ 'aria-labelledby'?: string;
+ isDisabled?: boolean;
+ size?: ButtonSize;
+ iconSize?: IconSize;
+}
+
+type EuiButtonIconPropsForAnchor = EuiButtonIconProps &
+ AnchorHTMLAttributes & {
+ href: string;
+ onClick?: MouseEventHandler;
+ buttonRef?: Ref;
+ };
+
+export type EuiButtonIconPropsForButton = EuiButtonIconProps &
+ ButtonHTMLAttributes & {
+ onClick?: MouseEventHandler;
+ buttonRef?: Ref;
+ };
+
+type Props = ExclusiveUnion<
+ EuiButtonIconPropsForAnchor,
+ EuiButtonIconPropsForButton
+>;
+
+const colorToClassNameMap: { [color in ButtonIconColor]: string } = {
+ danger: 'euiButtonIcon--danger',
+ disabled: 'euiButtonIcon--disabled',
+ ghost: 'euiButtonIcon--ghost',
+ primary: 'euiButtonIcon--primary',
+ subdued: 'euiButtonIcon--subdued',
+ success: 'euiButtonIcon--success',
+ text: 'euiButtonIcon--text',
+ warning: 'euiButtonIcon--warning',
+};
+
+export const COLORS = keysOf(colorToClassNameMap);
+
+export const EuiButtonIcon: FunctionComponent = ({
+ className,
+ iconType,
+ iconSize = 'm',
+ color = 'primary',
+ isDisabled,
+ href,
+ type = 'button',
+ target,
+ rel,
+ buttonRef,
+ ...rest
+}) => {
+ if (!rest['aria-label'] && !rest['aria-labelledby']) {
+ console.warn(
+ `EuiButtonIcon requires aria-label or aria-labelledby to be specified because icon-only
+ buttons are screen-reader-inaccessible without them.`
+ );
+ }
+ const classes = classNames(
+ 'euiButtonIcon',
+ colorToClassNameMap[color],
+ className
+ );
+
+ // Add an icon to the button if one exists.
+ let buttonIcon;
+
+ if (iconType) {
+ buttonIcon = (
+
+ );
+ }
+
+ // elements don't respect the `disabled` attribute. So if we're disabled, we'll just pretend
+ // this is a button and piggyback off its disabled styles.
+ if (href && !isDisabled) {
+ const secureRel = getSecureRelForTarget({ href, target, rel });
+
+ return (
+ }
+ {...rest as AnchorHTMLAttributes}>
+ {buttonIcon}
+
+ );
+ }
+
+ return (
+ }
+ {...rest as ButtonHTMLAttributes}>
+ {buttonIcon}
+
+ );
+};
diff --git a/src/components/button/button_icon/index.js b/src/components/button/button_icon/index.js
deleted file mode 100644
index 5609d91939e..00000000000
--- a/src/components/button/button_icon/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiButtonIcon } from './button_icon';
diff --git a/src/components/button/button_icon/index.ts b/src/components/button/button_icon/index.ts
new file mode 100644
index 00000000000..f1fa004845a
--- /dev/null
+++ b/src/components/button/button_icon/index.ts
@@ -0,0 +1,5 @@
+export {
+ EuiButtonIcon,
+ EuiButtonIconProps,
+ EuiButtonIconPropsForButton,
+} from './button_icon';
diff --git a/src/components/button/button_toggle/__snapshots__/button_toggle.test.js.snap b/src/components/button/button_toggle/__snapshots__/button_toggle.test.tsx.snap
similarity index 100%
rename from src/components/button/button_toggle/__snapshots__/button_toggle.test.js.snap
rename to src/components/button/button_toggle/__snapshots__/button_toggle.test.tsx.snap
diff --git a/src/components/button/button_toggle/button_toggle.test.js b/src/components/button/button_toggle/button_toggle.test.tsx
similarity index 100%
rename from src/components/button/button_toggle/button_toggle.test.js
rename to src/components/button/button_toggle/button_toggle.test.tsx
diff --git a/src/components/button/button_toggle/button_toggle.js b/src/components/button/button_toggle/button_toggle.tsx
similarity index 53%
rename from src/components/button/button_toggle/button_toggle.js
rename to src/components/button/button_toggle/button_toggle.tsx
index eea7162f7a0..83f9d9d51fe 100644
--- a/src/components/button/button_toggle/button_toggle.js
+++ b/src/components/button/button_toggle/button_toggle.tsx
@@ -1,13 +1,73 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ AnchorHTMLAttributes,
+ ButtonHTMLAttributes,
+ ChangeEventHandler,
+ FunctionComponent,
+ MouseEventHandler,
+} from 'react';
import classNames from 'classnames';
-import { EuiToggle, TOGGLE_TYPES } from '../../toggle';
-import { EuiButton } from '../button';
+import { ExclusiveUnion, Omit } from '../../common';
+import { EuiToggle, ToggleType } from '../../toggle';
+import { EuiButton, EuiButtonProps } from '../button';
-export const EuiButtonToggle = ({
+export interface EuiButtonToggleProps extends EuiButtonProps {
+ /**
+ * Simulates a `EuiButtonEmpty`
+ */
+ isEmpty?: boolean;
+
+ /**
+ * Hides the label from the button content and only displays the icon
+ */
+ isIconOnly?: boolean;
+
+ /**
+ * Initial state of the toggle
+ */
+ isSelected?: boolean;
+
+ /**
+ * Button label, which is also passed to `EuiToggle` as the input's label
+ */
+ label: string;
+
+ /**
+ * Classnames to add to `EuiToggle` instead of the `EuiButton`
+ */
+ toggleClassName?: string;
+
+ /**
+ * Is the button a single action or part of a group (multi)?
+ * Used primarily for `EuiButtonGroup`
+ */
+ type?: ToggleType;
+
+ onChange?: ChangeEventHandler;
+}
+
+type EuiButtonTogglePropsForAnchor = EuiButtonToggleProps &
+ Omit, 'name'> & {
+ href?: string;
+ name?: string;
+ onClick?: MouseEventHandler;
+ };
+
+type EuiButtonTogglePropsForButtonToggle = EuiButtonToggleProps &
+ Omit, 'name'> & {
+ onClick?: MouseEventHandler;
+ name?: string;
+ value?: string;
+ };
+
+type Props = ExclusiveUnion<
+ EuiButtonTogglePropsForAnchor,
+ EuiButtonTogglePropsForButtonToggle
+>;
+
+export const EuiButtonToggle: FunctionComponent = ({
className,
- color,
+ color = 'primary',
isDisabled,
isEmpty,
isIconOnly,
@@ -52,60 +112,17 @@ export const EuiButtonToggle = ({
title={label}
value={value}>
+ {...rest as Extract<
+ EuiButtonTogglePropsForAnchor,
+ EuiButtonTogglePropsForButtonToggle
+ >}>
{buttonContent}
);
};
-
-EuiButtonToggle.propTypes = {
- className: PropTypes.string,
-
- /**
- * Button label, which is also passed to `EuiToggle` as the input's label
- */
- label: PropTypes.string.isRequired,
- onChange: PropTypes.func,
-
- /**
- * See `EuiButton`
- */
- color: PropTypes.string,
- isDisabled: PropTypes.bool,
-
- /**
- * Hides the label from the button content and only displays the icon
- */
- isIconOnly: PropTypes.bool,
-
- /**
- * Simulates a `EuiButtonEmpty`
- */
- isEmpty: PropTypes.bool,
-
- /**
- * Initial state of the toggle
- */
- isSelected: PropTypes.bool,
-
- /**
- * Classnames to add to `EuiToggle` instead of the `EuiButton`
- */
- toggleClassName: PropTypes.string,
-
- /**
- * Is the button a single action or part of a group (multi)?
- * Used primarily for `EuiButtonGroup`
- */
- type: PropTypes.oneOf(TOGGLE_TYPES),
-};
-
-EuiButtonToggle.defaultProps = {
- color: 'primary',
-};
diff --git a/src/components/button/button_toggle/index.js b/src/components/button/button_toggle/index.ts
similarity index 100%
rename from src/components/button/button_toggle/index.js
rename to src/components/button/button_toggle/index.ts
diff --git a/src/components/button/index.d.ts b/src/components/button/index.d.ts
deleted file mode 100644
index 13ca41426fa..00000000000
--- a/src/components/button/index.d.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import { CommonProps, Omit } from '../common';
-import { IconType, IconSize } from '../icon';
-import { ToggleType } from '../toggle';
-
-import {
- FunctionComponent,
- ButtonHTMLAttributes,
- AnchorHTMLAttributes,
- MouseEventHandler,
- HTMLAttributes,
-} from 'react';
-
-declare module '@elastic/eui' {
- type EuiButtonPropsForButtonOrLink =
- | (Props & {
- onClick: MouseEventHandler;
- } & ButtonHTMLAttributes)
- | (Props & {
- href: string;
- onClick: MouseEventHandler;
- } & AnchorHTMLAttributes)
- | (Props &
- AnchorHTMLAttributes &
- ButtonHTMLAttributes);
-
- /**
- * Normal button type defs
- *
- * @see './button.js'
- */
-
- export type ButtonIconSide = 'left' | 'right';
- export type ButtonColor =
- | 'primary'
- | 'secondary'
- | 'warning'
- | 'danger'
- | 'ghost'
- | 'text';
- export type ButtonSize = 's' | 'm' | 'l';
-
- export interface EuiButtonProps {
- iconType?: IconType;
- iconSide?: ButtonIconSide;
- fill?: boolean;
- color?: ButtonColor;
- size?: ButtonSize;
- isLoading?: boolean;
- isDisabled?: boolean;
- fullWidth?: boolean;
- contentProps?: HTMLAttributes;
- textProps?: HTMLAttributes;
- }
- export const EuiButton: FunctionComponent<
- EuiButtonPropsForButtonOrLink
- >;
-
- /**
- * button icon type defs
- *
- * @see './button_icon/button_icon.js'
- */
-
- export type ButtonIconColor =
- | 'danger'
- | 'disabled'
- | 'ghost'
- | 'primary'
- | 'subdued'
- | 'success'
- | 'text'
- | 'warning';
-
- export interface EuiButtonIconProps {
- iconType?: IconType;
- color?: ButtonIconColor;
- 'aria-label'?: string;
- 'aria-labelledby'?: string;
- isDisabled?: boolean;
- size?: ButtonSize;
- iconSize?: IconSize;
- }
- export const EuiButtonIcon: FunctionComponent<
- EuiButtonPropsForButtonOrLink
- >;
-
- /**
- * empty button type defs
- *
- * @see './button_empty/button_empty.js'
- */
-
- export type EmptyButtonIconSide = 'left' | 'right';
- export type EmptyButtonColor =
- | 'primary'
- | 'danger'
- | 'disabled'
- | 'text'
- | 'ghost';
- export type EmptyButtonSizes = 'xs' | 's' | 'l';
- export type EmptyButtonFlush = 'left' | 'right';
-
- export type EuiButtonEmptyProps = EuiButtonPropsForButtonOrLink<
- CommonProps & {
- iconType?: IconType;
- iconSide?: EmptyButtonIconSide;
- color?: EmptyButtonColor;
- size?: EmptyButtonSizes;
- flush?: EmptyButtonFlush;
- isLoading?: boolean;
- isDisabled?: boolean;
- contentProps?: HTMLAttributes;
- textProps?: HTMLAttributes;
- }
- >;
-
- export const EuiButtonEmpty: FunctionComponent;
-
- /**
- * button toggle type defs
- *
- * @see './button_toggle/button_toggle.js'
- */
-
- export type EuiButtonToggleProps = EuiButtonProps & {
- isEmpty?: boolean;
- isIconOnly?: boolean;
- isSelected?: boolean;
- label: string;
- toggleClassName?: string;
- type?: ToggleType;
- };
-
- export const EuiButtonToggle: FunctionComponent<
- EuiButtonPropsForButtonOrLink
- >;
-
- /**
- * button group type defs
- *
- * @see './button_group/button_group.js'
- */
-
- export interface EuiButtonGroupIdToSelectedMap {
- [id: string]: boolean;
- }
- export type GroupButtonSize = 's' | 'm';
-
- export interface EuiButtonGroupOption {
- id: string;
- label: string;
- isDisabled?: boolean;
- }
- export interface EuiButtonGroupProps {
- options: EuiButtonGroupOption[];
- onChange: (id: string, value: any) => void;
- buttonSize?: GroupButtonSize;
- isDisabled?: boolean;
- isFullWidth?: boolean;
- isIconOnly?: boolean;
- idSelected?: string;
- idToSelectedMap?: EuiButtonGroupIdToSelectedMap;
- legend?: string;
- color?: ButtonColor;
- type?: ToggleType;
- name?: string;
- }
-
- export const EuiButtonGroup: FunctionComponent<
- Omit, 'onChange'> & EuiButtonGroupProps
- >;
-}
diff --git a/src/components/button/index.js b/src/components/button/index.js
deleted file mode 100644
index 1c986053ff5..00000000000
--- a/src/components/button/index.js
+++ /dev/null
@@ -1,9 +0,0 @@
-export { COLORS, EuiButton } from './button';
-
-export { EuiButtonEmpty } from './button_empty';
-
-export { EuiButtonIcon } from './button_icon';
-
-export { EuiButtonToggle } from './button_toggle';
-
-export { EuiButtonGroup } from './button_group';
diff --git a/src/components/button/index.ts b/src/components/button/index.ts
new file mode 100644
index 00000000000..5931242939d
--- /dev/null
+++ b/src/components/button/index.ts
@@ -0,0 +1,17 @@
+export { COLORS, ButtonColor, EuiButton, EuiButtonProps } from './button';
+
+export { EuiButtonEmpty, EuiButtonEmptyProps } from './button_empty';
+
+export {
+ EuiButtonIcon,
+ EuiButtonIconProps,
+ EuiButtonIconPropsForButton,
+} from './button_icon';
+
+export { EuiButtonToggle } from './button_toggle';
+
+export {
+ EuiButtonGroup,
+ EuiButtonGroupOption,
+ EuiButtonGroupProps,
+} from './button_group';
diff --git a/src/components/combo_box/index.d.ts b/src/components/combo_box/index.d.ts
index 204d9c86c7d..51a0d8e3c05 100644
--- a/src/components/combo_box/index.d.ts
+++ b/src/components/combo_box/index.d.ts
@@ -14,6 +14,7 @@ import {
EuiComboBoxProps,
} from '@elastic/eui'; // eslint-disable-line import/no-unresolved
import { RefCallback, CommonProps, Omit } from '../common';
+import { EuiPanelProps } from '../panel/panel';
declare module '@elastic/eui' {
export type EuiComboBoxOptionProps<
diff --git a/src/components/common.ts b/src/components/common.ts
index a1c0582894e..9fe52d6fc47 100644
--- a/src/components/common.ts
+++ b/src/components/common.ts
@@ -8,7 +8,7 @@ export interface CommonProps {
export type NoArgCallback = () => T;
-export type RefCallback = (
+export type RefCallback = (
element: Element
) => void;
diff --git a/src/components/context_menu/__snapshots__/context_menu.test.js.snap b/src/components/context_menu/__snapshots__/context_menu.test.tsx.snap
similarity index 100%
rename from src/components/context_menu/__snapshots__/context_menu.test.js.snap
rename to src/components/context_menu/__snapshots__/context_menu.test.tsx.snap
diff --git a/src/components/context_menu/__snapshots__/context_menu_item.test.js.snap b/src/components/context_menu/__snapshots__/context_menu_item.test.tsx.snap
similarity index 100%
rename from src/components/context_menu/__snapshots__/context_menu_item.test.js.snap
rename to src/components/context_menu/__snapshots__/context_menu_item.test.tsx.snap
diff --git a/src/components/context_menu/__snapshots__/context_menu_panel.test.js.snap b/src/components/context_menu/__snapshots__/context_menu_panel.test.tsx.snap
similarity index 91%
rename from src/components/context_menu/__snapshots__/context_menu_panel.test.js.snap
rename to src/components/context_menu/__snapshots__/context_menu_panel.test.tsx.snap
index cacbe2774df..8ddd71b50f3 100644
--- a/src/components/context_menu/__snapshots__/context_menu_panel.test.js.snap
+++ b/src/components/context_menu/__snapshots__/context_menu_panel.test.tsx.snap
@@ -379,11 +379,11 @@ exports[`EuiContextMenuPanel props transitionDirection previous with transitionT
exports[`EuiContextMenuPanel updating items and content updates to items should not re-render if any items's watchedItemProps did not change 1`] = `
"
-
+
-
+
@@ -392,7 +392,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
-
+
@@ -410,11 +410,11 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
exports[`EuiContextMenuPanel updating items and content updates to items should not re-render if any items's watchedItemProps did not change 2`] = `
"
-
+
-
+
@@ -423,7 +423,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
-
+
@@ -441,7 +441,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
exports[`EuiContextMenuPanel updating items and content updates to items should re-render at all times when children exists 1`] = `
"
-
+
@@ -455,7 +455,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
exports[`EuiContextMenuPanel updating items and content updates to items should re-render at all times when children exists 2`] = `
"
-
+
@@ -469,11 +469,11 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
exports[`EuiContextMenuPanel updating items and content updates to items should re-render if any items's watchedItemProps did change 1`] = `
"
-
+
-
+
@@ -482,7 +482,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
-
+
@@ -500,11 +500,11 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
exports[`EuiContextMenuPanel updating items and content updates to items should re-render if any items's watchedItemProps did change 2`] = `
"
-
+
-
+
@@ -513,7 +513,7 @@ exports[`EuiContextMenuPanel updating items and content updates to items should
-
+
diff --git a/src/components/context_menu/context_menu.test.js b/src/components/context_menu/context_menu.test.tsx
similarity index 100%
rename from src/components/context_menu/context_menu.test.js
rename to src/components/context_menu/context_menu.test.tsx
diff --git a/src/components/context_menu/context_menu.js b/src/components/context_menu/context_menu.tsx
similarity index 64%
rename from src/components/context_menu/context_menu.js
rename to src/components/context_menu/context_menu.tsx
index e4f92b5d4df..87b4e526739 100644
--- a/src/components/context_menu/context_menu.js
+++ b/src/components/context_menu/context_menu.tsx
@@ -1,12 +1,48 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ Component,
+ HTMLAttributes,
+ ReactElement,
+ ReactNode,
+} from 'react';
import classNames from 'classnames';
-import { EuiContextMenuPanel } from './context_menu_panel';
-import { EuiContextMenuItem } from './context_menu_item';
+import { CommonProps, Omit } from '../common';
+import {
+ EuiContextMenuPanel,
+ EuiContextMenuPanelTransitionDirection,
+ EuiContextMenuPanelTransitionType,
+} from './context_menu_panel';
+import {
+ EuiContextMenuItem,
+ EuiContextMenuItemProps,
+} from './context_menu_item';
+
+export type EuiContextMenuPanelId = string | number;
+
+export type EuiContextMenuPanelItemDescriptor = Omit<
+ EuiContextMenuItemProps,
+ 'hasPanel'
+> & {
+ name: string;
+ panel?: EuiContextMenuPanelId;
+};
+
+export interface EuiContextMenuPanelDescriptor {
+ id: EuiContextMenuPanelId;
+ title?: string;
+ items?: EuiContextMenuPanelItemDescriptor[];
+ content?: ReactNode;
+ width?: number;
+}
+
+export type EuiContextMenuProps = CommonProps &
+ Omit, 'style'> & {
+ panels?: EuiContextMenuPanelDescriptor[];
+ initialPanelId?: EuiContextMenuPanelId;
+ };
-function mapIdsToPanels(panels) {
- const map = {};
+function mapIdsToPanels(panels: EuiContextMenuPanelDescriptor[]) {
+ const map: { [id: string]: EuiContextMenuPanelDescriptor } = {};
panels.forEach(panel => {
map[panel.id] = panel;
@@ -15,15 +51,15 @@ function mapIdsToPanels(panels) {
return map;
}
-function mapIdsToPreviousPanels(panels) {
- const idToPreviousPanelIdMap = {};
+function mapIdsToPreviousPanels(panels: EuiContextMenuPanelDescriptor[]) {
+ const idToPreviousPanelIdMap: { [panel: string]: EuiContextMenuPanelId } = {};
panels.forEach(panel => {
if (Array.isArray(panel.items)) {
panel.items.forEach(item => {
const isCloseable = item.panel !== undefined;
if (isCloseable) {
- idToPreviousPanelIdMap[item.panel] = panel.id;
+ idToPreviousPanelIdMap[item.panel!] = panel.id;
}
});
}
@@ -32,8 +68,10 @@ function mapIdsToPreviousPanels(panels) {
return idToPreviousPanelIdMap;
}
-function mapPanelItemsToPanels(panels) {
- const idAndItemIndexToPanelIdMap = {};
+function mapPanelItemsToPanels(panels: EuiContextMenuPanelDescriptor[]) {
+ const idAndItemIndexToPanelIdMap: {
+ [id: string]: { [index: string]: EuiContextMenuPanelId };
+ } = {};
panels.forEach(panel => {
idAndItemIndexToPanelIdMap[panel.id] = {};
@@ -50,38 +88,38 @@ function mapPanelItemsToPanels(panels) {
return idAndItemIndexToPanelIdMap;
}
-export const EuiContextMenuPanelItemShape = PropTypes.shape({
- name: PropTypes.string,
- icon: PropTypes.node,
- onClick: PropTypes.func,
- // If given, shows the panel with this id when clicked:
- panel: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- disabled: PropTypes.bool,
-});
-
-export const EuiContextMenuPanelShape = PropTypes.shape({
- id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- width: PropTypes.number, // Pixel value to set the panel width to
- content: PropTypes.node, // Either content or items array should be given.
- items: PropTypes.arrayOf(EuiContextMenuPanelItemShape),
- title: PropTypes.string,
-});
-
-export class EuiContextMenu extends Component {
- static propTypes = {
- className: PropTypes.string,
- panels: PropTypes.arrayOf(EuiContextMenuPanelShape),
- initialPanelId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+interface State {
+ prevProps: {
+ panels?: EuiContextMenuPanelDescriptor[];
};
+ idToPanelMap: { [id: string]: EuiContextMenuPanelDescriptor };
+ idToPreviousPanelIdMap: { [panel: string]: EuiContextMenuPanelId };
+ idAndItemIndexToPanelIdMap: {
+ [id: string]: { [index: string]: EuiContextMenuPanelId };
+ };
+ idToRenderedItemsMap: { [id: string]: ReactElement[] };
+
+ height?: number;
+ outgoingPanelId?: EuiContextMenuPanelId;
+ incomingPanelId?: EuiContextMenuPanelId;
+ transitionDirection?: EuiContextMenuPanelTransitionDirection;
+ isOutgoingPanelVisible: boolean;
+ focusedItemIndex?: number;
+ isUsingKeyboardToNavigate: boolean;
+}
- static defaultProps = {
+export class EuiContextMenu extends Component {
+ static defaultProps: Partial = {
panels: [],
};
- static getDerivedStateFromProps(nextProps, prevState) {
+ static getDerivedStateFromProps(
+ nextProps: EuiContextMenuProps,
+ prevState: State
+ ): Partial | null {
const { panels } = nextProps;
- if (prevState.prevProps.panels !== panels) {
+ if (panels && prevState.prevProps.panels !== panels) {
return {
prevProps: { panels },
idToPanelMap: mapIdsToPanels(panels),
@@ -93,7 +131,7 @@ export class EuiContextMenu extends Component {
return null;
}
- constructor(props) {
+ constructor(props: EuiContextMenuProps) {
super(props);
this.state = {
@@ -113,7 +151,7 @@ export class EuiContextMenu extends Component {
};
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: EuiContextMenuProps) {
if (prevProps.panels !== this.props.panels) {
// eslint-disable-next-line react/no-did-update-set-state
this.setState({
@@ -122,12 +160,15 @@ export class EuiContextMenu extends Component {
}
}
- hasPreviousPanel = panelId => {
+ hasPreviousPanel = (panelId: EuiContextMenuPanelId) => {
const previousPanelId = this.state.idToPreviousPanelIdMap[panelId];
return typeof previousPanelId !== 'undefined';
};
- showPanel(panelId, direction) {
+ showPanel(
+ panelId: EuiContextMenuPanelId,
+ direction?: EuiContextMenuPanelTransitionDirection
+ ) {
this.setState({
outgoingPanelId: this.state.incomingPanelId,
incomingPanelId: panelId,
@@ -136,10 +177,15 @@ export class EuiContextMenu extends Component {
});
}
- showNextPanel = itemIndex => {
+ showNextPanel = (itemIndex?: number) => {
+ if (!itemIndex) {
+ return;
+ }
+
const nextPanelId = this.state.idAndItemIndexToPanelIdMap[
- this.state.incomingPanelId
+ this.state.incomingPanelId!
][itemIndex];
+
if (nextPanelId) {
if (this.state.isUsingKeyboardToNavigate) {
this.setState({
@@ -153,14 +199,14 @@ export class EuiContextMenu extends Component {
showPreviousPanel = () => {
// If there's a previous panel, then we can close the current panel to go back to it.
- if (this.hasPreviousPanel(this.state.incomingPanelId)) {
+ if (this.hasPreviousPanel(this.state.incomingPanelId!)) {
const previousPanelId = this.state.idToPreviousPanelIdMap[
- this.state.incomingPanelId
+ this.state.incomingPanelId!
];
// Set focus on the item which shows the panel we're leaving.
const previousPanel = this.state.idToPanelMap[previousPanelId];
- const focusedItemIndex = previousPanel.items.findIndex(
+ const focusedItemIndex = previousPanel.items!.findIndex(
item => item.panel === this.state.incomingPanelId
);
@@ -174,13 +220,13 @@ export class EuiContextMenu extends Component {
}
};
- onIncomingPanelHeightChange = height => {
+ onIncomingPanelHeightChange = (height: number) => {
this.setState(({ height: prevHeight }) => {
if (height === prevHeight) {
return null;
- } else {
- return { height };
}
+
+ return { height };
});
};
@@ -198,8 +244,8 @@ export class EuiContextMenu extends Component {
}
};
- mapIdsToRenderedItems = panels => {
- const idToRenderedItemsMap = {};
+ mapIdsToRenderedItems = (panels: EuiContextMenuPanelDescriptor[] = []) => {
+ const idToRenderedItemsMap: { [id: string]: ReactElement[] } = {};
// Pre-rendering the items lets us check reference equality inside of EuiContextMenuPanel.
panels.forEach(panel => {
@@ -209,7 +255,7 @@ export class EuiContextMenu extends Component {
return idToRenderedItemsMap;
};
- renderItems(items = []) {
+ renderItems(items: EuiContextMenuPanelItemDescriptor[] = []) {
return items.map((item, index) => {
const {
panel,
@@ -222,14 +268,16 @@ export class EuiContextMenu extends Component {
} = item;
const onClickHandler = panel
- ? event => {
+ ? (event: React.MouseEvent) => {
if (onClick && event) {
event.persist();
}
// This component is commonly wrapped in a EuiOutsideClickDetector, which means we'll
// need to wait for that logic to complete before re-rendering the DOM via showPanel.
window.requestAnimationFrame(() => {
- if (onClick) onClick(event);
+ if (onClick) {
+ onClick(event);
+ }
this.showNextPanel(index);
});
}
@@ -250,7 +298,10 @@ export class EuiContextMenu extends Component {
});
}
- renderPanel(panelId, transitionType) {
+ renderPanel(
+ panelId: EuiContextMenuPanelId,
+ transitionType: EuiContextMenuPanelTransitionType
+ ) {
const panel = this.state.idToPanelMap[panelId];
if (!panel) {
@@ -304,17 +355,17 @@ export class EuiContextMenu extends Component {
render() {
const { panels, className, initialPanelId, ...rest } = this.props;
- const incomingPanel = this.renderPanel(this.state.incomingPanelId, 'in');
+ const incomingPanel = this.renderPanel(this.state.incomingPanelId!, 'in');
let outgoingPanel;
if (this.state.isOutgoingPanelVisible) {
- outgoingPanel = this.renderPanel(this.state.outgoingPanelId, 'out');
+ outgoingPanel = this.renderPanel(this.state.outgoingPanelId!, 'out');
}
const width =
- this.state.idToPanelMap[this.state.incomingPanelId] &&
- this.state.idToPanelMap[this.state.incomingPanelId].width
- ? this.state.idToPanelMap[this.state.incomingPanelId].width
+ this.state.idToPanelMap[this.state.incomingPanelId!] &&
+ this.state.idToPanelMap[this.state.incomingPanelId!].width
+ ? this.state.idToPanelMap[this.state.incomingPanelId!].width
: undefined;
const classes = classNames('euiContextMenu', className);
diff --git a/src/components/context_menu/context_menu_item.test.js b/src/components/context_menu/context_menu_item.test.tsx
similarity index 89%
rename from src/components/context_menu/context_menu_item.test.js
rename to src/components/context_menu/context_menu_item.test.tsx
index b1074e1c19c..212cbcba49c 100644
--- a/src/components/context_menu/context_menu_item.test.js
+++ b/src/components/context_menu/context_menu_item.test.tsx
@@ -1,6 +1,5 @@
import React from 'react';
import { render, shallow, mount } from 'enzyme';
-import sinon from 'sinon';
import { requiredProps } from '../../test/required_props';
import { EuiContextMenuItem } from './context_menu_item';
@@ -43,15 +42,15 @@ describe('EuiContextMenuItem', () => {
});
test("isn't called upon instantiation", () => {
- const onClickHandler = sinon.stub();
+ const onClickHandler = jest.fn();
shallow( );
- sinon.assert.notCalled(onClickHandler);
+ expect(onClickHandler).not.toHaveBeenCalled();
});
test('is called when the item is clicked', () => {
- const onClickHandler = sinon.stub();
+ const onClickHandler = jest.fn();
const component = shallow(
@@ -59,11 +58,11 @@ describe('EuiContextMenuItem', () => {
component.simulate('click');
- sinon.assert.calledOnce(onClickHandler);
+ expect(onClickHandler).toHaveBeenCalledTimes(1);
});
test('is not called when the item is clicked but set to disabled', () => {
- const onClickHandler = sinon.stub();
+ const onClickHandler = jest.fn();
const component = mount(
@@ -71,7 +70,7 @@ describe('EuiContextMenuItem', () => {
component.simulate('click');
- sinon.assert.notCalled(onClickHandler);
+ expect(onClickHandler).not.toHaveBeenCalled();
});
});
diff --git a/src/components/context_menu/context_menu_item.js b/src/components/context_menu/context_menu_item.tsx
similarity index 60%
rename from src/components/context_menu/context_menu_item.js
rename to src/components/context_menu/context_menu_item.tsx
index c136f8922b3..63a4023180f 100644
--- a/src/components/context_menu/context_menu_item.js
+++ b/src/components/context_menu/context_menu_item.tsx
@@ -1,56 +1,64 @@
-import React, { cloneElement, Component } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ AnchorHTMLAttributes,
+ ButtonHTMLAttributes,
+ cloneElement,
+ Component,
+ ReactElement,
+ ReactNode,
+ Ref,
+} from 'react';
import classNames from 'classnames';
+import { CommonProps, keysOf, Omit } from '../common';
import { EuiIcon } from '../icon';
-import { EuiToolTip } from '../tool_tip';
+import { EuiToolTip, ToolTipPositions } from '../tool_tip';
import { getSecureRelForTarget } from '../../services';
-const layoutAlignToClassNames = {
+export type EuiContextMenuItemIcon = ReactElement | string | HTMLElement;
+
+type LayoutAlignment = 'center' | 'top' | 'bottom';
+
+export interface EuiContextMenuItemProps extends CommonProps {
+ icon?: EuiContextMenuItemIcon;
+ hasPanel?: boolean;
+ disabled?: boolean;
+ onClick?: (event: React.MouseEvent) => void;
+ buttonRef?: Ref;
+ /**
+ * Required if using a tooltip. Add an optional tooltip on hover
+ */
+ toolTipContent?: ReactNode;
+ /**
+ * Optional title for the tooltip
+ */
+ toolTipTitle?: ReactNode;
+ /**
+ * Dictates the position of the tooltip.
+ */
+ toolTipPosition?: ToolTipPositions;
+ href?: string;
+ target?: string;
+ rel?: string;
+ /**
+ * How to align icon with content of button
+ */
+ layoutAlign?: LayoutAlignment;
+}
+
+type Props = CommonProps &
+ Omit, 'type'> &
+ EuiContextMenuItemProps;
+
+const layoutAlignToClassNames: { [align in LayoutAlignment]: string | null } = {
center: null,
top: 'euiContextMenu__itemLayout--top',
bottom: 'euiContextMenu__itemLayout--bottom',
};
-export const LAYOUT_ALIGN = Object.keys(layoutAlignToClassNames);
-
-export class EuiContextMenuItem extends Component {
- static propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- /**
- * Icon used for the item
- */
- icon: PropTypes.oneOfType([PropTypes.element, PropTypes.string]),
- onClick: PropTypes.func,
- /**
- * Whether the item leads to a new set of items
- */
- hasPanel: PropTypes.bool,
- buttonRef: PropTypes.func,
- disabled: PropTypes.bool,
- /**
- * Required if using a tooltip. Add an optional tooltip on hover
- */
- toolTipContent: PropTypes.node,
- /**
- * Optional title for the tooltip
- */
- toolTipTitle: PropTypes.node,
- /**
- * Dictates the position of the tooltip.
- */
- toolTipPosition: PropTypes.string,
- href: PropTypes.string,
- target: PropTypes.string,
- rel: PropTypes.string,
- /**
- * How to align icon with content of button
- */
- layoutAlign: PropTypes.oneOf(LAYOUT_ALIGN),
- };
+export const LAYOUT_ALIGN = keysOf(layoutAlignToClassNames);
+export class EuiContextMenuItem extends Component {
render() {
const {
children,
@@ -59,10 +67,10 @@ export class EuiContextMenuItem extends Component {
icon,
buttonRef,
disabled,
- layoutAlign,
+ layoutAlign = 'center',
toolTipTitle,
toolTipContent,
- toolTipPosition,
+ toolTipPosition = 'right',
href,
target,
rel,
@@ -81,7 +89,7 @@ export class EuiContextMenuItem extends Component {
default:
// Assume it's already an instance of an icon.
- iconInstance = cloneElement(icon, {
+ iconInstance = cloneElement(icon as ReactElement, {
className: 'euiContextMenu__icon',
});
}
@@ -124,8 +132,8 @@ export class EuiContextMenuItem extends Component {
href={href}
target={target}
rel={secureRel}
- ref={buttonRef}
- {...rest}>
+ ref={buttonRef as Ref}
+ {...rest as AnchorHTMLAttributes}>
{buttonInner}
);
@@ -157,8 +165,3 @@ export class EuiContextMenuItem extends Component {
}
}
}
-
-EuiContextMenuItem.defaultProps = {
- toolTipPosition: 'right',
- layoutAlign: 'center',
-};
diff --git a/src/components/context_menu/context_menu_panel.test.js b/src/components/context_menu/context_menu_panel.test.tsx
similarity index 89%
rename from src/components/context_menu/context_menu_panel.test.js
rename to src/components/context_menu/context_menu_panel.test.tsx
index 2583215beac..e1322700034 100644
--- a/src/components/context_menu/context_menu_panel.test.js
+++ b/src/components/context_menu/context_menu_panel.test.tsx
@@ -1,6 +1,5 @@
import React from 'react';
-import { render, mount } from 'enzyme';
-import sinon from 'sinon';
+import { render, mount, ReactWrapper } from 'enzyme';
import { findTestSubject, requiredProps } from '../../test';
import { EuiContextMenuPanel } from './context_menu_panel';
@@ -51,15 +50,15 @@ describe('EuiContextMenuPanel', () => {
});
test("isn't called upon instantiation", () => {
- const onCloseHandler = sinon.stub();
+ const onCloseHandler = jest.fn();
mount( );
- sinon.assert.notCalled(onCloseHandler);
+ expect(onCloseHandler).not.toHaveBeenCalled();
});
test('is called when the title is clicked', () => {
- const onCloseHandler = sinon.stub();
+ const onCloseHandler = jest.fn();
const component = mount(
@@ -67,17 +66,17 @@ describe('EuiContextMenuPanel', () => {
component.find('button').simulate('click');
- sinon.assert.calledOnce(onCloseHandler);
+ expect(onCloseHandler).toHaveBeenCalledTimes(1);
});
});
describe('onHeightChange', () => {
it('is called with a height value', () => {
- const onHeightChange = sinon.stub();
+ const onHeightChange = jest.fn();
mount( );
- sinon.assert.calledWith(onHeightChange, 0);
+ expect(onHeightChange).toHaveBeenCalledWith(0);
});
});
@@ -159,7 +158,7 @@ describe('EuiContextMenuPanel', () => {
describe('onUseKeyboardToNavigate', () => {
it('is called when up arrow is pressed', () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.UP });
- sinon.assert.calledOnce(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).toHaveBeenCalledTimes(1);
});
it('is called when down arrow is pressed', () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.UP });
- sinon.assert.calledOnce(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).toHaveBeenCalledTimes(1);
});
describe('left arrow', () => {
it('calls handler if showPreviousPanel exists', () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.LEFT });
- sinon.assert.calledOnce(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).toHaveBeenCalledTimes(1);
});
it("doesn't call handler if showPreviousPanel doesn't exist", () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.LEFT });
- sinon.assert.notCalled(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).not.toHaveBeenCalled();
});
});
describe('right arrow', () => {
it('calls handler if showNextPanel exists', () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.RIGHT });
- sinon.assert.calledOnce(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).toHaveBeenCalledTimes(1);
});
it("doesn't call handler if showNextPanel doesn't exist", () => {
- const onUseKeyboardToNavigateHandler = sinon.stub();
+ const onUseKeyboardToNavigateHandler = jest.fn();
const component = mount(
{
);
component.simulate('keydown', { keyCode: keyCodes.RIGHT });
- sinon.assert.notCalled(onUseKeyboardToNavigateHandler);
+ expect(onUseKeyboardToNavigateHandler).not.toHaveBeenCalled();
});
});
});
@@ -280,13 +279,13 @@ describe('EuiContextMenuPanel', () => {
});
describe('keyboard navigation of items', () => {
- let component;
- let showNextPanelHandler;
- let showPreviousPanelHandler;
+ let component: ReactWrapper;
+ let showNextPanelHandler: jest.Mock;
+ let showPreviousPanelHandler: jest.Mock;
beforeEach(() => {
- showNextPanelHandler = sinon.stub();
- showPreviousPanelHandler = sinon.stub();
+ showNextPanelHandler = jest.fn();
+ showPreviousPanelHandler = jest.fn();
component = mount(
{
it("right arrow key shows next panel with focused item's index", () => {
component.simulate('keydown', { keyCode: keyCodes.DOWN });
component.simulate('keydown', { keyCode: keyCodes.RIGHT });
- sinon.assert.calledWith(showNextPanelHandler, 0);
+ expect(showNextPanelHandler).toHaveBeenCalledWith(0);
});
it('left arrow key shows previous panel', () => {
component.simulate('keydown', { keyCode: keyCodes.LEFT });
- sinon.assert.calledOnce(showPreviousPanelHandler);
+ expect(showPreviousPanelHandler).toHaveBeenCalledTimes(1);
});
});
});
diff --git a/src/components/context_menu/context_menu_panel.js b/src/components/context_menu/context_menu_panel.tsx
similarity index 74%
rename from src/components/context_menu/context_menu_panel.js
rename to src/components/context_menu/context_menu_panel.tsx
index 9aceb8731ca..0354c867394 100644
--- a/src/components/context_menu/context_menu_panel.js
+++ b/src/components/context_menu/context_menu_panel.tsx
@@ -1,13 +1,49 @@
-import React, { cloneElement, Component } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ cloneElement,
+ Component,
+ HTMLAttributes,
+ ReactElement,
+ ReactNode,
+} from 'react';
import classNames from 'classnames';
import tabbable from 'tabbable';
+import { CommonProps, NoArgCallback, Omit } from '../common';
import { EuiIcon } from '../icon';
import { EuiPopoverTitle } from '../popover';
import { EuiResizeObserver } from '../observer/resize_observer';
import { cascadingMenuKeyCodes } from '../../services';
+export type EuiContextMenuPanelHeightChangeHandler = (height: number) => void;
+export type EuiContextMenuPanelTransitionType = 'in' | 'out';
+export type EuiContextMenuPanelTransitionDirection = 'next' | 'previous';
+export type EuiContextMenuPanelShowPanelCallback = (
+ currentPanelIndex?: number
+) => void;
+
+export interface EuiContextMenuPanelProps {
+ hasFocus?: boolean;
+ initialFocusedItemIndex?: number;
+ items?: ReactElement[];
+ onClose?: NoArgCallback;
+ onHeightChange?: EuiContextMenuPanelHeightChangeHandler;
+ onTransitionComplete?: NoArgCallback;
+ onUseKeyboardToNavigate?: NoArgCallback;
+ showNextPanel?: EuiContextMenuPanelShowPanelCallback;
+ showPreviousPanel?: NoArgCallback;
+ title?: ReactNode;
+ transitionDirection?: EuiContextMenuPanelTransitionDirection;
+ transitionType?: EuiContextMenuPanelTransitionType;
+ watchedItemProps?: string[];
+}
+
+type Props = CommonProps &
+ Omit<
+ HTMLAttributes,
+ 'onKeyDown' | 'tabIndex' | 'onAnimationEnd'
+ > &
+ EuiContextMenuPanelProps;
+
const transitionDirectionAndTypeToClassNameMap = {
next: {
in: 'euiContextMenuPanel-txInLeft',
@@ -19,31 +55,29 @@ const transitionDirectionAndTypeToClassNameMap = {
},
};
-export class EuiContextMenuPanel extends Component {
- static propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- title: PropTypes.node,
- onClose: PropTypes.func,
- onHeightChange: PropTypes.func,
- transitionType: PropTypes.oneOf(['in', 'out']),
- transitionDirection: PropTypes.oneOf(['next', 'previous']),
- onTransitionComplete: PropTypes.func,
- onUseKeyboardToNavigate: PropTypes.func,
- hasFocus: PropTypes.bool,
- items: PropTypes.array,
- watchedItemProps: PropTypes.array,
- showNextPanel: PropTypes.func,
- showPreviousPanel: PropTypes.func,
- initialFocusedItemIndex: PropTypes.number,
+interface State {
+ prevProps: {
+ items: Props['items'];
};
+ menuItems: HTMLElement[];
+ isTransitioning: boolean;
+ focusedItemIndex?: number;
+ currentHeight?: number;
+ height?: number;
+}
- static defaultProps = {
+export class EuiContextMenuPanel extends Component {
+ static defaultProps: Partial = {
hasFocus: true,
items: [],
};
- constructor(props) {
+ private _isMounted = false;
+ private backButton?: HTMLElement | null = null;
+ private content?: HTMLElement | null = null;
+ private panel?: HTMLElement | null = null;
+
+ constructor(props: Props) {
super(props);
this.state = {
@@ -57,7 +91,7 @@ export class EuiContextMenuPanel extends Component {
};
}
- incrementFocusedItemIndex = amount => {
+ incrementFocusedItemIndex = (amount: number) => {
let nextFocusedItemIndex;
if (this.state.focusedItemIndex === undefined) {
@@ -79,21 +113,23 @@ export class EuiContextMenuPanel extends Component {
});
};
- onKeyDown = e => {
+ onKeyDown = (e: React.KeyboardEvent) => {
// If this panel contains items you can use the left arrow key to go back at any time.
// But if it doesn't contain items, then you have to focus on the back button specifically,
// since there could be content inside the panel which requires use of the left arrow key,
// e.g. text inputs.
+ const { items, showPreviousPanel } = this.props;
+
if (
- this.props.items.length ||
+ (items && items.length) ||
document.activeElement === this.backButton ||
document.activeElement === this.panel
) {
if (e.keyCode === cascadingMenuKeyCodes.LEFT) {
- if (this.props.showPreviousPanel) {
+ if (showPreviousPanel) {
e.preventDefault();
e.stopPropagation();
- this.props.showPreviousPanel();
+ showPreviousPanel();
if (this.props.onUseKeyboardToNavigate) {
this.props.onUseKeyboardToNavigate();
@@ -102,12 +138,12 @@ export class EuiContextMenuPanel extends Component {
}
}
- if (this.props.items.length) {
+ if (this.props.items && this.props.items.length) {
switch (e.keyCode) {
case cascadingMenuKeyCodes.TAB:
// We need to sync up with the user if s/he is tabbing through the items.
const focusedItemIndex = this.state.menuItems.indexOf(
- document.activeElement
+ document.activeElement as HTMLElement
);
this.setState({
@@ -163,8 +199,8 @@ export class EuiContextMenuPanel extends Component {
// If this panel has lost focus, then none of its content should be focused.
if (!this.props.hasFocus) {
- if (this.panel.contains(document.activeElement)) {
- document.activeElement.blur();
+ if (this.panel && this.panel.contains(document.activeElement)) {
+ (document.activeElement as HTMLElement).blur();
}
return;
}
@@ -178,7 +214,7 @@ export class EuiContextMenuPanel extends Component {
// If there aren't any items then this is probably a form or something.
if (!this.state.menuItems.length) {
// If we've already focused on something inside the panel, everything's fine.
- if (this.panel.contains(document.activeElement)) {
+ if (this.panel && this.panel.contains(document.activeElement)) {
return;
}
@@ -199,7 +235,7 @@ export class EuiContextMenuPanel extends Component {
}
// Focus on the panel as a last resort.
- if (!this.panel.contains(document.activeElement)) {
+ if (this.panel && !this.panel.contains(document.activeElement)) {
this.panel.focus();
}
});
@@ -224,9 +260,12 @@ export class EuiContextMenuPanel extends Component {
this._isMounted = false;
}
- static getDerivedStateFromProps(nextProps, prevState) {
+ static getDerivedStateFromProps(
+ nextProps: Props,
+ prevState: State
+ ): Partial | null {
let needsUpdate = false;
- const nextState = {};
+ const nextState: Partial = {};
// Clear refs to menuItems if we're getting new ones.
if (nextProps.items !== prevState.prevProps.items) {
@@ -246,7 +285,7 @@ export class EuiContextMenuPanel extends Component {
return null;
}
- getWatchedPropsForItems(items) {
+ getWatchedPropsForItems(items: ReactElement[]) {
// This lets us compare prevProps and nextProps among items so we can re-render if our items
// have changed.
const { watchedItemProps } = this.props;
@@ -256,10 +295,12 @@ export class EuiContextMenuPanel extends Component {
return JSON.stringify(
items.map(item => {
// Create object of item properties and values
- const props = {
+ const props: any = {
key: item.key,
};
- watchedItemProps.forEach(prop => (props[prop] = item.props[prop]));
+ watchedItemProps.forEach((prop: string) => {
+ props[prop] = item.props[prop];
+ });
return props;
})
);
@@ -268,7 +309,7 @@ export class EuiContextMenuPanel extends Component {
return null;
}
- didItemsChange(prevItems, nextItems) {
+ didItemsChange(prevItems: ReactElement[], nextItems: ReactElement[]) {
// If the count of items has changed then update
if (prevItems.length !== nextItems.length) {
return true;
@@ -283,7 +324,7 @@ export class EuiContextMenuPanel extends Component {
}
}
- shouldComponentUpdate(nextProps, nextState) {
+ shouldComponentUpdate(nextProps: Props, nextState: State) {
// Prevent calling `this.updateFocus()` below if we don't have to.
if (nextProps.hasFocus !== this.props.hasFocus) {
return true;
@@ -303,8 +344,11 @@ export class EuiContextMenuPanel extends Component {
// if there are children we can't know if they have changed so return true
// **
- if (this.props.items.length > 0 || nextProps.items.length > 0) {
- if (this.didItemsChange(this.props.items, nextProps.items)) {
+ if (
+ (this.props.items && this.props.items.length > 0) ||
+ (nextProps.items && nextProps.items.length > 0)
+ ) {
+ if (this.didItemsChange(this.props.items!, nextProps.items!)) {
return true;
}
}
@@ -333,7 +377,7 @@ export class EuiContextMenuPanel extends Component {
this.updateFocus();
}
- menuItemRef = (index, node) => {
+ menuItemRef = (index: number, node: HTMLElement) => {
// There's a weird bug where if you navigate to a panel without items, then this callback
// is still invoked, so we have to do a truthiness check.
if (node) {
@@ -342,13 +386,13 @@ export class EuiContextMenuPanel extends Component {
}
};
- panelRef = node => {
+ panelRef = (node: HTMLElement | null) => {
this.panel = node;
this.updateHeight();
};
- contentRef = node => {
+ contentRef = (node: HTMLElement | null) => {
this.content = node;
};
@@ -408,6 +452,8 @@ export class EuiContextMenuPanel extends Component {
'euiContextMenuPanel',
className,
this.state.isTransitioning &&
+ transitionDirection &&
+ transitionType &&
transitionDirectionAndTypeToClassNameMap[transitionDirection]
? transitionDirectionAndTypeToClassNameMap[transitionDirection][
transitionType
@@ -415,20 +461,21 @@ export class EuiContextMenuPanel extends Component {
: undefined
);
- const content = items.length
- ? items.map((MenuItem, index) =>
- cloneElement(MenuItem, {
- buttonRef: this.menuItemRef.bind(this, index),
- })
- )
- : children;
+ const content =
+ items && items.length
+ ? items.map((MenuItem, index) =>
+ cloneElement(MenuItem, {
+ buttonRef: this.menuItemRef.bind(this, index),
+ })
+ )
+ : children;
return (
{panelTitle}
diff --git a/src/components/context_menu/index.d.ts b/src/components/context_menu/index.d.ts
deleted file mode 100644
index 48282d163bf..00000000000
--- a/src/components/context_menu/index.d.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import { CommonProps, RefCallback, NoArgCallback, Omit } from '../common';
-
-import {
- FunctionComponent,
- ButtonHTMLAttributes,
- HTMLAttributes,
- ReactElement,
- ReactNode,
-} from 'react';
-
-declare module '@elastic/eui' {
- /**
- * context menu panel type defs
- *
- * @see './context_menu_panel.js`
- */
-
- export type EuiContextMenuPanelHeightChangeHandler = (height: number) => void;
- export type EuiContextMenuPanelTransitionType = 'in' | 'out';
- export type EuiContextMenuPanelTransitionDirection = 'next' | 'previous';
- export type EuiContextMenuPanelShowPanelCallback = (
- currentPanelIndex: number
- ) => void;
-
- export interface EuiContextMenuPanelProps {
- items?: ReactNode[];
- title?: ReactNode;
- onClose?: NoArgCallback
;
- onHeightChange?: EuiContextMenuPanelHeightChangeHandler;
- transitionType?: EuiContextMenuPanelTransitionType;
- transitionDirection?: EuiContextMenuPanelTransitionDirection;
- onTransitionComplete?: NoArgCallback;
- onUseKeyboardToNavigate?: NoArgCallback;
- hasFocus?: boolean;
- showNextPanel?: EuiContextMenuPanelShowPanelCallback;
- showPreviousPanel?: EuiContextMenuPanelShowPanelCallback;
- initialFocusedItemIndex?: number;
- }
-
- export const EuiContextMenuPanel: FunctionComponent<
- CommonProps &
- Omit<
- HTMLAttributes,
- 'onKeyDown' | 'tabIndex' | 'onAnimationEnd'
- > &
- EuiContextMenuPanelProps
- >;
-
- /**
- * context menu item type defs
- *
- * @see './context_menu_item.js`
- */
-
- export type EuiContextMenuItemIcon = ReactElement | string | HTMLElement;
-
- export interface EuiContextMenuItemProps extends CommonProps {
- icon?: EuiContextMenuItemIcon;
- hasPanel?: boolean;
- disabled?: boolean;
- onClick?: () => void;
- buttonRef?: RefCallback;
- toolTipContent?: ReactNode;
- toolTipTitle?: ReactNode;
- toolTipPosition?: string;
- href?: string;
- target?: string;
- rel?: string;
- children?: ReactNode;
- }
-
- export const EuiContextMenuItem: FunctionComponent<
- CommonProps &
- Omit, 'type'> &
- EuiContextMenuItemProps
- >;
-
- /**
- * context menu type defs
- *
- * @see './context_menu.js`
- */
-
- export type EuiContextMenuPanelId = string | number;
-
- export type EuiContextMenuPanelItemDescriptor = Omit<
- EuiContextMenuItemProps,
- 'hasPanel'
- > & {
- name: string;
- panel?: EuiContextMenuPanelId;
- };
-
- interface EuiContextMenuPanelDescriptor {
- id: EuiContextMenuPanelId;
- title?: string;
- items?: EuiContextMenuPanelItemDescriptor[];
- content?: ReactNode;
- width?: number;
- }
-
- export type EuiContextMenuProps = CommonProps &
- Omit, 'style'> & {
- panels?: EuiContextMenuPanelDescriptor[];
- initialPanelId?: EuiContextMenuPanelId;
- };
-
- export const EuiContextMenu: FunctionComponent;
-}
diff --git a/src/components/context_menu/index.js b/src/components/context_menu/index.js
deleted file mode 100644
index 14564fdbe20..00000000000
--- a/src/components/context_menu/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export { EuiContextMenu } from './context_menu';
-
-export { EuiContextMenuPanel } from './context_menu_panel';
-
-export { EuiContextMenuItem } from './context_menu_item';
diff --git a/src/components/context_menu/index.ts b/src/components/context_menu/index.ts
new file mode 100644
index 00000000000..0f823dd6699
--- /dev/null
+++ b/src/components/context_menu/index.ts
@@ -0,0 +1,15 @@
+export {
+ EuiContextMenu,
+ EuiContextMenuPanelDescriptor,
+ EuiContextMenuPanelItemDescriptor,
+} from './context_menu';
+
+export {
+ EuiContextMenuPanel,
+ EuiContextMenuPanelProps,
+} from './context_menu_panel';
+
+export {
+ EuiContextMenuItem,
+ EuiContextMenuItemIcon,
+} from './context_menu_item';
diff --git a/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.js.snap b/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.js.snap
index 49b64bdb1f4..d7a12a6699e 100644
--- a/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.js.snap
+++ b/src/components/date_picker/super_date_picker/__snapshots__/super_update_button.test.js.snap
@@ -9,18 +9,15 @@ exports[`EuiSuperUpdateButton is rendered 1`] = `
className="euiSuperUpdateButton"
color="primary"
fill={true}
- iconSide="left"
iconType="refresh"
isDisabled={false}
isLoading={false}
onClick={[Function]}
- size="m"
textProps={
Object {
"className": "euiSuperUpdateButton__text",
}
}
- type="button"
>
@@ -44,11 +41,8 @@ exports[`EuiQuickSelect is rendered 1`] = `
>
@@ -171,13 +165,9 @@ exports[`EuiQuickSelect is rendered 1`] = `
>
Apply
@@ -216,11 +206,8 @@ exports[`EuiQuickSelect prevQuickSelect 1`] = `
>
@@ -234,11 +221,8 @@ exports[`EuiQuickSelect prevQuickSelect 1`] = `
>
@@ -361,13 +345,9 @@ exports[`EuiQuickSelect prevQuickSelect 1`] = `
>
Apply
diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap
index 731246ec4a5..a9a9fd442a9 100644
--- a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap
+++ b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.js.snap
@@ -7,7 +7,6 @@ exports[`EuiQuickSelectPopover is rendered 1`] = `
-
import { Component, FunctionComponent, ButtonHTMLAttributes } from 'react';
+import { CommonProps } from '../common';
+import { EuiButtonEmptyProps } from '../button';
import { EuiFilterGroupProps } from './filter_group';
declare module '@elastic/eui' {
diff --git a/src/components/focus_trap/__snapshots__/focus_trap.test.js.snap b/src/components/focus_trap/__snapshots__/focus_trap.test.tsx.snap
similarity index 100%
rename from src/components/focus_trap/__snapshots__/focus_trap.test.js.snap
rename to src/components/focus_trap/__snapshots__/focus_trap.test.tsx.snap
diff --git a/src/components/focus_trap/focus_trap.test.js b/src/components/focus_trap/focus_trap.test.tsx
similarity index 93%
rename from src/components/focus_trap/focus_trap.test.js
rename to src/components/focus_trap/focus_trap.test.tsx
index bebbff6c161..7afb187186f 100644
--- a/src/components/focus_trap/focus_trap.test.js
+++ b/src/components/focus_trap/focus_trap.test.tsx
@@ -1,8 +1,9 @@
-import React from 'react';
+import React, { EventHandler } from 'react';
import { render, mount } from 'enzyme';
import { findTestSubject } from '../../test';
+import { EuiEvent } from '../outside_click_detector/outside_click_detector';
import { EuiFocusTrap } from './focus_trap';
import { EuiPortal } from '../portal';
@@ -86,14 +87,18 @@ describe('EuiFocusTrap', () => {
// enzyme doesn't mount the components into the global jsdom `document`
// but that's where the click detector listener is,
// pass the top-level mounted component's click event on to document
- const triggerDocumentMouseDown = e => {
- const event = new Event('mousedown');
+ const triggerDocumentMouseDown: EventHandler = (
+ e: React.MouseEvent
+ ) => {
+ const event = new Event('mousedown') as EuiEvent;
event.euiGeneratedBy = e.nativeEvent.euiGeneratedBy;
document.dispatchEvent(event);
};
- const triggerDocumentMouseUp = e => {
- const event = new Event('mouseup');
+ const triggerDocumentMouseUp: EventHandler = (
+ e: React.MouseEvent
+ ) => {
+ const event = new Event('mouseup') as EuiEvent;
event.euiGeneratedBy = e.nativeEvent.euiGeneratedBy;
document.dispatchEvent(event);
};
diff --git a/src/components/focus_trap/focus_trap.js b/src/components/focus_trap/focus_trap.tsx
similarity index 52%
rename from src/components/focus_trap/focus_trap.js
rename to src/components/focus_trap/focus_trap.tsx
index a98ac743c55..534aac640b2 100644
--- a/src/components/focus_trap/focus_trap.js
+++ b/src/components/focus_trap/focus_trap.tsx
@@ -1,30 +1,54 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import FocusLock from 'react-focus-lock';
+import React, {
+ Component,
+ FunctionComponent,
+ MouseEvent as ReactMouseEvent,
+ EventHandler,
+} from 'react';
+import FocusLock, { Props as ReactFocusLockProps } from 'react-focus-lock'; // eslint-disable-line import/named
+import { CommonProps } from '../common';
import { EuiOutsideClickDetector } from '../outside_click_detector';
-const OutsideEventDetector = ({ children, handleEvent, ...rest }) => {
- const eventHanders = ['onMouseDown', 'onTouchStart'].reduce(
- (obj, eventName) => {
- obj[eventName] = handleEvent;
- return obj;
- },
- {}
- );
+interface DetectorProps {
+ handleEvent: EventHandler;
+}
+
+const OutsideEventDetector: FunctionComponent = ({
+ children,
+ handleEvent,
+ ...rest
+}) => {
return (
-
+
{children}
);
};
-export class EuiFocusTrap extends React.Component {
- state = {
+/**
+ * A DOM node, a selector string (which will be passed to
+ * `document.querySelector()` to find the DOM node), or a function that
+ * returns a DOM node.
+ */
+export type FocusTarget = HTMLElement | string | (() => HTMLElement);
+
+interface EuiFocusTrapProps {
+ clickOutsideDisables?: boolean;
+ initialFocus?: FocusTarget;
+}
+
+type Props = CommonProps & ReactFocusLockProps & EuiFocusTrapProps;
+
+interface State {
+ hasBeenDisabledByClick: boolean;
+}
+
+export class EuiFocusTrap extends Component
{
+ state: State = {
hasBeenDisabledByClick: false,
};
- lastInterceptedEvent = null;
+ lastInterceptedEvent: Event | null = null;
preventFocusExit = false;
componentDidMount() {
@@ -32,17 +56,16 @@ export class EuiFocusTrap extends React.Component {
}
// Programmatically sets focus on a nested DOM node; optional
- setInitalFocus = initialFocus => {
- let node = initialFocus;
- if (typeof node === 'string') {
- node = document.querySelector(initialFocus);
- }
- if (typeof node === 'function') {
- node = initialFocus();
+ setInitalFocus = (initialFocus?: FocusTarget) => {
+ let node = initialFocus instanceof HTMLElement ? initialFocus : null;
+ if (typeof initialFocus === 'string') {
+ node = document.querySelector(initialFocus as string);
+ } else if (typeof initialFocus === 'function') {
+ node = (initialFocus as () => HTMLElement)();
}
if (!node) return;
// `data-autofocus` is part of the 'react-focus-lock' API
- node.setAttribute('data-autofocus', true);
+ node.setAttribute('data-autofocus', 'true');
};
// Sets whether the focus trap has been disabled by clicks outside its component tree
@@ -61,28 +84,37 @@ export class EuiFocusTrap extends React.Component {
// Event handler to determine whether a mouse or key event should disable the focus trap
// Only applicable for `clickOutsideDisables`
- handleOutsideClick = event => {
+ handleOutsideClick = (event: Event) => {
this.toggleExitPrevented(false);
if (
this.preventFocusExit &&
+ this.lastInterceptedEvent &&
event.target === this.lastInterceptedEvent.target
)
return;
this.toggleDisabled(true);
};
- // Event handler to capture events from within the focus trap subtree and prevent them from disabling the trap (mostly for portals)
+ // Event handler to capture events from within the focus trap subtree and
+ // prevent them from disabling the trap (mostly for portals)
// Only applicable for `clickOutsideDisables`
- handleBubbledEvent = e => {
+ handleBubbledEvent = (e: ReactMouseEvent) => {
this.lastInterceptedEvent = e.nativeEvent;
this.toggleExitPrevented(true);
};
render() {
- const { children, clickOutsideDisables, disabled, ...rest } = this.props;
+ const {
+ children,
+ clickOutsideDisables = false,
+ disabled = false,
+ returnFocus = true,
+ ...rest
+ } = this.props;
const isDisabled = disabled || this.state.hasBeenDisabledByClick;
const lockProps = {
disabled: isDisabled,
+ returnFocus,
...rest,
};
return clickOutsideDisables ? (
@@ -98,37 +130,3 @@ export class EuiFocusTrap extends React.Component {
);
}
}
-
-EuiFocusTrap.propTypes = {
- children: PropTypes.node.isRequired,
- /**
- * Disables the focus trap when clicks occur outside of the trapped area
- */
- clickOutsideDisables: PropTypes.bool,
- /**
- * From `react-focus-lock`.
- * Focus will not be trapped if false
- */
- disabled: PropTypes.bool,
- /**
- * A DOM node, a selector string (which will be passed to
- * `document.querySelector()` to find the DOM node), or a function that
- * returns a DOM node.
- */
- initialFocus: PropTypes.oneOfType([
- PropTypes.instanceOf(HTMLElement),
- PropTypes.func,
- PropTypes.string,
- ]),
- /**
- * From `react-focus-lock`.
- * When exiting/disabling, focus will return to the element that previously help focus
- */
- returnFocus: PropTypes.bool,
-};
-
-EuiFocusTrap.defaultProps = {
- clickOutsideDisables: false,
- disabled: false,
- returnFocus: true,
-};
diff --git a/src/components/focus_trap/index.d.ts b/src/components/focus_trap/index.d.ts
deleted file mode 100644
index 76f153e4cb9..00000000000
--- a/src/components/focus_trap/index.d.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { SFC } from 'react';
-import { Props as ReactFocusLockProps } from 'react-focus-lock'; // eslint-disable-line import/named
-import { CommonProps } from '../common';
-
-declare module '@elastic/eui' {
- /**
- * A DOM node, a selector string (which will be passed to
- * `document.querySelector()` to find the DOM node), or a function that
- * returns a DOM node.
- */
- export type FocusTarget = HTMLElement | string | { (): HTMLElement };
- /**
- * FocusTrap type defs
- *
- * @see './focus_trap.js'
- */
- interface EuiFocusTrapProps {
- clickOutsideDisables?: boolean;
- initialFocus?: FocusTarget;
- }
-
- export const EuiFocusTrap: SFC<
- CommonProps & ReactFocusLockProps & EuiFocusTrapProps
- >;
-}
diff --git a/src/components/focus_trap/index.js b/src/components/focus_trap/index.js
deleted file mode 100644
index 014950095ce..00000000000
--- a/src/components/focus_trap/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiFocusTrap } from './focus_trap';
diff --git a/src/components/focus_trap/index.ts b/src/components/focus_trap/index.ts
new file mode 100644
index 00000000000..ce0caaae35e
--- /dev/null
+++ b/src/components/focus_trap/index.ts
@@ -0,0 +1 @@
+export { EuiFocusTrap, FocusTarget } from './focus_trap';
diff --git a/src/components/form/super_select/__snapshots__/super_select.test.js.snap b/src/components/form/super_select/__snapshots__/super_select.test.js.snap
index 681d29405bd..b8229cc8483 100644
--- a/src/components/form/super_select/__snapshots__/super_select.test.js.snap
+++ b/src/components/form/super_select/__snapshots__/super_select.test.js.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`EuiSuperSelect is rendered 1`] = `
+exports['EuiSuperSelect is rendered 1'] = `
@@ -56,7 +56,7 @@ exports[`EuiSuperSelect is rendered 1`] = `
`;
-exports[`EuiSuperSelect props compressed is rendered 1`] = `
+exports['EuiSuperSelect props compressed is rendered 1'] = `
@@ -112,7 +112,7 @@ exports[`EuiSuperSelect props compressed is rendered 1`] = `
`;
-exports[`EuiSuperSelect props custom display is propagated to dropdown 1`] = `
+exports['EuiSuperSelect props custom display is propagated to dropdown 1'] = `
@@ -261,7 +261,7 @@ exports[`EuiSuperSelect props custom display is propagated to dropdown 1`] = `
`;
-exports[`EuiSuperSelect props fullWidth is rendered 1`] = `
+exports['EuiSuperSelect props fullWidth is rendered 1'] = `
@@ -317,7 +317,7 @@ exports[`EuiSuperSelect props fullWidth is rendered 1`] = `
`;
-exports[`EuiSuperSelect props more props are propogated to each option 1`] = `
+exports['EuiSuperSelect props more props are propogated to each option 1'] = `
@@ -471,7 +471,7 @@ exports[`EuiSuperSelect props more props are propogated to each option 1`] = `
`;
-exports[`EuiSuperSelect props more props are propogated to each option 2`] = `
+exports['EuiSuperSelect props more props are propogated to each option 2'] = `
@@ -946,13 +944,12 @@ exports[`EuiSuperSelect props more props are propogated to each option 2`] = `
Object {
"left": -22,
"top": 8,
- "zIndex": "0",
+ "zIndex": 0,
}
}
>
`;
-exports[`EuiSuperSelect props options are rendered when select is open 1`] = `
+exports['EuiSuperSelect props options are rendered when select is open 1'] = `
@@ -1289,7 +1292,7 @@ exports[`EuiSuperSelect props options are rendered when select is open 1`] = `
`;
-exports[`EuiSuperSelect props select component is rendered 1`] = `
+exports['EuiSuperSelect props select component is rendered 1'] = `
@@ -1343,7 +1346,7 @@ exports[`EuiSuperSelect props select component is rendered 1`] = `
`;
-exports[`EuiSuperSelect props valueSelected is rendered 1`] = `
+exports['EuiSuperSelect props valueSelected is rendered 1'] = `
diff --git a/src/components/index.d.ts b/src/components/index.d.ts
index 571bd6f1622..50edc2d4bb3 100644
--- a/src/components/index.d.ts
+++ b/src/components/index.d.ts
@@ -1,17 +1,14 @@
-///
///
///
///
///
///
-///
///
///
///
///
///
///
-///
///
///
///
@@ -19,10 +16,6 @@
///
///
///
-///
-///
-///
-///
///
declare module '@elastic/eui' {
diff --git a/src/components/list_group/list_group_item.tsx b/src/components/list_group/list_group_item.tsx
index 6f17d0cb6a0..1dbb468b7be 100644
--- a/src/components/list_group/list_group_item.tsx
+++ b/src/components/list_group/list_group_item.tsx
@@ -10,15 +10,7 @@ import React, {
} from 'react';
import classNames from 'classnames';
-import {
- EuiButtonIconProps,
- EuiButtonPropsForButtonOrLink,
- EuiButtonIcon as EuiButtonIconType,
-} from '@elastic/eui'; // eslint-disable-line import/no-unresolved
-// @ts-ignore
-import { EuiButtonIcon } from '../button';
-const EuiButtonIconTyped: typeof EuiButtonIconType = EuiButtonIcon;
-
+import { EuiButtonIcon, EuiButtonIconPropsForButton } from '../button';
import { EuiIcon, IconType } from '../icon';
import { EuiToolTip } from '../tool_tip';
import { useInnerText } from '../inner_text';
@@ -89,13 +81,9 @@ export type EuiListGroupItemProps = CommonProps &
* Adds an `EuiButtonIcon` to the right side of the item; `iconType` is required;
* pass `alwaysShow` if you don't want the default behavior of only showing on hover
*/
- extraAction?: EuiButtonPropsForButtonOrLink<
- CommonProps &
- EuiButtonIconProps & {
- iconType: IconType;
- alwaysShow?: boolean;
- }
- >;
+ extraAction?: EuiButtonIconPropsForButton & {
+ alwaysShow?: boolean;
+ };
/**
* Make the list item label a button.
@@ -167,7 +155,7 @@ export const EuiListGroupItem: FunctionComponent
= ({
);
extraActionNode = (
-
-///
+import { FocusTarget } from '../focus_trap';
import { ReactNode, FunctionComponent, HTMLAttributes } from 'react';
+import { ButtonColor } from '../button';
+
import { EuiModalFooterProps } from './modal_footer';
import { EuiModalHeaderProps } from './modal_header';
import { EuiModalBodyProps } from './modal_body';
diff --git a/src/components/outside_click_detector/__snapshots__/outside_click_detector.test.js.snap b/src/components/outside_click_detector/__snapshots__/outside_click_detector.test.tsx.snap
similarity index 100%
rename from src/components/outside_click_detector/__snapshots__/outside_click_detector.test.js.snap
rename to src/components/outside_click_detector/__snapshots__/outside_click_detector.test.tsx.snap
diff --git a/src/components/outside_click_detector/index.js b/src/components/outside_click_detector/index.ts
similarity index 100%
rename from src/components/outside_click_detector/index.js
rename to src/components/outside_click_detector/index.ts
diff --git a/src/components/outside_click_detector/outside_click_detector.test.js b/src/components/outside_click_detector/outside_click_detector.test.tsx
similarity index 79%
rename from src/components/outside_click_detector/outside_click_detector.test.js
rename to src/components/outside_click_detector/outside_click_detector.test.tsx
index 461c2d71146..fd23f381b2a 100644
--- a/src/components/outside_click_detector/outside_click_detector.test.js
+++ b/src/components/outside_click_detector/outside_click_detector.test.tsx
@@ -1,7 +1,7 @@
-import React from 'react';
+import React, { EventHandler, MouseEvent as ReactMouseEvent } from 'react';
import { render, mount } from 'enzyme';
-import { EuiOutsideClickDetector } from './outside_click_detector';
+import { EuiOutsideClickDetector, EuiEvent } from './outside_click_detector';
describe('EuiOutsideClickDetector', () => {
test('is rendered', () => {
@@ -23,14 +23,18 @@ describe('EuiOutsideClickDetector', () => {
// enzyme doesn't mount the components into the global jsdom `document`
// but that's where the click detector listener is,
// pass the top-level mounted component's click event on to document
- const triggerDocumentMouseDown = e => {
- const event = new Event('mousedown');
+ const triggerDocumentMouseDown: EventHandler = (
+ e: ReactMouseEvent
+ ) => {
+ const event = new Event('mousedown') as EuiEvent;
event.euiGeneratedBy = e.nativeEvent.euiGeneratedBy;
document.dispatchEvent(event);
};
- const triggerDocumentMouseUp = e => {
- const event = new Event('mouseup');
+ const triggerDocumentMouseUp: EventHandler = (
+ e: ReactMouseEvent
+ ) => {
+ const event = new Event('mouseup') as EuiEvent;
event.euiGeneratedBy = e.nativeEvent.euiGeneratedBy;
document.dispatchEvent(event);
};
diff --git a/src/components/outside_click_detector/outside_click_detector.js b/src/components/outside_click_detector/outside_click_detector.ts
similarity index 76%
rename from src/components/outside_click_detector/outside_click_detector.js
rename to src/components/outside_click_detector/outside_click_detector.ts
index dc8eb800778..96dc68383bc 100644
--- a/src/components/outside_click_detector/outside_click_detector.js
+++ b/src/components/outside_click_detector/outside_click_detector.ts
@@ -1,8 +1,28 @@
-import { Children, cloneElement, Component } from 'react';
-import PropTypes from 'prop-types';
+import {
+ Children,
+ cloneElement,
+ Component,
+ EventHandler,
+ MouseEvent as ReactMouseEvent,
+ ReactElement,
+} from 'react';
import { htmlIdGenerator } from '../../services/accessibility';
-export class EuiOutsideClickDetector extends Component {
+export interface EuiEvent extends Event {
+ euiGeneratedBy: string[];
+}
+
+interface Props {
+ children: ReactElement;
+ onOutsideClick: (event: EuiEvent) => void;
+ isDisabled?: boolean;
+ onMouseDown?: (event: ReactMouseEvent) => void;
+ onMouseUp?: (event: ReactMouseEvent) => void;
+ onTouchStart?: (event: ReactMouseEvent) => void;
+ onTouchEnd?: (event: ReactMouseEvent) => void;
+}
+
+export class EuiOutsideClickDetector extends Component {
// We are working with the assumption that a click event is
// equivalent to a sequential, compound press and release of
// the pointing device (mouse, finger, stylus, etc.).
@@ -14,14 +34,12 @@ export class EuiOutsideClickDetector extends Component {
// about user intention. So, consider the down/start and up/end
// items below as the deconstruction of a click event.
- static propTypes = {
- children: PropTypes.node.isRequired,
- onOutsideClick: PropTypes.func.isRequired,
- isDisabled: PropTypes.bool,
- };
+ private id: string;
+
+ private capturedDownIds: string[];
- constructor(...args) {
- super(...args);
+ constructor(props: Props) {
+ super(props);
// the id is used to identify which EuiOutsideClickDetector
// is the source of a click event; as the click event bubbles
@@ -44,7 +62,7 @@ export class EuiOutsideClickDetector extends Component {
this.capturedDownIds = [];
}
- onClickOutside = event => {
+ onClickOutside: EventHandler = (event: EuiEvent) => {
const { isDisabled, onOutsideClick } = this.props;
if (isDisabled) {
@@ -74,7 +92,10 @@ export class EuiOutsideClickDetector extends Component {
document.removeEventListener('touchend', this.onClickOutside);
}
- onChildClick = (event, cb) => {
+ onChildClick = (
+ event: ReactMouseEvent,
+ cb: (event: ReactMouseEvent) => void
+ ) => {
// to support nested click detectors, build an array
// of detector ids that have been encountered
if (event.nativeEvent.hasOwnProperty('euiGeneratedBy')) {
@@ -85,7 +106,7 @@ export class EuiOutsideClickDetector extends Component {
if (cb) cb(event);
};
- onChildMouseDown = event => {
+ onChildMouseDown = (event: ReactMouseEvent) => {
this.onChildClick(event, e => {
this.capturedDownIds = e.nativeEvent.euiGeneratedBy;
if (this.props.onMouseDown) this.props.onMouseDown(e);
@@ -93,7 +114,7 @@ export class EuiOutsideClickDetector extends Component {
});
};
- onChildMouseUp = event => {
+ onChildMouseUp = (event: ReactMouseEvent) => {
this.onChildClick(event, e => {
if (this.props.onMouseUp) this.props.onMouseUp(e);
if (this.props.onTouchEnd) this.props.onTouchEnd(e);
diff --git a/src/components/page/index.d.ts b/src/components/page/index.d.ts
index 23588a29638..61a6ee822a1 100644
--- a/src/components/page/index.d.ts
+++ b/src/components/page/index.d.ts
@@ -1,5 +1,5 @@
import { CommonProps } from '../common';
-///
+import { EuiPanelProps } from '../panel/panel';
import { FunctionComponent, HTMLAttributes } from 'react';
diff --git a/src/components/pagination/__snapshots__/pagination.test.js.snap b/src/components/pagination/__snapshots__/pagination.test.tsx.snap
similarity index 100%
rename from src/components/pagination/__snapshots__/pagination.test.js.snap
rename to src/components/pagination/__snapshots__/pagination.test.tsx.snap
diff --git a/src/components/pagination/__snapshots__/pagination_button.test.js.snap b/src/components/pagination/__snapshots__/pagination_button.test.tsx.snap
similarity index 100%
rename from src/components/pagination/__snapshots__/pagination_button.test.js.snap
rename to src/components/pagination/__snapshots__/pagination_button.test.tsx.snap
diff --git a/src/components/pagination/index.d.ts b/src/components/pagination/index.d.ts
deleted file mode 100644
index 6aafbbd99d5..00000000000
--- a/src/components/pagination/index.d.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-import { CommonProps, Omit } from '../common';
-///
-
-import { HTMLAttributes, FunctionComponent } from 'react';
-
-declare module '@elastic/eui' {
- /**
- * pagination type defs
- *
- * @see './pagination.js'
- */
-
- export type PageClickHandler = (pageIndex: number) => void;
-
- export interface EuiPaginationProps {
- pageCount?: number;
- activePage?: number;
- onPageClick?: PageClickHandler;
- }
-
- export const EuiPagination: FunctionComponent<
- CommonProps & HTMLAttributes & EuiPaginationProps
- >;
-
- /**
- * pagination button type defs
- *
- * @see './pagination_button.js'
- */
-
- export interface EuiPaginationButtonProps {
- isActive?: boolean;
- isPlaceholder?: boolean;
- hideOnMobile?: boolean;
- }
-
- export const EuiPaginationButton: FunctionComponent<
- CommonProps &
- Omit &
- EuiPaginationButtonProps
- >;
-}
diff --git a/src/components/pagination/index.js b/src/components/pagination/index.ts
similarity index 100%
rename from src/components/pagination/index.js
rename to src/components/pagination/index.ts
diff --git a/src/components/pagination/pagination.test.js b/src/components/pagination/pagination.test.tsx
similarity index 73%
rename from src/components/pagination/pagination.test.js
rename to src/components/pagination/pagination.test.tsx
index 15a92a97b4b..cd6d02340f4 100644
--- a/src/components/pagination/pagination.test.js
+++ b/src/components/pagination/pagination.test.tsx
@@ -6,9 +6,7 @@ import { EuiPagination } from './pagination';
describe('EuiPagination', () => {
test('is rendered', () => {
- const component = render(
- {}} {...requiredProps} />
- );
+ const component = render( );
expect(component).toMatchSnapshot();
});
diff --git a/src/components/pagination/pagination.js b/src/components/pagination/pagination.tsx
similarity index 80%
rename from src/components/pagination/pagination.js
rename to src/components/pagination/pagination.tsx
index 454a0d1f494..0e0517d4573 100644
--- a/src/components/pagination/pagination.js
+++ b/src/components/pagination/pagination.tsx
@@ -1,7 +1,7 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent, HTMLAttributes } from 'react';
import classNames from 'classnames';
+import { CommonProps } from '../common';
import { EuiPaginationButton } from './pagination_button';
import { EuiButtonIcon } from '../button';
import { EuiI18n } from '../i18n';
@@ -9,11 +9,35 @@ import { EuiI18n } from '../i18n';
const MAX_VISIBLE_PAGES = 5;
const NUMBER_SURROUNDING_PAGES = Math.floor(MAX_VISIBLE_PAGES * 0.5);
-export const EuiPagination = ({
+export type PageClickHandler = (pageIndex: number) => void;
+
+export interface EuiPaginationProps {
+ /**
+ * The total number of pages.
+ */
+ pageCount?: number;
+
+ /**
+ * The current page using a zero based index.
+ * So if you set the activePage to 1, it will activate the second page.
+ */
+ activePage?: number;
+
+ onPageClick?: PageClickHandler;
+
+ /**
+ * If true, will only show next/prev arrows instead of page numbers.
+ */
+ compressed?: boolean;
+}
+
+type Props = CommonProps & HTMLAttributes & EuiPaginationProps;
+
+export const EuiPagination: FunctionComponent = ({
className,
- pageCount,
- activePage,
- onPageClick,
+ pageCount = 1,
+ activePage = 1,
+ onPageClick = () => {},
compressed,
...rest
}) => {
@@ -39,7 +63,7 @@ export const EuiPagination = ({
token="euiPagination.pageOfTotal"
default="Page {page} of {total}"
values={{ page: i + 1, total: lastPageInRange }}>
- {pageOfTotal => (
+ {(pageOfTotal: string) => (
- {previousPage => (
+ {(previousPage: string) => (
- {pageOfTotal => (
+ {(pageOfTotal: string) => (
- {jumpToLastPage => (
+ {(jumpToLastPage: string) => (
- {nextPage => (
+ {(nextPage: string) => (
);
- } else {
- return (
-
- {previousButton}
- {firstPageButtons}
- {selectablePages}
- {lastPageButtons}
- {nextButton}
-
- );
}
- } else {
- // Don't render pagination if it isn't needed. Then span is here for a docs bug.
- return ;
- }
-};
-
-EuiPagination.propTypes = {
- className: PropTypes.string,
-
- /**
- * The total number of pages.
- */
- pageCount: PropTypes.number,
- /**
- * The current page using a zero based index.
- * So if you set the activePage to 1, it will activate the second page.
- */
- activePage: PropTypes.number,
- onPageClick: PropTypes.func,
+ return (
+
+ {previousButton}
+ {firstPageButtons}
+ {selectablePages}
+ {lastPageButtons}
+ {nextButton}
+
+ );
+ }
- /**
- * If true, will only show next/prev arrows instead of page numbers.
- */
- compressed: PropTypes.bool,
+ // Don't render pagination if it isn't needed. Then span is here for a docs bug.
+ return ;
};
diff --git a/src/components/pagination/pagination_button.test.js b/src/components/pagination/pagination_button.test.tsx
similarity index 100%
rename from src/components/pagination/pagination_button.test.js
rename to src/components/pagination/pagination_button.test.tsx
diff --git a/src/components/pagination/pagination_button.js b/src/components/pagination/pagination_button.tsx
similarity index 54%
rename from src/components/pagination/pagination_button.js
rename to src/components/pagination/pagination_button.tsx
index 23c07f1b995..ada0ead8899 100644
--- a/src/components/pagination/pagination_button.js
+++ b/src/components/pagination/pagination_button.tsx
@@ -1,10 +1,23 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
-import { EuiButtonEmpty } from '../button';
+import { CommonProps, Omit } from '../common';
+import { EuiButtonEmpty, EuiButtonEmptyProps } from '../button';
-export const EuiPaginationButton = ({
+export interface EuiPaginationButtonProps {
+ isActive?: boolean;
+ /**
+ * For ellipsis or other non-clickable buttons.
+ */
+ isPlaceholder?: boolean;
+ hideOnMobile?: boolean;
+}
+
+type Props = CommonProps &
+ Omit &
+ EuiPaginationButtonProps;
+
+export const EuiPaginationButton: FunctionComponent = ({
children,
className,
isActive,
@@ -23,21 +36,9 @@ export const EuiPaginationButton = ({
className={classes}
size="xs"
color="text"
- disabled={isPlaceholder}
+ isDisabled={isPlaceholder}
{...rest}>
{children}
);
};
-
-EuiPaginationButton.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- isActive: PropTypes.bool,
-
- /**
- * For ellipsis or other non-clickable buttons.
- */
- isPlaceholder: PropTypes.bool,
- hideOnMobile: PropTypes.bool,
-};
diff --git a/src/components/panel/__snapshots__/panel.test.js.snap b/src/components/panel/__snapshots__/panel.test.tsx.snap
similarity index 100%
rename from src/components/panel/__snapshots__/panel.test.js.snap
rename to src/components/panel/__snapshots__/panel.test.tsx.snap
diff --git a/src/components/panel/index.d.ts b/src/components/panel/index.d.ts
deleted file mode 100644
index 0762ec74c25..00000000000
--- a/src/components/panel/index.d.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { CommonProps, RefCallback } from '../common';
-
-import { HTMLAttributes, FunctionComponent } from 'react';
-
-declare module '@elastic/eui' {
- /**
- * panel type defs
- *
- * @see './panel.js'
- */
-
- export type PanelPaddingSize = 'none' | 's' | 'm' | 'l';
-
- export interface EuiPanelProps {
- hasShadow?: boolean;
- paddingSize?: PanelPaddingSize;
- grow?: boolean;
- panelRef?: RefCallback;
- }
-
- export const EuiPanel: FunctionComponent<
- CommonProps & HTMLAttributes & EuiPanelProps
- >;
-}
diff --git a/src/components/panel/index.js b/src/components/panel/index.js
deleted file mode 100644
index 9865743f221..00000000000
--- a/src/components/panel/index.js
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiPanel, SIZES } from './panel';
diff --git a/src/components/panel/index.ts b/src/components/panel/index.ts
new file mode 100644
index 00000000000..584c0b3391d
--- /dev/null
+++ b/src/components/panel/index.ts
@@ -0,0 +1 @@
+export { EuiPanel, PanelPaddingSize, SIZES } from './panel';
diff --git a/src/components/panel/panel.test.js b/src/components/panel/panel.test.tsx
similarity index 100%
rename from src/components/panel/panel.test.js
rename to src/components/panel/panel.test.tsx
diff --git a/src/components/panel/panel.js b/src/components/panel/panel.tsx
similarity index 54%
rename from src/components/panel/panel.js
rename to src/components/panel/panel.tsx
index 3da919006dd..283adac1837 100644
--- a/src/components/panel/panel.js
+++ b/src/components/panel/panel.tsx
@@ -1,9 +1,58 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ ButtonHTMLAttributes,
+ FunctionComponent,
+ HTMLAttributes,
+ MouseEventHandler,
+ ReactNode,
+ Ref,
+} from 'react';
import classNames from 'classnames';
+import { CommonProps, ExclusiveUnion, Omit } from '../common';
import { EuiBetaBadge } from '../badge/beta_badge';
+export type PanelPaddingSize = 'none' | 's' | 'm' | 'l';
+
+export interface EuiPanelProps {
+ /**
+ * If active, adds a deeper shadow to the panel
+ */
+ hasShadow?: boolean;
+ /**
+ * Padding applied to the panel
+ */
+ paddingSize?: PanelPaddingSize;
+ /**
+ * When true the panel will grow to match `EuiFlexItem`
+ */
+ grow?: boolean;
+
+ panelRef?: Ref;
+
+ /**
+ * Add a badge to the panel to label it as "Beta" or other non-GA state
+ */
+ betaBadgeLabel?: string;
+
+ /**
+ * Add a description to the beta badge (will appear in a tooltip)
+ */
+ betaBadgeTooltipContent?: ReactNode;
+
+ /**
+ * Optional title will be supplied as tooltip title or title attribute otherwise the label will be used
+ */
+ betaBadgeTitle?: string;
+}
+
+type Divlike = Omit, 'onClick'>;
+
+interface Buttonlike {
+ onClick?: MouseEventHandler;
+}
+
+type Props = CommonProps & EuiPanelProps & ExclusiveUnion;
+
const paddingSizeToClassNameMap = {
none: null,
s: 'euiPanel--paddingSmall',
@@ -13,12 +62,12 @@ const paddingSizeToClassNameMap = {
export const SIZES = Object.keys(paddingSizeToClassNameMap);
-export const EuiPanel = ({
+export const EuiPanel: FunctionComponent = ({
children,
className,
- paddingSize,
- hasShadow,
- grow,
+ paddingSize = 'm',
+ hasShadow = false,
+ grow = true,
panelRef,
onClick,
betaBadgeLabel,
@@ -28,7 +77,7 @@ export const EuiPanel = ({
}) => {
const classes = classNames(
'euiPanel',
- paddingSizeToClassNameMap[paddingSize],
+ paddingSize ? paddingSizeToClassNameMap[paddingSize] : null,
{
'euiPanel--shadow': hasShadow,
'euiPanel--flexGrowZero': !grow,
@@ -38,19 +87,6 @@ export const EuiPanel = ({
className
);
- const PanelTag = onClick ? 'button' : 'div';
-
- const props = {
- ref: panelRef,
- className: classes,
- };
-
- // Avoid passing down this prop if it hasn't been supplied, in order to
- // avoid noise in react-test-renderer snapshots.
- if (onClick != null) {
- props.onClick = onClick;
- }
-
let optionalBetaBadge;
if (betaBadgeLabel) {
optionalBetaBadge = (
@@ -65,49 +101,24 @@ export const EuiPanel = ({
);
}
+ if (onClick) {
+ return (
+ }
+ className={classes}
+ onClick={onClick}
+ {...rest as ButtonHTMLAttributes}>
+ {optionalBetaBadge}
+ {children}
+
+ );
+ }
+
return (
-
+ // ts-ignore seems to be some div / button confusion here
+
{optionalBetaBadge}
{children}
-
+
);
};
-
-EuiPanel.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- /**
- * If active, adds a deeper shadow to the panel
- */
- hasShadow: PropTypes.bool,
- /**
- * Padding applied to the panel
- */
- paddingSize: PropTypes.oneOf(SIZES),
- /**
- * When true the panel will grow to match `EuiFlexItem`
- */
- grow: PropTypes.bool,
- panelRef: PropTypes.func,
- onClick: PropTypes.func,
- /**
- * Add a badge to the panel to label it as "Beta" or other non-GA state
- */
- betaBadgeLabel: PropTypes.string,
-
- /**
- * Add a description to the beta badge (will appear in a tooltip)
- */
- betaBadgeTooltipContent: PropTypes.node,
-
- /**
- * Optional title will be supplied as tooltip title or title attribute otherwise the label will be used
- */
- betaBadgeTitle: PropTypes.string,
-};
-
-EuiPanel.defaultProps = {
- paddingSize: 'm',
- hasShadow: false,
- grow: true,
-};
diff --git a/src/components/popover/__snapshots__/popover.test.js.snap b/src/components/popover/__snapshots__/popover.test.tsx.snap
similarity index 100%
rename from src/components/popover/__snapshots__/popover.test.js.snap
rename to src/components/popover/__snapshots__/popover.test.tsx.snap
diff --git a/src/components/popover/index.d.ts b/src/components/popover/index.d.ts
deleted file mode 100644
index eb7d3422cc9..00000000000
--- a/src/components/popover/index.d.ts
+++ /dev/null
@@ -1,64 +0,0 @@
-import { CommonProps, NoArgCallback } from '../common';
-///
-///
-
-import { FunctionComponent, ReactNode, HTMLAttributes } from 'react';
-import { EuiPopoverTitleProps } from './popover_title';
-import { EuiPopoverFooterProps } from './popover_footer';
-
-declare module '@elastic/eui' {
- /**
- * popover type defs
- *
- * @see './popover.js'
- */
-
- export type PopoverAnchorPosition =
- | 'upCenter'
- | 'upLeft'
- | 'upRight'
- | 'downCenter'
- | 'downLeft'
- | 'downRight'
- | 'leftCenter'
- | 'leftUp'
- | 'leftDown'
- | 'rightCenter'
- | 'rightUp'
- | 'rightDown';
-
- interface EuiPopoverProps {
- id: string;
- closePopover: NoArgCallback;
- button: ReactNode;
- withTitle?: boolean;
- isOpen?: boolean;
- ownFocus?: boolean;
- initialFocus?: FocusTarget;
- hasArrow?: boolean;
- anchorClassName?: string;
- anchorPosition?: PopoverAnchorPosition;
- panelClassName?: string;
- panelPaddingSize?: PanelPaddingSize;
- repositionOnScroll?: boolean;
- attachToAnchor?: boolean;
- }
-
- export const EuiPopover: FunctionComponent<
- CommonProps & HTMLAttributes & EuiPopoverProps
- >;
-
- /**
- * Popover title type defs
- *
- * @see './popover_title.js'
- */
- export const EuiPopoverTitle: EuiPopoverTitleProps;
-
- /**
- * Popover footer type defs
- *
- * @see './popover_footer.js'
- */
- export const EuiPopoverFooter: EuiPopoverFooterProps;
-}
diff --git a/src/components/popover/index.js b/src/components/popover/index.ts
similarity index 72%
rename from src/components/popover/index.js
rename to src/components/popover/index.ts
index 28728b6fd49..243067fc014 100644
--- a/src/components/popover/index.js
+++ b/src/components/popover/index.ts
@@ -1,5 +1,5 @@
export { EuiInputPopover } from './input_popover';
-export { EuiPopover } from './popover';
+export { EuiPopover, EuiPopoverProps, PopoverAnchorPosition } from './popover';
export { EuiPopoverTitle } from './popover_title';
export { EuiPopoverFooter } from './popover_footer';
export { EuiWrappingPopover } from './wrapping_popover';
diff --git a/src/components/popover/input_popover.js b/src/components/popover/input_popover.tsx
similarity index 64%
rename from src/components/popover/input_popover.js
rename to src/components/popover/input_popover.tsx
index 8c2e4eccb75..83e1b1bd7f6 100644
--- a/src/components/popover/input_popover.js
+++ b/src/components/popover/input_popover.tsx
@@ -1,25 +1,41 @@
-import React, { useState, useEffect } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ FunctionComponent,
+ HTMLAttributes,
+ useState,
+ useEffect,
+} from 'react';
import classnames from 'classnames';
import tabbable from 'tabbable';
+import { CommonProps } from '../common';
import { EuiFocusTrap } from '../focus_trap';
-import { EuiPopover, EuiPopoverPropTypes } from './popover';
+import { EuiPopover, EuiPopoverProps } from './popover';
import { cascadingMenuKeyCodes } from '../../services';
-export const EuiInputPopover = ({
+interface EuiInputPopoverProps extends EuiPopoverProps {
+ fullWidth?: boolean;
+ input: EuiPopoverProps['button'];
+ inputRef: EuiPopoverProps['buttonRef'];
+}
+
+type Props = CommonProps &
+ HTMLAttributes &
+ EuiInputPopoverProps;
+
+export const EuiInputPopover: FunctionComponent = ({
children,
className,
fullWidth,
input,
- popoverZIndex,
...props
}) => {
const [inputEl, setInputEl] = useState();
const [inputElWidth, setInputElWidth] = useState();
const [panelEl, setPanelEl] = useState();
- const inputRef = node => setInputEl(node);
- const panelRef = node => setPanelEl(node);
+
+ const inputRef = (node: HTMLElement | null) => setInputEl(node);
+ const panelRef = (node: HTMLElement | null) => setPanelEl(node);
+
const setPanelWidth = () => {
if (panelEl && inputElWidth) {
panelEl.style.width = `${inputElWidth}px`;
@@ -35,11 +51,14 @@ export const EuiInputPopover = ({
useEffect(() => {
setPanelWidth();
}, [panelEl]);
- const onKeyDown = e => {
+
+ const onKeyDown = (e: React.KeyboardEvent) => {
if (e.keyCode === cascadingMenuKeyCodes.TAB) {
- const tabbableItems = tabbable(panelEl).filter(el => {
+ const tabbableItems = tabbable(panelEl).filter((el: HTMLElement) => {
return (
- [...el.attributes].map(el => el.name).indexOf('data-focus-guard') < 0
+ Array.from(el.attributes)
+ .map(el => el.name)
+ .indexOf('data-focus-guard') < 0
);
});
if (
@@ -50,6 +69,7 @@ export const EuiInputPopover = ({
}
}
};
+
const classes = classnames(
'euiInputPopover',
{
@@ -57,6 +77,7 @@ export const EuiInputPopover = ({
},
className
);
+
return (
({
- EuiPortal: ({ children }) => children,
+ EuiPortal: ({ children }: { children: ReactNode }) => children,
}));
let id = 0;
@@ -255,12 +256,14 @@ describe('getPopoverPositionFromAnchorPosition', () => {
expect(getPopoverPositionFromAnchorPosition('upLeft')).toBe('top');
expect(getPopoverPositionFromAnchorPosition('rightDown')).toBe('right');
expect(getPopoverPositionFromAnchorPosition('downRight')).toBe('bottom');
- expect(getPopoverPositionFromAnchorPosition('leftTop')).toBe('left');
+ expect(getPopoverPositionFromAnchorPosition('leftUp')).toBe('left');
});
it('returns undefined when an invalid position is extracted', () => {
expect(
- getPopoverPositionFromAnchorPosition('nowhereNohow')
+ getPopoverPositionFromAnchorPosition(
+ 'nowhereNohow' as PopoverAnchorPosition
+ )
).toBeUndefined();
});
});
@@ -274,6 +277,8 @@ describe('getPopoverAlignFromAnchorPosition', () => {
});
it('returns undefined when an invalid position is extracted', () => {
- expect(getPopoverAlignFromAnchorPosition('nowhereNohow')).toBeUndefined();
+ expect(
+ getPopoverAlignFromAnchorPosition('nowhereNohow' as PopoverAnchorPosition)
+ ).toBeUndefined();
});
});
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.tsx
similarity index 70%
rename from src/components/popover/popover.js
rename to src/components/popover/popover.tsx
index 481b2549799..e982e75dc92 100644
--- a/src/components/popover/popover.js
+++ b/src/components/popover/popover.tsx
@@ -1,8 +1,16 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ Component,
+ CSSProperties,
+ HTMLAttributes,
+ ReactNode,
+ Ref,
+} from 'react';
import classNames from 'classnames';
import tabbable from 'tabbable';
+import { CommonProps, NoArgCallback, RefCallback } from '../common';
+import { FocusTarget, EuiFocusTrap } from '../focus_trap';
+
import {
cascadingMenuKeyCodes,
getTransitionTimings,
@@ -10,13 +18,11 @@ import {
performOnFrame,
} from '../../services';
-import { EuiFocusTrap } from '../focus_trap';
-
import { EuiOutsideClickDetector } from '../outside_click_detector';
import { EuiScreenReaderOnly } from '../accessibility';
-import { EuiPanel, SIZES } from '../panel';
+import { EuiPanel, PanelPaddingSize } from '../panel';
import { EuiPortal } from '../portal';
@@ -25,16 +31,98 @@ import { EuiMutationObserver } from '../observer/mutation_observer';
import {
findPopoverPosition,
getElementZIndex,
-} from '../../services/popover/popover_positioning';
+ EuiPopoverPosition,
+} from '../../services/popover';
+
import { EuiI18n } from '../i18n';
-const anchorPositionToPopoverPositionMap = {
+export type PopoverAnchorPosition =
+ | 'upCenter'
+ | 'upLeft'
+ | 'upRight'
+ | 'downCenter'
+ | 'downLeft'
+ | 'downRight'
+ | 'leftCenter'
+ | 'leftUp'
+ | 'leftDown'
+ | 'rightCenter'
+ | 'rightUp'
+ | 'rightDown';
+
+export interface EuiPopoverProps {
+ anchorClassName?: string;
+
+ anchorPosition?: PopoverAnchorPosition;
+
+ /** Style and position alteration for arrow-less, left-aligned
+ * attachment. Intended for use with inputs as anchors, à la
+ * EuiColorPicker */
+ attachToAnchor?: boolean;
+
+ button: NonNullable;
+
+ buttonRef?: RefCallback;
+
+ closePopover: NoArgCallback;
+
+ container?: HTMLElement;
+
+ /** CSS display type for both the popover and anchor */
+ display?: keyof typeof displayToClassNameMap;
+
+ hasArrow?: boolean;
+
+ /** specifies what element should initially have focus; Can be a DOM
+ * node, or a selector string (which will be passed to
+ * document.querySelector() to find the DOM node), or a function that
+ * returns a DOM node. */
+ initialFocus?: FocusTarget;
+
+ /** Passed directly to EuiPortal for DOM positioning. Both properties are
+ * required if prop is specified **/
+ insert?: {
+ sibling: HTMLElement;
+ position: 'before' | 'after';
+ };
+
+ isOpen?: boolean;
+
+ ownFocus?: boolean;
+
+ panelClassName?: string;
+
+ panelPaddingSize?: PanelPaddingSize;
+
+ panelRef?: RefCallback;
+
+ popoverRef?: Ref;
+
+ /** When `true`, the popover's position is re-calculated when the user
+ * scrolls, this supports having fixed-position popover anchors. */
+ repositionOnScroll?: boolean;
+
+ withTitle?: boolean;
+
+ /** By default, popover content inherits the z-index of the anchor
+ * component; pass zIndex to override */
+ zIndex?: number;
+}
+
+type AnchorPosition = 'up' | 'right' | 'down' | 'left';
+
+const anchorPositionToPopoverPositionMap: {
+ [position in AnchorPosition]: EuiPopoverPosition
+} = {
up: 'top',
right: 'right',
down: 'bottom',
left: 'left',
};
-export function getPopoverPositionFromAnchorPosition(anchorPosition) {
+
+export function getPopoverPositionFromAnchorPosition(
+ anchorPosition: PopoverAnchorPosition
+) {
// maps the anchor position to the matching popover position
// e.g. "upLeft" -> "top", "downRight" -> "bottom"
@@ -42,22 +130,27 @@ export function getPopoverPositionFromAnchorPosition(anchorPosition) {
// starts at the beginning (" ^ ") of anchorPosition and
// captures all of the characters (" (.*?) ") until the
// first capital letter (" [A-Z] ") is encountered
- const [, primaryPosition] = anchorPosition.match(/^(.*?)[A-Z]/);
- return anchorPositionToPopoverPositionMap[primaryPosition];
+ const [, primaryPosition] = anchorPosition.match(/^(.*?)[A-Z]/)!;
+ return anchorPositionToPopoverPositionMap[primaryPosition as AnchorPosition];
}
-export function getPopoverAlignFromAnchorPosition(anchorPosition) {
+
+export function getPopoverAlignFromAnchorPosition(
+ anchorPosition: PopoverAnchorPosition
+) {
// maps the gravity to the matching popover position
// e.g. "upLeft" -> "left", "rightDown" -> "bottom"
// extract the second positional word from anchorPosition:
// starts a capture group at the first capital letter
// and includes everything after it
- const [, align] = anchorPosition.match(/([A-Z].*)/);
+ const [, align] = anchorPosition.match(/([A-Z].*)/)!;
// this performs two tasks:
// 1. normalizes the align position by lowercasing it
// 2. `center` doesn't exist in the lookup map which converts it to `undefined` meaning no align
- return anchorPositionToPopoverPositionMap[align.toLowerCase()];
+ return anchorPositionToPopoverPositionMap[
+ align.toLowerCase() as AnchorPosition
+ ];
}
const anchorPositionToClassNameMap = {
@@ -89,16 +182,64 @@ const DEFAULT_POPOVER_STYLES = {
left: 50,
};
-function getElementFromInitialFocus(initialFocus) {
+function getElementFromInitialFocus(
+ initialFocus?: FocusTarget
+): HTMLElement | null {
const initialFocusType = typeof initialFocus;
- if (initialFocusType === 'string')
- return document.querySelector(initialFocus);
- if (initialFocusType === 'function') return initialFocus();
- return initialFocus;
+
+ if (initialFocusType === 'string') {
+ return document.querySelector(initialFocus as string);
+ }
+
+ if (initialFocusType === 'function') {
+ return (initialFocus as () => HTMLElement | null)();
+ }
+
+ return initialFocus as HTMLElement | null;
}
-export class EuiPopover extends Component {
- static getDerivedStateFromProps(nextProps, prevState) {
+export type Props = CommonProps &
+ HTMLAttributes &
+ EuiPopoverProps;
+
+interface State {
+ prevProps: {
+ isOpen?: boolean;
+ };
+ suppressingPopover?: boolean;
+ isClosing: boolean;
+ isOpening: boolean;
+ popoverStyles: CSSProperties;
+ arrowStyles?: CSSProperties;
+ arrowPosition: any; // What should this be?
+ openPosition: any; // What should this be?
+ isOpenStable: boolean;
+}
+
+type PropsWithDefaults = Props & {
+ anchorPosition: PopoverAnchorPosition;
+ /** CSS display type for both the popover and anchor */
+ display: keyof typeof displayToClassNameMap;
+ hasArrow: boolean;
+ isOpen: boolean;
+ ownFocus: boolean;
+ panelPaddingSize: PanelPaddingSize;
+};
+
+export class EuiPopover extends Component {
+ static defaultProps: Partial = {
+ isOpen: false,
+ ownFocus: false,
+ anchorPosition: 'downCenter',
+ panelPaddingSize: 'm',
+ hasArrow: true,
+ display: 'inlineBlock',
+ };
+
+ static getDerivedStateFromProps(
+ nextProps: Props,
+ prevState: State
+ ): Partial | null {
if (prevState.prevProps.isOpen && !nextProps.isOpen) {
return {
prevProps: {
@@ -120,11 +261,12 @@ export class EuiPopover extends Component {
return null;
}
- constructor(props) {
- super(props);
+ private closingTransitionTimeout: number | undefined;
+ private button: HTMLElement | null = null;
+ private panel: HTMLElement | null = null;
- this.closingTransitionTimeout = undefined;
- this.button = null;
+ constructor(props: Props) {
+ super(props);
this.state = {
prevProps: {
@@ -141,7 +283,7 @@ export class EuiPopover extends Component {
};
}
- onKeyDown = e => {
+ onKeyDown = (e: React.KeyboardEvent) => {
if (e.keyCode === cascadingMenuKeyCodes.ESCAPE) {
if (this.state.isOpenStable || this.state.isOpening) {
e.preventDefault();
@@ -168,12 +310,14 @@ export class EuiPopover extends Component {
if (this.props.initialFocus != null) {
focusTarget = getElementFromInitialFocus(this.props.initialFocus);
- // there's a race condition between the popover content becoming visible and this function call
- // if the element isn't visible yet (due to css styling) then it can't accept focus
- // so wait for another render and try again
- const visibility = window.getComputedStyle(focusTarget).visibility;
- if (visibility === 'hidden') {
- this.updateFocus();
+ if (focusTarget) {
+ // there's a race condition between the popover content becoming visible and this function call
+ // if the element isn't visible yet (due to css styling) then it can't accept focus
+ // so wait for another render and try again
+ const visibility = window.getComputedStyle(focusTarget).visibility;
+ if (visibility === 'hidden') {
+ this.updateFocus();
+ }
}
} else {
const tabbableItems = tabbable(this.panel);
@@ -200,7 +344,7 @@ export class EuiPopover extends Component {
this.updateFocus();
}
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps: Props) {
// The popover is being opened.
if (!prevProps.isOpen && this.props.isOpen) {
clearTimeout(this.closingTransitionTimeout);
@@ -214,7 +358,7 @@ export class EuiPopover extends Component {
// for each child element of `this.panel`, find any transition duration we should wait for before stabilizing
const { durationMatch, delayMatch } = Array.prototype.slice
- .call(this.panel.children)
+ .call(this.panel ? this.panel.children : undefined)
.reduce(
({ durationMatch, delayMatch }, element) => {
const transitionTimings = getTransitionTimings(element);
@@ -251,7 +395,7 @@ export class EuiPopover extends Component {
if (prevProps.isOpen && !this.props.isOpen) {
// If the user has just closed the popover, queue up the removal of the content after the
// transition is complete.
- this.closingTransitionTimeout = setTimeout(() => {
+ this.closingTransitionTimeout = window.setTimeout(() => {
this.setState({
isClosing: false,
});
@@ -264,20 +408,20 @@ export class EuiPopover extends Component {
clearTimeout(this.closingTransitionTimeout);
}
- onMutation = records => {
+ onMutation = (records: MutationRecord[]) => {
const waitDuration = getWaitDuration(records);
this.positionPopoverFixed();
performOnFrame(waitDuration, this.positionPopoverFixed);
};
- positionPopover = allowEnforcePosition => {
+ positionPopover = (allowEnforcePosition: boolean) => {
if (this.button == null || this.panel == null) return;
- let position = getPopoverPositionFromAnchorPosition(
- this.props.anchorPosition
- );
- let forcePosition = null;
+ const { anchorPosition } = this.props as PropsWithDefaults;
+
+ let position = getPopoverPositionFromAnchorPosition(anchorPosition);
+ let forcePosition = undefined;
if (
allowEnforcePosition &&
this.state.isOpenStable &&
@@ -297,7 +441,7 @@ export class EuiPopover extends Component {
container: this.props.container,
position,
forcePosition,
- align: getPopoverAlignFromAnchorPosition(this.props.anchorPosition),
+ align: getPopoverAlignFromAnchorPosition(anchorPosition),
anchor: this.button,
popover: this.panel,
offset: !this.props.attachToAnchor && this.props.hasArrow ? 16 : 8,
@@ -319,12 +463,15 @@ export class EuiPopover extends Component {
const popoverStyles = {
top,
- left: this.props.attachToAnchor ? anchorBoundingBox.left : left,
+ left:
+ this.props.attachToAnchor && anchorBoundingBox
+ ? anchorBoundingBox.left
+ : left,
zIndex,
};
const willRenderArrow = !this.props.attachToAnchor && this.props.hasArrow;
- const arrowStyles = willRenderArrow ? arrow : null;
+ const arrowStyles = willRenderArrow ? arrow : undefined;
const arrowPosition = foundPosition;
this.setState({
@@ -343,7 +490,7 @@ export class EuiPopover extends Component {
this.positionPopover(false);
};
- panelRef = node => {
+ panelRef = (node: HTMLElement | null) => {
this.panel = node;
this.props.panelRef && this.props.panelRef(node);
@@ -364,7 +511,7 @@ export class EuiPopover extends Component {
}
};
- buttonRef = node => {
+ buttonRef = (node: HTMLElement | null) => {
this.button = node;
this.props.buttonRef && this.props.buttonRef(node);
};
@@ -397,8 +544,8 @@ export class EuiPopover extends Component {
const classes = classNames(
'euiPopover',
- anchorPositionToClassNameMap[anchorPosition],
- displayToClassNameMap[display],
+ anchorPosition ? anchorPositionToClassNameMap[anchorPosition] : null,
+ display ? displayToClassNameMap[display] : null,
{
'euiPopover-isOpen': this.state.isOpening,
'euiPopover--withTitle': withTitle,
@@ -423,13 +570,13 @@ export class EuiPopover extends Component {
if (!this.state.suppressingPopover && (isOpen || this.state.isClosing)) {
let tabIndex;
let initialFocus;
- let ariaLive;
+ let ariaLive: HTMLAttributes['aria-live'];
if (ownFocus) {
- tabIndex = '0';
+ tabIndex = 0;
ariaLive = 'off';
- initialFocus = () => this.panel;
+ initialFocus = () => this.panel!;
} else {
ariaLive = 'assertive';
}
@@ -503,51 +650,3 @@ export class EuiPopover extends Component {
);
}
}
-
-export const EuiPopoverPropTypes = {
- anchorClassName: PropTypes.string,
- anchorPosition: PropTypes.oneOf(ANCHOR_POSITIONS),
- isOpen: PropTypes.bool,
- ownFocus: PropTypes.bool,
- withTitle: PropTypes.bool,
- closePopover: PropTypes.func.isRequired,
- button: PropTypes.node.isRequired,
- buttonRef: PropTypes.func,
- children: PropTypes.node,
- panelClassName: PropTypes.string,
- panelPaddingSize: PropTypes.oneOf(SIZES),
- panelRef: PropTypes.func,
- popoverRef: PropTypes.func,
- hasArrow: PropTypes.bool,
- container: PropTypes.instanceOf(HTMLElement),
- /** When `true`, the popover's position is re-calculated when the user scrolls, this supports having fixed-position popover anchors. */
- repositionOnScroll: PropTypes.bool,
- /** By default, popover content inherits the z-index of the anchor component; pass zIndex to override */
- zIndex: PropTypes.number,
- /** specifies what element should initially have focus; Can be a DOM node, or a selector string (which will be passed to document.querySelector() to find the DOM node), or a function that returns a DOM node. */
- initialFocus: PropTypes.oneOfType([
- PropTypes.instanceOf(HTMLElement),
- PropTypes.func,
- PropTypes.string,
- ]),
- /** Passed directly to EuiPortal for DOM positioning. Both properties are required if prop is specified **/
- insert: PropTypes.shape({
- sibling: PropTypes.instanceOf(HTMLElement),
- position: PropTypes.oneOf(['before', 'after']),
- }),
- /** Style and position alteration for arrow-less, left-aligned attachment. Intended for use with inputs as anchors, à la EuiColorPicker */
- attachToAnchor: PropTypes.bool,
- /** CSS display type for both the popover and anchor */
- display: PropTypes.oneOf(DISPLAY),
-};
-
-EuiPopover.propTypes = EuiPopoverPropTypes;
-
-EuiPopover.defaultProps = {
- isOpen: false,
- ownFocus: false,
- anchorPosition: 'downCenter',
- panelPaddingSize: 'm',
- hasArrow: true,
- display: 'inlineBlock',
-};
diff --git a/src/components/popover/wrapping_popover.js b/src/components/popover/wrapping_popover.tsx
similarity index 60%
rename from src/components/popover/wrapping_popover.js
rename to src/components/popover/wrapping_popover.tsx
index c9854103f49..2fbb9d2c4c5 100644
--- a/src/components/popover/wrapping_popover.js
+++ b/src/components/popover/wrapping_popover.tsx
@@ -1,36 +1,39 @@
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-import { EuiPopover } from './popover';
+import { EuiPopover, Props as EuiPopoverProps } from './popover';
import { EuiPortal } from '../portal';
+interface Props extends EuiPopoverProps {
+ button: HTMLElement;
+}
+
/**
* Injects the EuiPopover next to the button via EuiPortal
* then the button element is moved into the popover dom.
* On unmount, the button is moved back to its original location.
*/
-export class EuiWrappingPopover extends Component {
- constructor(...args) {
- super(...args);
-
- this.portal = null;
- this.anchor = null;
- }
+export class EuiWrappingPopover extends Component {
+ private portal: HTMLElement | null = null;
+ private anchor: HTMLElement | null = null;
componentDidMount() {
- this.anchor.insertAdjacentElement('beforebegin', this.props.button);
+ if (this.anchor) {
+ this.anchor.insertAdjacentElement('beforebegin', this.props.button);
+ }
}
componentWillUnmount() {
if (this.props.button.parentNode) {
- this.portal.insertAdjacentElement('beforebegin', this.props.button);
+ if (this.portal) {
+ this.portal.insertAdjacentElement('beforebegin', this.props.button);
+ }
}
}
- setPortalRef = node => {
+ setPortalRef = (node: HTMLElement | null) => {
this.portal = node;
};
- setAnchorRef = node => {
+ setAnchorRef = (node: HTMLElement | null) => {
this.anchor = node;
};
@@ -54,7 +57,3 @@ export class EuiWrappingPopover extends Component {
);
}
}
-
-EuiWrappingPopover.propTypes = {
- button: PropTypes.instanceOf(HTMLElement),
-};
diff --git a/src/components/table/__snapshots__/table.test.js.snap b/src/components/table/__snapshots__/table.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table.test.js.snap
rename to src/components/table/__snapshots__/table.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_footer.test.js.snap b/src/components/table/__snapshots__/table_footer.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_footer.test.js.snap
rename to src/components/table/__snapshots__/table_footer.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_footer_cell.test.js.snap b/src/components/table/__snapshots__/table_footer_cell.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_footer_cell.test.js.snap
rename to src/components/table/__snapshots__/table_footer_cell.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_header.test.js.snap b/src/components/table/__snapshots__/table_header.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_header.test.js.snap
rename to src/components/table/__snapshots__/table_header.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_header_button.test.js.snap b/src/components/table/__snapshots__/table_header_button.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_header_button.test.js.snap
rename to src/components/table/__snapshots__/table_header_button.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_header_cell.test.js.snap b/src/components/table/__snapshots__/table_header_cell.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_header_cell.test.js.snap
rename to src/components/table/__snapshots__/table_header_cell.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_row.test.js.snap b/src/components/table/__snapshots__/table_row.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_row.test.js.snap
rename to src/components/table/__snapshots__/table_row.test.tsx.snap
diff --git a/src/components/table/__snapshots__/table_row_cell.test.js.snap b/src/components/table/__snapshots__/table_row_cell.test.tsx.snap
similarity index 100%
rename from src/components/table/__snapshots__/table_row_cell.test.js.snap
rename to src/components/table/__snapshots__/table_row_cell.test.tsx.snap
diff --git a/src/components/table/index.d.ts b/src/components/table/index.d.ts
deleted file mode 100644
index 252c6107343..00000000000
--- a/src/components/table/index.d.ts
+++ /dev/null
@@ -1,177 +0,0 @@
-///
-///
-import { CommonProps, NoArgCallback } from '../common';
-import { IconType } from '../icon';
-import { HorizontalAlignment } from '../../services/alignment';
-import { EuiTableRowCellCheckbox as TableRowCellCheckbox } from './table_row_cell_checkbox';
-import {
- EuiTableHeaderCellCheckboxScope as TableHeaderCellCheckboxScope,
- EuiTableHeaderCellCheckboxProps as TableHeaderCellCheckboxProps,
- EuiTableHeaderCellCheckbox as TableHeaderCellCheckbox,
-} from './table_header_cell_checkbox';
-
-import {
- FunctionComponent,
- HTMLAttributes,
- TableHTMLAttributes,
- ButtonHTMLAttributes,
- ThHTMLAttributes,
- TdHTMLAttributes,
- ReactNode,
-} from 'react';
-
-declare module '@elastic/eui' {
- /**
- * table type defs
- *
- * @see './table.js'
- */
-
- export interface EuiTableProps {
- compressed?: boolean;
- responsive?: boolean;
- }
-
- export const EuiTable: FunctionComponent<
- CommonProps & TableHTMLAttributes & EuiTableProps
- >;
-
- /**
- * table body type defs
- *
- * @see './table_body.js'
- */
-
- export interface EuiTableBodyProps {}
-
- export const EuiTableBody: FunctionComponent;
-
- /**
- * table header type defs
- *
- * @see './table_header.js'
- */
-
- export interface EuiTableHeaderProps {}
-
- export const EuiTableHeader: FunctionComponent<
- CommonProps & EuiTableHeaderProps
- >;
-
- /**
- * table header button type defs
- *
- * @see './table_header_button.js'
- */
-
- export interface EuiTableHeaderButtonProps {
- iconType?: IconType;
- }
-
- export const EuiTableHeaderButton: FunctionComponent<
- CommonProps &
- ButtonHTMLAttributes &
- EuiTableHeaderButtonProps
- >;
-
- /**
- * table header cell type defs
- *
- * @see './table_header_cell.js'
- */
-
- export type TableHeaderCellScope = 'col' | 'row' | 'colgroup' | 'rowgroup';
-
- export interface EuiTableHeaderCellProps {
- align?: HorizontalAlignment;
- width?: string;
- onSort?: NoArgCallback;
- isSorted?: boolean;
- isSortAscending?: boolean;
- scope?: TableHeaderCellScope;
- isMobileHeader?: boolean;
- hideForMobile?: boolean;
- mobileOptions?: {
- show?: boolean;
- only?: boolean;
- };
- }
-
- export const EuiTableHeaderCell: FunctionComponent<
- CommonProps &
- ThHTMLAttributes &
- EuiTableHeaderCellProps
- >;
-
- /**
- * table header cell checkbox type defs
- *
- * @see './table_header_cell_checkbox.js'
- */
-
- export type EuiTableHeaderCellCheckboxScope = TableHeaderCellCheckboxScope;
- export interface EuiTableHeaderCellCheckboxProps
- extends TableHeaderCellCheckboxProps {}
- export const EuiTableHeaderCellCheckbox: typeof TableHeaderCellCheckbox;
-
- /**
- * table row type defs
- *
- * @see './table_row.js'
- */
-
- export interface EuiTableRowProps {
- isSelected?: boolean;
- }
-
- export const EuiTableRow: FunctionComponent<
- CommonProps & EuiTableRowProps & HTMLAttributes
- >;
-
- /**
- * table row cell type defs
- *
- * @see './table_row_cell.js'
- */
-
- interface EuiTableRowCellSharedPropsShape {
- align?: HorizontalAlignment;
- showOnHover?: boolean;
- textOnly?: boolean;
- truncateText?: boolean;
- }
-
- export interface EuiTableRowCellMobileOptionsShape {
- show?: boolean;
- only?: boolean;
- render?: ReactNode;
- header?: ReactNode | boolean;
- enlarge?: boolean;
- fullWidth?: boolean;
- }
-
- export interface EuiTableRowCellProps {
- hasActions?: boolean;
- header?: string;
- hideForMobile?: boolean;
- isExpander?: boolean;
- isMobileFullWidth?: boolean;
- isMobileHeader?: boolean;
- mobileOptions?: EuiTableRowCellMobileOptionsShape &
- EuiTableRowCellSharedPropsShape;
- }
-
- export const EuiTableRowCell: FunctionComponent<
- CommonProps &
- TdHTMLAttributes &
- EuiTableRowCellSharedPropsShape &
- EuiTableRowCellProps
- >;
-
- /**
- * table row cell checkbox type defs
- *
- * @see './table_row_cell_checkbox.js'
- */
- export const EuiTableRowCellCheckbox: typeof TableRowCellCheckbox;
-}
diff --git a/src/components/table/index.js b/src/components/table/index.ts
similarity index 93%
rename from src/components/table/index.js
rename to src/components/table/index.ts
index 3abb0e41b3f..2585859d613 100644
--- a/src/components/table/index.js
+++ b/src/components/table/index.ts
@@ -1,4 +1,4 @@
-export { EuiTable } from './table';
+export { EuiTable, Props as EuiTableProps } from './table';
export { EuiTableBody } from './table_body';
export { EuiTableFooter } from './table_footer';
export { EuiTableFooterCell } from './table_footer_cell';
diff --git a/src/components/table/mobile/__snapshots__/table_sort_mobile.test.js.snap b/src/components/table/mobile/__snapshots__/table_sort_mobile.test.tsx.snap
similarity index 100%
rename from src/components/table/mobile/__snapshots__/table_sort_mobile.test.js.snap
rename to src/components/table/mobile/__snapshots__/table_sort_mobile.test.tsx.snap
diff --git a/src/components/table/mobile/__snapshots__/table_sort_mobile_item.test.js.snap b/src/components/table/mobile/__snapshots__/table_sort_mobile_item.test.tsx.snap
similarity index 100%
rename from src/components/table/mobile/__snapshots__/table_sort_mobile_item.test.js.snap
rename to src/components/table/mobile/__snapshots__/table_sort_mobile_item.test.tsx.snap
diff --git a/src/components/table/mobile/index.d.ts b/src/components/table/mobile/index.d.ts
deleted file mode 100644
index a54d79c7147..00000000000
--- a/src/components/table/mobile/index.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { EuiTableHeaderMobile } from './table_header_mobile';
diff --git a/src/components/table/mobile/index.js b/src/components/table/mobile/index.tsx
similarity index 100%
rename from src/components/table/mobile/index.js
rename to src/components/table/mobile/index.tsx
diff --git a/src/components/table/mobile/table_sort_mobile.test.js b/src/components/table/mobile/table_sort_mobile.test.tsx
similarity index 100%
rename from src/components/table/mobile/table_sort_mobile.test.js
rename to src/components/table/mobile/table_sort_mobile.test.tsx
diff --git a/src/components/table/mobile/table_sort_mobile.js b/src/components/table/mobile/table_sort_mobile.tsx
similarity index 84%
rename from src/components/table/mobile/table_sort_mobile.js
rename to src/components/table/mobile/table_sort_mobile.tsx
index 6e0b59585b2..6aa695999a2 100644
--- a/src/components/table/mobile/table_sort_mobile.js
+++ b/src/components/table/mobile/table_sort_mobile.tsx
@@ -1,27 +1,26 @@
import React, { Component } from 'react';
-import PropTypes from 'prop-types';
import classNames from 'classnames';
import { EuiButtonEmpty } from '../../button/button_empty';
-import { EuiPopover } from '../../popover';
+import { EuiPopover, PopoverAnchorPosition } from '../../popover';
import { EuiContextMenuPanel } from '../../context_menu';
import { EuiI18n } from '../../i18n';
import { EuiTableSortMobileItem } from './table_sort_mobile_item';
-export class EuiTableSortMobile extends Component {
- static propTypes = {
- className: PropTypes.string,
- anchorPosition: PropTypes.string,
- items: PropTypes.array,
- };
+interface Props {
+ className?: string;
+ anchorPosition?: PopoverAnchorPosition;
+ items?: any[];
+}
- constructor(props) {
- super(props);
+interface State {
+ isPopoverOpen: boolean;
+}
- this.state = {
- isPopoverOpen: false,
- };
- }
+export class EuiTableSortMobile extends Component {
+ state = {
+ isPopoverOpen: false,
+ };
onButtonClick = () => {
this.setState({
@@ -76,7 +75,7 @@ export class EuiTableSortMobile extends Component {
);
})
- : null
+ : undefined
}
watchedItemProps={['isSorted', 'isSortAscending']}
/>
diff --git a/src/components/table/mobile/table_sort_mobile_item.test.js b/src/components/table/mobile/table_sort_mobile_item.test.tsx
similarity index 100%
rename from src/components/table/mobile/table_sort_mobile_item.test.js
rename to src/components/table/mobile/table_sort_mobile_item.test.tsx
diff --git a/src/components/table/mobile/table_sort_mobile_item.js b/src/components/table/mobile/table_sort_mobile_item.tsx
similarity index 77%
rename from src/components/table/mobile/table_sort_mobile_item.js
rename to src/components/table/mobile/table_sort_mobile_item.tsx
index ef15e1d55e6..d6052acab54 100644
--- a/src/components/table/mobile/table_sort_mobile_item.js
+++ b/src/components/table/mobile/table_sort_mobile_item.tsx
@@ -1,10 +1,27 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent } from 'react';
import classNames from 'classnames';
+import { CommonProps } from '../../common';
import { EuiContextMenuItem } from '../../context_menu';
-export const EuiTableSortMobileItem = ({
+interface Props extends CommonProps {
+ /**
+ * Callback to know when an item has been clicked
+ */
+ onSort?: () => void;
+ /**
+ * Indicates current option is the sorted on column
+ */
+ isSorted?: boolean;
+ /**
+ * Indicates which direction the current column is sorted on
+ */
+ isSortAscending?: boolean;
+
+ ariaLabel?: string;
+}
+
+export const EuiTableSortMobileItem: FunctionComponent = ({
children,
onSort,
isSorted,
@@ -38,20 +55,3 @@ export const EuiTableSortMobileItem = ({
);
};
-
-EuiTableSortMobileItem.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- /**
- * Callback to know when an item has been clicked
- */
- onSort: PropTypes.func,
- /**
- * Indicates current option is the sorted on column
- */
- isSorted: PropTypes.bool,
- /**
- * Indicates which direction the current column is sorted on
- */
- isSortAscending: PropTypes.bool,
-};
diff --git a/src/components/table/table.test.js b/src/components/table/table.test.tsx
similarity index 100%
rename from src/components/table/table.test.js
rename to src/components/table/table.test.tsx
diff --git a/src/components/table/table.js b/src/components/table/table.tsx
similarity index 51%
rename from src/components/table/table.js
rename to src/components/table/table.tsx
index a9a4a423450..0928acde662 100644
--- a/src/components/table/table.js
+++ b/src/components/table/table.tsx
@@ -1,12 +1,18 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent, TableHTMLAttributes } from 'react';
import classNames from 'classnames';
+import { CommonProps } from '../common';
-export const EuiTable = ({
+export type Props = {
+ compressed?: boolean;
+ responsive?: boolean;
+} & CommonProps &
+ TableHTMLAttributes;
+
+export const EuiTable: FunctionComponent = ({
children,
className,
compressed,
- responsive,
+ responsive = true,
...rest
}) => {
const classes = classNames('euiTable', className, {
@@ -20,14 +26,3 @@ export const EuiTable = ({
);
};
-
-EuiTable.propTypes = {
- compressed: PropTypes.bool,
- children: PropTypes.node,
- className: PropTypes.string,
- responsive: PropTypes.bool,
-};
-
-EuiTable.defaultProps = {
- responsive: true,
-};
diff --git a/src/components/table/table_body.js b/src/components/table/table_body.js
deleted file mode 100644
index 698a610409f..00000000000
--- a/src/components/table/table_body.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export const EuiTableBody = ({ children, className, bodyRef, ...rest }) => {
- return (
-
- {children}
-
- );
-};
-
-EuiTableBody.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
-};
diff --git a/src/components/table/table_body.tsx b/src/components/table/table_body.tsx
new file mode 100644
index 00000000000..a64ec92fc35
--- /dev/null
+++ b/src/components/table/table_body.tsx
@@ -0,0 +1,19 @@
+import React, { FunctionComponent, Ref } from 'react';
+import { CommonProps } from '../common';
+
+type Props = CommonProps & {
+ bodyRef?: Ref;
+};
+
+export const EuiTableBody: FunctionComponent = ({
+ children,
+ className,
+ bodyRef,
+ ...rest
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/table/table_footer.js b/src/components/table/table_footer.js
deleted file mode 100644
index ee2d389bf94..00000000000
--- a/src/components/table/table_footer.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export const EuiTableFooter = ({ children, className, ...rest }) => {
- return (
-
- {children}
-
- );
-};
-
-EuiTableFooter.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
-};
diff --git a/src/components/table/table_footer.test.js b/src/components/table/table_footer.test.tsx
similarity index 100%
rename from src/components/table/table_footer.test.js
rename to src/components/table/table_footer.test.tsx
diff --git a/src/components/table/table_footer.tsx b/src/components/table/table_footer.tsx
new file mode 100644
index 00000000000..d4d89c60f90
--- /dev/null
+++ b/src/components/table/table_footer.tsx
@@ -0,0 +1,14 @@
+import React, { FunctionComponent } from 'react';
+import { CommonProps } from '../common';
+
+export const EuiTableFooter: FunctionComponent = ({
+ children,
+ className,
+ ...rest
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/table/table_footer_cell.test.js b/src/components/table/table_footer_cell.test.tsx
similarity index 100%
rename from src/components/table/table_footer_cell.test.js
rename to src/components/table/table_footer_cell.test.tsx
diff --git a/src/components/table/table_footer_cell.js b/src/components/table/table_footer_cell.tsx
similarity index 62%
rename from src/components/table/table_footer_cell.js
rename to src/components/table/table_footer_cell.tsx
index f0f1c5797fa..3917efb8243 100644
--- a/src/components/table/table_footer_cell.js
+++ b/src/components/table/table_footer_cell.tsx
@@ -1,18 +1,22 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { FunctionComponent, TdHTMLAttributes } from 'react';
+import { CommonProps } from '../common';
import classNames from 'classnames';
import {
+ HorizontalAlignment,
LEFT_ALIGNMENT,
RIGHT_ALIGNMENT,
CENTER_ALIGNMENT,
} from '../../services';
-const ALIGNMENT = [LEFT_ALIGNMENT, RIGHT_ALIGNMENT, CENTER_ALIGNMENT];
+type Props = CommonProps &
+ TdHTMLAttributes & {
+ align?: HorizontalAlignment;
+ };
-export const EuiTableFooterCell = ({
+export const EuiTableFooterCell: FunctionComponent = ({
children,
- align,
+ align = LEFT_ALIGNMENT,
colSpan,
className,
...rest
@@ -31,14 +35,3 @@ export const EuiTableFooterCell = ({
);
};
-
-EuiTableFooterCell.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- align: PropTypes.oneOf(ALIGNMENT),
- colSpan: PropTypes.number,
-};
-
-EuiTableFooterCell.defaultProps = {
- align: LEFT_ALIGNMENT,
-};
diff --git a/src/components/table/table_header.js b/src/components/table/table_header.js
deleted file mode 100644
index 4a97822ad5f..00000000000
--- a/src/components/table/table_header.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-export const EuiTableHeader = ({ children, className, ...rest }) => {
- return (
-
- {children}
-
- );
-};
-
-EuiTableHeader.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
-};
diff --git a/src/components/table/table_header.test.js b/src/components/table/table_header.test.tsx
similarity index 100%
rename from src/components/table/table_header.test.js
rename to src/components/table/table_header.test.tsx
diff --git a/src/components/table/table_header.tsx b/src/components/table/table_header.tsx
new file mode 100644
index 00000000000..6046d7f5c5f
--- /dev/null
+++ b/src/components/table/table_header.tsx
@@ -0,0 +1,14 @@
+import React, { FunctionComponent } from 'react';
+import { CommonProps } from '../common';
+
+export const EuiTableHeader: FunctionComponent = ({
+ children,
+ className,
+ ...rest
+}) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/table/table_header_button.test.js b/src/components/table/table_header_button.test.tsx
similarity index 100%
rename from src/components/table/table_header_button.test.js
rename to src/components/table/table_header_button.test.tsx
diff --git a/src/components/table/table_header_button.js b/src/components/table/table_header_button.tsx
similarity index 62%
rename from src/components/table/table_header_button.js
rename to src/components/table/table_header_button.tsx
index d57067cd31c..bf305e6d943 100644
--- a/src/components/table/table_header_button.js
+++ b/src/components/table/table_header_button.tsx
@@ -1,10 +1,15 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, { ButtonHTMLAttributes, FunctionComponent } from 'react';
import classNames from 'classnames';
+import { CommonProps } from '../common';
-import { IconPropType, EuiIcon } from '../icon';
+import { IconType, EuiIcon } from '../icon';
-export const EuiTableHeaderButton = ({
+type Props = CommonProps &
+ ButtonHTMLAttributes & {
+ iconType?: IconType;
+ };
+
+export const EuiTableHeaderButton: FunctionComponent = ({
children,
className,
iconType,
@@ -33,9 +38,3 @@ export const EuiTableHeaderButton = ({
);
};
-
-EuiTableHeaderButton.propTypes = {
- children: PropTypes.node,
- className: PropTypes.string,
- iconType: IconPropType,
-};
diff --git a/src/components/table/table_header_cell.test.js b/src/components/table/table_header_cell.test.tsx
similarity index 100%
rename from src/components/table/table_header_cell.test.js
rename to src/components/table/table_header_cell.test.tsx
diff --git a/src/components/table/table_header_cell.js b/src/components/table/table_header_cell.tsx
similarity index 57%
rename from src/components/table/table_header_cell.js
rename to src/components/table/table_header_cell.tsx
index dfb1866dff5..e743907d028 100644
--- a/src/components/table/table_header_cell.js
+++ b/src/components/table/table_header_cell.tsx
@@ -1,29 +1,75 @@
-import React from 'react';
-import PropTypes from 'prop-types';
+import React, {
+ FunctionComponent,
+ HTMLAttributes,
+ ThHTMLAttributes,
+} from 'react';
import classNames from 'classnames';
import { EuiScreenReaderOnly } from '../accessibility';
-
+import { CommonProps, NoArgCallback } from '../common';
import { EuiIcon } from '../icon';
import {
+ HorizontalAlignment,
LEFT_ALIGNMENT,
RIGHT_ALIGNMENT,
CENTER_ALIGNMENT,
} from '../../services';
-const ALIGNMENT = [LEFT_ALIGNMENT, RIGHT_ALIGNMENT, CENTER_ALIGNMENT];
+export type TableHeaderCellScope = 'col' | 'row' | 'colgroup' | 'rowgroup';
-export const EuiTableHeaderCell = ({
+type Props = CommonProps &
+ ThHTMLAttributes & {
+ align?: HorizontalAlignment;
+ /**
+ * Set `allowNeutralSort` on EuiInMemoryTable to false to force column
+ * sorting. EuiBasicTable always forces column sorting.
+ */
+ allowNeutralSort?: boolean;
+ /**
+ * _DEPRECATED: use `mobileOptions.show = false`_ Indicates if the
+ * column should not show for mobile users (typically hidden because a
+ * custom mobile header utilizes the column's contents)
+ */
+ hideForMobile?: boolean;
+ /**
+ * _DEPRECATED: use `mobileOptions.only = true`_ Indicates if the
+ * column was created to be the row's heading in mobile view (this
+ * column will be hidden at larger screens)
+ */
+ isMobileHeader?: boolean;
+ isSortAscending?: boolean;
+ isSorted?: boolean;
+ /**
+ * Mobile options for displaying differently at small screens
+ */
+ mobileOptions?: {
+ /**
+ * If false, will not render the column at all for mobile
+ */
+ show?: boolean;
+ /**
+ * Only show for mobile? If true, will not render the column at all
+ * for desktop
+ */
+ only?: boolean;
+ };
+ onSort?: NoArgCallback;
+ scope?: TableHeaderCellScope;
+ };
+
+export const EuiTableHeaderCell: FunctionComponent = ({
children,
- align,
+ align = LEFT_ALIGNMENT,
onSort,
isSorted,
isSortAscending,
allowNeutralSort,
className,
- scope,
- mobileOptions,
+ scope = 'col',
+ mobileOptions = {
+ show: true,
+ },
// Soon to be deprecated for {...mobileOptions}
isMobileHeader,
hideForMobile,
@@ -44,7 +90,7 @@ export const EuiTableHeaderCell = ({
'euiTableHeaderButton-isSorted': isSorted,
});
- let ariaSortValue = 'none';
+ let ariaSortValue: HTMLAttributes