Compare commits

..

3 Commits

Author SHA1 Message Date
vaidankarswapnil
9dc879b753 Removed comments from eslintignore file 2021-10-14 12:58:26 +05:30
vaidankarswapnil
1ce8d077d5 Fix eslint issues for some files 2021-10-13 14:42:37 +05:30
Hardikkumar Nai
271256bffb resolve_eslint_NodePropertiesComponent (#921)
* resolve_eslint_NodePropertiesComponent

* address commit

* Open new screen: Screen reader does not pass the 'Copied' information after selecting 'Copy' button.

* resolve lint error
2021-10-12 08:43:35 -07:00
22 changed files with 230 additions and 227 deletions

View File

@@ -68,31 +68,23 @@ src/Explorer/Notebook/NotebookComponent/reducers.ts
src/Explorer/Notebook/NotebookComponent/store.ts
src/Explorer/Notebook/NotebookComponent/types.ts
src/Explorer/Notebook/NotebookContainerClient.ts
src/Explorer/Notebook/NotebookContentClient.ts
src/Explorer/Notebook/NotebookContentItem.ts
src/Explorer/Notebook/NotebookUtil.ts
src/Explorer/OpenActionsStubs.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyNameValidator.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts
src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts
src/Explorer/SplashScreen/SplashScreen.test.ts
src/Explorer/Tables/DataTable/CacheBase.ts
src/Explorer/Tables/DataTable/DataTableBindingManager.ts
src/Explorer/Tables/DataTable/DataTableBuilder.ts
src/Explorer/Tables/DataTable/DataTableContextMenu.ts
src/Explorer/Tables/DataTable/DataTableOperationManager.ts
# src/Explorer/Tables/DataTable/DataTableOperations.ts
src/Explorer/Tables/DataTable/DataTableViewModel.ts
# src/Explorer/Tables/DataTable/TableCommands.ts
# src/Explorer/Tables/DataTable/TableEntityCache.ts
src/Explorer/Tables/DataTable/TableEntityListViewModel.ts
# src/Explorer/Tables/Entities.ts
src/Explorer/Tables/QueryBuilder/CustomTimestampHelper.ts
src/Explorer/Tables/TableDataClient.ts
src/Explorer/Tables/TableEntityProcessor.ts
src/Explorer/Tables/Utilities.ts
src/Explorer/Tabs/ConflictsTab.ts
src/Explorer/Tabs/DatabaseSettingsTab.ts
src/Explorer/Tabs/DocumentsTab.test.ts
src/Explorer/Tabs/DocumentsTab.ts
src/Explorer/Tabs/GraphTab.ts
src/Explorer/Tabs/MongoDocumentsTab.ts
@@ -117,6 +109,8 @@ src/Index.ts
src/Platform/Hosted/Authorization.ts
src/ReactDevTools.ts
src/Shared/Constants.ts
src/Shared/DefaultExperienceUtility.test.ts
src/Shared/DefaultExperienceUtility.ts
src/Shared/appInsights.ts
src/SparkClusterManager/ArcadiaResourceManager.ts
src/SparkClusterManager/SparkClusterManager.ts
@@ -131,20 +125,13 @@ src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
src/Explorer/Controls/TreeComponent/TreeComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.test.tsx
; src/Explorer/Graph/GraphExplorerComponent/GraphExplorer.tsx
src/Explorer/Graph/GraphExplorerComponent/GraphVizComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/LeftPaneComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/MiddlePaneComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.test.tsx
src/Explorer/Graph/GraphExplorerComponent/NodePropertiesComponent.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.test.tsx
src/Explorer/Graph/GraphExplorerComponent/ReadOnlyNodePropertiesComponent.tsx
src/Explorer/Menus/CommandBar/CommandBarUtil.tsx
src/Explorer/Notebook/NotebookComponent/NotebookComponentAdapter.tsx
; src/Explorer/Notebook/NotebookComponent/NotebookComponentBootstrapper.tsx
src/Explorer/Notebook/NotebookComponent/VirtualCommandBarComponent.tsx
src/Explorer/Notebook/NotebookComponent/contents/index.tsx
; src/Explorer/Notebook/NotebookRenderer/NotebookReadOnlyRenderer.tsx
src/Explorer/Notebook/NotebookRenderer/NotebookRenderer.tsx
src/Explorer/Notebook/NotebookRenderer/decorators/draggable/index.tsx
src/Explorer/Notebook/NotebookRenderer/decorators/hijack-scroll/index.tsx

View File

@@ -58,7 +58,7 @@ export class LeftPaneComponent extends React.Component<LeftPaneComponentProps> {
className={className}
as="tr"
aria-label={node.caption}
onActivated={(e) => this.props.onRootNodeSelected(node.id)}
onActivated={() => this.props.onRootNodeSelected(node.id)}
key={node.id}
>
<td className="resultItem">

View File

@@ -1,8 +1,8 @@
import React from "react";
import { mount, ReactWrapper } from "enzyme";
import * as Q from "q";
import { NodePropertiesComponent, NodePropertiesComponentProps, Mode } from "./NodePropertiesComponent";
import { GraphHighlightedNodeData, EditedProperties, EditedEdges, PossibleVertex } from "./GraphExplorer";
import React from "react";
import { GraphHighlightedNodeData, PossibleVertex } from "./GraphExplorer";
import { Mode, NodePropertiesComponent, NodePropertiesComponentProps } from "./NodePropertiesComponent";
describe("Property pane", () => {
const title = "My Title";
@@ -37,17 +37,18 @@ describe("Property pane", () => {
return {
expandedTitle: title,
isCollapsed: false,
onCollapsedChanged: (newValue: boolean): void => {},
onCollapsedChanged: jest.fn(),
node: highlightedNode,
getPkIdFromNodeData: (v: GraphHighlightedNodeData): string => null,
collectionPartitionKeyProperty: null,
updateVertexProperties: (editedProperties: EditedProperties): Q.Promise<void> => Q.resolve(),
selectNode: (id: string): void => {},
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(null),
possibleEdgeLabels: null,
editGraphEdges: (editedEdges: EditedEdges): Q.Promise<any> => Q.resolve(),
deleteHighlightedNode: (): void => {},
onModeChanged: (newMode: Mode): void => {},
getPkIdFromNodeData: (): string => undefined,
collectionPartitionKeyProperty: undefined,
updateVertexProperties: (): Q.Promise<void> => Q.resolve(),
selectNode: jest.fn(),
updatePossibleVertices: (): Q.Promise<PossibleVertex[]> => Q.resolve(undefined),
possibleEdgeLabels: undefined,
//eslint-disable-next-line
editGraphEdges: (): Q.Promise<any> => Q.resolve(),
deleteHighlightedNode: jest.fn(),
onModeChanged: jest.fn(),
viewMode: Mode.READONLY_PROP,
};
};

View File

@@ -72,7 +72,7 @@ export class NodePropertiesComponent extends React.Component<
super(props);
this.state = {
editedProperties: {
pkId: null,
pkId: undefined,
readOnlyProperties: [],
existingProperties: [],
addedProperties: [],
@@ -98,15 +98,12 @@ export class NodePropertiesComponent extends React.Component<
};
}
public static getDerivedStateFromProps(
props: NodePropertiesComponentProps,
state: NodePropertiesComponentState
): Partial<NodePropertiesComponentState> {
public static getDerivedStateFromProps(props: NodePropertiesComponentProps): Partial<NodePropertiesComponentState> {
if (props.viewMode !== Mode.READONLY_PROP) {
return { isDeleteConfirm: false };
}
return null;
return undefined;
}
public render(): JSX.Element {
@@ -138,10 +135,10 @@ export class NodePropertiesComponent extends React.Component<
* @param value
*/
private static getTypeOption(value: any): ViewModels.InputPropertyValueTypeString {
if (value == null) {
if (value === undefined) {
return "null";
}
let type = typeof value;
const type = typeof value;
switch (type) {
case "number":
case "boolean":
@@ -172,10 +169,9 @@ export class NodePropertiesComponent extends React.Component<
];
const existingProps: ViewModels.InputProperty[] = [];
if (this.props.node.hasOwnProperty("properties")) {
const hProps = this.props.node["properties"];
for (let p in hProps) {
for (const p in hProps) {
const propValues = hProps[p];
(p === partitionKeyProperty ? readOnlyProps : existingProps).push({
key: p,
@@ -437,7 +433,7 @@ export class NodePropertiesComponent extends React.Component<
</div>
);
} else {
return null;
return undefined;
}
}

View File

@@ -228,11 +228,12 @@ export class NotebookContentClient {
public async readFileContent(filePath: string): Promise<string> {
const xhr = await this.contentProvider.get(this.getServerConfig(), filePath, { content: 1 }).toPromise();
//eslint-disable-next-line
const content = (xhr.response as any).content;
if (!content) {
throw new Error("No content read");
}
//eslint-disable-next-line
const format = (xhr.response as any).format;
switch (format) {
case "text":

View File

@@ -12,6 +12,7 @@ import { NotebookContentItem, NotebookContentItemType } from "./NotebookContentI
// Must match rx-jupyter' FileType
export type FileType = "directory" | "file" | "notebook";
// Utilities for notebooks
//eslint-disable-next-line
export class NotebookUtil {
public static UntrustedNotebookRunHint = "Please trust notebook first before running any code cells";
@@ -190,6 +191,7 @@ export class NotebookUtil {
subSnapshots: SnapshotFragment[],
downloadFilename?: string
): Promise<{ imageSrc: string | undefined }> => {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
try {
// target.scrollIntoView();
@@ -258,6 +260,7 @@ export class NotebookUtil {
subSnapshots: SnapshotFragment[],
downloadFilename?: string
): Promise<{ imageSrc?: string }> => {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve, reject) => {
// target.scrollIntoView();
try {

View File

@@ -4,6 +4,8 @@ import * as React from "react";
import { useFullScreenURLs } from "../hooks/useFullScreenURLs";
export const OpenFullScreen: React.FunctionComponent = () => {
const [isReadUrlCopy, setIsReadUrlCopy] = React.useState<boolean>(false);
const [isReadWriteUrlCopy, setIsReadWriteUrlCopy] = React.useState<boolean>(false);
const result = useFullScreenURLs();
if (!result) {
return <Spinner label="Generating URLs..." ariaLive="assertive" labelPosition="right" />;
@@ -25,8 +27,9 @@ export const OpenFullScreen: React.FunctionComponent = () => {
<DefaultButton
onClick={() => {
copyToClipboard(readWriteUrl);
setIsReadWriteUrlCopy(true);
}}
text="Copy"
text={isReadWriteUrlCopy ? "Copied" : "Copy"}
iconProps={{ iconName: "Copy" }}
/>
<PrimaryButton
@@ -41,9 +44,10 @@ export const OpenFullScreen: React.FunctionComponent = () => {
<Stack horizontal tokens={{ childrenGap: 10 }}>
<DefaultButton
onClick={() => {
setIsReadUrlCopy(true);
copyToClipboard(readUrl);
}}
text="Copy"
text={isReadUrlCopy ? "Copied" : "Copy"}
iconProps={{ iconName: "Copy" }}
/>
<PrimaryButton

View File

@@ -7,7 +7,7 @@ import { Collection } from "Contracts/ViewModels";
import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useState } from "react";
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility";
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";

View File

@@ -8,7 +8,7 @@ import { Collection, Database } from "Contracts/ViewModels";
import { useSidePanel } from "hooks/useSidePanel";
import { useTabs } from "hooks/useTabs";
import React, { FunctionComponent, useState } from "react";
import * as DefaultExperienceUtility from "Shared/DefaultExperienceUtility";
import { DefaultExperienceUtility } from "Shared/DefaultExperienceUtility";
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";

View File

@@ -1,20 +1,18 @@
import * as ko from "knockout";
import * as _ from "underscore";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import * as Constants from "../Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import * as Entities from "../Entities";
import * as Utilities from "../Utilities";
import * as DataTableBuilder from "./DataTableBuilder";
import DataTableOperationManager from "./DataTableOperationManager";
import * as DataTableOperations from "./DataTableOperations";
import QueryTablesTab from "../../Tabs/QueryTablesTab";
import TableEntityListViewModel from "./TableEntityListViewModel";
import * as Utilities from "../Utilities";
import * as Entities from "../Entities";
/**
* Custom binding manager of datatable
*/
var tableEntityListViewModelMap: {
const tableEntityListViewModelMap: {
[key: string]: {
tableViewModel: TableEntityListViewModel;
operationManager: DataTableOperationManager;
@@ -22,12 +20,13 @@ var tableEntityListViewModelMap: {
};
} = {};
function bindDataTable(element: any, valueAccessor: any, allBindings: any, viewModel: any, bindingContext: any) {
var tableEntityListViewModel = bindingContext.$data;
//eslint-disable-next-line
function bindDataTable(element: any, bindingContext: any) {
const tableEntityListViewModel = bindingContext.$data;
tableEntityListViewModel.notifyColumnChanges = onTableColumnChange;
var $dataTable = $(element);
var queryTablesTab = bindingContext.$parent;
var operationManager = new DataTableOperationManager(
const $dataTable = $(element);
const queryTablesTab = bindingContext.$parent;
const operationManager = new DataTableOperationManager(
$dataTable,
tableEntityListViewModel,
queryTablesTab.tableCommands
@@ -44,9 +43,9 @@ function bindDataTable(element: any, valueAccessor: any, allBindings: any, viewM
operationManager.focusTable(); // Also selects the first row if needed.
}
function onTableColumnChange(enablePrompt: boolean = true, queryTablesTab: QueryTablesTab) {
var columnsFilter: boolean[] = null;
var tableEntityListViewModel = tableEntityListViewModelMap[queryTablesTab.tabId].tableViewModel;
function onTableColumnChange(queryTablesTab: QueryTablesTab) {
const columnsFilter: boolean[] = null;
const tableEntityListViewModel = tableEntityListViewModelMap[queryTablesTab.tabId].tableViewModel;
if (queryTablesTab.queryViewModel()) {
queryTablesTab.queryViewModel().queryBuilderViewModel().updateColumnOptions();
}
@@ -63,35 +62,36 @@ function createDataTable(
startIndex: number,
tableEntityListViewModel: TableEntityListViewModel,
queryTablesTab: QueryTablesTab,
destroy: boolean = false,
destroy = false,
columnsFilter: boolean[] = null
): void {
var $dataTable = tableEntityListViewModelMap[queryTablesTab.tabId].$dataTable;
const $dataTable = tableEntityListViewModelMap[queryTablesTab.tabId].$dataTable;
if (destroy) {
// Find currently displayed columns.
var currentColumns: string[] = tableEntityListViewModel.headers;
const currentColumns: string[] = tableEntityListViewModel.headers;
// Calculate how many more columns need to added to the current table.
var columnsToAdd: number = _.difference(tableEntityListViewModel.headers, currentColumns).length;
const columnsToAdd: number = _.difference(tableEntityListViewModel.headers, currentColumns).length;
// This is needed as current solution of adding column is more like a workaround
// The official support for dynamically add column is not yet there
// Please track github issue https://github.com/DataTables/DataTables/issues/273 for its offical support
for (var i = 0; i < columnsToAdd; i++) {
for (let i = 0; i < columnsToAdd; i++) {
$(".dataTables_scrollHead table thead tr th").eq(0).after("<th></th>");
}
tableEntityListViewModel.table.destroy();
$dataTable.empty();
}
var jsonColTable = [];
const jsonColTable = [];
for (var i = 0; i < tableEntityListViewModel.headers.length; i++) {
for (let i = 0; i < tableEntityListViewModel.headers.length; i++) {
jsonColTable.push({
sTitle: tableEntityListViewModel.headers[i],
data: tableEntityListViewModel.headers[i],
aTargets: [i],
mRender: bindColumn,
// eslint-disable-next-line no-extra-boolean-cast
visible: !!columnsFilter ? columnsFilter[i] : true,
});
}
@@ -154,9 +154,10 @@ function createDataTable(
table.setAttribute("summary", `Results for container ${tableEntityListViewModel.queryTablesTab.collection.id()}`);
});
}
function bindColumn(data: any, type: string, full: any) {
var displayedValue: any = null;
//eslint-disable-next-line
function bindColumn(data: any) {
//eslint-disable-next-line
let displayedValue: any = null;
if (data) {
displayedValue = data._;
@@ -173,7 +174,7 @@ function bindColumn(data: any, type: string, full: any) {
}
return displayedValue;
}
//eslint-disable-next-line
function getServerData(sSource: any, aoData: any, fnCallback: any, oSettings: any) {
tableEntityListViewModelMap[oSettings.ajax].tableViewModel.renderNextPageAndupdateCache(
sSource,
@@ -191,15 +192,15 @@ function bindClientId(nRow: Node, aData: Entities.ITableEntity) {
$(nRow).attr(Constants.htmlAttributeNames.dataTableRowKeyAttr, aData.RowKey._);
return nRow;
}
function selectionChanged(element: any, valueAccessor: any, allBindings: any, viewModel: any, bindingContext: any) {
//eslint-disable-next-line
function selectionChanged(bindingContext: any) {
$(".dataTable tr.selected").attr("tabindex", "-1").removeClass("selected");
const selected =
bindingContext && bindingContext.$data && bindingContext.$data.selected && bindingContext.$data.selected();
selected &&
selected.forEach((b: Entities.ITableEntity) => {
var sel = DataTableOperations.getRowSelector([
const sel = DataTableOperations.getRowSelector([
{
key: Constants.htmlAttributeNames.dataTableRowKeyAttr,
value: b.RowKey && b.RowKey._ && b.RowKey._.toString(),
@@ -211,7 +212,7 @@ function selectionChanged(element: any, valueAccessor: any, allBindings: any, vi
//selected = bindingContext.$data.selected();
}
function dataChanged(element: any, valueAccessor: any, allBindings: any, viewModel: any, bindingContext: any) {
function dataChanged() {
// do nothing for now
}
@@ -229,20 +230,21 @@ function updateTableScrollableRegionMetrics(): void {
* Update the table's scrollable region height. So the pagination control is always shown at the bottom of the page.
*/
function updateTableScrollableRegionHeight(): void {
$(".tab-pane").each(function (index, tabElement) {
$(".tab-pane").each((index, tabElement) => {
if (!$(tabElement).hasClass("tableContainer")) {
return;
}
// Add some padding to the table so it doesn't get too close to the container border.
var dataTablePaddingBottom = 10;
var bodyHeight = $(window).height();
var dataTablesScrollBodyPosY = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset().top;
var dataTablesInfoElem = $(tabElement).find(".dataTables_info");
var dataTablesPaginateElem = $(tabElement).find(".dataTables_paginate");
const dataTablePaddingBottom = 10;
const bodyHeight = $(window).height();
const dataTablesScrollBodyPosY = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset()
.top;
const dataTablesInfoElem = $(tabElement).find(".dataTables_info");
const dataTablesPaginateElem = $(tabElement).find(".dataTables_paginate");
const notificationConsoleHeight = 32; /** Header height **/
var scrollHeight =
let scrollHeight =
bodyHeight -
dataTablesScrollBodyPosY -
dataTablesPaginateElem.outerHeight(true) -
@@ -257,10 +259,10 @@ function updateTableScrollableRegionHeight(): void {
// TODO This is a work around for setting the outerheight since we don't have access to the JQuery.outerheight(numberValue)
// in the current version of JQuery we are using. Ideally, we would upgrade JQuery and use this line instead:
// $(Constants.htmlSelectors.dataTableScrollBodySelector).outerHeight(scrollHeight);
var element = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector)[0];
var style = getComputedStyle(element);
var actualHeight = parseInt(style.height);
var change = element.offsetHeight - scrollHeight;
const element = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector)[0];
const style = getComputedStyle(element);
const actualHeight = parseInt(style.height);
const change = element.offsetHeight - scrollHeight;
$(tabElement)
.find(Constants.htmlSelectors.dataTableScrollBodySelector)
.height(actualHeight - change);
@@ -271,15 +273,15 @@ function updateTableScrollableRegionHeight(): void {
* Update the table's scrollable region width to make efficient use of the remaining space.
*/
function updateTableScrollableRegionWidth(): void {
$(".tab-pane").each(function (index, tabElement) {
$(".tab-pane").each((index, tabElement) => {
if (!$(tabElement).hasClass("tableContainer")) {
return;
}
var bodyWidth = $(window).width();
var dataTablesScrollBodyPosLeft = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset()
const bodyWidth = $(window).width();
const dataTablesScrollBodyPosLeft = $(tabElement).find(Constants.htmlSelectors.dataTableScrollBodySelector).offset()
.left;
var scrollWidth = bodyWidth - dataTablesScrollBodyPosLeft;
const scrollWidth = bodyWidth - dataTablesScrollBodyPosLeft;
// jquery datatables automatically sets width:100% to both the header and the body when we use it's column autoWidth feature.
// We work around that by setting the height for it's container instead.
@@ -288,9 +290,9 @@ function updateTableScrollableRegionWidth(): void {
}
function initializeEventHandlers(): void {
var $headers: JQuery = $(Constants.htmlSelectors.dataTableHeaderTypeSelector);
var $firstHeader: JQuery = $headers.first();
var firstIndex: string = $firstHeader.attr(Constants.htmlAttributeNames.dataTableHeaderIndex);
const $headers: JQuery = $(Constants.htmlSelectors.dataTableHeaderTypeSelector);
const $firstHeader: JQuery = $headers.first();
const firstIndex: string = $firstHeader.attr(Constants.htmlAttributeNames.dataTableHeaderIndex);
$headers
.on("keydown", (event: JQueryEventObject) => {
@@ -302,7 +304,7 @@ function initializeEventHandlers(): void {
Utilities.onTab(
event,
($sourceElement: JQuery) => {
var sourceIndex: string = $sourceElement.attr(Constants.htmlAttributeNames.dataTableHeaderIndex);
const sourceIndex: string = $sourceElement.attr(Constants.htmlAttributeNames.dataTableHeaderIndex);
if (sourceIndex === firstIndex) {
event.preventDefault();
@@ -324,14 +326,14 @@ function initializeEventHandlers(): void {
});
});
}
//eslint-disable-next-line
function updateSelectionStatus(oSettings: any): void {
var $dataTableRows: JQuery = $(Constants.htmlSelectors.dataTableAllRowsSelector);
const $dataTableRows: JQuery = $(Constants.htmlSelectors.dataTableAllRowsSelector);
if ($dataTableRows) {
for (var i = 0; i < $dataTableRows.length; i++) {
var $row: JQuery = $dataTableRows.eq(i);
var rowKey: string = $row.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr);
var table = tableEntityListViewModelMap[oSettings.ajax].tableViewModel;
for (let i = 0; i < $dataTableRows.length; i++) {
const $row: JQuery = $dataTableRows.eq(i);
const rowKey: string = $row.attr(Constants.htmlAttributeNames.dataTableRowKeyAttr);
const table = tableEntityListViewModelMap[oSettings.ajax].tableViewModel;
if (table.isItemSelected(table.getTableEntityKeys(rowKey))) {
$row.attr("tabindex", "0");
}
@@ -346,10 +348,10 @@ function updateSelectionStatus(oSettings: any): void {
// TODO consider centralizing this "post-command" logic into some sort of Command Manager entity.
// See VSO:166520: "[Storage Explorer] Consider adding a 'command manager' to track command post-effects."
function updateDataTableFocus(queryTablesTabId: string): void {
var $activeElement: JQuery = $(document.activeElement);
var isFocusLost: boolean = $activeElement.is("body"); // When focus is lost, "body" becomes the active element.
var storageExplorerFrameHasFocus: boolean = document.hasFocus();
var operationManager = tableEntityListViewModelMap[queryTablesTabId].operationManager;
const $activeElement: JQuery = $(document.activeElement);
const isFocusLost: boolean = $activeElement.is("body"); // When focus is lost, "body" becomes the active element.
const storageExplorerFrameHasFocus: boolean = document.hasFocus();
const operationManager = tableEntityListViewModelMap[queryTablesTabId].operationManager;
if (operationManager) {
if (isFocusLost && storageExplorerFrameHasFocus) {
// We get here when no control is active, meaning that the table update was triggered
@@ -371,19 +373,20 @@ function updateDataTableFocus(queryTablesTabId: string): void {
}
}
}
//eslint-disable-next-line
(<any>ko.bindingHandlers).tableSource = {
init: bindDataTable,
update: dataChanged,
};
//eslint-disable-next-line
(<any>ko.bindingHandlers).tableSelection = {
update: selectionChanged,
};
//eslint-disable-next-line
(<any>ko.bindingHandlers).readOnly = {
update: function (element: any, valueAccessor: any) {
var value = ko.utils.unwrapObservable(valueAccessor());
//eslint-disable-next-line
update: (element: any, valueAccessor: any) => {
const value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
element.setAttribute("readOnly", true);
} else {

View File

@@ -8,7 +8,7 @@ import * as Utilities from "../Utilities";
* @param{$dataTableElem} JQuery data table element
* @param{$settings} Settings to use when creating the data table
*/
export function createDataTable($dataTableElem: JQuery, settings: any): DataTables.DataTable {
export function createDataTable($dataTableElem: JQuery, settings: DataTables.Settings): DataTables.DataTable {
return $dataTableElem.DataTable(applyDefaultRendering(settings));
}
@@ -18,8 +18,9 @@ export function createDataTable($dataTableElem: JQuery, settings: any): DataTabl
* @param{settings} The settings to check
* @return The given settings with all columns having a rendering function
*/
//eslint-disable-next-line
function applyDefaultRendering(settings: any): DataTables.SettingsLegacy {
var tableColumns: DataTables.ColumnLegacy[] = null;
let tableColumns: DataTables.ColumnLegacy[] = null;
if (settings.aoColumns) {
tableColumns = settings.aoColumns;
@@ -34,7 +35,7 @@ function applyDefaultRendering(settings: any): DataTables.SettingsLegacy {
return settings;
}
for (var i = 0; i < tableColumns.length; i++) {
for (let i = 0; i < tableColumns.length; i++) {
// the column does not have a render function
if (!tableColumns[i].mRender) {
tableColumns[i].mRender = defaultDataRender;
@@ -47,6 +48,7 @@ function applyDefaultRendering(settings: any): DataTables.SettingsLegacy {
* Default data render function, whatever is done to data in here
* will be done to any data which we do not specify a render for.
*/
function defaultDataRender(data: any, type: string, full: any) {
//eslint-disable-next-line
function defaultDataRender(data: any) {
return Utilities.htmlEncode(data);
}

View File

@@ -10,7 +10,7 @@ describe("Documents tab", () => {
describe("buildQuery", () => {
it("should generate the right select query for SQL API", () => {
const documentsTab = new DocumentsTab({
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
@@ -82,9 +82,9 @@ describe("Documents tab", () => {
container: mongoExplorer,
});
it("should be false for undefined or undefined collection", () => {
it("should be false for null or undefined collection", () => {
const documentsTab = new DocumentsTab({
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
@@ -94,10 +94,10 @@ describe("Documents tab", () => {
expect(documentsTab.showPartitionKey).toBe(false);
});
it("should be false for undefined or undefined partitionKey", () => {
it("should be false for null or undefined partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithoutPartitionKey,
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
@@ -110,7 +110,7 @@ describe("Documents tab", () => {
it("should be true for non-Mongo accounts with system partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithSystemPartitionKey,
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
@@ -126,7 +126,7 @@ describe("Documents tab", () => {
});
const documentsTab = new DocumentsTab({
collection: mongoCollectionWithSystemPartitionKey,
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",
@@ -139,7 +139,7 @@ describe("Documents tab", () => {
it("should be true for non-system partitionKey", () => {
const documentsTab = new DocumentsTab({
collection: collectionWithNonSystemPartitionKey,
partitionKey: undefined,
partitionKey: null,
documentIds: ko.observableArray<DocumentId>(),
tabKind: ViewModels.CollectionTabKind.Documents,
title: "",

View File

@@ -118,7 +118,7 @@ export default class NotebookTabV2 extends NotebookTabBase {
const saveButtonChildren = [];
if (this.container.notebookManager?.gitHubOAuthService.isLoggedIn()) {
saveButtonChildren.push({
iconName: "Copy",
iconName: copyToLabel,
onCommandClick: () => this.copyNotebook(),
commandButtonLabel: copyToLabel,
hasPopup: false,

View File

@@ -1,6 +1,6 @@
import * as ko from "knockout";
import * as Constants from "../Common/Constants";
import * as ViewModels from "../Contracts/ViewModels";
import * as Constants from "../Common/Constants";
export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTemplate {
public isTemplateReady: ko.Observable<boolean>;
@@ -14,11 +14,11 @@ export abstract class WaitsForTemplateViewModel implements ViewModels.WaitsForTe
callback(value);
});
document.addEventListener("keydown", (e: KeyboardEvent) => {
document.addEventListener("keydown", function (e: KeyboardEvent) {
// To trap keyboard focus in AddCollection pane
const firstFocusableElement = document.getElementById("closeBtnAddCollection");
const lastFocusableElement = document.getElementById("submitBtnAddCollection");
const isTabPressed = e.keyCode === Constants.KeyCodes.Tab;
let firstFocusableElement = document.getElementById("closeBtnAddCollection");
let lastFocusableElement = document.getElementById("submitBtnAddCollection");
var isTabPressed = e.keyCode === Constants.KeyCodes.Tab;
if (isTabPressed) {
if (e.shiftKey) {
/* shift + tab */ if (document.activeElement === firstFocusableElement) {

View File

@@ -1,29 +1,31 @@
import * as Constants from "../../Common/Constants";
import { configContext } from "../../ConfigContext";
import * as DataModels from "../../Contracts/DataModels";
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext";
export const _generateResourceUrl = (): string => {
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
const sid = subscriptionId || "";
const rg = resourceGroup || "";
const dba = databaseAccount?.name || "";
const resourceUrl = encodeURIComponent(accountEndpoint);
const rid = "";
const rtype = "";
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
};
export default class AuthHeadersUtil {
public static async generateEncryptedToken(readOnly: boolean = false): Promise<DataModels.GenerateTokenResponse> {
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + AuthHeadersUtil._generateResourceUrl();
const headers: any = { authorization: userContext.authorizationToken };
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
export const generateEncryptedToken = async (readOnly = false): Promise<DataModels.GenerateTokenResponse> => {
const url = configContext.BACKEND_ENDPOINT + "/api/tokens/generateToken" + _generateResourceUrl();
const headers: any = { authorization: userContext.authorizationToken };
headers[Constants.HttpHeaders.getReadOnlyKey] = readOnly;
const response = await fetch(url, { method: "POST", headers });
const result = await response.json();
// This API has a quirk where the response must be parsed to JSON twice
return JSON.parse(result);
}
const response = await fetch(url, { method: "POST", headers });
const result = await response.json();
// This API has a quirk where the response must be parsed to JSON twice
return JSON.parse(result);
};
private static _generateResourceUrl(): string {
const { databaseAccount, resourceGroup, subscriptionId } = userContext;
const apiKind: DataModels.ApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(userContext.apiType);
const accountEndpoint = databaseAccount?.properties?.documentEndpoint || "";
const sid = subscriptionId || "";
const rg = resourceGroup || "";
const dba = databaseAccount?.name || "";
const resourceUrl = encodeURIComponent(accountEndpoint);
const rid = "";
const rtype = "";
return `?resourceUrl=${resourceUrl}&rid=${rid}&rtype=${rtype}&sid=${sid}&rg=${rg}&dba=${dba}&api=${apiKind}`;
}
}

View File

@@ -1,6 +1,6 @@
import { AccountKind, CapabilityNames } from "../../Common/Constants";
import { AccessInputMetadata, ApiKind } from "../../Contracts/DataModels";
import * as DefaultExperienceUtility from "../../Shared/DefaultExperienceUtility";
import { DefaultExperienceUtility } from "../../Shared/DefaultExperienceUtility";
import { userContext } from "../../UserContext";
export function getDatabaseAccountPropertiesFromMetadata(metadata: AccessInputMetadata): unknown {

View File

@@ -1,13 +1,13 @@
import * as DataModels from "../Contracts/DataModels";
import { userContext } from "../UserContext";
import * as DefaultExperienceUtility from "./DefaultExperienceUtility";
import { DefaultExperienceUtility } from "./DefaultExperienceUtility";
describe("Default Experience Utility", () => {
describe("getDefaultExperienceFromApiKind()", () => {
const runScenario = (apiKind: number, expectedExperience: typeof userContext.apiType): void => {
function runScenario(apiKind: number, expectedExperience: typeof userContext.apiType): void {
const resolvedExperience = DefaultExperienceUtility.getDefaultExperienceFromApiKind(apiKind);
expect(resolvedExperience).toEqual(expectedExperience);
};
}
describe("On SQL", () => {
it("should return SQL", () => runScenario(DataModels.ApiKind.SQL, "SQL"));
@@ -35,10 +35,10 @@ describe("Default Experience Utility", () => {
});
describe("getApiKindFromDefaultExperience()", () => {
const runScenario = (defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void => {
function runScenario(defaultExperience: typeof userContext.apiType | null, expectedApiKind: number): void {
const resolvedApiKind = DefaultExperienceUtility.getApiKindFromDefaultExperience(defaultExperience);
expect(resolvedApiKind).toEqual(expectedApiKind);
};
}
describe("On SQL", () => {
it("should return SQL", () => runScenario("SQL", DataModels.ApiKind.SQL));
@@ -60,8 +60,8 @@ describe("Default Experience Utility", () => {
it("should return Graph", () => runScenario("Gremlin", DataModels.ApiKind.Graph));
});
describe("On undefined", () => {
it("should return SQL", () => runScenario(undefined, DataModels.ApiKind.SQL));
describe("On null", () => {
it("should return SQL", () => runScenario(null, DataModels.ApiKind.SQL));
});
});
});

View File

@@ -1,45 +1,47 @@
import * as DataModels from "../Contracts/DataModels";
import { userContext } from "../UserContext";
export const getApiKindFromDefaultExperience = (defaultExperience: typeof userContext.apiType): DataModels.ApiKind => {
if (!defaultExperience) {
return DataModels.ApiKind.SQL;
}
switch (defaultExperience) {
case "SQL":
export class DefaultExperienceUtility {
public static getApiKindFromDefaultExperience(defaultExperience: typeof userContext.apiType): DataModels.ApiKind {
if (!defaultExperience) {
return DataModels.ApiKind.SQL;
case "Mongo":
return DataModels.ApiKind.MongoDB;
case "Tables":
return DataModels.ApiKind.Table;
case "Cassandra":
return DataModels.ApiKind.Cassandra;
case "Gremlin":
return DataModels.ApiKind.Graph;
default:
return DataModels.ApiKind.SQL;
}
};
}
export const getDefaultExperienceFromApiKind = (apiKind: DataModels.ApiKind): typeof userContext.apiType => {
if (apiKind === undefined) {
return "SQL";
switch (defaultExperience) {
case "SQL":
return DataModels.ApiKind.SQL;
case "Mongo":
return DataModels.ApiKind.MongoDB;
case "Tables":
return DataModels.ApiKind.Table;
case "Cassandra":
return DataModels.ApiKind.Cassandra;
case "Gremlin":
return DataModels.ApiKind.Graph;
default:
return DataModels.ApiKind.SQL;
}
}
switch (apiKind) {
case DataModels.ApiKind.SQL:
return "SQL";
case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute:
return "Mongo";
case DataModels.ApiKind.Table:
return "Tables";
case DataModels.ApiKind.Cassandra:
return "Cassandra";
case DataModels.ApiKind.Graph:
return "Gremlin";
default:
public static getDefaultExperienceFromApiKind(apiKind: DataModels.ApiKind): typeof userContext.apiType {
if (apiKind == null) {
return "SQL";
}
switch (apiKind) {
case DataModels.ApiKind.SQL:
return "SQL";
case DataModels.ApiKind.MongoDB:
case DataModels.ApiKind.MongoDBCompute:
return "Mongo";
case DataModels.ApiKind.Table:
return "Tables";
case DataModels.ApiKind.Cassandra:
return "Cassandra";
case DataModels.ApiKind.Graph:
return "Gremlin";
default:
return "SQL";
}
}
};
}

View File

@@ -5,35 +5,37 @@ import { ServerConnection, TerminalManager } from "@jupyterlab/services";
import { Terminal } from "@jupyterlab/terminal";
import { Panel, Widget } from "@phosphor/widgets";
export const createTerminalApp = async (serverSettings: ServerConnection.ISettings) => {
const manager = new TerminalManager({
serverSettings: serverSettings,
});
const session = await manager.startNew();
const term = new Terminal(session, { theme: "dark", shutdownOnClose: true });
export class JupyterLabAppFactory {
public static async createTerminalApp(serverSettings: ServerConnection.ISettings) {
const manager = new TerminalManager({
serverSettings: serverSettings,
});
const session = await manager.startNew();
const term = new Terminal(session, { theme: "dark", shutdownOnClose: true });
if (!term) {
console.error("Failed starting terminal");
return;
if (!term) {
console.error("Failed starting terminal");
return;
}
term.title.closable = false;
term.addClass("terminalWidget");
let panel = new Panel();
panel.addWidget(term as any);
panel.id = "main";
// Attach the widget to the dom.
Widget.attach(panel, document.body);
// Handle resize events.
window.addEventListener("resize", () => {
panel.update();
});
// Dispose terminal when unloading.
window.addEventListener("unload", () => {
panel.dispose();
});
}
term.title.closable = false;
term.addClass("terminalWidget");
const panel = new Panel();
panel.addWidget(term as any);
panel.id = "main";
// Attach the widget to the dom.
Widget.attach(panel, document.body);
// Handle resize events.
window.addEventListener("resize", () => {
panel.update();
});
// Dispose terminal when unloading.
window.addEventListener("unload", () => {
panel.dispose();
});
};
}

View File

@@ -6,7 +6,7 @@ import { Action } from "../Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "../Shared/Telemetry/TelemetryProcessor";
import { updateUserContext } from "../UserContext";
import "./index.css";
import { createTerminalApp } from "./JupyterLabAppFactory";
import { JupyterLabAppFactory } from "./JupyterLabAppFactory";
import { TerminalProps } from "./TerminalProps";
const createServerSettings = (props: TerminalProps): ServerConnection.ISettings => {
@@ -54,7 +54,7 @@ const initTerminal = async (props: TerminalProps) => {
const startTime = TelemetryProcessor.traceStart(Action.OpenTerminal, data);
try {
await createTerminalApp(serverSettings);
await JupyterLabAppFactory.createTerminalApp(serverSettings);
TelemetryProcessor.traceSuccess(Action.OpenTerminal, data, startTime);
} catch (error) {
TelemetryProcessor.traceFailure(Action.OpenTerminal, data, startTime);

View File

@@ -1,6 +1,6 @@
import { useEffect, useState } from "react";
import { GenerateTokenResponse } from "../Contracts/DataModels";
import * as AuthHeadersUtil from "../Platform/Hosted/Authorization";
import AuthHeadersUtil from "../Platform/Hosted/Authorization";
export function useFullScreenURLs(): GenerateTokenResponse | undefined {
const [state, setState] = useState<GenerateTokenResponse>();

View File

@@ -26,7 +26,7 @@ import {
getDatabaseAccountPropertiesFromMetadata,
} from "../Platform/Hosted/HostedUtils";
import { CollectionCreation } from "../Shared/Constants";
import * as DefaultExperienceUtility from "../Shared/DefaultExperienceUtility";
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
import { PortalEnv, updateUserContext, userContext } from "../UserContext";
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";