From e8e5eb55cb707d4053eb27d4e0afd1857e0aaa50 Mon Sep 17 00:00:00 2001 From: Jordi Bunster Date: Tue, 23 Feb 2021 11:15:57 -0800 Subject: [PATCH] Remove SplashScreenComponentAdapter (#440) Merge SplashScreenComponentAdapter and SplashScreenComponent into one SplashScreen React component. --- .eslintignore | 5 +- .../SettingsComponent.test.tsx.snap | 24 --- src/Explorer/Explorer.tsx | 4 +- ...ScreenComponent.less => SplashScreen.less} | 0 ...ntAdapter.test.ts => SplashScreen.test.ts} | 16 +- ...ComponentApdapter.tsx => SplashScreen.tsx} | 171 ++++++++++++++---- .../SplashScreen/SplashScreenComponent.tsx | 133 -------------- src/Main.tsx | 7 +- 8 files changed, 153 insertions(+), 207 deletions(-) rename src/Explorer/SplashScreen/{SplashScreenComponent.less => SplashScreen.less} (100%) rename src/Explorer/SplashScreen/{SplashScreenComponentAdapter.test.ts => SplashScreen.test.ts} (75%) rename src/Explorer/SplashScreen/{SplashScreenComponentApdapter.tsx => SplashScreen.tsx} (53%) delete mode 100644 src/Explorer/SplashScreen/SplashScreenComponent.tsx diff --git a/.eslintignore b/.eslintignore index c2f9c30d8..5f664a0b7 100644 --- a/.eslintignore +++ b/.eslintignore @@ -162,7 +162,7 @@ src/Explorer/Panes/Tables/Validators/EntityPropertyValidationCommon.ts src/Explorer/Panes/Tables/Validators/EntityPropertyValueValidator.ts src/Explorer/Panes/UploadFilePane.ts src/Explorer/Panes/UploadItemsPane.ts -src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts +src/Explorer/SplashScreen/SplashScreen.test.ts src/Explorer/Tables/Constants.ts src/Explorer/Tables/DataTable/CacheBase.ts src/Explorer/Tables/DataTable/DataTableBindingManager.ts @@ -377,8 +377,7 @@ src/Explorer/Notebook/temp/inputs/editor.tsx src/Explorer/Notebook/temp/markdown-cell.tsx src/Explorer/Notebook/temp/source.tsx src/Explorer/Notebook/temp/syntax-highlighter/index.tsx -src/Explorer/SplashScreen/SplashScreenComponent.tsx -src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx +src/Explorer/SplashScreen/SplashScreen.tsx src/Explorer/Tabs/GalleryTab.tsx src/Explorer/Tabs/NotebookViewerTab.tsx src/Explorer/Tabs/TerminalTab.tsx diff --git a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap index b5868d658..d3328ca3c 100644 --- a/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap +++ b/src/Explorer/Controls/Settings/__snapshots__/SettingsComponent.test.tsx.snap @@ -1138,12 +1138,6 @@ exports[`SettingsComponent renders 1`] = ` "shouldShowShareDialogContents": [Function], "signInAad": [Function], "sparkClusterConnectionInfo": [Function], - "splashScreenAdapter": SplashScreenComponentAdapter { - "clearMostRecent": [Function], - "container": [Circular], - "forceRender": [Function], - "parameters": [Function], - }, "splitter": Splitter { "bounds": Object { "max": 400, @@ -2383,12 +2377,6 @@ exports[`SettingsComponent renders 1`] = ` "shouldShowShareDialogContents": [Function], "signInAad": [Function], "sparkClusterConnectionInfo": [Function], - "splashScreenAdapter": SplashScreenComponentAdapter { - "clearMostRecent": [Function], - "container": [Circular], - "forceRender": [Function], - "parameters": [Function], - }, "splitter": Splitter { "bounds": Object { "max": 400, @@ -3641,12 +3629,6 @@ exports[`SettingsComponent renders 1`] = ` "shouldShowShareDialogContents": [Function], "signInAad": [Function], "sparkClusterConnectionInfo": [Function], - "splashScreenAdapter": SplashScreenComponentAdapter { - "clearMostRecent": [Function], - "container": [Circular], - "forceRender": [Function], - "parameters": [Function], - }, "splitter": Splitter { "bounds": Object { "max": 400, @@ -4886,12 +4868,6 @@ exports[`SettingsComponent renders 1`] = ` "shouldShowShareDialogContents": [Function], "signInAad": [Function], "sparkClusterConnectionInfo": [Function], - "splashScreenAdapter": SplashScreenComponentAdapter { - "clearMostRecent": [Function], - "container": [Circular], - "forceRender": [Function], - "parameters": [Function], - }, "splitter": Splitter { "bounds": Object { "max": 400, diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index 53ca9c341..2e21a2e25 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -63,7 +63,7 @@ import { RouteHandler } from "../RouteHandlers/RouteHandler"; import { SaveQueryPane } from "./Panes/SaveQueryPane"; import { SettingsPane } from "./Panes/SettingsPane"; import { SetupNotebooksPane } from "./Panes/SetupNotebooksPane"; -import { SplashScreenComponentAdapter } from "./SplashScreen/SplashScreenComponentApdapter"; +import { SplashScreen } from "./SplashScreen/SplashScreen"; import { Splitter, SplitterBounds, SplitterDirection } from "../Common/Splitter"; import { StringInputPane } from "./Panes/StringInputPane"; import { TableColumnOptionsPane } from "./Panes/Tables/TableColumnOptionsPane"; @@ -263,7 +263,6 @@ export default class Explorer { // React adapters private commandBarComponentAdapter: CommandBarComponentAdapter; - private splashScreenAdapter: SplashScreenComponentAdapter; private dialogComponentAdapter: DialogComponentAdapter; private _dialogProps: ko.Observable; private addSynapseLinkDialog: DialogComponentAdapter; @@ -986,7 +985,6 @@ export default class Explorer { }); this.dialogComponentAdapter = new DialogComponentAdapter(); this.dialogComponentAdapter.parameters = this._dialogProps; - this.splashScreenAdapter = new SplashScreenComponentAdapter(this); this.mostRecentActivity = new MostRecentActivity.MostRecentActivity(this); this._addSynapseLinkDialogProps = ko.observable({ diff --git a/src/Explorer/SplashScreen/SplashScreenComponent.less b/src/Explorer/SplashScreen/SplashScreen.less similarity index 100% rename from src/Explorer/SplashScreen/SplashScreenComponent.less rename to src/Explorer/SplashScreen/SplashScreen.less diff --git a/src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts b/src/Explorer/SplashScreen/SplashScreen.test.ts similarity index 75% rename from src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts rename to src/Explorer/SplashScreen/SplashScreen.test.ts index 62832a4d8..68c4c1108 100644 --- a/src/Explorer/SplashScreen/SplashScreenComponentAdapter.test.ts +++ b/src/Explorer/SplashScreen/SplashScreen.test.ts @@ -1,6 +1,6 @@ import * as ko from "knockout"; import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil"; -import { SplashScreenComponentAdapter } from "./SplashScreenComponentApdapter"; +import { SplashScreen } from "./SplashScreen"; import { TabsManager } from "../Tabs/TabsManager"; import Explorer from "../Explorer"; jest.mock("../Explorer"); @@ -14,7 +14,7 @@ const createExplorer = () => { return mock as jest.Mocked; }; -describe("SplashScreenComponentAdapter", () => { +describe("SplashScreen", () => { it("allows sample collection creation for supported api's", () => { const explorer = createExplorer(); const dataSampleUtil = new DataSamplesUtil(explorer); @@ -25,9 +25,9 @@ describe("SplashScreenComponentAdapter", () => { // Sample is supported jest.spyOn(dataSampleUtil, "isSampleContainerCreationSupported").mockImplementation(() => true); - const splashScreenAdapter = new SplashScreenComponentAdapter(explorer); - jest.spyOn(splashScreenAdapter, "createDataSampleUtil").mockImplementation(() => dataSampleUtil); - const mainButtons = splashScreenAdapter.createMainItems(); + const splashScreen = new SplashScreen({ explorer }); + jest.spyOn(splashScreen, "createDataSampleUtil").mockImplementation(() => dataSampleUtil); + const mainButtons = splashScreen.createMainItems(); // Press all buttons and make sure create gets called mainButtons.forEach((button) => { @@ -50,9 +50,9 @@ describe("SplashScreenComponentAdapter", () => { // Sample is not supported jest.spyOn(dataSampleUtil, "isSampleContainerCreationSupported").mockImplementation(() => false); - const splashScreenAdapter = new SplashScreenComponentAdapter(explorerStub); - jest.spyOn(splashScreenAdapter, "createDataSampleUtil").mockImplementation(() => dataSampleUtil); - const mainButtons = splashScreenAdapter.createMainItems(); + const splashScreen = new SplashScreen({ explorer: explorerStub }); + jest.spyOn(splashScreen, "createDataSampleUtil").mockImplementation(() => dataSampleUtil); + const mainButtons = splashScreen.createMainItems(); // Press all buttons and make sure create doesn't get called mainButtons.forEach((button) => { diff --git a/src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx b/src/Explorer/SplashScreen/SplashScreen.tsx similarity index 53% rename from src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx rename to src/Explorer/SplashScreen/SplashScreen.tsx index 03efd6588..aab72c6c2 100644 --- a/src/Explorer/SplashScreen/SplashScreenComponentApdapter.tsx +++ b/src/Explorer/SplashScreen/SplashScreen.tsx @@ -1,63 +1,161 @@ /** * Accordion top class */ -import * as ko from "knockout"; import * as React from "react"; -import { ReactAdapter } from "../../Bindings/ReactBindingHandler"; import * as ViewModels from "../../Contracts/ViewModels"; +import * as Constants from "../../Common/Constants"; +import { Link } from "office-ui-fabric-react/lib/Link"; import NewContainerIcon from "../../../images/Hero-new-container.svg"; import NewNotebookIcon from "../../../images/Hero-new-notebook.svg"; import NewQueryIcon from "../../../images/AddSqlQuery_16x16.svg"; import OpenQueryIcon from "../../../images/BrowseQuery.svg"; import NewStoredProcedureIcon from "../../../images/AddStoredProcedure.svg"; import ScaleAndSettingsIcon from "../../../images/Scale_15x15.svg"; -import { SplashScreenComponent, SplashScreenItem } from "./SplashScreenComponent"; import * as MostRecentActivity from "../MostRecentActivity/MostRecentActivity"; import AddDatabaseIcon from "../../../images/AddDatabase.svg"; import SampleIcon from "../../../images/Hero-sample.svg"; import { DataSamplesUtil } from "../DataSamples/DataSamplesUtil"; import Explorer from "../Explorer"; import { userContext } from "../../UserContext"; +import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLauncher"; -/** - * TODO Remove this when fully ported to ReactJS - */ -export class SplashScreenComponentAdapter implements ReactAdapter { +export interface SplashScreenItem { + iconSrc: string; + title: string; + info?: string; + description: string; + onClick: () => void; +} + +export interface SplashScreenProps { + explorer: Explorer; +} + +export class SplashScreen extends React.Component { + private static readonly seeMoreItemTitle: string = "See more Cosmos DB documentation"; + private static readonly seeMoreItemUrl: string = "https://aka.ms/cosmosdbdocument"; private static readonly dataModelingUrl = "https://docs.microsoft.com/azure/cosmos-db/modeling-data"; private static readonly throughputEstimatorUrl = "https://cosmos.azure.com/capacitycalculator"; private static readonly failoverUrl = "https://docs.microsoft.com/azure/cosmos-db/high-availability"; - public parameters: ko.Observable; + private readonly container: Explorer; - constructor(private container: Explorer) { - this.parameters = ko.observable(Date.now()); - this.container.tabsManager.openedTabs.subscribe((tabs) => { - if (tabs.length === 0) { - this.forceRender(); - } - }); - this.container.selectedNode.subscribe(this.forceRender); - this.container.isNotebookEnabled.subscribe(this.forceRender); + constructor(props: SplashScreenProps) { + super(props); + this.container = props.explorer; + this.container.tabsManager.openedTabs.subscribe(() => this.setState({})); + this.container.selectedNode.subscribe(() => this.setState({})); + this.container.isNotebookEnabled.subscribe(() => this.setState({})); } - public forceRender = (): void => { - window.requestAnimationFrame(() => this.parameters(Date.now())); - }; + public shouldComponentUpdate() { + return this.container.tabsManager.openedTabs.length === 0; + } private clearMostRecent = (): void => { this.container.mostRecentActivity.clear(userContext.databaseAccount?.id); - this.forceRender(); + this.setState({}); }; - public renderComponent(): JSX.Element { + public render(): JSX.Element { + const mainItems = this.createMainItems(); + const commonTaskItems = this.createCommonTaskItems(); + const recentItems = this.createRecentItems(); + const tipsItems = this.createTipsItems(); + const onClearRecent = this.clearMostRecent; + return ( - +
+
+
+ Welcome to Cosmos DB + +
+
Globally distributed, multi-model database service for any scale
+
+ {mainItems.map((item) => ( +
this.onSplashScreenItemKeyPress(event, item.onClick)} + tabIndex={0} + role="button" + > + +
+
{item.title}
+
{item.description}
+
+
+ ))} +
+
+
+
Common Tasks
+
    + {commonTaskItems.map((item) => ( +
  • this.onSplashScreenItemKeyPress(event, item.onClick)} + tabIndex={0} + role="button" + > + + + {item.title} + +
  • + ))} +
+
+
+
Recents
+
    + {recentItems.map((item, index) => ( +
  • + + + + {item.title} + +
    {item.description}
    +
    +
  • + ))} +
+ {recentItems.length > 0 && onClearRecent()}>Clear Recents} +
+
+
Tips
+
    + {tipsItems.map((item) => ( +
  • this.onSplashScreenItemKeyPress(event, item.onClick)} + tabIndex={0} + role="link" + > +
    + {item.title} +
    +
    {item.description}
    +
  • + ))} +
  • + + {SplashScreen.seeMoreItemTitle} + +
  • +
+
+
+
+
); } @@ -198,7 +296,7 @@ export class SplashScreenComponentAdapter implements ReactAdapter { iconSrc: MostRecentActivity.MostRecentActivity.getItemIcon(item), title: item.title, description: item.description, - info: SplashScreenComponentAdapter.getInfo(item), + info: SplashScreen.getInfo(item), onClick: () => this.container.mostRecentActivity.onItemClicked(item), })); } @@ -209,20 +307,27 @@ export class SplashScreenComponentAdapter implements ReactAdapter { iconSrc: null, title: "Data Modeling", description: "Learn more about modeling", - onClick: () => window.open(SplashScreenComponentAdapter.dataModelingUrl), + onClick: () => window.open(SplashScreen.dataModelingUrl), }, { iconSrc: null, title: "Cost & Throughput Calculation", description: "Learn more about cost calculation", - onClick: () => window.open(SplashScreenComponentAdapter.throughputEstimatorUrl), + onClick: () => window.open(SplashScreen.throughputEstimatorUrl), }, { iconSrc: null, title: "Configure automatic failover", description: "Learn more about Cosmos DB high-availability", - onClick: () => window.open(SplashScreenComponentAdapter.failoverUrl), + onClick: () => window.open(SplashScreen.failoverUrl), }, ]; } + + private onSplashScreenItemKeyPress(event: React.KeyboardEvent, callback: () => void) { + if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) { + callback(); + event.stopPropagation(); + } + } } diff --git a/src/Explorer/SplashScreen/SplashScreenComponent.tsx b/src/Explorer/SplashScreen/SplashScreenComponent.tsx deleted file mode 100644 index 5da93c65a..000000000 --- a/src/Explorer/SplashScreen/SplashScreenComponent.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Accordion top class - */ - -import * as React from "react"; -import * as Constants from "../../Common/Constants"; -import { Link } from "office-ui-fabric-react/lib/Link"; -import { FeaturePanelLauncher } from "../Controls/FeaturePanel/FeaturePanelLauncher"; - -export interface SplashScreenItem { - iconSrc: string; - title: string; - info?: string; - description: string; - onClick: () => void; -} -export interface SplashScreenComponentProps { - mainItems: SplashScreenItem[]; - commonTaskItems: SplashScreenItem[]; - recentItems: SplashScreenItem[]; - tipsItems: SplashScreenItem[]; - onClearRecent: () => void; -} - -export class SplashScreenComponent extends React.Component { - private static readonly seeMoreItemTitle: string = "See more Cosmos DB documentation"; - private static readonly seeMoreItemUrl: string = "https://aka.ms/cosmosdbdocument"; - - public render(): JSX.Element { - return ( -
-
-
- Welcome to Cosmos DB - -
-
Globally distributed, multi-model database service for any scale
-
- {this.props.mainItems.map((item: SplashScreenItem) => ( -
this.onSplashScreenItemKeyPress(event, item.onClick)} - tabIndex={0} - role="button" - > - -
-
{item.title}
-
{item.description}
-
-
- ))} -
-
-
-
Common Tasks
-
    - {this.props.commonTaskItems.map((item: SplashScreenItem) => ( -
  • this.onSplashScreenItemKeyPress(event, item.onClick)} - tabIndex={0} - role="button" - > - - - {item.title} - -
  • - ))} -
-
-
-
Recents
-
    - {this.props.recentItems.map((item: SplashScreenItem, index: number) => ( -
  • - - - - {item.title} - -
    {item.description}
    -
    -
  • - ))} -
- {this.props.recentItems.length > 0 && ( - this.props.onClearRecent()}>Clear Recents - )} -
-
-
Tips
-
    - {this.props.tipsItems.map((item: SplashScreenItem) => ( -
  • this.onSplashScreenItemKeyPress(event, item.onClick)} - tabIndex={0} - role="link" - > -
    - {item.title} -
    -
    {item.description}
    -
  • - ))} -
  • - - {SplashScreenComponent.seeMoreItemTitle} - -
  • -
