From ead9ec4d39abc211540bc76616498533b31001a6 Mon Sep 17 00:00:00 2001 From: Ashish Baravaliya <49753983+AshishBarvaliya@users.noreply.github.com> Date: Mon, 26 Feb 2024 15:42:07 -0500 Subject: [PATCH] feat: Add descriptive statistics for number cells in data browser (#2529) --- .../BrowserCell/BrowserCell.react.js | 21 +++- src/components/BrowserCell/BrowserCell.scss | 66 ++++++++++- src/components/BrowserRow/BrowserRow.react.js | 2 + src/components/Toolbar/Toolbar.react.js | 103 +++++++++++++++++- src/components/Toolbar/Toolbar.scss | 40 +++++++ .../Data/Browser/BrowserTable.react.js | 6 + .../Data/Browser/BrowserToolbar.react.js | 3 + .../Data/Browser/DataBrowser.react.js | 78 +++++++++++++ 8 files changed, 315 insertions(+), 4 deletions(-) diff --git a/src/components/BrowserCell/BrowserCell.react.js b/src/components/BrowserCell/BrowserCell.react.js index 52323ecaa5..ec0880eadb 100644 --- a/src/components/BrowserCell/BrowserCell.react.js +++ b/src/components/BrowserCell/BrowserCell.react.js @@ -526,7 +526,6 @@ export default class BrowserCell extends Component { hidden, width, current, - onSelect, onEditChange, setCopyableValue, onPointerCmdClick, @@ -536,6 +535,8 @@ export default class BrowserCell extends Component { onEditSelectedRow, isRequired, markRequiredFieldRow, + handleCellClick, + selectedCells, } = this.props; const classes = [...this.state.classes]; @@ -573,6 +574,22 @@ export default class BrowserCell extends Component { ); } + if (selectedCells?.list.has(`${row}-${col}`)) { + if (selectedCells.rowStart === row) { + classes.push(styles.topBorder); + } + if (selectedCells.rowEnd === row) { + classes.push(styles.bottomBorder); + } + if (selectedCells.colStart === col) { + classes.push(styles.leftBorder); + } + if (selectedCells.colEnd === col) { + classes.push(styles.rightBorder); + } + classes.push(styles.selected); + } + return ( { diff --git a/src/components/BrowserCell/BrowserCell.scss b/src/components/BrowserCell/BrowserCell.scss index f2244498d8..af7914205e 100644 --- a/src/components/BrowserCell/BrowserCell.scss +++ b/src/components/BrowserCell/BrowserCell.scss @@ -36,7 +36,71 @@ } } -.hasMore{ +.leftBorder { + position: relative; + + &:after { + position: absolute; + pointer-events: none; + content: ''; + border-left: 2px solid #555572; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +} + +.rightBorder { + position: relative; + + &:after { + position: absolute; + pointer-events: none; + content: ''; + border-right: 2px solid #555572; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +} + +.topBorder { + position: relative; + + &:after { + position: absolute; + pointer-events: none; + content: ''; + border-top: 2px solid #555572; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +} + +.bottomBorder { + position: relative; + + &:after { + position: absolute; + pointer-events: none; + content: ''; + border-bottom: 2px solid #555572; + top: 0; + left: 0; + right: 0; + bottom: 0; + } +} + +.selected { + background-color: #e3effd; +} + +.hasMore { height: auto; max-height: 25px; overflow-y: scroll; diff --git a/src/components/BrowserRow/BrowserRow.react.js b/src/components/BrowserRow/BrowserRow.react.js index dce01aeb45..6d3085a1b8 100644 --- a/src/components/BrowserRow/BrowserRow.react.js +++ b/src/components/BrowserRow/BrowserRow.react.js @@ -136,6 +136,8 @@ export default class BrowserRow extends Component { showNote={this.props.showNote} onRefresh={this.props.onRefresh} scripts={this.props.scripts} + handleCellClick={this.props.handleCellClick} + selectedCells={this.props.selectedCells} /> ); })} diff --git a/src/components/Toolbar/Toolbar.react.js b/src/components/Toolbar/Toolbar.react.js index d62d43441a..bf5082f618 100644 --- a/src/components/Toolbar/Toolbar.react.js +++ b/src/components/Toolbar/Toolbar.react.js @@ -6,11 +6,110 @@ * the root directory of this source tree. */ import PropTypes from 'lib/PropTypes'; -import React from 'react'; +import React, { useEffect } from 'react'; import Icon from 'components/Icon/Icon.react'; import styles from 'components/Toolbar/Toolbar.scss'; +import Popover from 'components/Popover/Popover.react'; +import Position from 'lib/Position'; import { useNavigate, useNavigationType, NavigationType } from 'react-router-dom'; +const POPOVER_CONTENT_ID = 'toolbarStatsPopover'; + +const Stats = ({ data }) => { + const [selected, setSelected] = React.useState(null); + const [open, setOpen] = React.useState(false); + const buttonRef = React.useRef(); + + const statsOptions = [ + { + type: 'sum', + label: 'Sum', + getValue: data => data.reduce((sum, value) => sum + value, 0), + }, + { + type: 'mean', + label: 'Mean', + getValue: data => data.reduce((sum, value) => sum + value, 0) / data.length, + }, + { + type: 'count', + label: 'Count', + getValue: data => data.length, + }, + { + type: 'p99', + label: 'P99', + getValue: data => { + const sorted = data.sort((a, b) => a - b); + return sorted[Math.floor(sorted.length * 0.99)]; + }, + }, + ]; + + const toggle = () => { + setOpen(!open); + }; + + const renderPopover = () => { + const node = buttonRef.current; + const position = Position.inDocument(node); + return ( + +
+
+
+ {statsOptions.map(item => { + const itemStyle = [styles.stats_popover_item]; + if (item.type === selected?.type) { + itemStyle.push(styles.active); + } + return ( +
{ + setSelected(item); + toggle(); + }} + > + {item.label} +
+ ); + })} +
+
+
+ ); + }; + + useEffect(() => { + setSelected(statsOptions[0]); + }, []); + + return ( + <> + {selected ? ( + + ) : null} + {open ? renderPopover() : null} + + ); +}; + const Toolbar = props => { const action = useNavigationType(); const navigate = useNavigate(); @@ -34,6 +133,7 @@ const Toolbar = props => { + {props.selectedData.length ? : null}
{props.children}
); @@ -44,6 +144,7 @@ Toolbar.propTypes = { subsection: PropTypes.string, details: PropTypes.string, relation: PropTypes.object, + selectedData: PropTypes.array, }; export default Toolbar; diff --git a/src/components/Toolbar/Toolbar.scss b/src/components/Toolbar/Toolbar.scss index d56f51068a..b5d41666e4 100644 --- a/src/components/Toolbar/Toolbar.scss +++ b/src/components/Toolbar/Toolbar.scss @@ -85,3 +85,43 @@ body:global(.expanded) { right: 14px; top: 4px; } + +.stats { + position: absolute; + right: 20px; + bottom: 10px; + background: $blue; + border-radius: 3px; + padding: 2px 6px; + font-size: 14px; + color: white; + box-shadow: none; + border: none; +} + +.stats_popover_container { + display: flex; + flex-direction: column; + background: $blue; + border-radius: 3px; + margin-top: 1px; + gap: 2px; + padding: 2px 0px; + font-size: 14px; + color: white; +} + +.stats_popover_item { + cursor: pointer; + padding: 0px 6px; + + &:hover { + color: $blue; + background-color: white; + } +} + +.active { + color: $blue; + background-color: white; +} diff --git a/src/dashboard/Data/Browser/BrowserTable.react.js b/src/dashboard/Data/Browser/BrowserTable.react.js index 554b852195..27b1d97707 100644 --- a/src/dashboard/Data/Browser/BrowserTable.react.js +++ b/src/dashboard/Data/Browser/BrowserTable.react.js @@ -168,6 +168,8 @@ export default class BrowserTable extends React.Component { showNote={this.props.showNote} onRefresh={this.props.onRefresh} scripts={this.context.scripts} + selectedCells={this.props.selectedCells} + handleCellClick={this.props.handleCellClick} />