Migrate UploadItemPane to react (#17)
* Create GenericPaneComponent and use it to migrate UploadItemsPane to React * Add helper functions for building each panel section * Address comments and some styling changes * Unsubscribe to isNotificationConsoleExpanded when component unmounts
This commit is contained in:
parent
aa8236666e
commit
582ac865ff
|
@ -570,6 +570,12 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fileImportButton {
|
||||||
|
height: 24px;
|
||||||
|
border: @ButtonBorderWidth solid transparent;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
.fileUploadSummaryContainer {
|
.fileUploadSummaryContainer {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
|
||||||
|
@ -1016,6 +1022,18 @@ menuQuickStart {
|
||||||
background: #262626;
|
background: #262626;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.panelContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panelContentWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.contextual-pane {
|
.contextual-pane {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
right: 0 !important;
|
right: 0 !important;
|
||||||
|
@ -1232,23 +1250,25 @@ menuQuickStart {
|
||||||
padding: 2px 30px;
|
padding: 2px 30px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
border-color: #0072c6;
|
||||||
|
background-color: #0072c6;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.btncreatecoll1:hover {
|
.leftpanel-okbut .genericPaneSubmitBtn {
|
||||||
background: @AccentMediumHigh;
|
border: 1px solid @AccentMediumHigh;
|
||||||
|
background-color: @AccentMediumHigh;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: @AccentMediumHigh;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
height: 24px;
|
||||||
|
|
||||||
.btncreatecoll1:active {
|
&:active {
|
||||||
border: 1px solid #0072c6;
|
border-color: #0072c6;
|
||||||
background-color: #0072c6;
|
background-color: #0072c6;
|
||||||
color: white;
|
}
|
||||||
padding: 2px 30px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btncreatecoll1-off {
|
.btncreatecoll1-off {
|
||||||
|
@ -1361,6 +1381,15 @@ p {
|
||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.headerline .closePaneBtn {
|
||||||
|
float: right;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 4px;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
.closeImg {
|
.closeImg {
|
||||||
float: right;
|
float: right;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -1710,6 +1739,13 @@ input::-webkit-calendar-picker-indicator {
|
||||||
margin: (2 * @MediumSpace) 0px;
|
margin: (2 * @MediumSpace) 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contextual-pane .panelMainContent {
|
||||||
|
padding-left: 34px;
|
||||||
|
padding-right: 34px;
|
||||||
|
color: @BaseDark;
|
||||||
|
margin: (2 * @MediumSpace) 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.contextual-pane .paneFooter {
|
.contextual-pane .paneFooter {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
|
|
|
@ -127,6 +127,7 @@ export class Features {
|
||||||
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
public static readonly enableFixedCollectionWithSharedThroughput = "enablefixedcollectionwithsharedthroughput";
|
||||||
public static readonly enableAutoPilotV2 = "enableautopilotv2";
|
public static readonly enableAutoPilotV2 = "enableautopilotv2";
|
||||||
public static readonly ttl90Days = "ttl90days";
|
public static readonly ttl90Days = "ttl90days";
|
||||||
|
public static readonly enableRightPanelV2 = "enablerightpanelv2";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AfecFeatures {
|
export class AfecFeatures {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import { Splitter } from "../Common/Splitter";
|
||||||
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
import { StringInputPane } from "../Explorer/Panes/StringInputPane";
|
||||||
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
import { TextFieldProps } from "../Explorer/Controls/DialogReactComponent/DialogComponent";
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
|
import { UploadItemsPaneAdapter } from "../Explorer/Panes/UploadItemsPaneAdapter";
|
||||||
|
|
||||||
export interface ExplorerOptions {
|
export interface ExplorerOptions {
|
||||||
documentClientUtility: DocumentClientUtilityBase;
|
documentClientUtility: DocumentClientUtilityBase;
|
||||||
|
@ -87,6 +88,7 @@ export interface Explorer {
|
||||||
isGalleryEnabled: ko.Computed<boolean>;
|
isGalleryEnabled: ko.Computed<boolean>;
|
||||||
isGitHubPaneEnabled: ko.Observable<boolean>;
|
isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||||
isGraphsEnabled: ko.Computed<boolean>;
|
isGraphsEnabled: ko.Computed<boolean>;
|
||||||
|
isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
canExceedMaximumValue: ko.Computed<boolean>;
|
canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
||||||
isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
|
@ -141,6 +143,7 @@ export interface Explorer {
|
||||||
executeSprocParamsPane: ExecuteSprocParamsPane;
|
executeSprocParamsPane: ExecuteSprocParamsPane;
|
||||||
renewAdHocAccessPane: RenewAdHocAccessPane;
|
renewAdHocAccessPane: RenewAdHocAccessPane;
|
||||||
uploadItemsPane: UploadItemsPane;
|
uploadItemsPane: UploadItemsPane;
|
||||||
|
uploadItemsPaneAdapter: UploadItemsPaneAdapter;
|
||||||
loadQueryPane: LoadQueryPane;
|
loadQueryPane: LoadQueryPane;
|
||||||
saveQueryPane: ContextualPane;
|
saveQueryPane: ContextualPane;
|
||||||
browseQueriesPane: BrowseQueriesPane;
|
browseQueriesPane: BrowseQueriesPane;
|
||||||
|
|
|
@ -86,6 +86,7 @@ import { StringInputPane } from "./Panes/StringInputPane";
|
||||||
import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane";
|
import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane";
|
||||||
import { UploadFilePane } from "./Panes/UploadFilePane";
|
import { UploadFilePane } from "./Panes/UploadFilePane";
|
||||||
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
import { UploadItemsPane } from "./Panes/UploadItemsPane";
|
||||||
|
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
||||||
|
|
||||||
BindingHandlersRegisterer.registerBindingHandlers();
|
BindingHandlersRegisterer.registerBindingHandlers();
|
||||||
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
|
||||||
|
@ -188,6 +189,7 @@ export default class Explorer implements ViewModels.Explorer {
|
||||||
public executeSprocParamsPane: ViewModels.ExecuteSprocParamsPane;
|
public executeSprocParamsPane: ViewModels.ExecuteSprocParamsPane;
|
||||||
public renewAdHocAccessPane: ViewModels.RenewAdHocAccessPane;
|
public renewAdHocAccessPane: ViewModels.RenewAdHocAccessPane;
|
||||||
public uploadItemsPane: ViewModels.UploadItemsPane;
|
public uploadItemsPane: ViewModels.UploadItemsPane;
|
||||||
|
public uploadItemsPaneAdapter: UploadItemsPaneAdapter;
|
||||||
public loadQueryPane: ViewModels.LoadQueryPane;
|
public loadQueryPane: ViewModels.LoadQueryPane;
|
||||||
public saveQueryPane: ViewModels.ContextualPane;
|
public saveQueryPane: ViewModels.ContextualPane;
|
||||||
public browseQueriesPane: ViewModels.BrowseQueriesPane;
|
public browseQueriesPane: ViewModels.BrowseQueriesPane;
|
||||||
|
@ -205,6 +207,7 @@ export default class Explorer implements ViewModels.Explorer {
|
||||||
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||||
public isGraphsEnabled: ko.Computed<boolean>;
|
public isGraphsEnabled: ko.Computed<boolean>;
|
||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
|
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
public hasAutoPilotV2FeatureFlag: ko.Computed<boolean>;
|
||||||
|
|
||||||
|
@ -551,6 +554,7 @@ export default class Explorer implements ViewModels.Explorer {
|
||||||
!this.isRunningOnNationalCloud() &&
|
!this.isRunningOnNationalCloud() &&
|
||||||
!this.isPreferredApiGraph()
|
!this.isPreferredApiGraph()
|
||||||
);
|
);
|
||||||
|
this.isRightPanelV2Enabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableRightPanelV2));
|
||||||
this.defaultExperience.subscribe((defaultExperience: string) => {
|
this.defaultExperience.subscribe((defaultExperience: string) => {
|
||||||
if (
|
if (
|
||||||
defaultExperience &&
|
defaultExperience &&
|
||||||
|
@ -707,6 +711,8 @@ export default class Explorer implements ViewModels.Explorer {
|
||||||
container: this
|
container: this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.uploadItemsPaneAdapter = new UploadItemsPaneAdapter(this);
|
||||||
|
|
||||||
this.loadQueryPane = new LoadQueryPane({
|
this.loadQueryPane = new LoadQueryPane({
|
||||||
documentClientUtility: this.documentClientUtility,
|
documentClientUtility: this.documentClientUtility,
|
||||||
id: "loadquerypane",
|
id: "loadquerypane",
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { TableColumnOptionsPane } from "../../src/Explorer/Panes/Tables/TableCol
|
||||||
import { TextFieldProps } from "./Controls/DialogReactComponent/DialogComponent";
|
import { TextFieldProps } from "./Controls/DialogReactComponent/DialogComponent";
|
||||||
import { UploadDetails } from "../workers/upload/definitions";
|
import { UploadDetails } from "../workers/upload/definitions";
|
||||||
import { UploadFilePane } from "./Panes/UploadFilePane";
|
import { UploadFilePane } from "./Panes/UploadFilePane";
|
||||||
|
import { UploadItemsPaneAdapter } from "./Panes/UploadItemsPaneAdapter";
|
||||||
import { Versions } from "../../src/Contracts/ExplorerContracts";
|
import { Versions } from "../../src/Contracts/ExplorerContracts";
|
||||||
import { CollectionCreationDefaults } from "../Shared/Constants";
|
import { CollectionCreationDefaults } from "../Shared/Constants";
|
||||||
|
|
||||||
|
@ -86,6 +87,7 @@ export class ExplorerStub implements ViewModels.Explorer {
|
||||||
public settingsPane: ViewModels.SettingsPane;
|
public settingsPane: ViewModels.SettingsPane;
|
||||||
public executeSprocParamsPane: ViewModels.ExecuteSprocParamsPane;
|
public executeSprocParamsPane: ViewModels.ExecuteSprocParamsPane;
|
||||||
public uploadItemsPane: ViewModels.UploadItemsPane;
|
public uploadItemsPane: ViewModels.UploadItemsPane;
|
||||||
|
public uploadItemsPaneAdapter: UploadItemsPaneAdapter;
|
||||||
public loadQueryPane: ViewModels.LoadQueryPane;
|
public loadQueryPane: ViewModels.LoadQueryPane;
|
||||||
public saveQueryPane: ViewModels.ContextualPane;
|
public saveQueryPane: ViewModels.ContextualPane;
|
||||||
public browseQueriesPane: ViewModels.BrowseQueriesPane;
|
public browseQueriesPane: ViewModels.BrowseQueriesPane;
|
||||||
|
@ -97,6 +99,7 @@ export class ExplorerStub implements ViewModels.Explorer {
|
||||||
public isGalleryEnabled: ko.Computed<boolean>;
|
public isGalleryEnabled: ko.Computed<boolean>;
|
||||||
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
public isGitHubPaneEnabled: ko.Observable<boolean>;
|
||||||
public isGraphsEnabled: ko.Computed<boolean>;
|
public isGraphsEnabled: ko.Computed<boolean>;
|
||||||
|
public isRightPanelV2Enabled: ko.Computed<boolean>;
|
||||||
public canExceedMaximumValue: ko.Computed<boolean>;
|
public canExceedMaximumValue: ko.Computed<boolean>;
|
||||||
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
public isHostedDataExplorerEnabled: ko.Computed<boolean>;
|
||||||
public parentFrameDataExplorerVersion: ko.Observable<string> = ko.observable<string>(Versions.DataExplorer);
|
public parentFrameDataExplorerVersion: ko.Observable<string> = ko.observable<string>(Versions.DataExplorer);
|
||||||
|
|
|
@ -0,0 +1,141 @@
|
||||||
|
import * as React from "react";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import { IconButton, PrimaryButton } from "office-ui-fabric-react/lib/Button";
|
||||||
|
import { KeyCodes } from "../../Common/Constants";
|
||||||
|
import { Subscription } from "knockout";
|
||||||
|
import ErrorRedIcon from "../../../images/error_red.svg";
|
||||||
|
import LoadingIndicatorIcon from "../../../images/LoadingIndicator_3Squares.gif";
|
||||||
|
|
||||||
|
export interface GenericRightPaneProps {
|
||||||
|
container: ViewModels.Explorer;
|
||||||
|
content: JSX.Element;
|
||||||
|
formError: string;
|
||||||
|
formErrorDetail: string;
|
||||||
|
id: string;
|
||||||
|
isExecuting: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onSubmit: () => void;
|
||||||
|
submitButtonText: string;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenericRightPaneState {
|
||||||
|
panelHeight: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GenericRightPaneComponent extends React.Component<GenericRightPaneProps, GenericRightPaneState> {
|
||||||
|
private notificationConsoleSubscription: Subscription;
|
||||||
|
|
||||||
|
constructor(props: GenericRightPaneProps) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
panelHeight: this.getPanelHeight()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
this.notificationConsoleSubscription = this.props.container.isNotificationConsoleExpanded.subscribe(() => {
|
||||||
|
this.setState({ panelHeight: this.getPanelHeight() });
|
||||||
|
});
|
||||||
|
this.props.container.isNotificationConsoleExpanded.extend({ rateLimit: 10 });
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillUnmount(): void {
|
||||||
|
this.notificationConsoleSubscription && this.notificationConsoleSubscription.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div tabIndex={-1} onKeyDown={this.onKeyDown}>
|
||||||
|
<div className="contextual-pane-out" onClick={this.props.onClose}></div>
|
||||||
|
<div className="contextual-pane" id={this.props.id} style={{ height: this.state.panelHeight }} onKeyDown={this.onKeyDown}>
|
||||||
|
<div className="panelContentWrapper">
|
||||||
|
{this.createPanelHeader()}
|
||||||
|
{this.createErrorSection()}
|
||||||
|
{this.props.content}
|
||||||
|
{this.createPanelFooter()}
|
||||||
|
</div>
|
||||||
|
{this.createLoadingScreen()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private createPanelHeader = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="firstdivbg headerline">
|
||||||
|
<span id="databaseTitle">{this.props.title}</span>
|
||||||
|
<IconButton
|
||||||
|
ariaLabel="Close pane"
|
||||||
|
title="Close pane"
|
||||||
|
onClick={this.props.onClose}
|
||||||
|
tabIndex={0}
|
||||||
|
className="closePaneBtn"
|
||||||
|
iconProps={{ iconName: "Cancel" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private createErrorSection = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="warningErrorContainer" aria-live="assertive" hidden={!this.props.formError}>
|
||||||
|
<div className="warningErrorContent">
|
||||||
|
<span>
|
||||||
|
<img className="paneErrorIcon" src={ErrorRedIcon} alt="Error" />
|
||||||
|
</span>
|
||||||
|
<span className="warningErrorDetailsLinkContainer">
|
||||||
|
<span className="formErrors" title={this.props.formError}>
|
||||||
|
{this.props.formError}
|
||||||
|
</span>
|
||||||
|
<a className="errorLink" role="link" hidden={!this.props.formErrorDetail} onClick={this.showErrorDetail}>
|
||||||
|
More details
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private createPanelFooter = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="paneFooter">
|
||||||
|
<div className="leftpanel-okbut">
|
||||||
|
<PrimaryButton
|
||||||
|
ariaLabel="Submit"
|
||||||
|
title="Submit"
|
||||||
|
onClick={this.props.onSubmit}
|
||||||
|
tabIndex={0}
|
||||||
|
className="genericPaneSubmitBtn"
|
||||||
|
text={this.props.submitButtonText}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private createLoadingScreen = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="dataExplorerLoaderContainer dataExplorerPaneLoaderContainer" hidden={!this.props.isExecuting}>
|
||||||
|
<img className="dataExplorerLoader" src={LoadingIndicatorIcon} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>): void => {
|
||||||
|
if (event.keyCode === KeyCodes.Escape) {
|
||||||
|
this.props.onClose();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private showErrorDetail = (): void => {
|
||||||
|
this.props.container.expandConsole();
|
||||||
|
};
|
||||||
|
|
||||||
|
private getPanelHeight = (): number => {
|
||||||
|
const notificationConsoleElement: HTMLElement = document.getElementById("explorerNotificationConsole");
|
||||||
|
return window.innerHeight - $(notificationConsoleElement).height();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
import * as Constants from "../../Common/Constants";
|
||||||
|
import * as ErrorParserUtility from "../../Common/ErrorParserUtility";
|
||||||
|
import * as ko from "knockout";
|
||||||
|
import * as React from "react";
|
||||||
|
import * as ViewModels from "../../Contracts/ViewModels";
|
||||||
|
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||||
|
import { IconButton } from "office-ui-fabric-react/lib/Button";
|
||||||
|
import { GenericRightPaneComponent, GenericRightPaneProps } from "./GenericRightPaneComponent";
|
||||||
|
import { NotificationConsoleUtils } from "../../Utils/NotificationConsoleUtils";
|
||||||
|
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||||
|
import { UploadDetailsRecord, UploadDetails } from "../../workers/upload/definitions";
|
||||||
|
import InfoBubbleIcon from "../../../images/info-bubble.svg";
|
||||||
|
|
||||||
|
const UPLOAD_FILE_SIZE_LIMIT = 2097152;
|
||||||
|
|
||||||
|
export class UploadItemsPaneAdapter implements ReactAdapter {
|
||||||
|
public parameters: ko.Observable<number>;
|
||||||
|
private isOpened: boolean;
|
||||||
|
private isExecuting: boolean;
|
||||||
|
private formError: string;
|
||||||
|
private formErrorDetail: string;
|
||||||
|
private selectedFiles: FileList;
|
||||||
|
private selectedFilesTitle: string;
|
||||||
|
private uploadFileData: UploadDetailsRecord[];
|
||||||
|
|
||||||
|
public constructor(private container: ViewModels.Explorer) {
|
||||||
|
this.parameters = ko.observable(Date.now());
|
||||||
|
this.reset();
|
||||||
|
this.triggerRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderComponent(): JSX.Element {
|
||||||
|
if (!this.isOpened) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props: GenericRightPaneProps = {
|
||||||
|
container: this.container,
|
||||||
|
content: this.createContent(),
|
||||||
|
formError: this.formError,
|
||||||
|
formErrorDetail: this.formErrorDetail,
|
||||||
|
id: "uploaditemspane",
|
||||||
|
isExecuting: this.isExecuting,
|
||||||
|
title: "Upload Items",
|
||||||
|
submitButtonText: "Upload",
|
||||||
|
onClose: () => this.close(),
|
||||||
|
onSubmit: () => this.submit()
|
||||||
|
};
|
||||||
|
return <GenericRightPaneComponent {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
public triggerRender(): void {
|
||||||
|
window.requestAnimationFrame(() => this.parameters(Date.now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public open(): void {
|
||||||
|
this.isOpened = true;
|
||||||
|
this.triggerRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public close(): void {
|
||||||
|
this.reset();
|
||||||
|
this.triggerRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
public submit(): void {
|
||||||
|
this.formError = "";
|
||||||
|
if (!this.selectedFiles || this.selectedFiles.length === 0) {
|
||||||
|
this.formError = "No files specified";
|
||||||
|
this.formErrorDetail = "No files were specified. Please input at least one file.";
|
||||||
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
ConsoleDataType.Error,
|
||||||
|
"Could not upload items -- No files were specified. Please input at least one file."
|
||||||
|
);
|
||||||
|
this.triggerRender();
|
||||||
|
return;
|
||||||
|
} else if (this._totalFileSizeForFileList() > UPLOAD_FILE_SIZE_LIMIT) {
|
||||||
|
this.formError = "Upload file size limit exceeded";
|
||||||
|
this.formErrorDetail = "Total file upload size exceeds the 2 MB file size limit.";
|
||||||
|
NotificationConsoleUtils.logConsoleMessage(
|
||||||
|
ConsoleDataType.Error,
|
||||||
|
"Could not upload items -- Total file upload size exceeds the 2 MB file size limit."
|
||||||
|
);
|
||||||
|
this.triggerRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedCollection: ViewModels.Collection = this.container.findSelectedCollection();
|
||||||
|
this.isExecuting = true;
|
||||||
|
this.triggerRender();
|
||||||
|
selectedCollection &&
|
||||||
|
selectedCollection
|
||||||
|
.uploadFiles(this.selectedFiles)
|
||||||
|
.then(
|
||||||
|
(uploadDetails: UploadDetails) => {
|
||||||
|
this.uploadFileData = uploadDetails.data;
|
||||||
|
this.selectedFiles = undefined;
|
||||||
|
this.selectedFilesTitle = "";
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
const message = ErrorParserUtility.parse(error);
|
||||||
|
this.formError = message[0].message;
|
||||||
|
this.formErrorDetail = message[0].message;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.finally(() => {
|
||||||
|
this.triggerRender();
|
||||||
|
this.isExecuting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private createContent = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="panelContent">
|
||||||
|
{this.createMainContentSection()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private createMainContentSection = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="paneMainContent">
|
||||||
|
<div className="renewUploadItemsHeader">
|
||||||
|
<span> Select JSON Files </span>
|
||||||
|
<span className="infoTooltip" role="tooltip" tabIndex={0}>
|
||||||
|
<img className="infoImg" src={InfoBubbleIcon} alt="More information" />
|
||||||
|
<span className="tooltiptext infoTooltipWidth">
|
||||||
|
Select one or more JSON files to upload. Each file can contain a single JSON document or an array of JSON
|
||||||
|
documents. The combined size of all files in an individual upload operation must be less than 2 MB. You
|
||||||
|
can perform multiple upload operations for larger data sets.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
className="importFilesTitle"
|
||||||
|
type="text"
|
||||||
|
disabled
|
||||||
|
value={this.selectedFilesTitle}
|
||||||
|
aria-label="Select JSON Files"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="importDocsInput"
|
||||||
|
title="Upload Icon"
|
||||||
|
multiple
|
||||||
|
accept="application/json"
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
style={{ display: "none" }}
|
||||||
|
onChange={this.updateSelectedFiles}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "FolderHorizontal" }}
|
||||||
|
className="fileImportButton"
|
||||||
|
alt="Select JSON files to upload"
|
||||||
|
title="Select JSON files to upload"
|
||||||
|
onClick={this.onImportButtonClick}
|
||||||
|
onKeyPress={this.onImportButtonKeyPress}
|
||||||
|
/>
|
||||||
|
<div className="fileUploadSummaryContainer" hidden={this.uploadFileData.length === 0}>
|
||||||
|
<b>File upload status</b>
|
||||||
|
<table className="fileUploadSummary">
|
||||||
|
<thead>
|
||||||
|
<tr className="fileUploadSummaryHeader fileUploadSummaryTuple">
|
||||||
|
<th>FILE NAME</th>
|
||||||
|
<th>STATUS</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.uploadFileData.map(
|
||||||
|
(data: UploadDetailsRecord): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<tr className="fileUploadSummaryTuple" key={data.fileName}>
|
||||||
|
<td>{data.fileName}</td>
|
||||||
|
<td>{this.fileUploadSummaryText(data.numSucceeded, data.numFailed)}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
private updateSelectedFiles = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
|
this.selectedFiles = event.target.files;
|
||||||
|
this._updateSelectedFilesTitle();
|
||||||
|
this.triggerRender();
|
||||||
|
};
|
||||||
|
|
||||||
|
private _updateSelectedFilesTitle = (): void => {
|
||||||
|
this.selectedFilesTitle = "";
|
||||||
|
|
||||||
|
if (!this.selectedFiles || this.selectedFiles.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.selectedFiles.length; i++) {
|
||||||
|
this.selectedFilesTitle += `"${this.selectedFiles.item(i).name}"`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private _totalFileSizeForFileList(): number {
|
||||||
|
let totalFileSize = 0;
|
||||||
|
if (!this.selectedFiles) {
|
||||||
|
return totalFileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < this.selectedFiles.length; i++) {
|
||||||
|
totalFileSize += this.selectedFiles.item(i).size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalFileSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
private fileUploadSummaryText = (numSucceeded: number, numFailed: number): string => {
|
||||||
|
return `${numSucceeded} items created, ${numFailed} errors`;
|
||||||
|
};
|
||||||
|
|
||||||
|
private onImportButtonClick = (): void => {
|
||||||
|
document.getElementById("importDocsInput").click();
|
||||||
|
};
|
||||||
|
|
||||||
|
private onImportButtonKeyPress = (event: React.KeyboardEvent<HTMLButtonElement>): void => {
|
||||||
|
if (event.charCode === Constants.KeyCodes.Enter || event.charCode === Constants.KeyCodes.Space) {
|
||||||
|
this.onImportButtonClick();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private reset = (): void => {
|
||||||
|
this.isOpened = false;
|
||||||
|
this.isExecuting = false;
|
||||||
|
this.formError = "";
|
||||||
|
this.formErrorDetail = "";
|
||||||
|
this.selectedFiles = undefined;
|
||||||
|
this.selectedFilesTitle = "";
|
||||||
|
this.uploadFileData = [];
|
||||||
|
};
|
||||||
|
}
|
|
@ -965,7 +965,10 @@ export default class DocumentsTab extends TabsBase implements ViewModels.Documen
|
||||||
onCommandClick: () => {
|
onCommandClick: () => {
|
||||||
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
const selectedCollection: ViewModels.Collection = container.findSelectedCollection();
|
||||||
const focusElement = document.getElementById("itemImportLink");
|
const focusElement = document.getElementById("itemImportLink");
|
||||||
selectedCollection && container.uploadItemsPane.open();
|
const uploadItemsPane = container.isRightPanelV2Enabled()
|
||||||
|
? container.uploadItemsPaneAdapter
|
||||||
|
: container.uploadItemsPane;
|
||||||
|
selectedCollection && uploadItemsPane.open();
|
||||||
focusElement && focusElement.focus();
|
focusElement && focusElement.focus();
|
||||||
},
|
},
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
|
|
|
@ -431,7 +431,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Global loader - End -->
|
<!-- Global loader - End -->
|
||||||
|
<div data-bind="react:uploadItemsPaneAdapter"></div>
|
||||||
<add-database-pane params="{data: addDatabasePane}"></add-database-pane>
|
<add-database-pane params="{data: addDatabasePane}"></add-database-pane>
|
||||||
<add-collection-pane params="{data: addCollectionPane}"></add-collection-pane>
|
<add-collection-pane params="{data: addCollectionPane}"></add-collection-pane>
|
||||||
<delete-collection-confirmation-pane params="{data: deleteCollectionConfirmationPane}">
|
<delete-collection-confirmation-pane params="{data: deleteCollectionConfirmationPane}">
|
||||||
|
|
Loading…
Reference in New Issue