Fix click to show document issue. Table doesn't auto-select first document anymore. (#1864)

This commit is contained in:
Laurent Nguyen 2024-06-07 16:28:08 +02:00 committed by GitHub
parent 416743c548
commit 06d4829422
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 81 additions and 76 deletions

View File

@ -86,6 +86,9 @@ export class DocumentsTabV2 extends TabsBase {
} }
} }
// Use this value to initialize the very time the component is rendered
const RESET_INDEX = -1;
const filterButtonStyle: CSSProperties = { const filterButtonStyle: CSSProperties = {
marginLeft: 8, marginLeft: 8,
}; };
@ -465,7 +468,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const [selectedDocumentContentBaseline, setSelectedDocumentContentBaseline] = useState<string>(undefined); const [selectedDocumentContentBaseline, setSelectedDocumentContentBaseline] = useState<string>(undefined);
// Table user clicked on this row // Table user clicked on this row
const [clickedRow, setClickedRow] = useState<TableRowId>(undefined); const [clickedRowIndex, setClickedRowIndex] = useState<number>(RESET_INDEX);
// Table multiple selection // Table multiple selection
const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(() => new Set<TableRowId>([0])); const [selectedRows, setSelectedRows] = React.useState<Set<TableRowId>>(() => new Set<TableRowId>([0]));
@ -490,6 +493,23 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
} }
}, [isFilterFocused]); }, [isFilterFocused]);
// Clicked row must be defined
useEffect(() => {
if (documentIds.length > 0) {
let currentClickedRowIndex = clickedRowIndex;
if (
(currentClickedRowIndex === RESET_INDEX &&
editorState === ViewModels.DocumentExplorerState.noDocumentSelected) ||
currentClickedRowIndex > documentIds.length - 1
) {
// reset clicked row or the current clicked row is out of bounds
currentClickedRowIndex = 0;
setSelectedRows(new Set([0]));
onDocumentClicked(currentClickedRowIndex, documentIds);
}
}
}, [documentIds, clickedRowIndex, editorState]);
let lastFilterContents = ['WHERE c.id = "foo"', "ORDER BY c._ts DESC", 'WHERE c.id = "foo" ORDER BY c._ts DESC']; let lastFilterContents = ['WHERE c.id = "foo"', "ORDER BY c._ts DESC", 'WHERE c.id = "foo" ORDER BY c._ts DESC'];
const applyFilterButton = { const applyFilterButton = {
@ -550,7 +570,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
if (!documentsIterator) { if (!documentsIterator) {
try { try {
refreshDocumentsGrid(); refreshDocumentsGrid(false);
} catch (error) { } catch (error) {
if (onLoadStartKey !== null && onLoadStartKey !== undefined) { if (onLoadStartKey !== null && onLoadStartKey !== undefined) {
TelemetryProcessor.traceFailure( TelemetryProcessor.traceFailure(
@ -657,7 +677,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
setSelectedDocumentContent(defaultDocument); setSelectedDocumentContent(defaultDocument);
setSelectedDocumentContentBaseline(defaultDocument); setSelectedDocumentContentBaseline(defaultDocument);
setSelectedRows(new Set()); setSelectedRows(new Set());
setClickedRow(undefined); setClickedRowIndex(undefined);
setEditorState(ViewModels.DocumentExplorerState.newDocumentValid); setEditorState(ViewModels.DocumentExplorerState.newDocumentValid);
}; };
@ -673,8 +693,10 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return createDocument(_collection, document) return createDocument(_collection, document)
.then( .then(
(savedDocument: DataModels.DocumentId) => { (savedDocument: DataModels.DocumentId) => {
// TODO: Reuse initDocumentEditor() to remove code duplication
const value: string = renderObjectForEditor(savedDocument || {}, null, 4); const value: string = renderObjectForEditor(savedDocument || {}, null, 4);
setSelectedDocumentContentBaseline(value); setSelectedDocumentContentBaseline(value);
setSelectedDocumentContent(value);
setInitialDocumentContent(value); setInitialDocumentContent(value);
const partitionKeyValueArray: PartitionKey[] = extractPartitionKeyValues( const partitionKeyValueArray: PartitionKey[] = extractPartitionKeyValues(
savedDocument, savedDocument,
@ -738,7 +760,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
partitionKey as PartitionKeyDefinition, partitionKey as PartitionKeyDefinition,
); );
const selectedDocumentId = documentIds[clickedRow as number]; const selectedDocumentId = documentIds[clickedRowIndex as number];
selectedDocumentId.partitionKeyValue = partitionKeyValueArray; selectedDocumentId.partitionKeyValue = partitionKeyValueArray;
onExecutionErrorChange(false); onExecutionErrorChange(false);
@ -786,7 +808,15 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
}, },
) )
.finally(() => setIsExecuting(false)); .finally(() => setIsExecuting(false));
}, [onExecutionErrorChange, tabTitle, selectedDocumentContent, _collection, partitionKey, documentIds, clickedRow]); }, [
onExecutionErrorChange,
tabTitle,
selectedDocumentContent,
_collection,
partitionKey,
documentIds,
clickedRowIndex,
]);
const onRevertExistingDocumentClick = useCallback((): void => { const onRevertExistingDocumentClick = useCallback((): void => {
setSelectedDocumentContentBaseline(initialDocumentContent); setSelectedDocumentContentBaseline(initialDocumentContent);
@ -850,7 +880,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
setDocumentIds(newDocumentIds); setDocumentIds(newDocumentIds);
setSelectedDocumentContent(undefined); setSelectedDocumentContent(undefined);
setClickedRow(undefined); setClickedRowIndex(undefined);
setSelectedRows(new Set()); setSelectedRows(new Set());
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected); setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
useDialog useDialog
@ -974,8 +1004,27 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
return true; return true;
}; };
const updateDocumentIds = (newDocumentsIds: DocumentId[]): void => {
setDocumentIds(newDocumentsIds);
if (onLoadStartKey !== null && onLoadStartKey !== undefined) {
TelemetryProcessor.traceSuccess(
Action.Tab,
{
databaseName: _collection.databaseId,
collectionName: _collection.id(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle,
},
onLoadStartKey,
);
setOnLoadStartKey(undefined);
}
};
let loadNextPage = useCallback( let loadNextPage = useCallback(
(iterator: QueryIterator<ItemDefinition & Resource>, applyFilterButtonClicked?: boolean): Promise<unknown> => { (iterator: QueryIterator<ItemDefinition & Resource>, applyFilterButtonClicked: boolean): Promise<unknown> => {
setIsExecuting(true); setIsExecuting(true);
onExecutionErrorChange(false); onExecutionErrorChange(false);
let automaticallyCancelQueryAfterTimeout: boolean; let automaticallyCancelQueryAfterTimeout: boolean;
@ -1028,21 +1077,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
}); });
const merged = currentDocuments.concat(nextDocumentIds); const merged = currentDocuments.concat(nextDocumentIds);
setDocumentIds(merged); updateDocumentIds(merged);
if (onLoadStartKey !== null && onLoadStartKey !== undefined) {
TelemetryProcessor.traceSuccess(
Action.Tab,
{
databaseName: _collection.databaseId,
collectionName: _collection.id(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle,
},
onLoadStartKey,
);
setOnLoadStartKey(undefined);
}
}, },
(error) => { (error) => {
onExecutionErrorChange(true); onExecutionErrorChange(true);
@ -1112,7 +1147,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const onLoadMoreKeyInput: KeyboardEventHandler<HTMLAnchorElement> = (event) => { const onLoadMoreKeyInput: KeyboardEventHandler<HTMLAnchorElement> = (event) => {
if (event.key === " " || event.key === "Enter") { if (event.key === " " || event.key === "Enter") {
const focusElement = event.target as HTMLElement; const focusElement = event.target as HTMLElement;
loadNextPage(documentsIterator.iterator); loadNextPage(documentsIterator.iterator, false);
focusElement && focusElement.focus(); focusElement && focusElement.focus();
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
@ -1158,10 +1193,10 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
* Document has been clicked on in table * Document has been clicked on in table
* @param tabRowId * @param tabRowId
*/ */
const onDocumentClicked = (tabRowId: TableRowId) => { const onDocumentClicked = (tabRowId: TableRowId, currentDocumentIds: DocumentId[]) => {
const index = tabRowId as number; const index = tabRowId as number;
setClickedRow(index); setClickedRowIndex(index);
loadDocument(documentIds[index]); loadDocument(currentDocumentIds[index]);
}; };
let loadDocument = (documentId: DocumentId) => let loadDocument = (documentId: DocumentId) =>
@ -1267,13 +1302,13 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
confirmDiscardingChange(() => { confirmDiscardingChange(() => {
if (selectedRows.size === 0) { if (selectedRows.size === 0) {
setSelectedDocumentContent(undefined); setSelectedDocumentContent(undefined);
setClickedRow(undefined); setClickedRowIndex(undefined);
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected); setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
} }
// Find if clickedRow is in selectedRows.If not, clear clickedRow and content // Find if clickedRow is in selectedRows.If not, clear clickedRow and content
if (clickedRow !== undefined && !selectedRows.has(clickedRow)) { if (clickedRowIndex !== undefined && !selectedRows.has(clickedRowIndex)) {
setClickedRow(undefined); setClickedRowIndex(undefined);
setSelectedDocumentContent(undefined); setSelectedDocumentContent(undefined);
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected); setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
} }
@ -1281,6 +1316,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
// If only one selection, we consider as a click // If only one selection, we consider as a click
if (selectedRows.size === 1) { if (selectedRows.size === 1) {
setEditorState(ViewModels.DocumentExplorerState.existingDocumentNoEdits); setEditorState(ViewModels.DocumentExplorerState.existingDocumentNoEdits);
onDocumentClicked(selectedRows.values().next().value, documentIds);
} }
setSelectedRows(selectedRows); setSelectedRows(selectedRows);
@ -1441,6 +1477,8 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
const value: string = renderObjectForEditor(savedDocument || {}, null, 4); const value: string = renderObjectForEditor(savedDocument || {}, null, 4);
setSelectedDocumentContentBaseline(value); setSelectedDocumentContentBaseline(value);
setSelectedDocumentContent(value);
setInitialDocumentContent(value);
setDocumentIds(ids); setDocumentIds(ids);
setEditorState(ViewModels.DocumentExplorerState.existingDocumentNoEdits); setEditorState(ViewModels.DocumentExplorerState.existingDocumentNoEdits);
@ -1491,7 +1529,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
tabTitle, tabTitle,
}); });
const selectedDocumentId = documentIds[clickedRow as number]; const selectedDocumentId = documentIds[clickedRowIndex as number];
return MongoProxyClient.updateDocument( return MongoProxyClient.updateDocument(
_collection.databaseId, _collection.databaseId,
_collection as ViewModels.Collection, _collection as ViewModels.Collection,
@ -1559,8 +1597,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
.then( .then(
({ continuationToken: newContinuationToken, documents }) => { ({ continuationToken: newContinuationToken, documents }) => {
setContinuationToken(newContinuationToken); setContinuationToken(newContinuationToken);
let currentDocuments = documentIds; const currentDocumentsRids = documentIds.map((currentDocument) => currentDocument.rid);
const currentDocumentsRids = currentDocuments.map((currentDocument) => currentDocument.rid);
const nextDocumentIds = documents const nextDocumentIds = documents
.filter((d: { _rid: string }) => { .filter((d: { _rid: string }) => {
return currentDocumentsRids.indexOf(d._rid) < 0; return currentDocumentsRids.indexOf(d._rid) < 0;
@ -1569,34 +1606,10 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
.map((rawDocument: any) => { .map((rawDocument: any) => {
const partitionKeyValue = rawDocument._partitionKeyValue; const partitionKeyValue = rawDocument._partitionKeyValue;
return newDocumentId(rawDocument, partitionKeyProperties, [partitionKeyValue]); return newDocumentId(rawDocument, partitionKeyProperties, [partitionKeyValue]);
// return new DocumentId(this, rawDocument, [partitionKeyValue]);
}); });
const merged = currentDocuments.concat(nextDocumentIds); const merged = documentIds.concat(nextDocumentIds);
updateDocumentIds(merged);
setDocumentIds(merged);
currentDocuments = merged;
if (filterContent.length > 0 && currentDocuments.length > 0) {
currentDocuments[0].click();
} else {
setSelectedDocumentContent("");
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
}
if (_onLoadStartKey !== null && _onLoadStartKey !== undefined) {
TelemetryProcessor.traceSuccess(
Action.Tab,
{
databaseName: _collection.databaseId,
collectionName: _collection.id(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle,
},
_onLoadStartKey,
);
setOnLoadStartKey(undefined);
}
}, },
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(error: any) => { (error: any) => {
@ -1624,7 +1637,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
// ***************** Mongo *************************** // ***************** Mongo ***************************
const refreshDocumentsGrid = useCallback( const refreshDocumentsGrid = useCallback(
async (applyFilterButtonPressed?: boolean): Promise<void> => { (applyFilterButtonPressed: boolean): void => {
// clear documents grid // clear documents grid
setDocumentIds([]); setDocumentIds([]);
setContinuationToken(undefined); // For mongo setContinuationToken(undefined); // For mongo
@ -1638,6 +1651,13 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
// collapse filter // collapse filter
setAppliedFilter(filterContent); setAppliedFilter(filterContent);
setIsFilterExpanded(false); setIsFilterExpanded(false);
// If apply filter is pressed, reset current selected document
if (applyFilterButtonPressed) {
setClickedRowIndex(RESET_INDEX);
setEditorState(ViewModels.DocumentExplorerState.noDocumentSelected);
setSelectedDocumentContent(undefined);
}
} catch (error) { } catch (error) {
console.error(error); console.error(error);
useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error)); useDialog.getState().showOkModalDialog("Refresh documents grid failed", getErrorMessage(error));
@ -1774,7 +1794,7 @@ export const DocumentsTabComponent: React.FunctionComponent<IDocumentsTabCompone
> >
<DocumentsTableComponent <DocumentsTableComponent
items={tableItems} items={tableItems}
onItemClicked={onDocumentClicked} onItemClicked={(index) => onDocumentClicked(index, documentIds)}
onSelectedRowsChange={onSelectedRowsChange} onSelectedRowsChange={onSelectedRowsChange}
selectedRows={selectedRows} selectedRows={selectedRows}
size={tableContainerSizePx} size={tableContainerSizePx}

View File

@ -25,7 +25,7 @@ import {
import { NormalizedEventKey } from "Common/Constants"; import { NormalizedEventKey } from "Common/Constants";
import { selectionHelper } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper"; import { selectionHelper } from "Explorer/Tabs/DocumentsTabV2/SelectionHelper";
import { isEnvironmentCtrlPressed, isEnvironmentShiftPressed } from "Utils/KeyboardUtils"; import { isEnvironmentCtrlPressed, isEnvironmentShiftPressed } from "Utils/KeyboardUtils";
import React, { useCallback, useEffect, useMemo } from "react"; import React, { useCallback, useMemo } from "react";
import { FixedSizeList as List, ListChildComponentProps } from "react-window"; import { FixedSizeList as List, ListChildComponentProps } from "react-window";
export type DocumentsTableComponentItem = { export type DocumentsTableComponentItem = {
@ -59,7 +59,6 @@ interface ReactWindowRenderFnProps extends ListChildComponentProps {
export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> = ({
items, items,
onItemClicked,
onSelectedRowsChange, onSelectedRowsChange,
selectedRows, selectedRows,
style, style,
@ -67,8 +66,6 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
columnHeaders, columnHeaders,
isSelectionDisabled, isSelectionDisabled,
}: IDocumentsTableComponentProps) => { }: IDocumentsTableComponentProps) => {
const [activeItemIndex, setActiveItemIndex] = React.useState<number>(undefined);
const initialSizingOptions: TableColumnSizingOptions = { const initialSizingOptions: TableColumnSizingOptions = {
id: { id: {
idealWidth: 280, idealWidth: 280,
@ -250,18 +247,6 @@ export const DocumentsTableComponent: React.FC<IDocumentsTableComponentProps> =
[toggleAllRows], [toggleAllRows],
); );
// Load document depending on selection
useEffect(() => {
if (selectedRows.size === 1 && items.length > 0) {
const newActiveItemIndex = selectedRows.values().next().value;
if (newActiveItemIndex !== activeItemIndex) {
onItemClicked(newActiveItemIndex);
setActiveItemIndex(newActiveItemIndex);
setSelectionStartIndex(newActiveItemIndex);
}
}
}, [selectedRows, items, activeItemIndex, onItemClicked]);
// Cell keyboard navigation // Cell keyboard navigation
const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" }); const keyboardNavAttr = useArrowNavigationGroup({ axis: "grid" });