-
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] Support for column and row reordering #963
Conversation
Should undo before merging
Use it in DragSelectable to prevent DragReorderable from triggering its handleActivate on the same mousedown event.
I'm also seeing a lot of duplicated functionality between |
Bugfix: enable reordering right away if selection exists on mountPreview: documentation | landing | table |
Fix tests and increase coverage to 79% againPreview: documentation | landing | table |
packages/table/preview/index.tsx
Outdated
country: array[3], | ||
city: array[4], | ||
// tslint:enable:object-literal-sort-keys | ||
})); |
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 mean you might as well go hard...
([letter, fruit, ...]) => ({ letter, fruit, ... })
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
packages/table/preview/index.tsx
Outdated
class ReorderableTableExample extends React.Component<{}, IReorderableTableExampleState> { | ||
public state = { | ||
data: REORDERABLE_TABLE_DATA, | ||
} as IReorderableTableExampleState; |
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.
always better to declare type up front instead of casting. sometimes you lose type safety when casting, which you don't lose if you type the var first.
public state: IReorderableTableExampleState = { ... }
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
isResizable: true, | ||
loading: false, | ||
}; | ||
|
||
public state = { | ||
hasSelectionEnded: false, | ||
} as IColumnHeaderState; |
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.
again, do it up front. and on one line.
public state: IColumnHeaderState = { hasSelectionEnded: false };
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
return Regions.column(index1, index2); | ||
} | ||
|
||
private isColumnTemporarilyReorderable(isColumnSelected: 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.
what is this for? what do these five conditions mean? code comment please
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
@@ -168,50 +205,98 @@ export class ColumnHeader extends React.Component<IColumnHeaderProps, {}> { | |||
}); | |||
const cellLoading = cell.props.loading != null ? cell.props.loading : loading; | |||
const isColumnSelected = Regions.hasFullColumn(selectedRegions, columnIndex); | |||
const cellProps: IColumnHeaderCellProps = { className, isColumnSelected, loading: cellLoading }; | |||
const isColumnTemporarilyReorderable = this.isColumnTemporarilyReorderable(isColumnSelected); |
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.
does it matter that it's "temporary?" i think "currently" might be a better word, cuz it's determined by props and state. but that's just pure react, so perhaps we don't need such a word at all.
usage doesn't suggest any notion of temporary--you immediately rename it to isColumnReorderable
, which as a prop is understood to only matter in the now, not next render.
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 wasn't sure about this, but I wanted to more strongly disambiguate isReorderable
from isColumnReorderable
. It's not necessarily clear at a glance what the difference is.
@@ -161,45 +197,93 @@ export class RowHeader extends React.Component<IRowHeaderProps, {}> { | |||
}); | |||
const cellLoading = cell.props.loading != null ? cell.props.loading : loading; | |||
const isRowSelected = Regions.hasFullRow(selectedRegions, rowIndex); | |||
const cellProps: IRowHeaderCellProps = { className, isRowSelected, loading: cellLoading }; | |||
const isRowTemporarilyReorderable = this.isRowTemporarilyReorderable(isRowSelected); |
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.
again with the temporary
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
private getDragSelectedRegions(event: MouseEvent, coords: ICoordinateData) { | ||
let region = (this.props.allowMultipleSelection) ? | ||
this.props.locateDrag(event, coords) : | ||
this.props.locateClick(event); |
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.
condition
? true
: false;
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. Done
packages/table/src/regions.ts
Outdated
@@ -548,11 +541,52 @@ export class Regions { | |||
} | |||
} | |||
|
|||
private static overlapsRegionInternal(regions: IRegion[], query: IRegion, allowPartialOverlap = true) { |
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.
👎 why can't this just be overlapsRegion
? the third prop is optional with a default anyway.
i don't see the need for both.
packages/table/src/regions.ts
Outdated
private static regionsEqual(regionA: IRegion, regionB: IRegion) { | ||
return Regions.intervalsEqual(regionA.rows, regionB.rows) | ||
&& Regions.intervalsEqual(regionA.cols, regionB.cols); | ||
} | ||
|
||
private static regionContains(regionA: IRegion, regionB: IRegion) { | ||
// containsRegion expects an array of regions as the first param | ||
return Regions.overlapsRegionInternal([regionA], regionB, /* allowPartialOverlap */ false); |
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 don't think we need the prop name comment in typescript. i can easily see it by hovering on function name.
Update hasSelectionEnded on componentWillReceivePropsPreview: documentation | landing | table |
@giladgray - I fixed the stuff in your CR just now. |
Respond to Gilad CR feedbackPreview: documentation | landing | table |
* Enables/disables the reordering interaction. | ||
* @default true | ||
*/ | ||
isReorderable?: 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 agree with @themadcreator, this should not be exposed at this level if we don't expect to support it. How about if you make it @internal
, does that work? If not, you could try to use component context
to allow <Table>
to talk to its sub components.
* @param newIndex the new index of the element or set of elements | ||
* @param length the number of contiguous elements that were moved | ||
*/ | ||
onReordered: (oldIndex: number, newIndex: number, length: number) => void; |
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.
onReorderEnd
might be more intuitive.
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 couldn't find great precedent for this in the codebase. Slider does onChange
and onRelease
, and DragSelectable
coincidentally now has onSelection
and onSelectionEnd
as of a recent commit in this PR. Regardless, do you think onReordering
still makes sense if we change onReordered
to onReorderEnd
?
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.
well, wouldn't onReorder
work? You have onReordering
while it's going then onReorder
once it's finished. That kind of matches the other method names
* @param newIndex the new index of the element or set of elements | ||
* @param length the number of contiguous elements that were moved | ||
*/ | ||
onReordering: (oldIndex: number, newIndex: number, length: number) => void; |
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.
what is this useful for?
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.
Updating the position of the vertical and horizontal guides as you drag. See table.tsx
.
@cmslewis if I were you, I'd be itching to merge this but I do think that perf regression is pretty bad. Also, it's probably just a matter of checking the stack of Drop some console logs in the render methods and compare before after your changes. If you can't find anything obvious with a concerted effort, we can move on, but I expect users who upgrade to this will not be happy. Edit: Upon further investigation, I've noticed the same perf issue in @gscshoyru's auto-resize PR. So either you both broke it in the same way, or some other change caused the issue. Would still like someone to look into it though less pressure to do it in this PR. |
@cmslewis re: I think I didn't originally do it because there were so many places where we use x/width vs. y/height sprinkled around that it seemed a little annoying to combine. But, If you can reduce those class files by moving at least a few methods into an abstract class, go for it! (not necessary for this PR obvs) |
Yeah I saw the perf issues on @gscshoyru's PR too. Are you sure it's not a difference caused by using production vs. dev builds of React? Either way, try using why-did-you-update. |
approving since the code change looks legit, but we def need some serious perf work ASAP. |
@themadcreator @adidahiya @giladgray would love to look into perf stuff ASAP. From chatting offline, sounds like our plan is to merge this PR and then address perf immediately in another one. The next Blueprint release would block on the followup PR merging. Sound good? |
Also @giladgray FYI I just pushed a fix for a minor visual regression (hiding selected regions while resizing) and added tests to verify everything works there as expected. |
Re-add isGuidesShowing behavior + unit testsPreview: documentation | landing | 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.
Cool, let's do this! What a sweet feature
@@ -56,6 +56,10 @@ export class ElementHarness { | |||
this.element = element; | |||
} | |||
|
|||
public exists() { | |||
return this.element != null; | |||
} |
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.
👍
Fixes #213
Checklist
Update documentation(will add language in a separate PR if we want it)TruncatedPopover
Changes proposed in this pull request:
Behold: column and row reordering in
Table
.Basic points:
Table
props.FULL_COLUMNS
orFULL_ROWS
cardinality).Table
does not handle the actual reordering! It simply emits(oldIndex, newIndex, length)
and lets the caller pass in updated data.Reviewers should focus on:
grabbing
no matter where your mouse moved during the drag. LMK what you think.Screenshot