-
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
[Table] scrollToRegion instance method #1496
Conversation
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.
Clarifying comments + self-review.
packages/table/preview/index.tsx
Outdated
); | ||
|
||
// Table: "Scroll to" | ||
const scrollToRegionTypeSelectMenu = this.renderSelectMenu( |
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 whole area is a mess now. Wanted to get a preview up quickly, but would like to clean this up before merging.
packages/table/preview/index.tsx
Outdated
private renderSwitch( | ||
label: string, | ||
stateKey: keyof IMutableTableState, | ||
prereqStateKey?: keyof IMutableTableState, | ||
prereqStateKeyValue?: any, | ||
prereqStateKeyValues?: any[], |
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.
Can undo all these changes; ended up going in a different direction.
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.
Done
|
||
// NOTE: The following are not exported in the public API: | ||
// - Errors | ||
// - internal/ |
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.
Thoughts on this common/internal/
folder to clearly denote things that won't be exported?
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.
👍 yes nice
@@ -0,0 +1,68 @@ | |||
/** |
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.
Really want to get into the practice of taking utility code out of table.tsx
. I'd also like to avoid putting all utility code into one huge Utils
file if possible. Thus, here's a new scrollUtils
file.
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.
that's cool, if you add more do you think it's useful to keep track of an index.ts
that re-exports? it's a tradeoff between maintenance of index.ts and simplifying import lines.
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'd be into that. 👍 Another immediate idea is focusUtils.ts
. Happy to introduce a /common/internal/index.ts
file when I create that.
getTopOffset: (rowIndex: number) => number, | ||
numFrozenRows: number = 0, | ||
numFrozenColumns: number = 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.
A lot of params, but this way I don't have to pass a bloated viewportRect
or grid
object from <Table>
. Thoughts?
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.
seems legit
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.
yeeesh. how many args would passing those props around save? this is an internal method and those args are passed by reference, so no perf penalty.
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 would save two args (get{Top,Left}Offset => grid
, currScroll{Top,Left} => viewportRect
), but the current args are much easier to mock for unit tests.
Also, we don't really need to pass in the current scroll offsets. We could simply have this function return null
for scrollLeft
or scrollTop
if that respective coordinate doesn't need to change, then the caller can substitute the current value for null
in its scope. That reduces the params to:
region: IRegion;
getLeftOffset: (columnIndex: number) => number;
getTopOffset: (rowIndex: number) => number;
numFrozenRows: number = 0;
numFrozenColumns: number = 0;
Thoughts? @themadcreator @giladgray
EDIT: Actually, I guess this isn't quite true given the need for clamping of values within the frozen area. Ignore the above.
const { scrollContainer } = this.quadrantRefs[QuadrantType.MAIN]; | ||
this.wasMainQuadrantScrollChangedFromOtherOnWheelCallback = false; | ||
// this will trigger the main quadrant's scroll callback below | ||
|
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.
Can delete this new line.
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.
Done
@@ -368,12 +380,17 @@ export class TableQuadrantStack extends AbstractComponent<ITableQuadrantStackPro | |||
// use the more generic "scroll" event for the main quadrant, which captures both click+dragging | |||
// on the scrollbar and trackpad/mousewheel gestures | |||
private handleMainQuadrantScroll = (event: React.UIEvent<HTMLElement>) => { | |||
console.log("[TableQuadrantStack] handleMainQuadrantScroll"); |
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.
Can delete these console.log
s.
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.
Done
@@ -1030,48 +1081,31 @@ export class Table extends AbstractComponent<ITableProps, ITableState> { | |||
const rowIndexEnd = showFrozenRowsOnly ? numFrozenRows : rowIndices.rowIndexEnd; | |||
|
|||
return ( | |||
// <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.
Went ahead and deleted this unrelated commented code. Was a relic from the frozen rows/columns refactor.
// we need to modify the body element explicitly for the viewport to shift | ||
// we need to modify the scroll container explicitly for the viewport to shift. in so | ||
// doing, we add the size of the header elements, which are not technically part of the | ||
// "grid" concept (the grid only consists of body cells at present). |
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 was actually a regression that happened during the frozen rows/columns refactor. But all that broke was auto-scrolling as you use arrow keys to move the focus cell around. Still, works again now. 👍
|
||
// NOTE: The following are not exported in the public API: | ||
// - Errors | ||
// - internal/ |
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.
👍 yes nice
import { IRegion, RegionCardinality, Regions } from "../../regions"; | ||
|
||
// Public | ||
// ====== |
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.
not sure these headings are useful... the keyword export
gets the point across.
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.
Removed
getTopOffset: (rowIndex: number) => number, | ||
numFrozenRows: number = 0, | ||
numFrozenColumns: number = 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.
seems legit
// scroll to the left side of the column block | ||
const leftOffset = getLeftOffset(region.cols[0]); | ||
nextLeft = getAdjustedScrollPosition(leftOffset, frozenColumnsCumulativeWidth); | ||
} else { |
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 might make more sense as a switch
? cuz that's exactly what it is, on enum values.
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 like to avoid switches unless I can return
immediately within each case; otherwise I have an extra break;
line at the end of every case, which I find bloat-y and unsightly.
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.
Also, TIL if you redeclare variables with the same name within different switch
cases (e.g. topOffset
and leftOffset
), you get this compilation error: [ts] Cannot redeclare block-scoped variable 'leftOffset'.
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 can always put your switch-case in a block
case 1: {
const foo = 1;
break;
}
case 2: {
const foo = 2;
break;
}
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.
break
s are annoying yes, but the benefit of compile-time warnings shouldn't be discarded.
also, you can refactor the switch to a function so you can use returns
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.
@themadcreator nice, will change
function getAdjustedScrollPosition(scrollOffset: number, frozenRegionCumulativeSize: number) { | ||
return scrollOffset < frozenRegionCumulativeSize | ||
? 0 | ||
: scrollOffset - frozenRegionCumulativeSize; |
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.
equivalent to Math.min(scrollOffset - frozenRegionCumulativeSize, 0)
, i think.
perhaps, instead of this function, you can just do the subtraction inline and the Math.min
once when building the return
object?
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 a little clearer to me as is, but I can change it and just add a comment about the intent like // if the new scroll offset falls within the frozen region, clamp it to 0
EDIT: Math.max
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.
Fixed
if (this.wasMainQuadrantScrollChangedFromOtherOnWheelCallback) { | ||
console.log(" this.wasMainQuadrantScrollChangedFromOtherOnWheelCallback == true."); | ||
console.log(" Setting this.wasMainQuadrantScrollChangedFromOtherOnWheelCallback = false. RETURNING"); |
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.
😱 🌳 logs 🌳 😱
packages/table/src/table.tsx
Outdated
* Scroll the left-most column in the target region to the left side of the viewport. | ||
* | ||
* FULL_TABLE: | ||
* Scroll the top-left cell to the top-left corner of the viewport. |
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.
can cut lines in a third by formatting as list.
- `CELLS`: words words
- `FULL_ROWS`: more words
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.
One of them was too long to fit and line-wrapped; this format takes more lines but keeps everything looking perfectly consistent. I'll see if I can whittle the words down and go back to a list.
Fix lint againPreview: documentation | table |
a01046f
to
dfb5a9c
Compare
seeing 1 small possible |
@tgreenwatts I can't repro that. When I focus on C1 and then |
Added docs, new 'Instance methods' sectionPreview: documentation | table |
Update stuff per CR feedbackPreview: documentation | table |
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.
👌
Oops, revert changes to table examplePreview: documentation | table |
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.
baller tests. many nits, but nothing blocking.
return; | ||
} | ||
|
||
this.tableInstance.scrollToRegion(region); |
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.
is invoking methods on the table ref the standard way for actions like this? do we have other components/features that do this? is this what users will expect?
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 guess the resizeRows does this. still, is this the idiomatic blueprint way to invoke actions on react components?
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, there are a few instance methods around the codebase, each of which operates on a ref
. The goal here was to provide a way to briefly control the scroll without forcing the caller to control it forever and always (would be annoying and unnecessary). See:
// Table
// =====
resizeRowsByTallestCell(columnIndices?: number | number[]): void;
// Toast
// =====
clear(): void;
dismiss(key: string): void;
getToasts(): IToastProps[];
show(props: IToastProps): string;
update(key: string, props: IToastProps): void;
// Tree
// ====
getNodeContentElement(nodeId: string | number): HTMLElement | undefined;
@@ -724,6 +832,10 @@ class MutableTable extends React.Component<{}, IMutableTableState> { | |||
return handleNumberChange((value) => this.setState({ [stateKey]: value })); | |||
} | |||
|
|||
private handleRegionCardinalityChange = (stateKey: keyof IMutableTableState) => { | |||
return handleNumberChange((value) => this.setState({ [stateKey]: value })); |
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.
man this pattern is so common, i wish react had a shorthand for this.
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.
e.g. 4 lines above
@@ -0,0 +1,68 @@ | |||
/** |
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.
that's cool, if you add more do you think it's useful to keep track of an index.ts
that re-exports? it's a tradeoff between maintenance of index.ts and simplifying import lines.
getTopOffset: (rowIndex: number) => number, | ||
numFrozenRows: number = 0, | ||
numFrozenColumns: number = 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.
yeeesh. how many args would passing those props around save? this is an internal method and those args are passed by reference, so no perf penalty.
const frozenColumnsCumulativeWidth = getLeftOffset(numFrozenColumns); | ||
const frozenRowsCumulativeHeight = getTopOffset(numFrozenRows); | ||
|
||
if (cardinality === RegionCardinality.CELLS) { |
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 enums, i think we should use switch statements to make default:
lintable.
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.
Ah, good thought. Will change.
@@ -435,6 +439,74 @@ export class Table extends AbstractComponent<ITableProps, ITableState> { | |||
}; | |||
} | |||
|
|||
// Instance methods |
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.
nit: these sort of block comments indicate the file is too big and should be split. "nit" because i know that splitting is extremely difficult at this point.
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.
Agree. I want to change this later (not this PR).
* If the target region is close to the bottom-right corner of the table, this function will | ||
* simply scroll the target region as close to the top-left as possible until the bottom-right | ||
* corner is reached. | ||
*/ |
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 wish there were some way to annotate these methods so we could easily use the jsdoc comment in our docs. appreciate your update the docs markdown though
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.
Samesies.
* corner is reached. | ||
*/ | ||
public scrollToRegion(region: IRegion) { | ||
const { left: currScrollLeft, top: currScrollTop } = this.state.viewportRect; |
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.
holy crap, i didn't know you could rename with destructuring assignment!
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 I just learned this last month. Super nifty. 👍
this.grid.getCumulativeHeightBefore, | ||
numFrozenRows, | ||
numFrozenColumns, | ||
); |
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.
looks like passing the objects would only save a couple args =/
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.
still, i prefer many args over jamming them into a custom argument object
if (didScrollTopChange) { | ||
this.bodyElement.scrollTop = nextScrollTop; | ||
const topCorrection = this.shouldDisableVerticalScroll() ? 0 : this.columnHeaderElement.clientHeight; |
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 you use "adjustment" and "correction" interchangeably. choose one
@giladgray @themadcreator think I addressed all of your immediate comments. |
Fix stuff per bdwyer CRPreview: documentation | table |
Addressed all comments from before
Merge branch 'master' into cl/table-scroll-to-positionPreview: documentation | table |
Fixes #1065, Fixes #1430
Checklist
Changes proposed in this pull request:
Table
now offers ascrollToRegion(region: IRegion)
instance method to programmatically scroll a desired row, column, or cell into view!scrollTop=0
.scrollLeft=0
.TOP_LEFT
cell will move the table back to (0, 0).Table
instance methods:resizeRowsByTallestCell
scrollToRegion
scrollTo
controls on theTable
example page.Followup work (for later)
animated
option to scrollToRegion instance method #1501 Add ananimated: boolean
option to animate the scroll to the new region. // CC @llorcaReviewers should focus on:
animated
option for now, because that'd actually be a lot more work.Screenshot