@@ -231,7 +233,7 @@ class TableOptionsModal extends Component {
- Close
+ {translate('Close')}
:
@@ -247,6 +249,7 @@ TableOptionsModal.propTypes = {
isOpen: PropTypes.bool.isRequired,
columns: PropTypes.arrayOf(PropTypes.object).isRequired,
pageSize: PropTypes.number,
+ maxPageSize: PropTypes.number,
canModifyColumns: PropTypes.bool.isRequired,
optionsComponent: PropTypes.elementType,
onTableOptionChange: PropTypes.func.isRequired,
diff --git a/frontend/src/Components/Table/TableOptions/TableOptionsModalWrapper.js b/frontend/src/Components/Table/TableOptions/TableOptionsModalWrapper.js
index ff2b8538b..6bc18bb82 100644
--- a/frontend/src/Components/Table/TableOptions/TableOptionsModalWrapper.js
+++ b/frontend/src/Components/Table/TableOptions/TableOptionsModalWrapper.js
@@ -20,11 +20,11 @@ class TableOptionsModalWrapper extends Component {
onTableOptionsPress = () => {
this.setState({ isTableOptionsModalOpen: true });
- }
+ };
onTableOptionsModalClose = () => {
this.setState({ isTableOptionsModalOpen: false });
- }
+ };
//
// Render
diff --git a/frontend/src/Components/Table/TablePager.css b/frontend/src/Components/Table/TablePager.css
index 19f5a8f6b..d73a0d0c0 100644
--- a/frontend/src/Components/Table/TablePager.css
+++ b/frontend/src/Components/Table/TablePager.css
@@ -46,11 +46,11 @@
}
.records {
- color: $disabledColor;
+ color: var(--disabledColor);
}
.disabledPageButton {
- color: $disabledColor;
+ color: var(--disabledColor);
}
.pageSelect {
diff --git a/frontend/src/Components/Table/TablePager.css.d.ts b/frontend/src/Components/Table/TablePager.css.d.ts
new file mode 100644
index 000000000..5c50a2cdd
--- /dev/null
+++ b/frontend/src/Components/Table/TablePager.css.d.ts
@@ -0,0 +1,17 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'controls': string;
+ 'controlsContainer': string;
+ 'disabledPageButton': string;
+ 'loading': string;
+ 'loadingContainer': string;
+ 'pageLink': string;
+ 'pageNumber': string;
+ 'pageSelect': string;
+ 'pager': string;
+ 'records': string;
+ 'recordsContainer': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/TablePager.js b/frontend/src/Components/Table/TablePager.js
index 3c7c5a8f1..d58824169 100644
--- a/frontend/src/Components/Table/TablePager.js
+++ b/frontend/src/Components/Table/TablePager.js
@@ -1,11 +1,12 @@
+import classNames from 'classnames';
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import classNames from 'classnames';
-import { icons } from 'Helpers/Props';
+import SelectInput from 'Components/Form/SelectInput';
import Icon from 'Components/Icon';
import Link from 'Components/Link/Link';
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
-import SelectInput from 'Components/Form/SelectInput';
+import { icons } from 'Helpers/Props';
+import translate from 'Utilities/String/translate';
import styles from './TablePager.css';
class TablePager extends Component {
@@ -26,16 +27,16 @@ class TablePager extends Component {
onOpenPageSelectClick = () => {
this.setState({ isShowingPageSelect: true });
- }
+ };
onPageSelect = ({ value: page }) => {
this.setState({ isShowingPageSelect: false });
this.props.onPageSelect(parseInt(page));
- }
+ };
onPageSelectBlur = () => {
this.setState({ isShowingPageSelect: false });
- }
+ };
//
// Render
@@ -156,7 +157,7 @@ class TablePager extends Component {
- Total records: {totalRecords}
+ {translate('TotalRecords', { totalRecords })}
diff --git a/frontend/src/Components/Table/TableRow.css b/frontend/src/Components/Table/TableRow.css
index dcc6ad8cf..df297a5fe 100644
--- a/frontend/src/Components/Table/TableRow.css
+++ b/frontend/src/Components/Table/TableRow.css
@@ -2,6 +2,6 @@
transition: background-color 500ms;
&:hover {
- background-color: $tableRowHoverBackgroundColor;
+ background-color: var(--tableRowHoverBackgroundColor);
}
}
diff --git a/frontend/src/Components/Table/TableRow.css.d.ts b/frontend/src/Components/Table/TableRow.css.d.ts
new file mode 100644
index 000000000..d4b245cd1
--- /dev/null
+++ b/frontend/src/Components/Table/TableRow.css.d.ts
@@ -0,0 +1,7 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'row': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/TableRowButton.css.d.ts b/frontend/src/Components/Table/TableRowButton.css.d.ts
new file mode 100644
index 000000000..d4b245cd1
--- /dev/null
+++ b/frontend/src/Components/Table/TableRowButton.css.d.ts
@@ -0,0 +1,7 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'row': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/TableSelectAllHeaderCell.css.d.ts b/frontend/src/Components/Table/TableSelectAllHeaderCell.css.d.ts
new file mode 100644
index 000000000..ede42755e
--- /dev/null
+++ b/frontend/src/Components/Table/TableSelectAllHeaderCell.css.d.ts
@@ -0,0 +1,8 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'input': string;
+ 'selectAllHeaderCell': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/VirtualTable.css.d.ts b/frontend/src/Components/Table/VirtualTable.css.d.ts
new file mode 100644
index 000000000..26af27e05
--- /dev/null
+++ b/frontend/src/Components/Table/VirtualTable.css.d.ts
@@ -0,0 +1,8 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'tableBodyContainer': string;
+ 'tableContainer': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/VirtualTable.js b/frontend/src/Components/Table/VirtualTable.js
index b2d68a888..5473413cb 100644
--- a/frontend/src/Components/Table/VirtualTable.js
+++ b/frontend/src/Components/Table/VirtualTable.js
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { scrollDirections } from 'Helpers/Props';
+import { Grid, WindowScroller } from 'react-virtualized';
import Measure from 'Components/Measure';
import Scroller from 'Components/Scroller/Scroller';
-import { WindowScroller, Grid } from 'react-virtualized';
+import { scrollDirections } from 'Helpers/Props';
import hasDifferentItemsOrOrder from 'Utilities/Object/hasDifferentItemsOrOrder';
import styles from './VirtualTable.css';
@@ -39,7 +39,8 @@ class VirtualTable extends Component {
super(props, context);
this.state = {
- width: 0
+ width: 0,
+ scrollRestored: false
};
this._grid = null;
@@ -48,11 +49,13 @@ class VirtualTable extends Component {
componentDidUpdate(prevProps, prevState) {
const {
items,
- scrollIndex
+ scrollIndex,
+ scrollTop
} = this.props;
const {
- width
+ width,
+ scrollRestored
} = this.state;
if (this._grid && (prevState.width !== width || hasDifferentItemsOrOrder(prevProps.items, items))) {
@@ -60,6 +63,11 @@ class VirtualTable extends Component {
this._grid.recomputeGridSize();
}
+ if (this._grid && scrollTop !== undefined && scrollTop !== 0 && !scrollRestored) {
+ this.setState({ scrollRestored: true });
+ this._grid.scrollToPosition({ scrollTop });
+ }
+
if (scrollIndex != null && scrollIndex !== prevProps.scrollIndex) {
this._grid.scrollToCell({
rowIndex: scrollIndex,
@@ -73,7 +81,7 @@ class VirtualTable extends Component {
setGridRef = (ref) => {
this._grid = ref;
- }
+ };
//
// Listeners
@@ -82,7 +90,7 @@ class VirtualTable extends Component {
this.setState({
width
});
- }
+ };
//
// Render
@@ -95,6 +103,7 @@ class VirtualTable extends Component {
scroller,
header,
headerHeight,
+ rowHeight,
rowRenderer,
...otherProps
} = this.props;
@@ -137,6 +146,7 @@ class VirtualTable extends Component {
{header}
@@ -176,6 +185,7 @@ VirtualTable.propTypes = {
className: PropTypes.string.isRequired,
items: PropTypes.arrayOf(PropTypes.object).isRequired,
scrollIndex: PropTypes.number,
+ scrollTop: PropTypes.number,
scroller: PropTypes.instanceOf(Element).isRequired,
header: PropTypes.node.isRequired,
headerHeight: PropTypes.number.isRequired,
@@ -185,7 +195,8 @@ VirtualTable.propTypes = {
VirtualTable.defaultProps = {
className: styles.tableContainer,
- headerHeight: 38
+ headerHeight: 38,
+ rowHeight: ROW_HEIGHT
};
export default VirtualTable;
diff --git a/frontend/src/Components/Table/VirtualTableHeader.css.d.ts b/frontend/src/Components/Table/VirtualTableHeader.css.d.ts
new file mode 100644
index 000000000..b6d43be5a
--- /dev/null
+++ b/frontend/src/Components/Table/VirtualTableHeader.css.d.ts
@@ -0,0 +1,7 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'header': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/VirtualTableHeaderCell.css.d.ts b/frontend/src/Components/Table/VirtualTableHeaderCell.css.d.ts
new file mode 100644
index 000000000..a944c417d
--- /dev/null
+++ b/frontend/src/Components/Table/VirtualTableHeaderCell.css.d.ts
@@ -0,0 +1,8 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'headerCell': string;
+ 'sortIcon': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/VirtualTableHeaderCell.js b/frontend/src/Components/Table/VirtualTableHeaderCell.js
index bf51062e9..55c688d01 100644
--- a/frontend/src/Components/Table/VirtualTableHeaderCell.js
+++ b/frontend/src/Components/Table/VirtualTableHeaderCell.js
@@ -1,8 +1,8 @@
import PropTypes from 'prop-types';
import React, { Component } from 'react';
-import { icons, sortDirections } from 'Helpers/Props';
-import Link from 'Components/Link/Link';
import Icon from 'Components/Icon';
+import Link from 'Components/Link/Link';
+import { icons, sortDirections } from 'Helpers/Props';
import styles from './VirtualTableHeaderCell.css';
export function headerRenderer(headerProps) {
@@ -13,6 +13,8 @@ export function headerRenderer(headerProps) {
} = headerProps;
return (
+
+ // eslint-disable-next-line no-use-before-define
+ );
+}
+
+export default VirtualTableRowButton;
diff --git a/frontend/src/Components/Table/VirtualTableSelectAllHeaderCell.css.d.ts b/frontend/src/Components/Table/VirtualTableSelectAllHeaderCell.css.d.ts
new file mode 100644
index 000000000..ede42755e
--- /dev/null
+++ b/frontend/src/Components/Table/VirtualTableSelectAllHeaderCell.css.d.ts
@@ -0,0 +1,8 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'input': string;
+ 'selectAllHeaderCell': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Table/usePaging.ts b/frontend/src/Components/Table/usePaging.ts
new file mode 100644
index 000000000..dfebb2355
--- /dev/null
+++ b/frontend/src/Components/Table/usePaging.ts
@@ -0,0 +1,54 @@
+import { useCallback, useMemo } from 'react';
+import { useDispatch } from 'react-redux';
+
+interface PagingOptions {
+ page: number;
+ totalPages: number;
+ gotoPage: ({ page }: { page: number }) => void;
+}
+
+function usePaging(options: PagingOptions) {
+ const { page, totalPages, gotoPage } = options;
+ const dispatch = useDispatch();
+
+ const handleFirstPagePress = useCallback(() => {
+ dispatch(gotoPage({ page: 1 }));
+ }, [dispatch, gotoPage]);
+
+ const handlePreviousPagePress = useCallback(() => {
+ dispatch(gotoPage({ page: Math.max(page - 1, 1) }));
+ }, [page, dispatch, gotoPage]);
+
+ const handleNextPagePress = useCallback(() => {
+ dispatch(gotoPage({ page: Math.min(page + 1, totalPages) }));
+ }, [page, totalPages, dispatch, gotoPage]);
+
+ const handleLastPagePress = useCallback(() => {
+ dispatch(gotoPage({ page: totalPages }));
+ }, [totalPages, dispatch, gotoPage]);
+
+ const handlePageSelect = useCallback(
+ (page: number) => {
+ dispatch(gotoPage({ page }));
+ },
+ [dispatch, gotoPage]
+ );
+
+ return useMemo(() => {
+ return {
+ handleFirstPagePress,
+ handlePreviousPagePress,
+ handleNextPagePress,
+ handleLastPagePress,
+ handlePageSelect,
+ };
+ }, [
+ handleFirstPagePress,
+ handlePreviousPagePress,
+ handleNextPagePress,
+ handleLastPagePress,
+ handlePageSelect,
+ ]);
+}
+
+export default usePaging;
diff --git a/frontend/src/Components/TagList.css.d.ts b/frontend/src/Components/TagList.css.d.ts
new file mode 100644
index 000000000..bf5da21eb
--- /dev/null
+++ b/frontend/src/Components/TagList.css.d.ts
@@ -0,0 +1,7 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'tags': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/TagList.js b/frontend/src/Components/TagList.js
index 485651bdc..fe700b8fe 100644
--- a/frontend/src/Components/TagList.js
+++ b/frontend/src/Components/TagList.js
@@ -1,21 +1,20 @@
-import _ from 'lodash';
import PropTypes from 'prop-types';
import React from 'react';
import { kinds } from 'Helpers/Props';
+import sortByProp from 'Utilities/Array/sortByProp';
import Label from './Label';
import styles from './TagList.css';
function TagList({ tags, tagList }) {
+ const sortedTags = tags
+ .map((tagId) => tagList.find((tag) => tag.id === tagId))
+ .filter((tag) => !!tag)
+ .sort(sortByProp('label'));
+
return (
{
- tags.map((t) => {
- const tag = _.find(tagList, { id: t });
-
- if (!tag) {
- return null;
- }
-
+ sortedTags.map((tag) => {
return (
-
- {title}
-
-
-
- {body}
-
-
- }
- />
- );
-}
-
-Popover.propTypes = {
- title: PropTypes.string.isRequired,
- body: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired
-};
-
-export default Popover;
diff --git a/frontend/src/Components/Tooltip/Popover.tsx b/frontend/src/Components/Tooltip/Popover.tsx
new file mode 100644
index 000000000..4c6781343
--- /dev/null
+++ b/frontend/src/Components/Tooltip/Popover.tsx
@@ -0,0 +1,26 @@
+import React from 'react';
+import Tooltip, { TooltipProps } from './Tooltip';
+import styles from './Popover.css';
+
+interface PopoverProps extends Omit
{
+ title: string;
+ body: React.ReactNode;
+}
+
+function Popover({ title, body, ...otherProps }: PopoverProps) {
+ return (
+
+ {title}
+
+ {body}
+
+ }
+ />
+ );
+}
+
+export default Popover;
diff --git a/frontend/src/Components/Tooltip/Tooltip.css b/frontend/src/Components/Tooltip/Tooltip.css
index 7075042d2..8ab533d07 100644
--- a/frontend/src/Components/Tooltip/Tooltip.css
+++ b/frontend/src/Components/Tooltip/Tooltip.css
@@ -7,14 +7,14 @@
position: relative;
&.default {
- background-color: $white;
- box-shadow: 0 5px 10px $popoverShadowColor;
+ background-color: var(--popoverBodyBackgroundColor);
+ box-shadow: 0 5px 10px var(--popoverShadowColor);
}
&.inverse {
- background-color: $themeDarkColor;
- box-shadow: 0 5px 10px $popoverShadowInverseColor;
- color: $white;
+ background-color: var(--themeDarkColor);
+ box-shadow: 0 5px 10px var(--popoverShadowInverseColor);
+ color: var(--white);
}
}
@@ -50,20 +50,20 @@
content: ' ';
&.default {
- border-top-color: $popoverArrowBorderColor;
+ border-top-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-top-color: $popoverArrowBorderInverseColor;
+ border-top-color: var(--popoverArrowBorderInverseColor);
}
}
&.default {
- border-top-color: $popoverArrowBorderColor;
+ border-top-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-top-color: $popoverArrowBorderInverseColor;
+ border-top-color: var(--popoverArrowBorderInverseColor);
}
}
@@ -79,20 +79,20 @@
content: ' ';
&.default {
- border-right-color: $popoverArrowBorderColor;
+ border-right-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-right-color: $popoverArrowBorderInverseColor;
+ border-right-color: var(--popoverArrowBorderInverseColor);
}
}
&.default {
- border-right-color: $popoverArrowBorderColor;
+ border-right-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-right-color: $popoverArrowBorderInverseColor;
+ border-right-color: var(--popoverArrowBorderInverseColor);
}
}
@@ -108,20 +108,20 @@
content: ' ';
&.default {
- border-bottom-color: $popoverArrowBorderColor;
+ border-bottom-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-bottom-color: $popoverArrowBorderInverseColor;
+ border-bottom-color: var(--popoverArrowBorderInverseColor);
}
}
&.default {
- border-bottom-color: $popoverArrowBorderColor;
+ border-bottom-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-bottom-color: $popoverArrowBorderInverseColor;
+ border-bottom-color: var(--popoverArrowBorderInverseColor);
}
}
@@ -137,20 +137,20 @@
content: ' ';
&.default {
- border-left-color: $popoverArrowBorderColor;
+ border-left-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-left-color: $popoverArrowBorderInverseColor;
+ border-left-color: var(--popoverArrowBorderInverseColor);
}
}
&.default {
- border-left-color: $popoverArrowBorderColor;
+ border-left-color: var(--popoverArrowBorderColor);
}
&.inverse {
- border-left-color: $popoverArrowBorderInverseColor;
+ border-left-color: var(--popoverArrowBorderInverseColor);
}
}
diff --git a/frontend/src/Components/Tooltip/Tooltip.css.d.ts b/frontend/src/Components/Tooltip/Tooltip.css.d.ts
new file mode 100644
index 000000000..59fd7f49a
--- /dev/null
+++ b/frontend/src/Components/Tooltip/Tooltip.css.d.ts
@@ -0,0 +1,19 @@
+// This file is automatically generated.
+// Please do not change this file!
+interface CssExports {
+ 'arrow': string;
+ 'arrowDisabled': string;
+ 'body': string;
+ 'bottom': string;
+ 'default': string;
+ 'horizontalContainer': string;
+ 'inverse': string;
+ 'left': string;
+ 'right': string;
+ 'tooltip': string;
+ 'tooltipContainer': string;
+ 'top': string;
+ 'verticalContainer': string;
+}
+export const cssExports: CssExports;
+export default cssExports;
diff --git a/frontend/src/Components/Tooltip/Tooltip.js b/frontend/src/Components/Tooltip/Tooltip.js
deleted file mode 100644
index f3f44b7a2..000000000
--- a/frontend/src/Components/Tooltip/Tooltip.js
+++ /dev/null
@@ -1,235 +0,0 @@
-import PropTypes from 'prop-types';
-import React, { Component } from 'react';
-import { Manager, Popper, Reference } from 'react-popper';
-import classNames from 'classnames';
-import { isMobile as isMobileUtil } from 'Utilities/mobile';
-import { kinds, tooltipPositions } from 'Helpers/Props';
-import Portal from 'Components/Portal';
-import dimensions from 'Styles/Variables/dimensions';
-import styles from './Tooltip.css';
-
-let maxWidth = null;
-
-function getMaxWidth() {
- const windowWidth = window.innerWidth;
-
- if (windowWidth >= parseInt(dimensions.breakpointLarge)) {
- maxWidth = 800;
- } else if (windowWidth >= parseInt(dimensions.breakpointMedium)) {
- maxWidth = 650;
- } else if (windowWidth >= parseInt(dimensions.breakpointSmall)) {
- maxWidth = 500;
- } else {
- maxWidth = 450;
- }
-
- return maxWidth;
-}
-
-class Tooltip extends Component {
-
- //
- // Lifecycle
-
- constructor(props, context) {
- super(props, context);
-
- this._scheduleUpdate = null;
- this._closeTimeout = null;
- this._maxWidth = maxWidth || getMaxWidth();
-
- this.state = {
- isOpen: false
- };
- }
-
- componentDidUpdate() {
- if (this._scheduleUpdate && this.state.isOpen) {
- this._scheduleUpdate();
- }
- }
-
- componentWillUnmount() {
- if (this._closeTimeout) {
- this._closeTimeout = clearTimeout(this._closeTimeout);
- }
- }
-
- //
- // Control
-
- computeMaxSize = (data) => {
- const {
- top,
- right,
- bottom,
- left
- } = data.offsets.reference;
-
- const windowWidth = window.innerWidth;
- const windowHeight = window.innerHeight;
-
- if ((/^top/).test(data.placement)) {
- data.styles.maxHeight = top - 20;
- } else if ((/^bottom/).test(data.placement)) {
- data.styles.maxHeight = windowHeight - bottom - 20;
- } else if ((/^right/).test(data.placement)) {
- data.styles.maxWidth = Math.min(this._maxWidth, windowWidth - right - 20);
- data.styles.maxHeight = top - 20;
- } else {
- data.styles.maxWidth = Math.min(this._maxWidth, left - 20);
- data.styles.maxHeight = top - 20;
- }
-
- return data;
- }
-
- //
- // Listeners
-
- onMeasure = ({ width }) => {
- this.setState({ width });
- }
-
- onClick = () => {
- if (isMobileUtil()) {
- this.setState({ isOpen: !this.state.isOpen });
- }
- }
-
- onMouseEnter = () => {
- if (this._closeTimeout) {
- this._closeTimeout = clearTimeout(this._closeTimeout);
- }
-
- this.setState({ isOpen: true });
- }
-
- onMouseLeave = () => {
- this._closeTimeout = setTimeout(() => {
- this.setState({ isOpen: false });
- }, 100);
- }
-
- //
- // Render
-
- render() {
- const {
- className,
- bodyClassName,
- anchor,
- tooltip,
- kind,
- position,
- canFlip
- } = this.props;
-
- return (
-