Skip to content
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

Merged
merged 51 commits into from
Oct 17, 2019
Merged

Summary rows #1773

merged 51 commits into from
Oct 17, 2019

Conversation

qili26
Copy link
Contributor

@qili26 qili26 commented Oct 7, 2019

  • Add summaryRows to ReactDataGrid.tsx and rows sticky container
  • Add Special logic for IE / IE Edge
  • Add isSummaryRow to Row.tsx / Cell.tsx
  • unit test

@qili26 qili26 self-assigned this Oct 7, 2019
}

.rdg-bottom-pinned-rows-sticky-wrapper .react-grid-checkbox-container {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example only.

Copy link
Contributor

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>
Copy link
Contributor Author

@qili26 qili26 Oct 7, 2019

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)],
Copy link
Contributor Author

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[];
Copy link
Contributor Author

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?

Copy link
Contributor

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;
Copy link
Contributor Author

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?

@qili26
Copy link
Contributor Author

qili26 commented Oct 7, 2019

supersedes #1759

Copy link
Contributor

@amanmahajan7 amanmahajan7 left a 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 change rowGetter to rows. I am not sure what is the need or rowGetter. 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;
Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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.

}

.rdg-bottom-pinned-rows-sticky-wrapper .react-grid-checkbox-container {
Copy link
Contributor

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;
Copy link
Contributor

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));
Copy link
Contributor

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

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[];
Copy link
Contributor

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

@@ -0,0 +1,572 @@
/* Header */
#head {
Copy link
Contributor

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

Copy link
Contributor Author

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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>}
Copy link
Contributor

@amanmahajan7 amanmahajan7 Oct 9, 2019

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

Copy link
Contributor Author

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.

Copy link
Contributor

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 {
Copy link
Contributor

@amanmahajan7 amanmahajan7 Oct 9, 2019

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

Copy link
Contributor Author

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>
Copy link
Contributor

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?

Copy link
Contributor Author

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>
Copy link
Contributor

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>

Copy link
Contributor

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) {
Copy link
Contributor

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

  1. 10 rows + 2 summary rows are renderered
  2. 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.

Copy link
Contributor Author

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>;
Copy link
Contributor

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

Copy link
Contributor

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.

@qili26 qili26 changed the title Bottom pinned rows Summary rows Oct 9, 2019
@qili26 qili26 marked this pull request as ready for review October 9, 2019 20:26
@@ -46,6 +46,7 @@
.example {
overflow: auto;
flex-grow: 1;
margin: 0 8px;
Copy link
Contributor Author

@qili26 qili26 Oct 15, 2019

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}>
Copy link
Contributor Author

@qili26 qili26 Oct 15, 2019

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.

  1. The container is fixed width. so that when this div is set the scrollLeft, the rows container (with full width, the next line) could be moving around.
  2. 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

The latest result.
summary row - new structure

@@ -21,6 +21,7 @@ type SharedDataGridProps<R, K extends keyof R> = Pick<ReactDataGridProps<R, K>,
| 'RowsContainer'
| 'getSubRowDetails'
| 'selectedRows'
| 'summaryRows'
Copy link
Contributor Author

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.

https://github.com/adazzle/react-data-grid/pull/1773/files/eee9db5748ec0b44115a398ed8a4d123411fd227..7a99fb7419876457d88de7dcb75a2dbd2abb7cfe#diff-61ebc5f10f9ee6875894862bf0d64cd6R9

https://github.com/adazzle/react-data-grid/blob/canary/packages/react-data-grid/src/RowRenderer.tsx#L6

Copy link
Contributor

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>>());
Copy link
Contributor Author

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()}
Copy link
Contributor Author

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)

Copy link
Contributor

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) 🤔

@nstepien nstepien merged commit 0f40988 into canary Oct 17, 2019
@nstepien nstepien deleted the ql-total-rows branch October 17, 2019 16:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants