Save and restore states for QueryTab and DocumentsTab for SQL and Mongo

This commit is contained in:
Laurent Nguyen 2024-11-04 09:55:05 +01:00
parent f7dffa4183
commit 966ca2ee90
13 changed files with 149 additions and 92 deletions

View File

@ -9,6 +9,7 @@ export enum TabKind {
Graph, Graph,
SQLQuery, SQLQuery,
ScaleSettings, ScaleSettings,
MongoQuery,
} }
/** /**

View File

@ -155,7 +155,13 @@ export interface Collection extends CollectionBase {
onSettingsClick: () => Promise<void>; onSettingsClick: () => Promise<void>;
onNewGraphClick(): void; onNewGraphClick(): void;
onNewMongoQueryClick(source: any, event?: MouseEvent, queryText?: string): void; onNewMongoQueryClick(
source: any,
event?: MouseEvent,
queryText?: string,
stringsplitterDirection?: "horizontal" | "vertical",
queryViewSizePercent?: number,
): void;
onNewMongoShellClick(): void; onNewMongoShellClick(): void;
onNewStoredProcedureClick(source: Collection, event?: MouseEvent): void; onNewStoredProcedureClick(source: Collection, event?: MouseEvent): void;
onNewUserDefinedFunctionClick(source: Collection, event?: MouseEvent): void; onNewUserDefinedFunctionClick(source: Collection, event?: MouseEvent): void;

View File

@ -9,10 +9,13 @@ import { MessageTypes } from "Contracts/ExplorerContracts";
import { handleOpenAction } from "Explorer/OpenActions/OpenActions"; import { handleOpenAction } from "Explorer/OpenActions/OpenActions";
import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane"; import { useDataPlaneRbac } from "Explorer/Panes/SettingsPane/SettingsPane";
import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient"; import { getCopilotEnabled, isCopilotFeatureRegistered } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { OPEN_TABS_SUBCOMPONENT_NAME } from "Explorer/Tabs/QueryTab/QueryTabStateUtil";
import { IGalleryItem } from "Juno/JunoClient"; import { IGalleryItem } from "Juno/JunoClient";
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil"; import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
import { AppStateComponentNames, readSubComponentState } from "Shared/AppStatePersistenceUtility"; import {
AppStateComponentNames,
OPEN_TABS_SUBCOMPONENT_NAME,
readSubComponentState,
} from "Shared/AppStatePersistenceUtility";
import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility"; import { LocalStorageUtility, StorageKey } from "Shared/StorageUtility";
import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils"; import { acquireMsalTokenForAccount } from "Utils/AuthorizationUtils";
import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils"; import { allowedNotebookServerUrls, validateEndpoint } from "Utils/EndpointUtils";
@ -1212,7 +1215,7 @@ export default class Explorer {
} }
private restoreOpenTabs() { private restoreOpenTabs() {
const openTabsState = readSubComponentState<DataExplorerAction[]>( const openTabsState = readSubComponentState<(DataExplorerAction | undefined)[]>(
AppStateComponentNames.DataExplorerAction, AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME, OPEN_TABS_SUBCOMPONENT_NAME,
undefined, undefined,

View File

@ -132,6 +132,21 @@ function openCollectionTab(
break; break;
} }
if (
action.tabKind === ActionContracts.TabKind.MongoQuery ||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.MongoQuery]
) {
const openQueryTabAction = action as ActionContracts.OpenQueryTab;
collection.onNewMongoQueryClick(
collection,
undefined,
generateQueryText(openQueryTabAction, collection.partitionKeyProperties),
openQueryTabAction.splitterDirection,
openQueryTabAction.queryViewSizePercent,
);
break;
}
if ( if (
action.tabKind === ActionContracts.TabKind.ScaleSettings || action.tabKind === ActionContracts.TabKind.ScaleSettings ||
action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.ScaleSettings] action.tabKind === ActionContracts.TabKind[ActionContracts.TabKind.ScaleSettings]

View File

@ -23,6 +23,7 @@ import { queryDocuments } from "Common/dataAccess/queryDocuments";
import { readDocument } from "Common/dataAccess/readDocument"; import { readDocument } from "Common/dataAccess/readDocument";
import { updateDocument } from "Common/dataAccess/updateDocument"; import { updateDocument } from "Common/dataAccess/updateDocument";
import { Platform, configContext } from "ConfigContext"; import { Platform, configContext } from "ConfigContext";
import { ActionType, OpenCollectionTab, TabKind } from "Contracts/ActionContracts";
import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "Explorer/Controls/CommandButton/CommandButtonComponent";
import { useDialog } from "Explorer/Controls/Dialog"; import { useDialog } from "Explorer/Controls/Dialog";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
@ -146,6 +147,8 @@ export class DocumentsTabV2 extends TabsBase {
private title: string; private title: string;
private resourceTokenPartitionKey: string; private resourceTokenPartitionKey: string;
protected persistedState: OpenCollectionTab;
constructor(options: ViewModels.DocumentsTabOptions) { constructor(options: ViewModels.DocumentsTabOptions) {
super(options); super(options);
@ -153,6 +156,13 @@ export class DocumentsTabV2 extends TabsBase {
this.title = options.title; this.title = options.title;
this.partitionKey = options.partitionKey; this.partitionKey = options.partitionKey;
this.resourceTokenPartitionKey = options.resourceTokenPartitionKey; this.resourceTokenPartitionKey = options.resourceTokenPartitionKey;
this.persistedState = {
actionType: ActionType.OpenCollectionTab,
tabKind: options.isPreferredApiMongoDB ? TabKind.MongoDocuments : TabKind.SQLDocuments,
databaseResourceId: options.collection.databaseId,
collectionResourceId: options.collection.id(),
};
} }
public render(): JSX.Element { public render(): JSX.Element {

View File

@ -1,3 +1,4 @@
import { ActionType, TabKind } from "Contracts/ActionContracts";
import React from "react"; import React from "react";
import MongoUtility from "../../../Common/MongoUtility"; import MongoUtility from "../../../Common/MongoUtility";
import * as ViewModels from "../../../Contracts/ViewModels"; import * as ViewModels from "../../../Contracts/ViewModels";
@ -20,7 +21,7 @@ export class NewMongoQueryTab extends NewQueryTab {
private mongoQueryTabProps: IMongoQueryTabProps, private mongoQueryTabProps: IMongoQueryTabProps,
) { ) {
super(options, mongoQueryTabProps); super(options, mongoQueryTabProps);
this.queryText = ""; this.queryText = options.queryText ?? "";
this.iMongoQueryTabComponentProps = { this.iMongoQueryTabComponentProps = {
collection: options.collection, collection: options.collection,
isExecutionError: this.isExecutionError(), isExecutionError: this.isExecutionError(),
@ -28,6 +29,8 @@ export class NewMongoQueryTab extends NewQueryTab {
tabsBaseInstance: this, tabsBaseInstance: this,
queryText: this.queryText, queryText: this.queryText,
partitionKey: this.partitionKey, partitionKey: this.partitionKey,
stringsplitterDirection: options.stringsplitterDirection,
queryViewSizePercent: options.queryViewSizePercent,
container: this.mongoQueryTabProps.container, container: this.mongoQueryTabProps.container,
onTabAccessor: (instance: ITabAccessor): void => { onTabAccessor: (instance: ITabAccessor): void => {
this.iTabAccessor = instance; this.iTabAccessor = instance;
@ -35,6 +38,26 @@ export class NewMongoQueryTab extends NewQueryTab {
isPreferredApiMongoDB: true, isPreferredApiMongoDB: true,
monacoEditorSetting: "plaintext", monacoEditorSetting: "plaintext",
viewModelcollection: this.mongoQueryTabProps.viewModelcollection, viewModelcollection: this.mongoQueryTabProps.viewModelcollection,
onUpdatePersistedState: (state: {
queryText: string;
splitterDirection: string;
queryViewSizePercent: number;
}): void => {
this.persistedState = {
actionType: ActionType.OpenCollectionTab,
tabKind: TabKind.SQLQuery,
databaseResourceId: options.collection.databaseId,
collectionResourceId: options.collection.id(),
query: {
text: state.queryText,
},
splitterDirection: state.splitterDirection as "vertical" | "horizontal",
queryViewSizePercent: state.queryViewSizePercent,
};
if (this.triggerPersistState) {
this.triggerPersistState();
}
},
}; };
} }

View File

@ -1,4 +1,5 @@
import { sendMessage } from "Common/MessageHandler"; import { sendMessage } from "Common/MessageHandler";
import { ActionType, OpenQueryTab, TabKind } from "Contracts/ActionContracts";
import { MessageTypes } from "Contracts/MessageTypes"; import { MessageTypes } from "Contracts/MessageTypes";
import { CopilotProvider } from "Explorer/QueryCopilot/QueryCopilotContext"; import { CopilotProvider } from "Explorer/QueryCopilot/QueryCopilotContext";
import { userContext } from "UserContext"; import { userContext } from "UserContext";
@ -26,6 +27,8 @@ export class NewQueryTab extends TabsBase {
public iQueryTabComponentProps: IQueryTabComponentProps; public iQueryTabComponentProps: IQueryTabComponentProps;
public iTabAccessor: ITabAccessor; public iTabAccessor: ITabAccessor;
protected persistedState: OpenQueryTab;
constructor( constructor(
options: QueryTabOptions, options: QueryTabOptions,
private props: IQueryTabProps, private props: IQueryTabProps,
@ -46,7 +49,34 @@ export class NewQueryTab extends TabsBase {
this.iTabAccessor = instance; this.iTabAccessor = instance;
}, },
isPreferredApiMongoDB: false, isPreferredApiMongoDB: false,
onUpdatePersistedState: (state: {
queryText: string;
splitterDirection: string;
queryViewSizePercent: number;
}): void => {
this.persistedState = {
actionType: ActionType.OpenCollectionTab,
tabKind: TabKind.SQLQuery,
databaseResourceId: options.collection.databaseId,
collectionResourceId: options.collection.id(),
query: {
text: state.queryText,
},
splitterDirection: state.splitterDirection as "vertical" | "horizontal",
queryViewSizePercent: state.queryViewSizePercent,
};
if (this.triggerPersistState) {
this.triggerPersistState();
}
},
}; };
// set initial state
this.iQueryTabComponentProps.onUpdatePersistedState({
queryText: options.queryText,
splitterDirection: options.stringsplitterDirection,
queryViewSizePercent: options.queryViewSizePercent,
});
} }
public render(): JSX.Element { public render(): JSX.Element {

View File

@ -13,7 +13,6 @@ import { readCopilotToggleStatus, saveCopilotToggleStatus } from "Explorer/Query
import { OnExecuteQueryClick, QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient"; import { OnExecuteQueryClick, QueryDocumentsPerPage } from "Explorer/QueryCopilot/Shared/QueryCopilotClient";
import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar"; import { QueryCopilotSidebar } from "Explorer/QueryCopilot/V2/Sidebar/QueryCopilotSidebar";
import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection"; import { QueryResultSection } from "Explorer/Tabs/QueryTab/QueryResultSection";
import { deleteQueryTabState, saveQueryTabState } from "Explorer/Tabs/QueryTab/QueryTabStateUtil";
import { QueryTabStyles, useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles"; import { QueryTabStyles, useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles";
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil"; import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
import { useSelectedNode } from "Explorer/useSelectedNode"; import { useSelectedNode } from "Explorer/useSelectedNode";
@ -96,6 +95,11 @@ export interface IQueryTabComponentProps {
copilotStore?: Partial<QueryCopilotState>; copilotStore?: Partial<QueryCopilotState>;
stringsplitterDirection?: "horizontal" | "vertical"; stringsplitterDirection?: "horizontal" | "vertical";
queryViewSizePercent?: number; queryViewSizePercent?: number;
onUpdatePersistedState: (state: {
queryText: string;
splitterDirection: "vertical" | "horizontal";
queryViewSizePercent: number;
}) => void;
} }
interface IQueryTabStates { interface IQueryTabStates {
@ -183,7 +187,7 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
copilotActive: this._queryCopilotActive(), copilotActive: this._queryCopilotActive(),
currentTabActive: true, currentTabActive: true,
queryResultsView: queryResultsView:
props.stringsplitterDirection === "horizontal" ? SplitterDirection.Horizontal : SplitterDirection.Vertical, props.stringsplitterDirection === "vertical" ? SplitterDirection.Vertical : SplitterDirection.Horizontal,
queryViewSizePercent: props.queryViewSizePercent, queryViewSizePercent: props.queryViewSizePercent,
}; };
this.isCloseClicked = false; this.isCloseClicked = false;
@ -213,10 +217,6 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
onSaveClickEvent: this.getCurrentEditorQuery.bind(this), onSaveClickEvent: this.getCurrentEditorQuery.bind(this),
onCloseClickEvent: this.onCloseClick.bind(this), onCloseClickEvent: this.onCloseClick.bind(this),
}); });
// Update persistence
this.saveQueryTabStateDebounced();
// DO THIS IN useTabs.activateNewTab() INSTEAD
} }
/** /**
@ -228,15 +228,11 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
clearTimeout(this.timeoutId); clearTimeout(this.timeoutId);
} }
this.timeoutId = setTimeout(async () => { this.timeoutId = setTimeout(async () => {
saveQueryTabState( this.props.onUpdatePersistedState({
this.props.collection, queryText: this.state.sqlQueryEditorContent,
{ splitterDirection: this.state.queryResultsView,
queryText: this.state.sqlQueryEditorContent, queryViewSizePercent: this.state.queryViewSizePercent,
splitterDirection: this.state.queryResultsView, });
queryViewSizePercent: this.state.queryViewSizePercent,
},
this.props.tabIndex,
);
}, QueryTabComponentImpl.DEBOUNCE_DELAY_MS); }, QueryTabComponentImpl.DEBOUNCE_DELAY_MS);
}; };
@ -363,9 +359,9 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
this._iterator = this.props.isPreferredApiMongoDB this._iterator = this.props.isPreferredApiMongoDB
? queryIterator(this.props.collection.databaseId, this.props.viewModelcollection, query) ? queryIterator(this.props.collection.databaseId, this.props.viewModelcollection, query)
: queryDocuments(this.props.collection.databaseId, this.props.collection.id(), query, { : queryDocuments(this.props.collection.databaseId, this.props.collection.id(), query, {
enableCrossPartitionQuery: HeadersUtility.shouldEnableCrossPartitionKey(), enableCrossPartitionQuery: HeadersUtility.shouldEnableCrossPartitionKey(),
abortSignal: this.queryAbortController.signal, abortSignal: this.queryAbortController.signal,
} as unknown as FeedOptions); } as unknown as FeedOptions);
} }
await this._queryDocumentsPage(firstItemIndex); await this._queryDocumentsPage(firstItemIndex);
@ -724,9 +720,6 @@ class QueryTabComponentImpl extends React.Component<QueryTabComponentImplProps,
componentWillUnmount(): void { componentWillUnmount(): void {
document.removeEventListener("keydown", this.handleCopilotKeyDown); document.removeEventListener("keydown", this.handleCopilotKeyDown);
// Remove persistence
deleteQueryTabState(this.props.tabIndex);
} }
private getEditorAndQueryResult(): JSX.Element { private getEditorAndQueryResult(): JSX.Element {

View File

@ -1,65 +0,0 @@
// Definitions of State data
import { ActionType, OpenQueryTab, TabKind } from "Contracts/ActionContracts";
import {
AppStateComponentNames,
readSubComponentState,
saveSubComponentState,
} from "Shared/AppStatePersistenceUtility";
import * as ViewModels from "../../../Contracts/ViewModels";
export const OPEN_TABS_SUBCOMPONENT_NAME = "OpenTabs";
export const saveQueryTabState = (
collection: ViewModels.CollectionBase,
state: {
queryText: string;
splitterDirection: "vertical" | "horizontal";
queryViewSizePercent: number;
},
tabIndex: number,
): void => {
const openTabsState = readSubComponentState<OpenQueryTab[]>(
AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME,
undefined,
[],
);
openTabsState[tabIndex] = {
actionType: ActionType.OpenCollectionTab,
tabKind: TabKind.SQLQuery,
databaseResourceId: collection.databaseId,
collectionResourceId: collection.id(),
query: {
text: state.queryText,
},
splitterDirection: state.splitterDirection,
queryViewSizePercent: state.queryViewSizePercent,
};
saveSubComponentState<OpenQueryTab[]>(
AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME,
undefined,
openTabsState,
);
};
export const deleteQueryTabState = (tabIndex: number): void => {
const openTabsState = readSubComponentState<OpenQueryTab[]>(
AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME,
undefined,
[],
);
openTabsState.splice(tabIndex, 1);
saveSubComponentState<OpenQueryTab[]>(
AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME,
undefined,
openTabsState,
);
};

View File

@ -1,3 +1,4 @@
import { OpenTab } from "Contracts/ActionContracts";
import { KeyboardActionGroup, clearKeyboardActionGroup } from "KeyboardShortcuts"; import { KeyboardActionGroup, clearKeyboardActionGroup } from "KeyboardShortcuts";
import * as ko from "knockout"; import * as ko from "knockout";
import * as Constants from "../../Common/Constants"; import * as Constants from "../../Common/Constants";
@ -30,6 +31,8 @@ export default class TabsBase extends WaitsForTemplateViewModel {
protected _theme: string; protected _theme: string;
public onLoadStartKey: number; public onLoadStartKey: number;
protected persistedState: OpenTab | undefined = undefined; // Used to store state of tab for persistence
constructor(options: ViewModels.TabOptions) { constructor(options: ViewModels.TabOptions) {
super(); super();
this.index = options.index; this.index = options.index;
@ -55,6 +58,10 @@ export default class TabsBase extends WaitsForTemplateViewModel {
}; };
} }
// Called by useTabs to persist
public getPersistedState = (): OpenTab | null => this.persistedState;
public triggerPersistState: () => void = undefined;
public onCloseTabButtonClick(): void { public onCloseTabButtonClick(): void {
useTabs.getState().closeTab(this); useTabs.getState().closeTab(this);
TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, { TelemetryProcessor.trace(Action.Tab, ActionModifiers.Close, {

View File

@ -663,7 +663,13 @@ export default class Collection implements ViewModels.Collection {
); );
} }
public onNewMongoQueryClick(source: any, event: MouseEvent, queryText?: string) { public onNewMongoQueryClick(
source: any,
event: MouseEvent,
queryText?: string,
stringsplitterDirection?: "horizontal" | "vertical",
queryViewSizePercent?: number,
) {
const collection: ViewModels.Collection = source.collection || source; const collection: ViewModels.Collection = source.collection || source;
const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Query).length + 1; const id = useTabs.getState().getTabs(ViewModels.CollectionTabKind.Query).length + 1;
@ -685,6 +691,9 @@ export default class Collection implements ViewModels.Collection {
node: this, node: this,
partitionKey: collection.partitionKey, partitionKey: collection.partitionKey,
onLoadStartKey: startKey, onLoadStartKey: startKey,
queryText,
stringsplitterDirection,
queryViewSizePercent,
}, },
{ {
container: this.container, container: this.container,

View File

@ -12,6 +12,9 @@ export enum AppStateComponentNames {
DataExplorerAction = "DataExplorerAction", DataExplorerAction = "DataExplorerAction",
} }
// Subcomponent for DataExplorerAction
export const OPEN_TABS_SUBCOMPONENT_NAME = "OpenTabs";
export const PATH_SEPARATOR = "/"; // export for testing purposes export const PATH_SEPARATOR = "/"; // export for testing purposes
const SCHEMA_VERSION = 1; const SCHEMA_VERSION = 1;

View File

@ -1,5 +1,11 @@
import { clamp } from "@fluentui/react"; import { clamp } from "@fluentui/react";
import { OpenTab } from "Contracts/ActionContracts";
import { useSelectedNode } from "Explorer/useSelectedNode"; import { useSelectedNode } from "Explorer/useSelectedNode";
import {
AppStateComponentNames,
OPEN_TABS_SUBCOMPONENT_NAME,
saveSubComponentState,
} from "Shared/AppStatePersistenceUtility";
import create, { UseStore } from "zustand"; import create, { UseStore } from "zustand";
import * as ViewModels from "../Contracts/ViewModels"; import * as ViewModels from "../Contracts/ViewModels";
import { CollectionTabKind } from "../Contracts/ViewModels"; import { CollectionTabKind } from "../Contracts/ViewModels";
@ -36,6 +42,7 @@ export interface TabsState {
selectLeftTab: () => void; selectLeftTab: () => void;
selectRightTab: () => void; selectRightTab: () => void;
closeActiveTab: () => void; closeActiveTab: () => void;
persistTabsState: () => void;
} }
export enum ReactTabKind { export enum ReactTabKind {
@ -73,7 +80,9 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
}, },
activateNewTab: (tab: TabsBase): void => { activateNewTab: (tab: TabsBase): void => {
set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined })); set((state) => ({ openedTabs: [...state.openedTabs, tab], activeTab: tab, activeReactTab: undefined }));
tab.triggerPersistState = get().persistTabsState;
tab.onActivate(); tab.onActivate();
get().persistTabsState();
}, },
activateReactTab: (tabKind: ReactTabKind): void => { activateReactTab: (tabKind: ReactTabKind): void => {
// Clear the selected node when switching to a react tab. // Clear the selected node when switching to a react tab.
@ -130,6 +139,8 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
} }
set({ openedTabs: updatedTabs }); set({ openedTabs: updatedTabs });
get().persistTabsState();
}, },
closeAllNotebookTabs: (hardClose): void => { closeAllNotebookTabs: (hardClose): void => {
const isNotebook = (tabKind: CollectionTabKind): boolean => { const isNotebook = (tabKind: CollectionTabKind): boolean => {
@ -226,4 +237,15 @@ export const useTabs: UseStore<TabsState> = create((set, get) => ({
state.closeTab(state.activeTab); state.closeTab(state.activeTab);
} }
}, },
persistTabsState: () => {
const state = get();
const openTabsStates = state.openedTabs.map((tab) => tab.getPersistedState());
saveSubComponentState<OpenTab[]>(
AppStateComponentNames.DataExplorerAction,
OPEN_TABS_SUBCOMPONENT_NAME,
undefined,
openTabsStates,
);
},
})); }));