-
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
Editor2 #2102
Editor2 #2102
Conversation
src/DataGrid.tsx
Outdated
if (isUsingEditor2) { | ||
if (key === 'Delete' || key === 'Backspace') { | ||
setEditorValue(''); | ||
} else if (key === 'Enter' || key === 'F2') { |
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.
@@ -504,8 +548,10 @@ function DataGrid<R, K extends keyof R, SR>({ | |||
|
|||
function selectCell(position: Position, enableEditor = false): void { | |||
if (!isCellWithinBounds(position)) return; | |||
handleCommit2(); |
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 it is better to have some control over when the value is committed. This way we can add validation in the future. Using an effect to commit the value is probably not the best way
https://github.com/adazzle/react-data-grid/blob/c/src/editors/EditorContainer.tsx#L70
src/DataGrid.tsx
Outdated
|
||
if (canOpenEditor && (key === 'Enter' || isActivatedByUser)) { | ||
const isActivatedByUser = isUsingEditor2 | ||
? defaultCellInput(event) && column.editor2Props?.onCellInput?.(event) !== 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 think we should open the editor on Enter
, F2
, or on any other alpha numeric key. If the users need to restrict it then they can use onCellInput
. This is also the recommended way
https://www.w3.org/TR/wai-aria-practices/#gridNav_inside
src/types.ts
Outdated
editor2Props?: { | ||
createPortal?: boolean; | ||
editOnSingleClick?: boolean; | ||
commitOnOutsideClick?: 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.
This needs to be implemented.
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.
Perhaps add a TODO
comment?
stories/demos/TreeView.tsx
Outdated
} | ||
// editor2Props: { |
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 can handle it differently using https://www.w3.org/TR/wai-aria-practices/#gridNav_focus
export function TextEditor<R>({ value, onChange, rowHeight }: Editor2Props<string, R>) { | ||
const inputRef = useRef<HTMLInputElement>(null); | ||
|
||
useLayoutEffect(() => { |
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 no longer have the bug where the input value is initially selected and typing another key deletes it
}) | ||
}} | ||
onKeyDown={event => { | ||
if (['ArrowDown', 'ArrowUp', 'Enter'].includes(event.key) && selectRef.current!.getProp('menuIsOpen')) { |
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 is the best way to cancel keyboard navigation IMO
src/DataGrid.tsx
Outdated
|
||
if (canOpenEditor && (key === 'Enter' || isActivatedByUser)) { | ||
if (selectedPosition.mode === 'EDIT') { | ||
if (isUsingEditor2 && (key === 'Enter' || key === 'F2')) { |
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 happens if we have such editors that has 2 or more input / textarea?
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 it is not possible to have one solution for every type of editor and we need some way to to tweak the default behavior. If the editor do not need to commit changes on Enter
then it should handle the keydown
event and stop propagation. The question is what default behavior we want to use? Right now we do the following
- Open editor on
Enter
,F2
and alphanumeric keys - Cancel changes on
Escape
- Commit changes on
Enter
andF2
- Commit changes when another cell is clicked
- Commit changes when header or summary row is clicked
Any suggestions?
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 for the Enter
case, we shall leave a prop for the user to decide if we want to use Enter
to close the the editor.
It is a common use case that people might want to put a save button with 2 inputs for example, Enter
would mean something else for them. As for F2, maybe we should keep like excel to only open the cell rather than closing it. Esc
is pretty straightforward, just close without saving / commit.
src/Cell.tsx
Outdated
rowIdx={rowIdx} | ||
row={row} | ||
column={column} | ||
left={gridLeft} |
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.
Should we move left / top before ln#80, so that we can retain the ability for overriding the position to make it more flexible?
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.
Editor can choose not to use left, right
props. The only way to override them is in the editor itself. We control editor2Props
and not the user
src/types.ts
Outdated
editor2Props?: { | ||
createPortal?: boolean; | ||
editOnSingleClick?: boolean; | ||
commitOnOutsideClick?: 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.
Perhaps add a TODO
comment?
src/DataGrid.tsx
Outdated
onRowsUpdate?.({ | ||
cellKey, | ||
fromRow: rowIdx, | ||
toRow: rowIdx, | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
updated: { [cellKey]: editorValue } as never, | ||
action: UpdateActions.CELL_UPDATE | ||
}); |
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 like to rethink this someday, maybe something like
// single value update
onDataUpdate({
type: ENUM.UPDATE_TYPE,
value: unknown, // is it possible to type this? probably not, I think each column should have its own handler
row,
rowIdx,
column
})
// multi value
update
onDataUpdate({
type: ENUM.MULTI_UPDATE_TYPE,
updates: [{ value, row, rowIdx, column }]
})
An object for updated
doesn't make sense to me, going forward we should aim to make rows completely generic.
src/DataGrid.tsx
Outdated
@@ -223,6 +223,7 @@ function DataGrid<R, K extends keyof R, SR>({ | |||
const [copiedPosition, setCopiedPosition] = useState<Position & { value: unknown } | null>(null); | |||
const [isDragging, setDragging] = useState(false); | |||
const [draggedOverRowIdx, setOverRowIdx] = useState<number | undefined>(undefined); | |||
const [editorValue, setEditorValue] = useState<unknown>(); |
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.
Should this state and all the handlers really be in the root component?
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 to control when the value gets committed #2102 (comment) and for that root component needs access to editorValue. Any other way to handle it?
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 maybe the editor container could have it in its scope
src/DataGrid.tsx
Outdated
if (isCellEditable(selectedPosition) && defaultCellInput(event)) { | ||
if (isUsingEditor2) { | ||
if (key === 'Delete' || key === 'Backspace') { | ||
setEditorValue(''); |
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.
''
doesn't make sense for non-string columns.
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 need to define the default behavior and provide a way to override it
#2102 (comment)
src/DataGrid.tsx
Outdated
if (key === 'Delete' || key === 'Backspace') { | ||
setEditorValue(''); | ||
} else if (key === 'Enter' || key === 'F2') { | ||
setEditorValue(column.editor2Options?.getInitialValue ? column.editor2Options?.getInitialValue(event, row) : row[column.key as keyof 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.
row[column.key as keyof R]
assumes that the row is an object.
src/editors/EditorContainer.tsx
Outdated
@@ -133,7 +133,7 @@ export default function EditorContainer<R, SR>({ | |||
function onKeyDown(e: KeyboardEvent) { | |||
if (preventDefaultNavigation(e.key)) { | |||
e.stopPropagation(); | |||
} else if (['Enter', 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) { | |||
} else if (['Enter', 'F2', 'Tab', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(e.key)) { |
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.
In Windows' file explorer, if I press F2 twice, it doesn't commit anything, it keeps the editing state.
Same thing in a google spreadsheet.
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 it. I was following the accessibility guidelines
src/types.ts
Outdated
getInitialValue?: (event: React.KeyboardEvent<HTMLDivElement>, row: TRow) => unknown; | ||
onCellInput?: (event: React.KeyboardEvent<HTMLDivElement>) => 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's the difference between these two?
Co-authored-by: Nicolas Stepien <[email protected]>
src/SummaryRow.tsx
Outdated
@@ -29,6 +32,7 @@ function SummaryRow<R, SR>({ | |||
aria-rowindex={ariaRowIndex} | |||
className={`rdg-row rdg-row-${rowIdx % 2 === 0 ? 'even' : 'odd'} rdg-summary-row`} | |||
style={{ bottom }} | |||
onClick={onClick} |
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 accidentally deleted your comment
Why this change? I don't think we expose SummaryRow?
So we can commit the value when summary row is clicked. We need to decide on when to commit the 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.
Needs a changelog
src/types.ts
Outdated
editor2Options?: { | ||
createPortal?: boolean; | ||
singleClickEdit?: boolean; | ||
commitOnOutsideClick?: 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.
Let's remove or comment this out if it won't be implemented in this PR.
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 was a quick implementation so I added it
src/types.ts
Outdated
// editOnDoubleClick?: boolean; | ||
// commitOnScroll?: boolean; | ||
// commitOnOutsideClick?: boolean; | ||
onCellInput?: (event: React.KeyboardEvent<HTMLDivElement>) => void; // Prevent default to cancel editing |
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.
Should we rename it to onKeyDown
or onCellKeyDown
?
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's only used when the editor is closed right? Then onCellKeyDown
makes more sense.
src/DataGrid.tsx
Outdated
} | ||
|
||
function handleSort(columnKey: string, direction: SortDirection) { | ||
handleCommit2(); |
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 still needed as the cell is unmounted before useOutsideClick
runs
))} | ||
{viewportColumns.map(column => { | ||
const isCellSelected = selectedCellProps?.idx === column.idx; | ||
if (selectedCellProps?.mode === 'EDIT' && isCellSelected) { |
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.
if (selectedCellProps?.mode === 'EDIT' && isCellSelected) { | |
if (isCellSelected && selectedCellProps!.mode === 'EDIT') { |
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.
src/Row.tsx
Outdated
isCellSelected={isCellSelected} | ||
isRowSelected={isRowSelected} | ||
eventBus={eventBus} | ||
dragHandleProps={isCellSelected ? (selectedCellProps as SelectedCellProps)?.dragHandleProps : 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.
Do we need the type assertion and ?.
?
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.
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.
if isCellSelected
is true, then we can assert that selectedCellProps
isn't nullish, so we can add !
.
src/Row.tsx
Outdated
isRowSelected={isRowSelected} | ||
eventBus={eventBus} | ||
dragHandleProps={isCellSelected ? (selectedCellProps as SelectedCellProps)?.dragHandleProps : undefined} | ||
onKeyDown={isCellSelected ? selectedCellProps?.onKeyDown : 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.
Do we need ?.
?
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.
Same reason.
New API
How to use
DataGrid
now keeps the updated row in a local state so it is easy to handle commit, cancel and validations (future).