@instructure/ui-table 9.7.0 → 9.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,28 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [9.7.2](https://github.com/instructure/instructure-ui/compare/v9.7.0...v9.7.2) (2024-10-10)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ui-table:** non standrd Row children props ([700e462](https://github.com/instructure/instructure-ui/commit/700e462cc8de0cfb68f0ed8a442dcd23961e4b77))
12
+
13
+
14
+
15
+
16
+
17
+ ## [9.7.1](https://github.com/instructure/instructure-ui/compare/v9.7.0...v9.7.1) (2024-10-10)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **ui-table:** non standrd Row children props ([700e462](https://github.com/instructure/instructure-ui/commit/700e462cc8de0cfb68f0ed8a442dcd23961e4b77))
23
+
24
+
25
+
26
+
27
+
6
28
  # [9.7.0](https://github.com/instructure/instructure-ui/compare/v9.6.0...v9.7.0) (2024-09-23)
7
29
 
8
30
  **Note:** Version bump only for package @instructure/ui-table
@@ -24,7 +24,7 @@ var _dec, _class, _Body;
24
24
  */
25
25
 
26
26
  /** @jsx jsx */
27
- import { Component, Children } from 'react';
27
+ import { Component, Children, isValidElement } from 'react';
28
28
  import { safeCloneElement, omitProps } from '@instructure/ui-react-utils';
29
29
  import { View } from '@instructure/ui-view';
30
30
  import { withStyle, jsx } from '@instructure/emotion';
@@ -58,12 +58,17 @@ let Body = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_class
58
58
  as: isStacked ? 'div' : 'tbody',
59
59
  css: styles === null || styles === void 0 ? void 0 : styles.body,
60
60
  role: isStacked ? 'rowgroup' : void 0
61
- }), Children.map(children, child => safeCloneElement(child, {
62
- key: child.props.name,
63
- hover,
64
- isStacked,
65
- headers
66
- })));
61
+ }), Children.map(children, child => {
62
+ if ( /*#__PURE__*/isValidElement(child)) {
63
+ return safeCloneElement(child, {
64
+ key: child.props.name,
65
+ isStacked,
66
+ hover,
67
+ headers
68
+ });
69
+ }
70
+ return child;
71
+ }));
67
72
  }
