@urbanos/react-discovery-ui 2.1.44 → 2.1.46
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/README.md +8 -2
- package/lib/components/data-view/data-view.js +211 -165
- package/lib/components/data-view/data-view.scss +22 -1
- package/lib/components/data-view/data-view.test.js +24 -30
- package/lib/components/dataset-dictionary/dataset-dictionary.js +132 -76
- package/lib/components/dataset-dictionary/dataset-dictionary.scss +4 -2
- package/lib/components/dataset-dictionary/dataset-dictionary.test.js +18 -27
- package/lib/components/dataset-metadata/dataset-metadata.js +180 -146
- package/lib/components/dataset-metadata/dataset-metadata.test.js +23 -12
- package/lib/components/dataset-preview/dataset-preview.js +0 -1
- package/lib/pages/query-view/query-view.test.js +0 -1
- package/lib/pages/user-profile-view/user-profile-view.js +106 -27
- package/lib/pages/user-profile-view/user-profile-view.test.js +4 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
React (also known as React.js or ReactJS) is an open-source, front end, JavaScript library for building user interfaces or UI components.
|
|
7
7
|
|
|
8
|
-
[react_discovery_ui](https://www.npmjs.com/package/@
|
|
8
|
+
[react_discovery_ui](https://www.npmjs.com/package/@urbanos/react-discovery-ui) is a UI component for consuming the [Discovery API](https://github.com/Datastillery/smartcitiesdata/tree/master/apps/discovery_api) of the SmartCitiesData platform. It is exported as a React Component, `<ReactDiscoveryUI>`, and can be imported into another site such as seen in [SmartColumbusOS/Discovery_UI](https://github.com/SmartColumbusOS/discovery_ui).
|
|
9
9
|
|
|
10
10
|
## What to Install
|
|
11
11
|
|
|
@@ -99,7 +99,7 @@ with `npm run build:library`
|
|
|
99
99
|
To consume this component in your react app place the <ReactDiscoveryUI> component into your app or other sub-components such as in the below example:
|
|
100
100
|
|
|
101
101
|
```
|
|
102
|
-
import ReactDiscoveryUI from '@
|
|
102
|
+
import ReactDiscoveryUI from '@urbanos/react-discovery-ui'
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
export default class App extends Component {
|
|
@@ -116,3 +116,9 @@ export default class App extends Component {
|
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
# Trigger a build
|
|
119
|
+
|
|
120
|
+
The primary way to promote react-discovery-ui code to a production environment is to produce an npmjs release (i.e. publish it to npmjs). Previously (before 2026)
|
|
121
|
+
this was triggered by creating a GitHub Release, which ran a GitHub Actions workflow. Currently in 2026 this is being done by publishing from the local environment
|
|
122
|
+
with npmjs credentials. See the publish target in the Makefile for details.
|
|
123
|
+
|
|
124
|
+
In the future we should re-enable the previous GitHub Actions flow by generating a new npm access token and storing it as the npm_token secret in the GitHub repository settings.
|
|
@@ -7,188 +7,234 @@ exports.default = void 0;
|
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _variables = _interopRequireDefault(require("../../styles/variables.scss"));
|
|
9
9
|
require("./data-view.scss");
|
|
10
|
-
var _reactTable =
|
|
11
|
-
require("react-table/react-table.css");
|
|
10
|
+
var _reactTable = require("@tanstack/react-table");
|
|
12
11
|
var _reactJsonView = _interopRequireDefault(require("react-json-view"));
|
|
13
12
|
var _loadingElement = _interopRequireDefault(require("../../components/generic-elements/loading-element"));
|
|
14
13
|
var _reactTabs = require("react-tabs");
|
|
15
14
|
var _lodash = _interopRequireDefault(require("lodash"));
|
|
16
15
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
16
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
18
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
19
17
|
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
|
|
20
18
|
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
21
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
22
19
|
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
|
|
23
20
|
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
|
|
24
|
-
function
|
|
21
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
22
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
23
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
25
24
|
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
26
|
-
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
|
|
27
|
-
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
|
|
28
|
-
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
|
|
29
25
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
30
26
|
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
31
|
-
function
|
|
32
|
-
function
|
|
33
|
-
function
|
|
34
|
-
function
|
|
35
|
-
function
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
var isGeojson = this.props.format == 'geojson';
|
|
54
|
-
var cleanData = isGeojson ? undefined : this.getCleanData(this.props.data);
|
|
55
|
-
var columns = this.props.columns.map(function (column) {
|
|
56
|
-
return {
|
|
57
|
-
Header: column,
|
|
58
|
-
id: column,
|
|
59
|
-
accessor: function accessor(row) {
|
|
60
|
-
return row[column];
|
|
61
|
-
},
|
|
62
|
-
headerClassName: 'table-header'
|
|
63
|
-
};
|
|
64
|
-
});
|
|
65
|
-
return /*#__PURE__*/_react.default.createElement("div", {
|
|
66
|
-
id: "data-view"
|
|
67
|
-
}, /*#__PURE__*/_react.default.createElement(_reactTabs.Tabs, {
|
|
68
|
-
selectedIndex: this.state.index,
|
|
69
|
-
onSelect: function onSelect(tabIndex) {
|
|
70
|
-
return _this2.setState({
|
|
71
|
-
index: tabIndex
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}, /*#__PURE__*/_react.default.createElement(_reactTabs.TabList, {
|
|
75
|
-
className: "header"
|
|
76
|
-
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
77
|
-
className: "tab-area"
|
|
78
|
-
}, !isGeojson && /*#__PURE__*/_react.default.createElement(_reactTabs.Tab, {
|
|
79
|
-
"data-testid": "data-table"
|
|
80
|
-
}, "Table"), /*#__PURE__*/_react.default.createElement(_reactTabs.Tab, {
|
|
81
|
-
"data-testid": "data-json"
|
|
82
|
-
}, "JSON"))), !isGeojson && /*#__PURE__*/_react.default.createElement(_reactTabs.TabPanel, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
83
|
-
id: "data-view-table"
|
|
84
|
-
}, /*#__PURE__*/_react.default.createElement(_reactTable.default, {
|
|
85
|
-
data: cleanData,
|
|
86
|
-
columns: columns,
|
|
87
|
-
loading: this.props.loading,
|
|
88
|
-
defaultPageSize: 50,
|
|
89
|
-
style: {
|
|
90
|
-
height: '400px'
|
|
91
|
-
},
|
|
92
|
-
className: "-striped -highlight",
|
|
93
|
-
page: this.props.page,
|
|
94
|
-
onPageChange: this.props.onNextPageClicked
|
|
95
|
-
}))), /*#__PURE__*/_react.default.createElement(_reactTabs.TabPanel, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
96
|
-
id: "data-view-raw"
|
|
97
|
-
}, this.renderJsonOrLoading(this.props.loading)))));
|
|
98
|
-
}
|
|
99
|
-
}, {
|
|
100
|
-
key: "cleanseData",
|
|
101
|
-
value: function cleanseData(data) {
|
|
102
|
-
var _this3 = this;
|
|
103
|
-
if (!data.map) {
|
|
104
|
-
return [];
|
|
105
|
-
}
|
|
106
|
-
return data.map(function (row) {
|
|
107
|
-
return _this3.cleanseRow(row);
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
}, {
|
|
111
|
-
key: "cleanseRow",
|
|
112
|
-
value: function cleanseRow(row) {
|
|
113
|
-
var _this4 = this;
|
|
114
|
-
var deconstructedObject = Object.entries(row);
|
|
115
|
-
var listOfKeyValues = deconstructedObject.map(function (field) {
|
|
116
|
-
return _defineProperty({}, field[0], _this4.cleanseField(field[1]));
|
|
27
|
+
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
28
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
29
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
30
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
31
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
32
|
+
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
|
|
33
|
+
var DataTable = function DataTable(_ref) {
|
|
34
|
+
var data = _ref.data,
|
|
35
|
+
columns = _ref.columns,
|
|
36
|
+
page = _ref.page,
|
|
37
|
+
onNextPageClicked = _ref.onNextPageClicked;
|
|
38
|
+
var _useState = (0, _react.useState)({
|
|
39
|
+
pageIndex: page || 0,
|
|
40
|
+
pageSize: 50
|
|
41
|
+
}),
|
|
42
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
43
|
+
pagination = _useState2[0],
|
|
44
|
+
setPagination = _useState2[1];
|
|
45
|
+
(0, _react.useEffect)(function () {
|
|
46
|
+
setPagination(function (prev) {
|
|
47
|
+
return _objectSpread(_objectSpread({}, prev), {}, {
|
|
48
|
+
pageIndex: page || 0
|
|
117
49
|
});
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return value;
|
|
50
|
+
});
|
|
51
|
+
}, [page]);
|
|
52
|
+
var table = (0, _reactTable.useReactTable)({
|
|
53
|
+
data: data || [],
|
|
54
|
+
columns: columns,
|
|
55
|
+
state: {
|
|
56
|
+
pagination: pagination
|
|
57
|
+
},
|
|
58
|
+
onPaginationChange: function onPaginationChange(updater) {
|
|
59
|
+
var newPagination = typeof updater === 'function' ? updater(pagination) : updater;
|
|
60
|
+
setPagination(newPagination);
|
|
61
|
+
if (onNextPageClicked) {
|
|
62
|
+
onNextPageClicked(newPagination.pageIndex);
|
|
132
63
|
}
|
|
64
|
+
},
|
|
65
|
+
getCoreRowModel: (0, _reactTable.getCoreRowModel)(),
|
|
66
|
+
getPaginationRowModel: (0, _reactTable.getPaginationRowModel)()
|
|
67
|
+
});
|
|
68
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
69
|
+
style: {
|
|
70
|
+
height: '400px',
|
|
71
|
+
overflowY: 'auto'
|
|
133
72
|
}
|
|
134
|
-
}, {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
73
|
+
}, /*#__PURE__*/_react.default.createElement("table", null, /*#__PURE__*/_react.default.createElement("thead", null, table.getHeaderGroups().map(function (headerGroup) {
|
|
74
|
+
return /*#__PURE__*/_react.default.createElement("tr", {
|
|
75
|
+
key: headerGroup.id
|
|
76
|
+
}, headerGroup.headers.map(function (header) {
|
|
77
|
+
var _header$column$column;
|
|
78
|
+
return /*#__PURE__*/_react.default.createElement("th", {
|
|
79
|
+
key: header.id,
|
|
80
|
+
scope: "col",
|
|
81
|
+
className: ((_header$column$column = header.column.columnDef.meta) === null || _header$column$column === void 0 ? void 0 : _header$column$column.headerClassName) || 'table-header'
|
|
82
|
+
}, header.isPlaceholder ? null : (0, _reactTable.flexRender)(header.column.columnDef.header, header.getContext()));
|
|
83
|
+
}));
|
|
84
|
+
})), /*#__PURE__*/_react.default.createElement("tbody", null, table.getRowModel().rows.length === 0 ? /*#__PURE__*/_react.default.createElement("tr", null, /*#__PURE__*/_react.default.createElement("td", {
|
|
85
|
+
colSpan: columns.length,
|
|
86
|
+
className: "no-data-message"
|
|
87
|
+
}, "No rows found")) : table.getRowModel().rows.map(function (row, i) {
|
|
88
|
+
return /*#__PURE__*/_react.default.createElement("tr", {
|
|
89
|
+
key: row.id,
|
|
90
|
+
className: i % 2 === 0 ? 'striped-row' : ''
|
|
91
|
+
}, row.getVisibleCells().map(function (cell) {
|
|
92
|
+
return /*#__PURE__*/_react.default.createElement("td", {
|
|
93
|
+
key: cell.id
|
|
94
|
+
}, (0, _reactTable.flexRender)(cell.column.columnDef.cell, cell.getContext()));
|
|
95
|
+
}));
|
|
96
|
+
}))), /*#__PURE__*/_react.default.createElement("div", {
|
|
97
|
+
className: "pagination"
|
|
98
|
+
}, /*#__PURE__*/_react.default.createElement("button", {
|
|
99
|
+
onClick: function onClick() {
|
|
100
|
+
return table.previousPage();
|
|
101
|
+
},
|
|
102
|
+
disabled: !table.getCanPreviousPage(),
|
|
103
|
+
"aria-label": "Previous page"
|
|
104
|
+
}, '<'), /*#__PURE__*/_react.default.createElement("span", null, "Page", ' ', /*#__PURE__*/_react.default.createElement("input", {
|
|
105
|
+
"data-testid": "page-number-input",
|
|
106
|
+
value: table.getState().pagination.pageIndex + 1,
|
|
107
|
+
onChange: function onChange(e) {
|
|
108
|
+
var pageNum = e.target.value ? Number(e.target.value) - 1 : 0;
|
|
109
|
+
table.setPageIndex(pageNum);
|
|
110
|
+
},
|
|
111
|
+
style: {
|
|
112
|
+
width: '3rem'
|
|
138
113
|
}
|
|
139
|
-
}, {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
114
|
+
}), ' ', "of", ' ', /*#__PURE__*/_react.default.createElement("span", {
|
|
115
|
+
"data-testid": "total-pages"
|
|
116
|
+
}, table.getPageCount())), /*#__PURE__*/_react.default.createElement("button", {
|
|
117
|
+
"data-testid": "next-page-button",
|
|
118
|
+
onClick: function onClick() {
|
|
119
|
+
return table.nextPage();
|
|
120
|
+
},
|
|
121
|
+
disabled: !table.getCanNextPage(),
|
|
122
|
+
"aria-label": "Next page"
|
|
123
|
+
}, '>')));
|
|
124
|
+
};
|
|
125
|
+
var getTheme = function getTheme() {
|
|
126
|
+
return {
|
|
127
|
+
// react-json-view uses the 'base16' theme structure:
|
|
128
|
+
// https://github.com/chriskempson/base16/blob/master/styling.md
|
|
129
|
+
base00: 'white',
|
|
130
|
+
// Default Background
|
|
131
|
+
base01: 'white',
|
|
132
|
+
// Lighter Background
|
|
133
|
+
base02: _variables.default.headerGrey,
|
|
134
|
+
// Null background
|
|
135
|
+
base03: _variables.default.almostBlack,
|
|
136
|
+
// Null Text
|
|
137
|
+
base04: _variables.default.mediumGrey,
|
|
138
|
+
// Item counts
|
|
139
|
+
base05: _variables.default.darkGrey,
|
|
140
|
+
// Unused
|
|
141
|
+
base06: _variables.default.darkGrey,
|
|
142
|
+
// Unused
|
|
143
|
+
base07: _variables.default.lightBlue,
|
|
144
|
+
// Field names
|
|
145
|
+
base08: _variables.default.darkGrey,
|
|
146
|
+
// Unused
|
|
147
|
+
base09: _variables.default.green,
|
|
148
|
+
// Field values
|
|
149
|
+
base0A: _variables.default.darkGrey,
|
|
150
|
+
// Unused
|
|
151
|
+
base0B: _variables.default.darkGrey,
|
|
152
|
+
// Unused
|
|
153
|
+
base0C: _variables.default.darkGrey,
|
|
154
|
+
// Unused
|
|
155
|
+
base0D: _variables.default.darkGrey,
|
|
156
|
+
// Expanders
|
|
157
|
+
base0E: _variables.default.darkGrey,
|
|
158
|
+
// Expanders
|
|
159
|
+
base0F: _variables.default.darkGrey // Expanders
|
|
160
|
+
};
|
|
161
|
+
};
|
|
162
|
+
var cleanseField = function cleanseField(value) {
|
|
163
|
+
if (typeof value === 'boolean') {
|
|
164
|
+
return value.toString();
|
|
165
|
+
} else if (_lodash.default.isNull(value) || _lodash.default.isNaN(value)) {
|
|
166
|
+
return '';
|
|
167
|
+
} else if (_typeof(value) === 'object') {
|
|
168
|
+
return JSON.stringify(value);
|
|
169
|
+
} else {
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var cleanseRow = function cleanseRow(row) {
|
|
174
|
+
var reconstructedObject = Object.assign.apply(Object, [{}].concat(_toConsumableArray(Object.entries(row).map(function (_ref2) {
|
|
175
|
+
var _ref3 = _slicedToArray(_ref2, 2),
|
|
176
|
+
k = _ref3[0],
|
|
177
|
+
v = _ref3[1];
|
|
178
|
+
return _defineProperty({}, k, cleanseField(v));
|
|
179
|
+
}))));
|
|
180
|
+
return reconstructedObject;
|
|
181
|
+
};
|
|
182
|
+
var cleanseData = function cleanseData(data) {
|
|
183
|
+
if (!data || !data.map) return [];
|
|
184
|
+
return data.map(function (row) {
|
|
185
|
+
return cleanseRow(row);
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
var _default = exports.default = function _default(props) {
|
|
189
|
+
var _useState3 = (0, _react.useState)(0),
|
|
190
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
191
|
+
index = _useState4[0],
|
|
192
|
+
setIndex = _useState4[1];
|
|
193
|
+
var isGeojson = props.format === 'geojson';
|
|
194
|
+
var cleanData = isGeojson ? undefined : props.data ? cleanseData(props.data) : props.data;
|
|
195
|
+
var columns = (0, _react.useMemo)(function () {
|
|
196
|
+
return (props.columns || []).map(function (column) {
|
|
159
197
|
return {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
base04: _variables.default.mediumGrey,
|
|
169
|
-
// Item counts
|
|
170
|
-
base05: _variables.default.darkGrey,
|
|
171
|
-
// Unused
|
|
172
|
-
base06: _variables.default.darkGrey,
|
|
173
|
-
// Unused
|
|
174
|
-
base07: _variables.default.lightBlue,
|
|
175
|
-
// Field names
|
|
176
|
-
base08: _variables.default.darkGrey,
|
|
177
|
-
// Unused
|
|
178
|
-
base09: _variables.default.green,
|
|
179
|
-
// Field values
|
|
180
|
-
base0A: _variables.default.darkGrey,
|
|
181
|
-
// Unused
|
|
182
|
-
base0B: _variables.default.darkGrey,
|
|
183
|
-
// Unused
|
|
184
|
-
base0C: _variables.default.darkGrey,
|
|
185
|
-
// Unused
|
|
186
|
-
base0D: _variables.default.darkGrey,
|
|
187
|
-
// Expanders
|
|
188
|
-
base0E: _variables.default.darkGrey,
|
|
189
|
-
// Expanders
|
|
190
|
-
base0F: _variables.default.darkGrey // Expanders
|
|
198
|
+
header: column,
|
|
199
|
+
id: column,
|
|
200
|
+
accessorFn: function accessorFn(row) {
|
|
201
|
+
return row[column];
|
|
202
|
+
},
|
|
203
|
+
meta: {
|
|
204
|
+
headerClassName: 'table-header'
|
|
205
|
+
}
|
|
191
206
|
};
|
|
207
|
+
});
|
|
208
|
+
}, [props.columns]);
|
|
209
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
210
|
+
id: "data-view"
|
|
211
|
+
}, /*#__PURE__*/_react.default.createElement(_reactTabs.Tabs, {
|
|
212
|
+
selectedIndex: index,
|
|
213
|
+
onSelect: function onSelect(tabIndex) {
|
|
214
|
+
return setIndex(tabIndex);
|
|
192
215
|
}
|
|
193
|
-
}
|
|
194
|
-
|
|
216
|
+
}, /*#__PURE__*/_react.default.createElement(_reactTabs.TabList, {
|
|
217
|
+
className: "header"
|
|
218
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
219
|
+
className: "tab-area"
|
|
220
|
+
}, !isGeojson && /*#__PURE__*/_react.default.createElement(_reactTabs.Tab, {
|
|
221
|
+
"data-testid": "data-table"
|
|
222
|
+
}, "Table"), /*#__PURE__*/_react.default.createElement(_reactTabs.Tab, {
|
|
223
|
+
"data-testid": "data-json"
|
|
224
|
+
}, "JSON"))), !isGeojson && /*#__PURE__*/_react.default.createElement(_reactTabs.TabPanel, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
225
|
+
id: "data-view-table"
|
|
226
|
+
}, /*#__PURE__*/_react.default.createElement(DataTable, {
|
|
227
|
+
data: cleanData,
|
|
228
|
+
columns: columns,
|
|
229
|
+
page: props.page,
|
|
230
|
+
onNextPageClicked: props.onNextPageClicked
|
|
231
|
+
}))), /*#__PURE__*/_react.default.createElement(_reactTabs.TabPanel, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
232
|
+
id: "data-view-raw"
|
|
233
|
+
}, props.loading ? /*#__PURE__*/_react.default.createElement(_loadingElement.default, {
|
|
234
|
+
className: "spinner"
|
|
235
|
+
}) : /*#__PURE__*/_react.default.createElement(_reactJsonView.default, {
|
|
236
|
+
src: props.data,
|
|
237
|
+
theme: getTheme(),
|
|
238
|
+
collapsed: 2
|
|
239
|
+
})))));
|
|
240
|
+
};
|
|
@@ -7,10 +7,18 @@
|
|
|
7
7
|
justify-content: space-between;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
.
|
|
10
|
+
.no-data-message {
|
|
11
11
|
color: black;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
tbody tr:nth-child(even).striped-row {
|
|
15
|
+
background-color: rgba(0, 0, 0, 0.03);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
tbody tr:hover {
|
|
19
|
+
background-color: rgba(0, 0, 0, 0.05);
|
|
20
|
+
}
|
|
21
|
+
|
|
14
22
|
.header-text-items {
|
|
15
23
|
margin: 1rem;
|
|
16
24
|
}
|
|
@@ -22,6 +30,19 @@
|
|
|
22
30
|
#data-view-table {
|
|
23
31
|
color: $almost-black;
|
|
24
32
|
|
|
33
|
+
table {
|
|
34
|
+
table-layout: fixed;
|
|
35
|
+
width: 100%;
|
|
36
|
+
border-collapse: collapse;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
th, td {
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
text-overflow: ellipsis;
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
padding: 7px 5px;
|
|
44
|
+
}
|
|
45
|
+
|
|
25
46
|
.table-header {
|
|
26
47
|
background-color: $header-grey;
|
|
27
48
|
text-align: left;
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
} from 'enzyme'
|
|
4
4
|
import DataView from './data-view'
|
|
5
5
|
import { Tab, TabPanel } from 'react-tabs'
|
|
6
|
-
import ReactTable from 'react-table'
|
|
7
6
|
import ReactJson from 'react-json-view'
|
|
8
7
|
import LoadingElement from '../../components/generic-elements/loading-element'
|
|
9
8
|
|
|
@@ -27,14 +26,14 @@ describe('data view', () => {
|
|
|
27
26
|
})
|
|
28
27
|
|
|
29
28
|
test('should render table headers including column names with dots in them', () => {
|
|
30
|
-
const headers = subject.find('
|
|
29
|
+
const headers = subject.find('#data-view-table th')
|
|
31
30
|
const actual = headers.map(x => x.text())
|
|
32
31
|
|
|
33
32
|
expect(actual).toContain('firstName', 'lastName', 'enrolled', 'firstName.lastName')
|
|
34
33
|
})
|
|
35
34
|
|
|
36
35
|
test('should render table rows including data for column names with dots', () => {
|
|
37
|
-
const table_elements = subject.find('
|
|
36
|
+
const table_elements = subject.find('#data-view-table tbody td')
|
|
38
37
|
const expected = [
|
|
39
38
|
'Joe', 'Smith', 'true', 'Joe Smith',
|
|
40
39
|
'Jane', 'Doe', 'false', 'Jane Doe'
|
|
@@ -52,12 +51,12 @@ describe('data view', () => {
|
|
|
52
51
|
expect(subject.find(TabPanel).length).toEqual(2)
|
|
53
52
|
})
|
|
54
53
|
|
|
55
|
-
it('has a
|
|
56
|
-
expect(subject.find(
|
|
54
|
+
it('has a data table on the first tab, and it is shown by default', () => {
|
|
55
|
+
expect(subject.find('#data-view-table table').length).toEqual(1)
|
|
57
56
|
})
|
|
58
57
|
|
|
59
58
|
it('has a react json component on the second tab', () => {
|
|
60
|
-
subject.
|
|
59
|
+
subject.find('[data-testid="data-json"]').first().simulate('click')
|
|
61
60
|
expect(subject.find(ReactJson).length).toEqual(1)
|
|
62
61
|
})
|
|
63
62
|
})
|
|
@@ -78,8 +77,8 @@ describe('data view', () => {
|
|
|
78
77
|
expect(subject.find(TabPanel).length).toEqual(1)
|
|
79
78
|
})
|
|
80
79
|
|
|
81
|
-
it('does not have a
|
|
82
|
-
expect(subject.find(
|
|
80
|
+
it('does not have a data table component', () => {
|
|
81
|
+
expect(subject.find('#data-view-table').length).toEqual(0)
|
|
83
82
|
})
|
|
84
83
|
|
|
85
84
|
it('has a react json component on the first tab', () => {
|
|
@@ -93,7 +92,7 @@ describe('data view', () => {
|
|
|
93
92
|
const data = []
|
|
94
93
|
const columns = ['firstName', 'lastName', 'enrolled', 'firstName.lastName']
|
|
95
94
|
subject = mount(<DataView data={data} columns={columns} loading />)
|
|
96
|
-
subject.
|
|
95
|
+
subject.find('[data-testid="data-json"]').first().simulate('click')
|
|
97
96
|
})
|
|
98
97
|
|
|
99
98
|
it('does not render a react json component', () => {
|
|
@@ -106,7 +105,7 @@ describe('data view', () => {
|
|
|
106
105
|
})
|
|
107
106
|
|
|
108
107
|
describe('data table', () => {
|
|
109
|
-
it('converts unrenderable values to strings
|
|
108
|
+
it('converts unrenderable values to strings for the data table', () => {
|
|
110
109
|
const queryData = [
|
|
111
110
|
{ object: { value: 1 }, boolean: true, array: [1], nan: NaN, null: null },
|
|
112
111
|
{ object: { value: 2 }, boolean: false, array: [2, 3], nan: NaN, null: null }
|
|
@@ -115,16 +114,16 @@ describe('data view', () => {
|
|
|
115
114
|
|
|
116
115
|
const subject = mount(<DataView data={queryData} columns={columns} />)
|
|
117
116
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
117
|
+
const cells = subject.find('#data-view-table tbody td')
|
|
118
|
+
const actualValues = cells.map(c => c.text())
|
|
119
|
+
expect(actualValues).toEqual([
|
|
120
|
+
'{"value":1}', 'true', '[1]', '', '',
|
|
121
|
+
'{"value":2}', 'false', '[2,3]', '', ''
|
|
122
|
+
])
|
|
124
123
|
expect(subject.prop('data')).toEqual(queryData)
|
|
125
124
|
})
|
|
126
125
|
|
|
127
|
-
it('can handle column names with dots (.) by
|
|
126
|
+
it('can handle column names with dots (.) by rendering correct cell values', () => {
|
|
128
127
|
const queryData = [
|
|
129
128
|
{ 'first.name': 'Mark', 'last.name': 'Johnson' },
|
|
130
129
|
{ 'first.name': 'George', 'last.name': 'Lakoff' }
|
|
@@ -136,20 +135,15 @@ describe('data view', () => {
|
|
|
136
135
|
|
|
137
136
|
const subject = mount(<DataView data={queryData} columns={columns} />)
|
|
138
137
|
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const expectedColumns = [
|
|
145
|
-
{ Header: 'first.name', id: 'first.name', accessor: (row) => row[column], headerClassName: 'table-header' },
|
|
146
|
-
{ Header: 'last.name', id: 'last.name', accessor: (row) => row[column], headerClassName: 'table-header' }
|
|
147
|
-
]
|
|
148
|
-
expectedColumns.map(stringifyAccessor)
|
|
149
|
-
|
|
150
|
-
const actualColumns = subject.find(ReactTable).prop('columns').map(stringifyAccessor)
|
|
138
|
+
const headers = subject.find('#data-view-table th').map(h => h.text())
|
|
139
|
+
expect(headers).toContain('first.name')
|
|
140
|
+
expect(headers).toContain('last.name')
|
|
151
141
|
|
|
152
|
-
|
|
142
|
+
const cells = subject.find('#data-view-table tbody td').map(c => c.text())
|
|
143
|
+
expect(cells).toContain('Mark')
|
|
144
|
+
expect(cells).toContain('Johnson')
|
|
145
|
+
expect(cells).toContain('George')
|
|
146
|
+
expect(cells).toContain('Lakoff')
|
|
153
147
|
})
|
|
154
148
|
})
|
|
155
149
|
})
|