-
-
-
-
- ); - } - - private onSplashScreenItemKeyPress(event: React.KeyboardEvent, callback: () => void) { - if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) { - callback(); - event.stopPropagation(); - } - } -} diff --git a/src/Main.tsx b/src/Main.tsx index ac7f77018..d424b90ab 100644 --- a/src/Main.tsx +++ b/src/Main.tsx @@ -30,7 +30,7 @@ import "./Explorer/Panes/GraphNewVertexPane.less"; import "./Explorer/Tabs/QueryTab.less"; import "./Explorer/Controls/TreeComponent/treeComponent.less"; import "./Explorer/Controls/Accordion/AccordionComponent.less"; -import "./Explorer/SplashScreen/SplashScreenComponent.less"; +import "./Explorer/SplashScreen/SplashScreen.less"; import "./Explorer/Controls/Notebook/NotebookTerminalComponent.less"; // Image Dependencies @@ -68,6 +68,7 @@ import { useKnockoutExplorer } from "./hooks/useKnockoutExplorer"; import { useSidePanel } from "./hooks/useSidePanel"; import { NotificationConsoleComponent } from "./Explorer/Menus/NotificationConsole/NotificationConsoleComponent"; import { PanelContainerComponent } from "./Explorer/Panes/PanelContainerComponent"; +import { SplashScreen } from "./Explorer/SplashScreen/SplashScreen"; initializeIcons(); @@ -87,7 +88,7 @@ const App: React.FunctionComponent = () => { closeSidePanel, }; const config = useConfig(); - useKnockoutExplorer(config?.platform, explorerParams); + const explorer = useKnockoutExplorer(config?.platform, explorerParams); return (
@@ -275,7 +276,7 @@ const App: React.FunctionComponent = () => { data-bind="visible: !isRefreshingExplorer() && tabsManager.openedTabs().length === 0" >
-
+