68
73
  }, _Body.displayName = "Body", _Body.componentId = 'Table.Body', _Body.allowedProps = allowedProps, _Body.propTypes = propTypes, _Body.defaultProps = {
69
74
  children: null
@@ -25,13 +25,14 @@ var _dec, _class, _Row;
25
25
 
26
26
  /** @jsx jsx */
27
27
  import { Component, Children } from 'react';
28
- import { omitProps, safeCloneElement } from '@instructure/ui-react-utils';
28
+ import { omitProps, safeCloneElement, matchComponentTypes } from '@instructure/ui-react-utils';
29
29
  import { View } from '@instructure/ui-view';
30
30
  import { withStyle, jsx } from '@instructure/emotion';
31
31
  import generateStyle from './styles';
32
32
  import generateComponentTheme from './theme';
33
+ import { Cell } from '../Cell';
34
+ import { RowHeader } from '../RowHeader';
33
35
  import { allowedProps, propTypes } from './props';
34
-
35
36
  /**
36
37
  ---
37
38
  parent: Table
@@ -58,11 +59,20 @@ let Row = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_class
58
59
  css: styles === null || styles === void 0 ? void 0 : styles.row,
59
60
  role: isStacked ? 'row' : void 0
60
61
  }), Children.toArray(children).filter(Boolean).map((child, index) => {
61
- return safeCloneElement(child, {
62
- key: child.props.name,
63
- isStacked,
64
- header: headers && headers[index]
65
- });
62
+ if (matchComponentTypes(child, [RowHeader])) {
63
+ return safeCloneElement(child, {
64
+ key: index,
65
+ isStacked
66
+ });
67
+ }
68
+ if (matchComponentTypes(child, [Cell])) {
69
+ return safeCloneElement(child, {
70
+ key: index,
71
+ isStacked,
72
+ header: headers && headers[index]
73
+ });
74
+ }
75
+ return child;
66
76
  }));
67
77
  }
68
78
  }, _Row.displayName = "Row", _Row.componentId = 'Table.Row', _Row.allowedProps = allowedProps, _Row.propTypes = propTypes, _Row.defaultProps = {
@@ -1,4 +1,4 @@
1
- var _Table$Head, _Table$Body, _Table, _Table2, _Table3, _Table$Body2;
1
+ var _Table$Head, _Table$Body, _Table, _Table2, _Table3, _span, _Table$Row, _span2, _span3, _Table$Row2, _Table$Body2;
2
2
  /*
3
3
  * The MIT License (MIT)
4
4
  *
@@ -127,6 +127,15 @@ describe('<Table />', async () => {
127
127
  expect(stackedTable).toBeInTheDocument();
128
128
  expect(stackedTable).not.toHaveTextContent('Foo');
129
129
  });
130
+ it('does not crash for invalid children', async () => {
131
+ render( /*#__PURE__*/React.createElement(Table, {
132
+ caption: "Test table",
133
+ layout: "stacked"
134
+ }, /*#__PURE__*/React.createElement(Table.Head, null, _span || (_span = /*#__PURE__*/React.createElement("span", null, "test")), "test2", _Table$Row || (_Table$Row = /*#__PURE__*/React.createElement(Table.Row, null, "test3", /*#__PURE__*/React.createElement("span", null, "test"), /*#__PURE__*/React.createElement(Table.Cell, null, "Foo"))), "test4", _span2 || (_span2 = /*#__PURE__*/React.createElement("span", null, "test"))), "test5", /*#__PURE__*/React.createElement(Table.Body, null, "test", _span3 || (_span3 = /*#__PURE__*/React.createElement("span", null, "test")), _Table$Row2 || (_Table$Row2 = /*#__PURE__*/React.createElement(Table.Row, null, "test", /*#__PURE__*/React.createElement("span", null, "test"), /*#__PURE__*/React.createElement(Table.Cell, null, "Foo"), "test", /*#__PURE__*/React.createElement("span", null, "test"))))));
135
+ const table = screen.getByRole('table');
136
+ expect(table).toBeInTheDocument();
137
+ expect(table).toHaveTextContent('Foo');
138
+ });
130
139
  describe('when table is sortable', async () => {
131
140
  const renderSortableTable = (props, handlers = {}, layout = 'auto') => render( /*#__PURE__*/React.createElement(Table, {
132
141
  caption: "Sortable table",
package/es/Table/index.js CHANGED
@@ -25,7 +25,7 @@ var _dec, _class, _Table;
25
25
  */
26
26
 
27
27
  /** @jsx jsx */
28
- import { Component, Children } from 'react';
28
+ import { Component, Children, isValidElement } from 'react';
29
29
  import { safeCloneElement, omitProps } from '@instructure/ui-react-utils';
30
30
  import { View } from '@instructure/ui-view';
31
31
  import { ScreenReaderContent } from '@instructure/ui-a11y-content';
@@ -66,15 +66,16 @@ let Table = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_clas
66
66
  (_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2);
67
67
  }
68
68
  getHeaders() {
69
- const children = this.props.children;
70
- const _ref = Children.toArray(children),
71
- _ref2 = _slicedToArray(_ref, 1),
72
- head = _ref2[0];
73
- const _Children$toArray = Children.toArray(head.props.children),
69
+ const _Children$toArray = Children.toArray(this.props.children),
74
70
  _Children$toArray2 = _slicedToArray(_Children$toArray, 1),
75
- row = _Children$toArray2[0];
76
- if (!row) return void 0;
77
- return Children.map(row.props.children, colHeader => {
71
+ headChild = _Children$toArray2[0];
72
+ if (!headChild || ! /*#__PURE__*/isValidElement(headChild)) return void 0;
73
+ const _Children$toArray3 = Children.toArray(headChild.props.children),
74
+ _Children$toArray4 = _slicedToArray(_Children$toArray3, 1),
75
+ firstRow = _Children$toArray4[0];
76
+ if (!firstRow || ! /*#__PURE__*/isValidElement(firstRow)) return void 0;
77
+ return Children.map(firstRow.props.children, colHeader => {
78
+ if (! /*#__PURE__*/isValidElement(colHeader)) return void 0;
78
79
  return colHeader.props.children;
79
80
  });
80
81
  }
@@ -96,12 +97,15 @@ let Table = (_dec = withStyle(generateStyle, generateComponentTheme), _dec(_clas
96
97
  role: isStacked ? 'table' : void 0,
97
98
  "aria-label": isStacked ? caption : void 0
98
99
  }), !isStacked && jsx("caption", null, jsx(ScreenReaderContent, null, caption)), Children.map(children, child => {
99
- return safeCloneElement(child, {
100
- key: child.props.name,
101
- isStacked,
102
- hover,
103
- headers
104
- });
100
+ if ( /*#__PURE__*/isValidElement(child)) {
101
+ return safeCloneElement(child, {
102
+ key: child.props.name,
103
+ isStacked,
104
+ hover,
105
+ headers
106
+ });
107
+ }
108
+ return child;
105
109
  }));
106
110
  }
107
111
  }, _Table.displayName = "Table", _Table.componentId = 'Table', _Table.allowedProps = allowedProps, _Table.propTypes = propTypes, _Table.defaultProps = {
@@ -64,12 +64,17 @@ let Body = exports.Body = (_dec = (0, _emotion.withStyle)(_styles.default, _them
64
64
  as: isStacked ? 'div' : 'tbody',
65
65
  css: styles === null || styles === void 0 ? void 0 : styles.body,
66
66
  role: isStacked ? 'rowgroup' : void 0
67
- }), _react.Children.map(children, child => (0, _safeCloneElement.safeCloneElement)(child, {
68
- key: child.props.name,
69
- hover,
70
- isStacked,
71
- headers
72
- })));
67
+ }), _react.Children.map(children, child => {
68
+ if ( /*#__PURE__*/(0, _react.isValidElement)(child)) {
69
+ return (0, _safeCloneElement.safeCloneElement)(child, {
70
+ key: child.props.name,
71
+ isStacked,
72
+ hover,
73
+ headers
74
+ });
75
+ }
76
+ return child;
77
+ }));
73
78
  }
