Migrate DocumentsTab to React and add bulk delete and column resize (#1770)

* Document page now loads list of docs and displays selection

* DocumentsTabV2 now properly loads documents, show partition keys and display first doc with proper selection behavior. Move it to its own folder.

* Extract table in a separate component

* Resizable columns on the document table

* Fix selection behavior and some layout issue

* Adding table scrolling

* Fix NaN height issue

* Fix NaN height issue

* Fix column sizing + cell selection

* Improvement in width size. Add Load More

* Add react editor and pass column headers

* Dynamic columns for pk

* Fix initial columns size

* Add nav buttons

* Editing content updates buttons state

* Discard and save buttons working

* Fix save new document. Implement delete.

* Remove debug display

* Fix unexpand filter and reformat

* Fix compil issues

* Add refresh button

* Update column header placeholder style

* Implement delete multiple docs

* Fix multi delete

* Fix show/hide delete button

* Fix selection behavior

* Fix UX with buttons behavior and editor display

* Fix UX issue with not discarding edit changes

* Add some TODO's

* Remove debugging info and reformat

* Add mongo support

* Fix build issues

* Fix table header. Remove debug statement

* Restore broken nosql

* Fix mongo save new document/update document

* Fix bugs with clicking on newly created documents

* Fix comment

* Fix double fetch issue when clicking on an item

* Auto-select last document when saving new document

* Fix resourceTokenPartitionKey code

* Fix format

* Fix isQueryCopilotSampleContainer flag

* Fix unused code

* Call tab when updating error flag

* Destructure props to make useEffect dependencies work

* Fix loadStartKey

* minor update

* Fix format

* Add title to table

* Fix table coming off its container with unwanted horizontal scrollbar

* Increase table width. Fix eslint issue.

* Move refresh documents button from table back to DocumentsTabV2

* Fix load more text centering

* Don't show Load More if nothing to show

* Fix columns min width

* Add keyboard shortcuts

* Add keyboard handlers to load more and refresh button

* Add keyboard support to select table row

* Disable eslint issue from fluent library

* Connect cancel query button

* Add Fluent V9 theme for Fabric (#1821)

* Clean up dependencies and memoize internal functions and object. Move methods and object that don't depend on state outside of component.

* Fix filter disappearing when clicking Apply Filter

* Fix typo and format

* Implement bulk delete for nosql

* Replace filter ui components with fluent ui

* Remove jquery calls

* Migrate unit test to DocumentsTabV2

* Remove DocumentsTab and MongoDocumentsTab. Fix build issues.

* Properly handle activetab

* Remove comments and unused code

* Port keyboard shortcuts from commitId 1f4d0f2

* Port item editor shortcuts to improved Items tab branch (#1831)

* set filter focus on Ctrl+Shift+F

* implement filter enter/esc keybinds

* remove debugging

* Collapse filter when query is executed

* Fix monaco editor not happy when parent is null

* Fix how bulk delete operation gets called when no partition key

* Fix update id list after delete

* Fix deleteDocuments

* Fix build issue

* Fix bug in mongo delete

* Fix mongo delete flow

* Proper error handling in mongo

* Handle >100 bulk delete operations

* Add unit tests for DocumentsTableComponent

* More improvements to table unit tests

* Fix import. Disable selection test for now

* Add more DocumentsTab unit react tests

* Remove selection test

* Add more unit tests. Add lcov coverage report to display in vscode

* Move unit tests to correct file

* Add unit test on command bar

* Fix build issues

* Add more unit tests

* Remove unneeded call

* Add DocumentsTab for Mongo API

* Fix linting errors

* Update fluent ui v9 dependency. Color columns separation. Fix refresh button placement to not interfere with header cell width.

* Revert @fluentui/react-components to a safe version that compiles

* Add confirmation window when documents have been deleted

* Fix mongo unit tests

* Fix format

* Update src/Common/dataAccess/deleteDocument.ts

Co-authored-by: Ashley Stanton-Nurse <ashleyst@microsoft.com>

* Update src/Common/dataAccess/deleteDocument.ts

Co-authored-by: Ashley Stanton-Nurse <ashleyst@microsoft.com>

* Update src/Common/dataAccess/deleteDocument.ts

Co-authored-by: Ashley Stanton-Nurse <ashleyst@microsoft.com>

* Fix bug with markup. Simplify code.

* Protect against creating React editor without parent node

* Replace rendering tests with snapshot match

* Add test screenshot to troubleshoot e2e test

* Revert "Add test screenshot to troubleshoot e2e test"

This reverts commit 1b8138ade0.

* Attempt 2 at troubleshooting failing test

* Revert "Attempt 2 at troubleshooting failing test"

This reverts commit 3e51a593bf.

* Delete button now shows if one or more rows are selected

---------

Co-authored-by: Vsevolod Kukol <sevoku@microsoft.com>
Co-authored-by: Ashley Stanton-Nurse <ashleyst@microsoft.com>
This commit is contained in:
Laurent Nguyen
2024-05-29 09:09:13 +02:00
committed by GitHub
parent 19d1e0d1df
commit 36736882ee
31 changed files with 5143 additions and 2114 deletions

View File

@@ -1,5 +1,6 @@
import { Resource, StoredProcedureDefinition, TriggerDefinition, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { useNotebook } from "Explorer/Notebook/useNotebook";
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import * as ko from "knockout";
import * as _ from "underscore";
import * as Constants from "../../Common/Constants";
@@ -27,9 +28,7 @@ import Explorer from "../Explorer";
import { useCommandBar } from "../Menus/CommandBar/CommandBarComponentAdapter";
import { CassandraAPIDataClient, CassandraTableKey, CassandraTableKeys } from "../Tables/TableDataClient";
import ConflictsTab from "../Tabs/ConflictsTab";
import DocumentsTab from "../Tabs/DocumentsTab";
import GraphTab from "../Tabs/GraphTab";
import MongoDocumentsTab from "../Tabs/MongoDocumentsTab";
import { NewMongoQueryTab } from "../Tabs/MongoQueryTab/MongoQueryTab";
import { NewMongoShellTab } from "../Tabs/MongoShellTab/MongoShellTab";
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
@@ -292,13 +291,13 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const documentsTabs: DocumentsTab[] = useTabs
const documentsTabs: DocumentsTabV2[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id(),
) as DocumentsTab[];
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
) as DocumentsTabV2[];
let documentsTab: DocumentsTabV2 = documentsTabs && documentsTabs[0];
if (documentsTab) {
useTabs.getState().activateTab(documentsTab);
@@ -312,7 +311,7 @@ export default class Collection implements ViewModels.Collection {
});
this.documentIds([]);
documentsTab = new DocumentsTab({
documentsTab = new DocumentsTabV2({
partitionKey: this.partitionKey,
documentIds: ko.observableArray<DocumentId>([]),
tabKind: ViewModels.CollectionTabKind.Documents,
@@ -494,13 +493,13 @@ export default class Collection implements ViewModels.Collection {
dataExplorerArea: Constants.Areas.ResourceTree,
});
const mongoDocumentsTabs: MongoDocumentsTab[] = useTabs
const mongoDocumentsTabs: DocumentsTabV2[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab) => tab.collection && tab.collection.databaseId === this.databaseId && tab.collection.id() === this.id(),
) as MongoDocumentsTab[];
let mongoDocumentsTab: MongoDocumentsTab = mongoDocumentsTabs && mongoDocumentsTabs[0];
) as DocumentsTabV2[];
let mongoDocumentsTab: DocumentsTabV2 = mongoDocumentsTabs && mongoDocumentsTabs[0];
if (mongoDocumentsTab) {
useTabs.getState().activateTab(mongoDocumentsTab);
@@ -514,7 +513,7 @@ export default class Collection implements ViewModels.Collection {
});
this.documentIds([]);
mongoDocumentsTab = new MongoDocumentsTab({
mongoDocumentsTab = new DocumentsTabV2({
partitionKey: this.partitionKey,
documentIds: this.documentIds,
tabKind: ViewModels.CollectionTabKind.Documents,

View File

@@ -1,10 +1,18 @@
import * as ko from "knockout";
import * as DataModels from "../../Contracts/DataModels";
import { useDialog } from "../Controls/Dialog";
import DocumentsTab from "../Tabs/DocumentsTab";
/**
* Replaces DocumentsTab so we can plug any object
*/
export interface IDocumentIdContainer {
partitionKeyProperties?: string[];
partitionKey: DataModels.PartitionKey;
isEditorDirty: () => boolean;
selectDocument: (documentId: DocumentId) => Promise<void>;
}
export default class DocumentId {
public container: DocumentsTab;
public container: IDocumentIdContainer;
public rid: string;
public self: string;
public ts: string;
@@ -15,7 +23,7 @@ export default class DocumentId {
public stringPartitionKeyValues: string[];
public isDirty: ko.Observable<boolean>;
constructor(container: DocumentsTab, data: any, partitionKeyValue: any[]) {
constructor(container: IDocumentIdContainer, data: any, partitionKeyValue: any[]) {
this.container = container;
this.self = data._self;
this.rid = data._rid;

View File

@@ -1,9 +1,8 @@
import * as ko from "knockout";
import DocumentId from "./DocumentId";
import DocumentsTab from "../Tabs/DocumentsTab";
import DocumentId, { IDocumentIdContainer } from "./DocumentId";
export default class ObjectId extends DocumentId {
constructor(container: DocumentsTab, data: any, partitionKeyValue: any) {
constructor(container: IDocumentIdContainer, data: any, partitionKeyValue: any) {
super(container, data, partitionKeyValue);
if (typeof data._id === "object") {
this.id = ko.observable(data._id[Object.keys(data._id)[0]]);

View File

@@ -1,3 +1,4 @@
import { DocumentsTabV2 } from "Explorer/Tabs/DocumentsTabV2/DocumentsTabV2";
import * as ko from "knockout";
import * as Constants from "../../Common/Constants";
import * as DataModels from "../../Contracts/DataModels";
@@ -7,7 +8,6 @@ import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import { useTabs } from "../../hooks/useTabs";
import Explorer from "../Explorer";
import DocumentsTab from "../Tabs/DocumentsTab";
import { NewQueryTab } from "../Tabs/QueryTab/QueryTab";
import TabsBase from "../Tabs/TabsBase";
import { useDatabases } from "../useDatabases";
@@ -118,15 +118,15 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
dataExplorerArea: Constants.Areas.ResourceTree,
});
const documentsTabs: DocumentsTab[] = useTabs
const documentsTabs: DocumentsTabV2[] = useTabs
.getState()
.getTabs(
ViewModels.CollectionTabKind.Documents,
(tab: TabsBase) =>
tab.collection?.id() === this.id() &&
(tab.collection as ViewModels.CollectionBase).databaseId === this.databaseId,
) as DocumentsTab[];
let documentsTab: DocumentsTab = documentsTabs && documentsTabs[0];
) as DocumentsTabV2[];
let documentsTab: DocumentsTabV2 = documentsTabs && documentsTabs[0];
if (documentsTab) {
useTabs.getState().activateTab(documentsTab);
@@ -139,7 +139,7 @@ export default class ResourceTokenCollection implements ViewModels.CollectionBas
tabTitle: "Items",
});
documentsTab = new DocumentsTab({
documentsTab = new DocumentsTabV2({
partitionKey: this.partitionKey,
resourceTokenPartitionKey: userContext.parsedResourceToken?.partitionKey,
documentIds: ko.observableArray<DocumentId>([]),