-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Summary rows #1773
Summary rows #1773
Conversation
examples/assets/css/main.css
Outdated
} | ||
|
||
.rdg-bottom-pinned-rows-sticky-wrapper .react-grid-checkbox-container { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example only.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a generic way to handle this. It is a common requirement to have selection and totals row
key: 'hours', | ||
name: '# of working hours', | ||
width: 200, | ||
formatter: ({ value }) => <div>{ value.toLocaleString('en-US', { maximumFractionDigits: 0 }) }</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to display the value with US number format like 1,000
id: i, | ||
task: `Task ${i}`, | ||
complete: Math.min(100, Math.round(Math.random() * 110)), | ||
priority: ['Critical', 'High', 'Medium', 'Low'][Math.floor(Math.random() * 4)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Array[0 ~ 4]
* The rows can be used as total rows to display some summary information. | ||
* Bottom horizontal scroll bar can move the row left / right. Or a customized row renderer can be used to disabled the scrolling support. | ||
*/ | ||
pinnedRows?: R[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we should use bottomPinnedRows
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about it, more I incline towards summary/totals
row because of a few reasons
pinnedRows
can be shown at the bottom or at the top.totals/summaryRows
should always be shown at the bottom.pinnedRows
should be treated like regular rows. i.e. it should have a selector, editor and all the features that a regular row has
// Set minHeight to show horizontal scrollbar when there are no rows | ||
const scrollableRowsWrapperStyle: React.CSSProperties = { width, paddingTop, paddingBottom }; | ||
const pinnedRowsWrapperStyle: React.CSSProperties = { width }; | ||
let ieStickyWrapperStyle: React.CSSProperties | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any better ideas for IE specific codes?
supersedes #1759 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Grouping and totaling features are quite interlinked so we can probably keep grouping in mind while designing an API for the totals row. Following is just a rough idea about what we need
groupBy
prop. It can be a array of keys[id, name]
. For it to work we need to changerowGetter
torows
. I am not sure what is the need orrowGetter
. Performance may be?- We need a way to show a totals row for the group as well as some way to change group row values. May be we can add some
aggregate
props to column. - Is there any overlap between group totals and row totals? There may not be any but I want us to start thinking about both features
const paddingTop = rowOverscanStartIdx * rowHeight; | ||
const paddingBottom = (rowsCount - 1 - rowOverscanEndIdx) * rowHeight; | ||
const pinnedRowsHeight = isIEOrEdge && pinnedRows ? pinnedRows.length * rowHeight : 0; | ||
const paddingTop = rowOverscanStartIdx > 0 ? rowOverscanStartIdx * rowHeight : 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const paddingTop = rowOverscanStartIdx * rowHeight
should be enough
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rowOverscanStartIdx
will be guaranteed to be a positive integer, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's 0 minimum.
examples/assets/css/main.css
Outdated
} | ||
|
||
.rdg-bottom-pinned-rows-sticky-wrapper .react-grid-checkbox-container { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need a generic way to handle this. It is a common requirement to have selection and totals row
@@ -57,6 +58,7 @@ interface RendererProps<R> extends Pick<CanvasProps<R>, 'cellMetaData' | 'onRowS | |||
isScrolling: boolean; | |||
colOverscanStartIdx: number; | |||
colOverscanEndIdx: number; | |||
isBottomPinned?: boolean; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not 100% sure if we should pass isBottomPinned
prop to each formatter. I think we should have a separation between pinnedRows
and regular rows
. More details in the next comment
return []; | ||
} | ||
|
||
return pinnedRows.map((row, idx) => renderRow(row, rowsCount + 1 + idx)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think pinnedRows
should not use renderRow
function because of a few reasons
- We want to hide checkbox selector on totals row
pinnedRows
should not be editable and currently we do not handle it. This results in a weird bug, when you click on the totals cell, selected cell is set to edit mode
https://www.loom.com/share/95ab947e0f91453b99e4f1150296cebf
I am thinking we should have a trimmed down version of renderRow
that removes editor and props that are not required. We also do not want all the cell events to fire.
handleCellClick = () => { |
I think adding TotalCell
component may be a good idea.
* The rows can be used as total rows to display some summary information. | ||
* Bottom horizontal scroll bar can move the row left / right. Or a customized row renderer can be used to disabled the scrolling support. | ||
*/ | ||
pinnedRows?: R[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The more I think about it, more I incline towards summary/totals
row because of a few reasons
pinnedRows
can be shown at the bottom or at the top.totals/summaryRows
should always be shown at the bottom.pinnedRows
should be treated like regular rows. i.e. it should have a selector, editor and all the features that a regular row has
examples/assets/css/main.css
Outdated
@@ -0,0 +1,572 @@ | |||
/* Header */ | |||
#head { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this file has been deleted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought I removed this after merging, will clean this.
@@ -241,7 +272,8 @@ export default function Canvas<R>({ | |||
colOverscanEndIdx, | |||
lastFrozenColumnIndex: columnMetrics.lastFrozenColumnIndex, | |||
isScrolling, | |||
scrollLeft | |||
scrollLeft, | |||
isSummaryRow: summaryRows && summaryRows.includes(row) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will always be false, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will always be false
only when summaryRows
doesn't exist. Same story like isRowSelected
, if it's not enabled, it will just be false and populated to next levels.
/> | ||
</span> | ||
</div> | ||
{tooltip && <span className="cell-tooltip-text">{tooltip}</span>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should deprecate tooltip
prop. Row/Cell
renderers must be used to set it and it makes using grid difficult IMO and we should just set the tooltip in the formatter. We do not have to remove tooltip
prop in this PR but we can remove it from summaryRow
. Our component extensively use this prop so we have to keep in on the regular row for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we need to update some styles as well. (we should do this in another PR). Currently if we set the tooltip in the formatter, the tooltip will only display when the formatter area is hovered but not the whole cell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was referring to that we should remove {tooltip && <span className="cell-tooltip-text">{tooltip}</span>}
from this block. We do not need to handle tooltip on the totals row
} | ||
} | ||
|
||
.rdg-bottom-pinned-rows { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be we can use rdg-summary-*
classnames to match the prop name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change this.
> | ||
<div className={cellValueClassName}> | ||
<div className="react-grid-Cell__container"> | ||
<span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove the span?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to remove the span, but I am not sure if we should do it in this PR? Maybe in another cell refactor PR?
<div className={cellValueClassName}> | ||
<div className="react-grid-Cell__container"> | ||
<span> | ||
<CellValue<R> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do something like
const cellValue = (<CellValue<R> ... />);
if (isSummaryRow) {
....
<div className="react-grid-Cell__container">
{cellValue}
</div>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I think about it, I think it is better to use CellValue
for summary rows instead of CellContent
. This way we do not have to move the Expand/Collapse
functionality here
@@ -261,6 +293,16 @@ export default function Canvas<R>({ | |||
return <Row<R> {...rendererProps} />; | |||
} | |||
|
|||
function getRowRef(row: R, idx: number) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Summary rows ref
will never reset from what I can tell. Consider a case
- 10 rows + 2 summary rows are renderered
- rowCount is changed to 30. This will override summary row refs
I am quite sure this will happen but we can double check. We may have to use 2 maps
React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, I will use another way for the summary rows ref.
|
||
function DecimalFormatter({ value, isSummaryRow, maximumFractionDigits }) { | ||
const text = value.toLocaleString('en-US', maximumFractionDigits == null ? { maximumFractionDigits: 2 } : { maximumFractionDigits: 0 }); | ||
return isSummaryRow ? <strong>{text}</strong> : <div>{text}</div>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@MayhemYDG thoughts on the this API where we pass isSummaryRow
prop to Row/Cell/Formatters
? Another option is to have a dedicated column.GroupFormatter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with it.
@@ -46,6 +46,7 @@ | |||
.example { | |||
overflow: auto; | |||
flex-grow: 1; | |||
margin: 0 8px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this margin so that I can see the RDG border.
</div> | ||
{summaryRows && summaryRows.length && ( | ||
<div className="rdg-summary"> | ||
<div ref={summary} style={boundaryStyle}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to mimic canvas horizontal scrolling so that for Summary it can just reuse the existing Row.tsx
.
- The container is fixed width. so that when this
div
is set thescrollLeft
, the rows container (with full width, the next line) could be moving around. - For sticky frozen divs, we do nothing. For IE 11 specifically, we need to set the
scrollLeft
to each frozen cells. (This logic is just same as what we had)
w3c/csswg-drafts#865
An insteresting finding is that the reason why previously Header
sticky doesn't work for EDGE is because we set the .react-grid-HeaderRow
as overflow: hidden
, it will break the cell position sticky only in EDGE. However, the overflow: hidden
like the structure of canvas and summary area would work.
Since EDGE sticky support is buggy, maybe we should consider not using sticky for EDGE? @MayhemYDG
This reverts commit 7fdbfaf.
@@ -21,6 +21,7 @@ type SharedDataGridProps<R, K extends keyof R> = Pick<ReactDataGridProps<R, K>, | |||
| 'RowsContainer' | |||
| 'getSubRowDetails' | |||
| 'selectedRows' | |||
| 'summaryRows' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot add comment to above line so I added here. Just FYI, I found a circular type referencing that we might want to fix in another PR. However, not sure if this is a real problem in terms of types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circular imports are fine, type imports will be erased in the resulting JS, and proper es module implementations should support circular imports.
https://twitter.com/awbjs/status/1160933193320525825
@@ -78,7 +78,6 @@ export default function Canvas<R, K extends keyof R>({ | |||
const summaryRef = useRef<HTMLDivElement>(null); | |||
const prevScrollToRowIndex = useRef<number | undefined>(); | |||
const [rowRefs] = useState(() => new Map<number, Row<R>>()); | |||
const [summaryRowRefs] = useState(() => new Map<number, Row<R>>()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I just realized this is never used after seeing this.
<Row<R> | ||
idx={idx} | ||
row={rowData} | ||
width={columnMetrics.totalColumnWidth + getScrollbarSize()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Header
has same calculation, should we add this directly to columnMetrics
like columnMetrics.fullWidth
? (Looks like we had this before but removed)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we'll do that when we'll refactor header/summary rows to render them directly in the grid (if we can work around the edge bug) 🤔
summaryRows
toReactDataGrid.tsx
and rows sticky containerisSummaryRow
toRow.tsx
/Cell.tsx