74
79
  }, _Body.displayName = "Body", _Body.componentId = 'Table.Body', _Body.allowedProps = _props.allowedProps, _Body.propTypes = _props.propTypes, _Body.defaultProps = {
75
80
  children: null
@@ -8,10 +8,13 @@ exports.default = exports.Row = void 0;
8
8
  var _react = require("react");
9
9
  var _omitProps = require("@instructure/ui-react-utils/lib/omitProps.js");
10
10
  var _safeCloneElement = require("@instructure/ui-react-utils/lib/safeCloneElement.js");
11
+ var _matchComponentTypes = require("@instructure/ui-react-utils/lib/matchComponentTypes.js");
11
12
  var _View = require("@instructure/ui-view/lib/View");
12
13
  var _emotion = require("@instructure/emotion");
13
14
  var _styles = _interopRequireDefault(require("./styles"));
14
15
  var _theme = _interopRequireDefault(require("./theme"));
16
+ var _Cell = require("../Cell");
17
+ var _RowHeader = require("../RowHeader");
15
18
  var _props = require("./props");
16
19
  var _dec, _class, _Row;
17
20
  /*
@@ -64,11 +67,20 @@ let Row = exports.Row = (_dec = (0, _emotion.withStyle)(_styles.default, _theme.
64
67
  css: styles === null || styles === void 0 ? void 0 : styles.row,
65
68
  role: isStacked ? 'row' : void 0
66
69
  }), _react.Children.toArray(children).filter(Boolean).map((child, index) => {
67
- return (0, _safeCloneElement.safeCloneElement)(child, {
68
- key: child.props.name,
69
- isStacked,
70
- header: headers && headers[index]
71
- });
70
+ if ((0, _matchComponentTypes.matchComponentTypes)(child, [_RowHeader.RowHeader])) {
71
+ return (0, _safeCloneElement.safeCloneElement)(child, {
72
+ key: index,
73
+ isStacked
74
+ });
75
+ }
76
+ if ((0, _matchComponentTypes.matchComponentTypes)(child, [_Cell.Cell])) {
77
+ return (0, _safeCloneElement.safeCloneElement)(child, {
78
+ key: index,
79
+ isStacked,
80
+ header: headers && headers[index]
81
+ });
82
+ }
83
+ return child;
72
84
  }));
73
85
  }
74
86
  }, _Row.displayName = "Row", _Row.componentId = 'Table.Row', _Row.allowedProps = _props.allowedProps, _Row.propTypes = _props.propTypes, _Row.defaultProps = {
@@ -8,7 +8,7 @@ var _userEvent = require("@testing-library/user-event");
8
8
  require("@testing-library/jest-dom");
9
9
  var _index = require("../index");
10
10
  var _runAxeCheck = require("@instructure/ui-axe-check/lib/runAxeCheck.js");
11
- var _Table$Head, _Table$Body, _Table, _Table2, _Table3, _Table$Body2;
11
+ var _Table$Head, _Table$Body, _Table, _Table2, _Table3, _span, _Table$Row, _span2, _span3, _Table$Row2, _Table$Body2;
12
12
  /*
13
13
  * The MIT License (MIT)
14
14
  *
@@ -129,6 +129,15 @@ describe('<Table />', async () => {
129
129
  expect(stackedTable).toBeInTheDocument();
130
130
  expect(stackedTable).not.toHaveTextContent('Foo');
131
131
  });
132
+ it('does not crash for invalid children', async () => {
133
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.Table, {
134
+ caption: "Test table",
135
+ layout: "stacked"
136
+ }, /*#__PURE__*/_react.default.createElement(_index.Table.Head, null, _span || (_span = /*#__PURE__*/_react.default.createElement("span", null, "test")), "test2", _Table$Row || (_Table$Row = /*#__PURE__*/_react.default.createElement(_index.Table.Row, null, "test3", /*#__PURE__*/_react.default.createElement("span", null, "test"), /*#__PURE__*/_react.default.createElement(_index.Table.Cell, null, "Foo"))), "test4", _span2 || (_span2 = /*#__PURE__*/_react.default.createElement("span", null, "test"))), "test5", /*#__PURE__*/_react.default.createElement(_index.Table.Body, null, "test", _span3 || (_span3 = /*#__PURE__*/_react.default.createElement("span", null, "test")), _Table$Row2 || (_Table$Row2 = /*#__PURE__*/_react.default.createElement(_index.Table.Row, null, "test", /*#__PURE__*/_react.default.createElement("span", null, "test"), /*#__PURE__*/_react.default.createElement(_index.Table.Cell, null, "Foo"), "test", /*#__PURE__*/_react.default.createElement("span", null, "test"))))));
137
+ const table = _react2.screen.getByRole('table');
138
+ expect(table).toBeInTheDocument();
139
+ expect(table).toHaveTextContent('Foo');
140
+ });
132
141
  describe('when table is sortable', async () => {
133
142
  const renderSortableTable = (props, handlers = {}, layout = 'auto') => (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.Table, {
134
143
  caption: "Sortable table",
@@ -72,15 +72,16 @@ let Table = exports.Table = (_dec = (0, _emotion.withStyle)(_styles.default, _th
72
72
  (_this$props$makeStyle2 = (_this$props2 = this.props).makeStyles) === null || _this$props$makeStyle2 === void 0 ? void 0 : _this$props$makeStyle2.call(_this$props2);
73
73
  }
74
74
  getHeaders() {
75
- const children = this.props.children;
76
- const _ref = _react.Children.toArray(children),
77
- _ref2 = (0, _slicedToArray2.default)(_ref, 1),
78
- head = _ref2[0];
79
- const _Children$toArray = _react.Children.toArray(head.props.children),
75
+ const _Children$toArray = _react.Children.toArray(this.props.children),
80
76
  _Children$toArray2 = (0, _slicedToArray2.default)(_Children$toArray, 1),
81
- row = _Children$toArray2[0];
82
- if (!row) return void 0;
83
- return _react.Children.map(row.props.children, colHeader => {
77
+ headChild = _Children$toArray2[0];
78
+ if (!headChild || ! /*#__PURE__*/(0, _react.isValidElement)(headChild)) return void 0;
79
+ const _Children$toArray3 = _react.Children.toArray(headChild.props.children),
80
+ _Children$toArray4 = (0, _slicedToArray2.default)(_Children$toArray3, 1),
81
+ firstRow = _Children$toArray4[0];
82
+ if (!firstRow || ! /*#__PURE__*/(0, _react.isValidElement)(firstRow)) return void 0;
83
+ return _react.Children.map(firstRow.props.children, colHeader => {
84
+ if (! /*#__PURE__*/(0, _react.isValidElement)(colHeader)) return void 0;
84
85
  return colHeader.props.children;
85
86
  });
86
87
  }
@@ -102,12 +103,15 @@ let Table = exports.Table = (_dec = (0, _emotion.withStyle)(_styles.default, _th
102
103
  role: isStacked ? 'table' : void 0,
103
104
  "aria-label": isStacked ? caption : void 0
104
105
  }), !isStacked && (0, _emotion.jsx)("caption", null, (0, _emotion.jsx)(_ScreenReaderContent.ScreenReaderContent, null, caption)), _react.Children.map(children, child => {
105
- return (0, _safeCloneElement.safeCloneElement)(child, {
106
- key: child.props.name,
107
- isStacked,
108
- hover,
109
- headers
110
- });
106
+ if ( /*#__PURE__*/(0, _react.isValidElement)(child)) {
107
+ return (0, _safeCloneElement.safeCloneElement)(child, {
108
+ key: child.props.name,
109
+ isStacked,
110
+ hover,
111
+ headers
112
+ });
113
+ }
114
+ return child;
111
115
  }));
112
116
  }
113
117
  }, _Table.displayName = "Table", _Table.componentId = 'Table', _Table.allowedProps = _props.allowedProps, _Table.propTypes = _props.propTypes, _Table.defaultProps = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instructure/ui-table",
3
- "version": "9.7.0",
3
+ "version": "9.7.2",
4
4
  "description": "A styled HTML table component",
5
5
  "author": "Instructure, Inc. Engineering and Product Design",
6
6
  "module": "./es/index.js",
@@ -23,11 +23,11 @@
23
23
  },
24
24
  "license": "MIT",
25
25
  "devDependencies": {
26
- "@instructure/ui-axe-check": "9.7.0",
27
- "@instructure/ui-babel-preset": "9.7.0",
28
- "@instructure/ui-color-utils": "9.7.0",
29
- "@instructure/ui-test-utils": "9.7.0",
30
- "@instructure/ui-themes": "9.7.0",
26
+ "@instructure/ui-axe-check": "9.7.2",
27
+ "@instructure/ui-babel-preset": "9.7.2",
28
+ "@instructure/ui-color-utils": "9.7.2",
29
+ "@instructure/ui-test-utils": "9.7.2",
30
+ "@instructure/ui-themes": "9.7.2",
31
31
  "@testing-library/jest-dom": "^6.4.6",
32
32
  "@testing-library/react": "^15.0.7",
33
33
  "@testing-library/user-event": "^14.5.2",
@@ -35,17 +35,17 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@babel/runtime": "^7.24.5",
38
- "@instructure/console": "9.7.0",
39
- "@instructure/emotion": "9.7.0",
40
- "@instructure/shared-types": "9.7.0",
41
- "@instructure/ui-a11y-content": "9.7.0",
42
- "@instructure/ui-icons": "9.7.0",
43
- "@instructure/ui-prop-types": "9.7.0",
44
- "@instructure/ui-react-utils": "9.7.0",
45
- "@instructure/ui-simple-select": "9.7.0",
46
- "@instructure/ui-testable": "9.7.0",
47
- "@instructure/ui-utils": "9.7.0",
48
- "@instructure/ui-view": "9.7.0",
38
+ "@instructure/console": "9.7.2",
39
+ "@instructure/emotion": "9.7.2",
40
+ "@instructure/shared-types": "9.7.2",
41
+ "@instructure/ui-a11y-content": "9.7.2",
42
+ "@instructure/ui-icons": "9.7.2",
43
+ "@instructure/ui-prop-types": "9.7.2",
44
+ "@instructure/ui-react-utils": "9.7.2",
45
+ "@instructure/ui-simple-select": "9.7.2",
46
+ "@instructure/ui-testable": "9.7.2",
47
+ "@instructure/ui-utils": "9.7.2",
48
+ "@instructure/ui-view": "9.7.2",
49
49
  "prop-types": "^15.8.1"
50
50
  },
51
51
  "peerDependencies": {
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  /** @jsx jsx */
26
- import { Component, Children } from 'react'
26
+ import { Component, Children, isValidElement } from 'react'
27
27
 
28
28
  import { safeCloneElement, omitProps } from '@instructure/ui-react-utils'
29
29
  import { View } from '@instructure/ui-view'
@@ -70,14 +70,17 @@ class Body extends Component<TableBodyProps> {
70
70
  css={styles?.body}
71
71
  role={isStacked ? 'rowgroup' : undefined}
72
72
  >
73
- {Children.map(children as RowChild[], (child) =>
74
- safeCloneElement(child, {
75
- key: child.props.name,
76
- hover,
77
- isStacked,
78
- headers
79
- })
80
- )}
73
+ {Children.map(children as RowChild[], (child) => {
74
+ if (isValidElement(child)) {
75
+ return safeCloneElement(child, {
76
+ key: child.props.name,
77
+ isStacked,
78
+ hover,
79
+ headers
80
+ })
81
+ }
82
+ return child
83
+ })}
81
84
  </View>
82
85
  )
83
86
  }
@@ -25,7 +25,11 @@
25
25
  /** @jsx jsx */
26
26
  import { Component, Children } from 'react'
27
27
 
28
- import { omitProps, safeCloneElement } from '@instructure/ui-react-utils'
28
+ import {
29
+ omitProps,
30
+ safeCloneElement,
31
+ matchComponentTypes
32
+ } from '@instructure/ui-react-utils'
29
33
  import { View } from '@instructure/ui-view'
30
34
 
31
35
  import { withStyle, jsx } from '@instructure/emotion'
@@ -33,8 +37,12 @@ import { withStyle, jsx } from '@instructure/emotion'
33
37
  import generateStyle from './styles'
34
38
  import generateComponentTheme from './theme'
35
39
 
40
+ import { Cell } from '../Cell'
41
+ import { RowHeader } from '../RowHeader'
42
+
36
43
  import type { TableRowProps } from './props'
37
44
  import { allowedProps, propTypes } from './props'
45
+ import type { RowHeaderChild, CellChild } from '../props'
38
46
 
39
47
  /**
40
48
  ---
@@ -73,12 +81,22 @@ class Row extends Component<TableRowProps> {
73
81
  >
74
82
  {Children.toArray(children)
75
83
  .filter(Boolean)
76
- .map((child: any, index) => {
77
- return safeCloneElement(child, {
78
- key: child.props.name,
79
- isStacked,
80
- header: headers && headers[index]
81
- })
84
+ .map((child, index) => {
85
+ if (matchComponentTypes<RowHeaderChild>(child, [RowHeader])) {
86
+ return safeCloneElement(child, {
87
+ key: index,
88
+ isStacked
89
+ })
90
+ }
91
+ if (matchComponentTypes<CellChild>(child, [Cell])) {
92
+ return safeCloneElement(child, {
93
+ key: index,
94
+ isStacked,
95
+ header: headers && headers[index]
96
+ })
97
+ }
98
+
99
+ return child
82
100
  })}
83
101
  </View>
84
102
  )
@@ -167,6 +167,42 @@ describe('<Table />', async () => {
167
167
  expect(stackedTable).not.toHaveTextContent('Foo')
168
168
  })
169
169
 
170
+ it('does not crash for invalid children', async () => {
171
+ render(
172
+ <Table caption="Test table" layout="stacked">
173
+ <Table.Head>
174
+ <span>test</span>
175
+ test2
176
+ {/* @ts-ignore error is normal here */}
177
+ <Table.Row>
178
+ test3
179
+ <span>test</span>
180
+ <Table.Cell>Foo</Table.Cell>
181
+ </Table.Row>
182
+ test4
183
+ <span>test</span>
184
+ </Table.Head>
185
+ test5
186
+ <Table.Body>
187
+ test
188
+ <span>test</span>
189
+ {/* @ts-ignore error is normal here */}
190
+ <Table.Row>
191
+ test
192
+ <span>test</span>
193
+ <Table.Cell>Foo</Table.Cell>
194
+ test
195
+ <span>test</span>
196
+ </Table.Row>
197
+ </Table.Body>
198
+ </Table>
199
+ )
200
+ const table = screen.getByRole('table')
201
+
202
+ expect(table).toBeInTheDocument()
203
+ expect(table).toHaveTextContent('Foo')
204
+ })
205
+
170
206
  describe('when table is sortable', async () => {
171
207
  const renderSortableTable = (
172
208
  props: TableColHeaderProps | null,
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  /** @jsx jsx */
26
- import { Component, Children } from 'react'
26
+ import { Component, Children, isValidElement } from 'react'
27
27
 
28
28
  import { safeCloneElement, omitProps } from '@instructure/ui-react-utils'
29
29
  import { View } from '@instructure/ui-view'
@@ -41,13 +41,7 @@ import { ColHeader } from './ColHeader'
41
41
  import { RowHeader } from './RowHeader'
42
42
  import { Cell } from './Cell'
43
43
 
44
- import type {
45
- TableProps,
46
- HeadChild,
47
- ColHeaderChild,
48
- RowHeaderChild,
49
- CellChild
50
- } from './props'
44
+ import type { TableProps } from './props'
51
45
 
52
46
  import { allowedProps, propTypes } from './props'
53
47
 
@@ -97,17 +91,14 @@ class Table extends Component<TableProps> {
97
91
  }
98
92
 
99
93
  getHeaders() {
100
- const { children } = this.props
101
- const [head] = Children.toArray(children) as HeadChild[]
102
- const [row]: any = Children.toArray(head.props.children)
103
- if (!row) return undefined
104
-
105
- return Children.map(
106
- row.props.children as (ColHeaderChild | RowHeaderChild | CellChild)[],
107
- (colHeader) => {
108
- return colHeader.props.children
109
- }
110
- )
94
+ const [headChild] = Children.toArray(this.props.children)
95
+ if (!headChild || !isValidElement(headChild)) return undefined
96
+ const [firstRow] = Children.toArray(headChild.props.children)
97
+ if (!firstRow || !isValidElement(firstRow)) return undefined
98
+ return Children.map(firstRow.props.children, (colHeader) => {
99
+ if (!isValidElement<{ children?: any }>(colHeader)) return undefined
100
+ return colHeader.props.children
101
+ })
111
102
  }
112
103
 
113
104
  render() {
@@ -133,13 +124,16 @@ class Table extends Component<TableProps> {
133
124
  <ScreenReaderContent>{caption}</ScreenReaderContent>
134
125
  </caption>
135
126
  )}
136
- {Children.map(children, (child: any) => {
137
- return safeCloneElement(child, {
138
- key: child.props.name,
139
- isStacked,
140
- hover,
141
- headers
142
- })
127
+ {Children.map(children, (child) => {
128
+ if (isValidElement(child)) {
129
+ return safeCloneElement(child, {
130
+ key: child.props.name,
131
+ isStacked,
132
+ hover,
133
+ headers
134
+ })
135
+ }
136
+ return child
143
137
  })}
144
138
  </View>
145
139
  )