mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-24 22:46:40 +00:00
Initial transfer from ADO (#13)
This commit is contained in:
parent
ab3486bd05
commit
e9d3160b57
@ -343,7 +343,7 @@ src/Explorer/Controls/LibraryManagement/LibraryManageComponentAdapter.tsx
|
||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.test.tsx
|
||||
src/Explorer/Controls/Notebook/NotebookTerminalComponent.tsx
|
||||
src/Explorer/Controls/NotebookViewer/NotebookMetadataComponent.tsx
|
||||
src/Explorer/Controls/NotebookViewer/NotebookViewer.tsx
|
||||
src/NotebookViewer/NotebookViewer.tsx
|
||||
src/Explorer/Controls/NotebookViewer/NotebookViewerComponent.tsx
|
||||
src/Explorer/Controls/QueriesGridReactComponent/QueriesGridComponent.tsx
|
||||
src/Explorer/Controls/QueriesGridReactComponent/QueriesGridComponentAdapter.tsx
|
||||
|
209
package-lock.json
generated
209
package-lock.json
generated
@ -1159,11 +1159,6 @@
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz",
|
||||
@ -1604,96 +1599,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "4.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.10.tgz",
|
||||
"integrity": "sha512-CQuZU9Y10RkwSdxjn785kw2EPcXhv5GKauuVQufR9LlD37kjfn21Im1yvr6wsUzn81oLhEvVPz727UWC0gbqxg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/styles": "^4.9.10",
|
||||
"@material-ui/system": "^4.9.10",
|
||||
"@material-ui/types": "^5.0.1",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"@types/react-transition-group": "^4.2.0",
|
||||
"clsx": "^1.0.4",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"popper.js": "^1.16.1-lts",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.0",
|
||||
"react-transition-group": "^4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"dom-helpers": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz",
|
||||
"integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^2.6.7"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz",
|
||||
"integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@material-ui/styles": {
|
||||
"version": "4.9.14",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.14.tgz",
|
||||
"integrity": "sha512-zecwWKgRU2VzdmutNovPB4s5LKI0TWyZKc/AHfPu9iY8tg4UoLjpa4Rn9roYrRfuTbBZHI6b0BXcQ8zkis0nzQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@material-ui/types": "^5.1.0",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"clsx": "^1.0.4",
|
||||
"csstype": "^2.5.2",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"jss": "^10.0.3",
|
||||
"jss-plugin-camel-case": "^10.0.3",
|
||||
"jss-plugin-default-unit": "^10.0.3",
|
||||
"jss-plugin-global": "^10.0.3",
|
||||
"jss-plugin-nested": "^10.0.3",
|
||||
"jss-plugin-props-sort": "^10.0.3",
|
||||
"jss-plugin-rule-value-function": "^10.0.3",
|
||||
"jss-plugin-vendor-prefixer": "^10.0.3",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@material-ui/system": {
|
||||
"version": "4.9.14",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz",
|
||||
"integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"@material-ui/utils": "^4.9.6",
|
||||
"csstype": "^2.5.2",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@material-ui/types": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz",
|
||||
"integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A=="
|
||||
},
|
||||
"@material-ui/utils": {
|
||||
"version": "4.9.12",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.12.tgz",
|
||||
"integrity": "sha512-/0rgZPEOcZq5CFA4+4n6Q6zk7fi8skHhH2Bcra8R3epoJEYy5PL55LuMazPtPH1oKeRausDV/Omz4BbgFsn1HQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.4.4",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.8.0"
|
||||
}
|
||||
},
|
||||
"@microsoft/applicationinsights-analytics-js": {
|
||||
"version": "2.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-analytics-js/-/applicationinsights-analytics-js-2.5.4.tgz",
|
||||
@ -3371,14 +3276,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz",
|
||||
"integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/shallowequal": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/shallowequal/-/shallowequal-1.1.1.tgz",
|
||||
@ -5624,11 +5521,6 @@
|
||||
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
|
||||
"dev": true
|
||||
},
|
||||
"clsx": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz",
|
||||
"integrity": "sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -6193,15 +6085,6 @@
|
||||
"postcss-value-parser": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"css-vendor": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
|
||||
"integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.3",
|
||||
"is-in-browser": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
|
||||
@ -9915,11 +9798,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"hyphenate-style-name": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz",
|
||||
"integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
@ -10459,11 +10337,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz",
|
||||
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="
|
||||
},
|
||||
"is-in-browser": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz",
|
||||
"integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU="
|
||||
},
|
||||
"is-number": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
|
||||
@ -11333,83 +11206,6 @@
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"jss": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz",
|
||||
"integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"csstype": "^2.6.5",
|
||||
"is-in-browser": "^1.1.3",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-camel-case": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz",
|
||||
"integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"hyphenate-style-name": "^1.0.3",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-default-unit": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz",
|
||||
"integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-global": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz",
|
||||
"integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-nested": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz",
|
||||
"integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.1.1",
|
||||
"tiny-warning": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"jss-plugin-props-sort": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz",
|
||||
"integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-rule-value-function": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz",
|
||||
"integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jss-plugin-vendor-prefixer": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz",
|
||||
"integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.3.1",
|
||||
"css-vendor": "^2.0.7",
|
||||
"jss": "10.1.1"
|
||||
}
|
||||
},
|
||||
"jsx-ast-utils": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
|
||||
@ -16795,11 +16591,6 @@
|
||||
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
|
||||
"optional": true
|
||||
},
|
||||
"tiny-warning": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
|
||||
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
|
@ -8,7 +8,6 @@
|
||||
"@azure/cosmos-language-service": "0.0.4",
|
||||
"@jupyterlab/services": "4.2.0",
|
||||
"@jupyterlab/terminal": "1.2.1",
|
||||
"@material-ui/core": "4.9.10",
|
||||
"@microsoft/applicationinsights-web": "2.5.4",
|
||||
"@nteract/commutable": "7.1.4",
|
||||
"@nteract/connected-components": "6.7.8",
|
||||
|
@ -738,6 +738,8 @@ export interface GitHubInfoJunoResponse {
|
||||
gitUrl: string;
|
||||
htmlUrl: string;
|
||||
metadata?: NotebookMetadata;
|
||||
officialSamplesIndex?: number;
|
||||
isLikedNotebook?: boolean;
|
||||
}
|
||||
|
||||
export interface LikedNotebooksJunoResponse {
|
||||
|
@ -229,7 +229,12 @@ export interface Explorer {
|
||||
importAndOpenFromGallery: (path: string, newName: string, content: any) => Promise<boolean>;
|
||||
openNotebookTerminal: (kind: TerminalKind) => void;
|
||||
openGallery: () => void;
|
||||
openNotebookViewer: (notebookUrl: string, notebookMetadata: DataModels.NotebookMetadata) => void;
|
||||
openNotebookViewer: (
|
||||
notebookUrl: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => void;
|
||||
notebookWorkspaceManager: NotebookWorkspaceManager;
|
||||
sparkClusterManager: SparkClusterManager;
|
||||
notebookContentProvider: IContentProvider;
|
||||
@ -887,6 +892,8 @@ export interface NotebookViewerTabOptions extends TabOptions {
|
||||
notebookUrl: string;
|
||||
notebookName: string;
|
||||
notebookMetadata: DataModels.NotebookMetadata;
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>;
|
||||
isLikedNotebook: boolean;
|
||||
}
|
||||
|
||||
export interface DocumentsTabOptions extends TabOptions {
|
||||
|
@ -22,11 +22,28 @@ export const subtleHelpfulTextStyles: ITextStyles = {
|
||||
}
|
||||
};
|
||||
|
||||
export const iconButtonStyles: IIconStyles = {
|
||||
root: {
|
||||
marginLeft: "10px",
|
||||
color: "#0078D4",
|
||||
backgroundColor: "#FFF",
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeights.regular,
|
||||
display: "inline-block",
|
||||
selectors: {
|
||||
":hover .ms-Button-icon": {
|
||||
color: "#ccc"
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const iconStyles: IIconStyles = {
|
||||
root: {
|
||||
marginLeft: "10px",
|
||||
color: "#0078D4",
|
||||
fontSize: 12,
|
||||
backgroundColor: "#FFF",
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeights.regular,
|
||||
display: "inline-block"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import { GalleryCardComponent, GalleryCardComponentProps } from "./GalleryCardComponent";
|
||||
|
||||
describe("GalleryCardComponent", () => {
|
||||
it("renders", () => {
|
||||
const props: GalleryCardComponentProps = {
|
||||
name: "mycard",
|
||||
url: "url",
|
||||
notebookMetadata: undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
onClick: () => {}
|
||||
};
|
||||
|
||||
const wrapper = shallow(<GalleryCardComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import { Card, ICardTokens, ICardSectionTokens } from "@uifabric/react-cards";
|
||||
import { Icon, Image, Persona, Text } from "office-ui-fabric-react";
|
||||
import {
|
||||
@ -10,7 +10,7 @@ import {
|
||||
subtleIconStyles
|
||||
} from "./CardStyleConstants";
|
||||
|
||||
interface GalleryCardComponentProps {
|
||||
export interface GalleryCardComponentProps {
|
||||
name: string;
|
||||
url: string;
|
||||
notebookMetadata: DataModels.NotebookMetadata;
|
@ -0,0 +1,28 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`GalleryCardComponent renders 1`] = `
|
||||
<Card
|
||||
aria-label="Notebook Card"
|
||||
onClick={[Function]}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenMargin": 12,
|
||||
}
|
||||
}
|
||||
>
|
||||
<CardSection>
|
||||
<Text
|
||||
styles={
|
||||
Object {
|
||||
"root": Object {
|
||||
"color": "#333333",
|
||||
"fontWeight": 600,
|
||||
},
|
||||
}
|
||||
}
|
||||
>
|
||||
mycard
|
||||
</Text>
|
||||
</CardSection>
|
||||
</Card>
|
||||
`;
|
@ -0,0 +1,9 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
.galleryContainer {
|
||||
padding: @LargeSpace @LargeSpace 30px @LargeSpace;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
font-family: @DataExplorerFont;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import {
|
||||
GalleryViewerContainerComponent,
|
||||
GalleryViewerContainerComponentProps,
|
||||
FullWidthTabs,
|
||||
FullWidthTabsProps,
|
||||
GalleryCardsComponent,
|
||||
GalleryCardsComponentProps,
|
||||
GalleryViewerComponent,
|
||||
GalleryViewerComponentProps
|
||||
} from "./GalleryViewerComponent";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
|
||||
describe("GalleryCardsComponent", () => {
|
||||
it("renders", () => {
|
||||
// TODO Mock this
|
||||
const props: GalleryCardsComponentProps = {
|
||||
data: [],
|
||||
userMetadata: undefined,
|
||||
onNotebookMetadataChange: (officialSamplesIndex: number, notebookMetadata: DataModels.NotebookMetadata) =>
|
||||
Promise.resolve(),
|
||||
onClick: (
|
||||
url: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => Promise.resolve()
|
||||
};
|
||||
|
||||
const wrapper = shallow(<GalleryCardsComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("FullWidthTabs", () => {
|
||||
it("renders", () => {
|
||||
const props: FullWidthTabsProps = {
|
||||
officialSamplesContent: [],
|
||||
likedNotebooksContent: [],
|
||||
userMetadata: undefined,
|
||||
onClick: (
|
||||
url: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => Promise.resolve()
|
||||
};
|
||||
|
||||
const wrapper = shallow(<FullWidthTabs {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("GalleryViewerContainerComponent", () => {
|
||||
it("renders", () => {
|
||||
const props: GalleryViewerContainerComponentProps = {
|
||||
container: undefined
|
||||
};
|
||||
|
||||
const wrapper = shallow(<GalleryViewerContainerComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe("GalleryCardComponent", () => {
|
||||
it("renders", () => {
|
||||
const props: GalleryViewerComponentProps = {
|
||||
container: undefined,
|
||||
officialSamplesData: [],
|
||||
likedNotebookData: undefined
|
||||
};
|
||||
|
||||
const wrapper = shallow(<GalleryViewerComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
356
src/Explorer/Controls/NotebookGallery/GalleryViewerComponent.tsx
Normal file
356
src/Explorer/Controls/NotebookGallery/GalleryViewerComponent.tsx
Normal file
@ -0,0 +1,356 @@
|
||||
/**
|
||||
* Gallery Viewer
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { GalleryCardComponent } from "./Cards/GalleryCardComponent";
|
||||
import { Stack, IStackTokens } from "office-ui-fabric-react";
|
||||
import { JunoUtils } from "../../../Utils/JunoUtils";
|
||||
import { CosmosClient } from "../../../Common/CosmosClient";
|
||||
import { config } from "../../../Config";
|
||||
import path from "path";
|
||||
import { SessionStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
||||
import { NotificationConsoleUtils } from "../../../Utils/NotificationConsoleUtils";
|
||||
import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationConsoleComponent";
|
||||
import * as TabComponent from "../Tabs/TabComponent";
|
||||
|
||||
import "./GalleryViewerComponent.less";
|
||||
|
||||
export interface GalleryCardsComponentProps {
|
||||
data: DataModels.GitHubInfoJunoResponse[];
|
||||
userMetadata: DataModels.UserMetadata;
|
||||
onNotebookMetadataChange: (
|
||||
officialSamplesIndex: number,
|
||||
notebookMetadata: DataModels.NotebookMetadata
|
||||
) => Promise<void>;
|
||||
onClick: (
|
||||
url: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export class GalleryCardsComponent extends React.Component<GalleryCardsComponentProps> {
|
||||
private sectionStackTokens: IStackTokens = { childrenGap: 30 };
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Stack horizontal wrap tokens={this.sectionStackTokens}>
|
||||
{this.props.data.map((githubInfo: DataModels.GitHubInfoJunoResponse, index: any) => {
|
||||
const name = githubInfo.name;
|
||||
const url = githubInfo.downloadUrl;
|
||||
const notebookMetadata = githubInfo.metadata || {
|
||||
date: "2008-12-01",
|
||||
description: "Great notebook",
|
||||
tags: ["favorite", "sample"],
|
||||
author: "Laurent Nguyen",
|
||||
views: 432,
|
||||
likes: 123,
|
||||
downloads: 56,
|
||||
imageUrl:
|
||||
"https://media.magazine.ferrari.com/images/2019/02/27/170304506-c1bcf028-b513-45f6-9f27-0cadac619c3d.jpg"
|
||||
};
|
||||
const officialSamplesIndex = githubInfo.officialSamplesIndex;
|
||||
const isLikedNotebook = githubInfo.isLikedNotebook;
|
||||
const updateTabsStatePerNotebook = this.props.onNotebookMetadataChange
|
||||
? (notebookMetadata: DataModels.NotebookMetadata) =>
|
||||
this.props.onNotebookMetadataChange(officialSamplesIndex, notebookMetadata)
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
name !== ".gitignore" &&
|
||||
url && (
|
||||
<GalleryCardComponent
|
||||
key={url}
|
||||
name={name}
|
||||
url={url}
|
||||
notebookMetadata={notebookMetadata}
|
||||
onClick={() => this.props.onClick(url, notebookMetadata, updateTabsStatePerNotebook, isLikedNotebook)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface FullWidthTabsProps {
|
||||
officialSamplesContent: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebooksContent: DataModels.GitHubInfoJunoResponse[];
|
||||
userMetadata: DataModels.UserMetadata;
|
||||
onClick: (
|
||||
url: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
interface FullWidthTabsState {
|
||||
activeTabIndex: number;
|
||||
officialSamplesContent: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebooksContent: DataModels.GitHubInfoJunoResponse[];
|
||||
userMetadata: DataModels.UserMetadata;
|
||||
}
|
||||
|
||||
export class FullWidthTabs extends React.Component<FullWidthTabsProps, FullWidthTabsState> {
|
||||
private authorizationToken = CosmosClient.authorizationToken();
|
||||
private appTabs: TabComponent.Tab[];
|
||||
|
||||
constructor(props: FullWidthTabsProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeTabIndex: 0,
|
||||
officialSamplesContent: this.props.officialSamplesContent,
|
||||
likedNotebooksContent: this.props.likedNotebooksContent,
|
||||
userMetadata: this.props.userMetadata
|
||||
};
|
||||
|
||||
this.appTabs = [
|
||||
{
|
||||
title: "Official Samples",
|
||||
content: {
|
||||
className: "",
|
||||
render: () => (
|
||||
<GalleryCardsComponent
|
||||
data={this.state.officialSamplesContent}
|
||||
onClick={this.props.onClick}
|
||||
userMetadata={this.state.userMetadata}
|
||||
onNotebookMetadataChange={this.updateTabsState}
|
||||
/>
|
||||
)
|
||||
},
|
||||
isVisible: () => true
|
||||
},
|
||||
{
|
||||
title: "Liked Notebooks",
|
||||
content: {
|
||||
className: "",
|
||||
render: () => (
|
||||
<GalleryCardsComponent
|
||||
data={this.state.likedNotebooksContent}
|
||||
onClick={this.props.onClick}
|
||||
userMetadata={this.state.userMetadata}
|
||||
onNotebookMetadataChange={this.updateTabsState}
|
||||
/>
|
||||
)
|
||||
},
|
||||
isVisible: () => true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
public updateTabsState = async (officialSamplesIndex: number, notebookMetadata: DataModels.NotebookMetadata) => {
|
||||
let currentLikedNotebooksContent = [...this.state.likedNotebooksContent];
|
||||
let currentUserMetadata = { ...this.state.userMetadata };
|
||||
let currentLikedNotebooks = [...currentUserMetadata.likedNotebooks];
|
||||
|
||||
const currentOfficialSamplesContent = [...this.state.officialSamplesContent];
|
||||
const currentOfficialSamplesObject = { ...currentOfficialSamplesContent[officialSamplesIndex] };
|
||||
const metadata = { ...currentOfficialSamplesObject.metadata };
|
||||
const metadataLikesUpdates = metadata.likes - notebookMetadata.likes;
|
||||
|
||||
metadata.views = notebookMetadata.views;
|
||||
metadata.downloads = notebookMetadata.downloads;
|
||||
metadata.likes = notebookMetadata.likes;
|
||||
currentOfficialSamplesObject.metadata = metadata;
|
||||
|
||||
// Notebook has been liked. Add To likedNotebooksContent, update isLikedNotebook flag
|
||||
if (metadataLikesUpdates < 0) {
|
||||
currentOfficialSamplesObject.isLikedNotebook = true;
|
||||
currentLikedNotebooksContent = currentLikedNotebooksContent.concat(currentOfficialSamplesObject);
|
||||
currentLikedNotebooks = currentLikedNotebooks.concat(currentOfficialSamplesObject.path);
|
||||
currentUserMetadata = { likedNotebooks: currentLikedNotebooks };
|
||||
} else if (metadataLikesUpdates > 0) {
|
||||
// Notebook has been unliked. Remove from likedNotebooksContent after matching the path, update isLikedNotebook flag
|
||||
|
||||
currentOfficialSamplesObject.isLikedNotebook = false;
|
||||
const likedNotebookIndex = currentLikedNotebooks.findIndex((path: string) => {
|
||||
return path === currentOfficialSamplesObject.path;
|
||||
});
|
||||
currentLikedNotebooksContent.splice(likedNotebookIndex, 1);
|
||||
currentLikedNotebooks.splice(likedNotebookIndex, 1);
|
||||
currentUserMetadata = { likedNotebooks: currentLikedNotebooks };
|
||||
}
|
||||
|
||||
currentOfficialSamplesContent[officialSamplesIndex] = currentOfficialSamplesObject;
|
||||
|
||||
this.setState({
|
||||
activeTabIndex: 0,
|
||||
userMetadata: currentUserMetadata,
|
||||
likedNotebooksContent: currentLikedNotebooksContent,
|
||||
officialSamplesContent: currentOfficialSamplesContent
|
||||
});
|
||||
|
||||
JunoUtils.updateNotebookMetadata(this.authorizationToken, notebookMetadata).then(
|
||||
async returnedNotebookMetadata => {
|
||||
if (metadataLikesUpdates !== 0) {
|
||||
JunoUtils.updateUserMetadata(this.authorizationToken, currentUserMetadata);
|
||||
// TODO: update state here?
|
||||
}
|
||||
},
|
||||
error => {
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`Error updating notebook metadata: ${JSON.stringify(error)}`
|
||||
);
|
||||
// TODO add telemetry
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
private onTabIndexChange = (activeTabIndex: number) => this.setState({ activeTabIndex });
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<TabComponent.TabComponent
|
||||
tabs={this.appTabs}
|
||||
onTabIndexChange={this.onTabIndexChange.bind(this)}
|
||||
currentTabIndex={this.state.activeTabIndex}
|
||||
hideHeader={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface GalleryViewerContainerComponentProps {
|
||||
container: ViewModels.Explorer;
|
||||
}
|
||||
|
||||
interface GalleryViewerContainerComponentState {
|
||||
officialSamplesData: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebooksData: DataModels.LikedNotebooksJunoResponse;
|
||||
}
|
||||
|
||||
export class GalleryViewerContainerComponent extends React.Component<
|
||||
GalleryViewerContainerComponentProps,
|
||||
GalleryViewerContainerComponentState
|
||||
> {
|
||||
constructor(props: GalleryViewerContainerComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
officialSamplesData: undefined,
|
||||
likedNotebooksData: undefined
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const authToken = CosmosClient.authorizationToken();
|
||||
JunoUtils.getOfficialSampleNotebooks(authToken).then(
|
||||
(data1: DataModels.GitHubInfoJunoResponse[]) => {
|
||||
const officialSamplesData = data1;
|
||||
|
||||
JunoUtils.getLikedNotebooks(authToken).then(
|
||||
(data2: DataModels.LikedNotebooksJunoResponse) => {
|
||||
const likedNotebooksData = data2;
|
||||
|
||||
officialSamplesData.map((value: DataModels.GitHubInfoJunoResponse, index: number) => {
|
||||
value.officialSamplesIndex = index;
|
||||
value.isLikedNotebook = likedNotebooksData.userMetadata.likedNotebooks.includes(value.path);
|
||||
});
|
||||
|
||||
likedNotebooksData.likedNotebooksContent.map((value: DataModels.GitHubInfoJunoResponse) => {
|
||||
value.isLikedNotebook = true;
|
||||
value.officialSamplesIndex = officialSamplesData.findIndex(
|
||||
(officialSample: DataModels.GitHubInfoJunoResponse) => {
|
||||
return officialSample.path === value.path;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
this.setState({
|
||||
officialSamplesData: officialSamplesData,
|
||||
likedNotebooksData: likedNotebooksData
|
||||
});
|
||||
},
|
||||
error => {
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`Error fetching liked notebooks: ${JSON.stringify(error)}`
|
||||
);
|
||||
// TODO Add telemetry
|
||||
}
|
||||
);
|
||||
},
|
||||
error => {
|
||||
NotificationConsoleUtils.logConsoleMessage(
|
||||
ConsoleDataType.Error,
|
||||
`Error fetching sample notebooks: ${JSON.stringify(error)}`
|
||||
);
|
||||
// TODO Add telemetry
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return this.state.officialSamplesData && this.state.likedNotebooksData ? (
|
||||
<GalleryViewerComponent
|
||||
container={this.props.container}
|
||||
officialSamplesData={this.state.officialSamplesData}
|
||||
likedNotebookData={this.state.likedNotebooksData}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface GalleryViewerComponentProps {
|
||||
container: ViewModels.Explorer;
|
||||
officialSamplesData: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebookData: DataModels.LikedNotebooksJunoResponse;
|
||||
}
|
||||
|
||||
export class GalleryViewerComponent extends React.Component<GalleryViewerComponentProps> {
|
||||
public render(): JSX.Element {
|
||||
return this.props.container ? (
|
||||
<div className="galleryContainer">
|
||||
<FullWidthTabs
|
||||
officialSamplesContent={this.props.officialSamplesData}
|
||||
likedNotebooksContent={this.props.likedNotebookData.likedNotebooksContent}
|
||||
userMetadata={this.props.likedNotebookData.userMetadata}
|
||||
onClick={this.openNotebookViewer}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="galleryContainer">
|
||||
<GalleryCardsComponent
|
||||
data={this.props.officialSamplesData}
|
||||
onClick={this.openNotebookViewer}
|
||||
userMetadata={undefined}
|
||||
onNotebookMetadataChange={undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public getOfficialSamplesData(): DataModels.GitHubInfoJunoResponse[] {
|
||||
return this.props.officialSamplesData;
|
||||
}
|
||||
|
||||
public getLikedNotebookData(): DataModels.LikedNotebooksJunoResponse {
|
||||
return this.props.likedNotebookData;
|
||||
}
|
||||
|
||||
public openNotebookViewer = async (
|
||||
url: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) => {
|
||||
if (!this.props.container) {
|
||||
SessionStorageUtility.setEntryString(
|
||||
StorageKey.NotebookMetadata,
|
||||
notebookMetadata ? JSON.stringify(notebookMetadata) : null
|
||||
);
|
||||
SessionStorageUtility.setEntryString(StorageKey.NotebookName, path.basename(url));
|
||||
window.open(`${config.hostedExplorerURL}notebookViewer.html?notebookurl=${url}`, "_blank");
|
||||
} else {
|
||||
this.props.container.openNotebookViewer(url, notebookMetadata, onNotebookMetadataChange, isLikedNotebook);
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FullWidthTabs renders 1`] = `
|
||||
<TabComponent
|
||||
currentTabIndex={0}
|
||||
hideHeader={false}
|
||||
onTabIndexChange={[Function]}
|
||||
tabs={
|
||||
Array [
|
||||
Object {
|
||||
"content": Object {
|
||||
"className": "",
|
||||
"render": [Function],
|
||||
},
|
||||
"isVisible": [Function],
|
||||
"title": "Official Samples",
|
||||
},
|
||||
Object {
|
||||
"content": Object {
|
||||
"className": "",
|
||||
"render": [Function],
|
||||
},
|
||||
"isVisible": [Function],
|
||||
"title": "Liked Notebooks",
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`GalleryCardComponent renders 1`] = `
|
||||
<div
|
||||
className="galleryContainer"
|
||||
>
|
||||
<GalleryCardsComponent
|
||||
data={Array []}
|
||||
onClick={[Function]}
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`GalleryCardsComponent renders 1`] = `
|
||||
<Stack
|
||||
horizontal={true}
|
||||
tokens={
|
||||
Object {
|
||||
"childrenGap": 30,
|
||||
}
|
||||
}
|
||||
wrap={true}
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`GalleryViewerContainerComponent renders 1`] = `<Fragment />`;
|
@ -0,0 +1,11 @@
|
||||
.notebookViewerMetadataContainer {
|
||||
margin: 0px 10px;
|
||||
|
||||
.title, .decoration, .persona {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.extras {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import React from "react";
|
||||
import { shallow } from "enzyme";
|
||||
import { NotebookMetadataComponentProps, NotebookMetadataComponent } from "./NotebookMetadataComponent";
|
||||
import { NotebookMetadata } from "../../../Contracts/DataModels";
|
||||
|
||||
describe("NotebookMetadataComponent", () => {
|
||||
it("renders un-liked notebook", () => {
|
||||
const props: NotebookMetadataComponentProps = {
|
||||
notebookName: "My notebook",
|
||||
container: undefined,
|
||||
notebookMetadata: undefined,
|
||||
notebookContent: {},
|
||||
onNotebookMetadataChange: (newNotebookMetadata: NotebookMetadata) => Promise.resolve(),
|
||||
isLikedNotebook: false
|
||||
};
|
||||
|
||||
const wrapper = shallow(<NotebookMetadataComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders liked notebook", () => {
|
||||
const props: NotebookMetadataComponentProps = {
|
||||
notebookName: "My notebook",
|
||||
container: undefined,
|
||||
notebookMetadata: undefined,
|
||||
notebookContent: {},
|
||||
onNotebookMetadataChange: (newNotebookMetadata: NotebookMetadata) => Promise.resolve(),
|
||||
isLikedNotebook: true
|
||||
};
|
||||
|
||||
const wrapper = shallow(<NotebookMetadataComponent {...props} />);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
// TODO Add test for metadata display
|
||||
});
|
@ -6,47 +6,97 @@ import * as React from "react";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { NotebookMetadata } from "../../../Contracts/DataModels";
|
||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
import { Icon, Persona, Text } from "office-ui-fabric-react";
|
||||
import CSS from "csstype";
|
||||
import { Icon, Persona, Text, IconButton } from "office-ui-fabric-react";
|
||||
import {
|
||||
siteTextStyles,
|
||||
subtleIconStyles,
|
||||
iconStyles,
|
||||
iconButtonStyles,
|
||||
mainHelpfulTextStyles,
|
||||
subtleHelpfulTextStyles,
|
||||
helpfulTextStyles
|
||||
} from "../../../GalleryViewer/Cards/CardStyleConstants";
|
||||
} from "../NotebookGallery/Cards/CardStyleConstants";
|
||||
|
||||
import "./NotebookViewerComponent.less";
|
||||
|
||||
initializeIcons();
|
||||
|
||||
interface NotebookMetadataComponentProps {
|
||||
export interface NotebookMetadataComponentProps {
|
||||
notebookName: string;
|
||||
container: ViewModels.Explorer;
|
||||
notebookMetadata: NotebookMetadata;
|
||||
notebookContent: any;
|
||||
onNotebookMetadataChange: (newNotebookMetadata: NotebookMetadata) => Promise<void>;
|
||||
isLikedNotebook: boolean;
|
||||
}
|
||||
|
||||
export class NotebookMetadataComponent extends React.Component<NotebookMetadataComponentProps> {
|
||||
private inlineBlockStyle: CSS.Properties = {
|
||||
display: "inline-block"
|
||||
interface NotebookMetadatComponentState {
|
||||
liked: boolean;
|
||||
notebookMetadata: NotebookMetadata;
|
||||
}
|
||||
|
||||
export class NotebookMetadataComponent extends React.Component<
|
||||
NotebookMetadataComponentProps,
|
||||
NotebookMetadatComponentState
|
||||
> {
|
||||
constructor(props: NotebookMetadataComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
liked: this.props.isLikedNotebook,
|
||||
notebookMetadata: this.props.notebookMetadata
|
||||
};
|
||||
}
|
||||
|
||||
private onDownloadClick = (newNotebookName: string) => {
|
||||
this.props.container
|
||||
.importAndOpenFromGallery(this.props.notebookName, newNotebookName, JSON.stringify(this.props.notebookContent))
|
||||
.then(() => {
|
||||
if (this.props.notebookMetadata) {
|
||||
if (this.props.onNotebookMetadataChange) {
|
||||
const notebookMetadata = { ...this.state.notebookMetadata };
|
||||
notebookMetadata.downloads += 1;
|
||||
this.props.onNotebookMetadataChange(notebookMetadata).then(() => {
|
||||
this.setState({ notebookMetadata: notebookMetadata });
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private marginTopStyle: CSS.Properties = {
|
||||
marginTop: "5px"
|
||||
componentDidMount() {
|
||||
if (this.props.onNotebookMetadataChange) {
|
||||
const notebookMetadata = { ...this.state.notebookMetadata };
|
||||
if (this.props.notebookMetadata) {
|
||||
notebookMetadata.views += 1;
|
||||
this.props.onNotebookMetadataChange(notebookMetadata).then(() => {
|
||||
this.setState({ notebookMetadata: notebookMetadata });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onLike = (): void => {
|
||||
if (this.props.onNotebookMetadataChange) {
|
||||
const notebookMetadata = { ...this.state.notebookMetadata };
|
||||
let liked: boolean;
|
||||
if (this.state.liked) {
|
||||
liked = false;
|
||||
notebookMetadata.likes -= 1;
|
||||
} else {
|
||||
liked = true;
|
||||
notebookMetadata.likes += 1;
|
||||
}
|
||||
|
||||
this.props.onNotebookMetadataChange(notebookMetadata).then(() => {
|
||||
this.setState({ liked: liked, notebookMetadata: notebookMetadata });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
private onDownloadClick: (newNotebookName: string) => void = (newNotebookName: string) => {
|
||||
this.props.container.importAndOpenFromGallery(
|
||||
this.props.notebookName,
|
||||
newNotebookName,
|
||||
JSON.stringify(this.props.notebookContent)
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
private onDownload = (): void => {
|
||||
const promptForNotebookName = () => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
var newNotebookName = this.props.notebookName;
|
||||
let newNotebookName = this.props.notebookName;
|
||||
this.props.container.showOkCancelTextFieldModalDialog(
|
||||
"Save notebook as",
|
||||
undefined,
|
||||
@ -68,27 +118,35 @@ export class NotebookMetadataComponent extends React.Component<NotebookMetadataC
|
||||
});
|
||||
};
|
||||
|
||||
promptForNotebookName().then((newNotebookName: string) => {
|
||||
this.onDownloadClick(newNotebookName);
|
||||
});
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<div className="notebookViewerMetadataContainer">
|
||||
<h3 style={this.inlineBlockStyle}>{this.props.notebookName}</h3>
|
||||
<h3 className="title">{this.props.notebookName}</h3>
|
||||
|
||||
{this.props.notebookMetadata && (
|
||||
<div style={this.inlineBlockStyle}>
|
||||
<Icon iconName="Heart" styles={iconStyles} />
|
||||
<Text variant="medium" styles={mainHelpfulTextStyles}>
|
||||
{this.props.notebookMetadata.likes} likes
|
||||
<div className="decoration">
|
||||
{this.props.container ? (
|
||||
<IconButton
|
||||
iconProps={{ iconName: this.state.liked ? "HeartFill" : "Heart" }}
|
||||
styles={iconButtonStyles}
|
||||
onClick={this.onLike}
|
||||
/>
|
||||
) : (
|
||||
<Icon iconName="Heart" styles={iconStyles} />
|
||||
)}
|
||||
<Text variant="large" styles={mainHelpfulTextStyles}>
|
||||
{this.state.notebookMetadata.likes} likes
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.props.container && (
|
||||
<button
|
||||
aria-label="downloadButton"
|
||||
className="downloadButton"
|
||||
onClick={async () => {
|
||||
promptForNotebookName().then(this.onDownloadClick);
|
||||
}}
|
||||
>
|
||||
<button aria-label="downloadButton" className="downloadButton" onClick={this.onDownload}>
|
||||
Download Notebook
|
||||
</button>
|
||||
)}
|
||||
@ -97,20 +155,20 @@ export class NotebookMetadataComponent extends React.Component<NotebookMetadataC
|
||||
<>
|
||||
<div>
|
||||
<Persona
|
||||
style={this.inlineBlockStyle}
|
||||
className="persona"
|
||||
text={this.props.notebookMetadata.author}
|
||||
secondaryText={this.props.notebookMetadata.date}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div style={this.marginTopStyle}>
|
||||
<div className="extras">
|
||||
<Icon iconName="RedEye" styles={subtleIconStyles} />
|
||||
<Text variant="small" styles={subtleHelpfulTextStyles}>
|
||||
{this.props.notebookMetadata.views}
|
||||
{this.state.notebookMetadata.views}
|
||||
</Text>
|
||||
<Icon iconName="Download" styles={subtleIconStyles} />
|
||||
<Text variant="small" styles={subtleHelpfulTextStyles}>
|
||||
{this.props.notebookMetadata.downloads}
|
||||
{this.state.notebookMetadata.downloads}
|
||||
</Text>
|
||||
</div>
|
||||
<Text variant="small" styles={siteTextStyles}>
|
||||
|
@ -4,7 +4,7 @@
|
||||
padding: @DefaultSpace;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.downloadButton {
|
||||
@ -20,7 +20,7 @@
|
||||
display: "inline-block";
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
|
||||
.active, .downloadButton:hover {
|
||||
color: @BaseMedium;
|
||||
}
|
||||
|
@ -16,12 +16,14 @@ import "./NotebookViewerComponent.less";
|
||||
export interface NotebookViewerComponentProps {
|
||||
notebookName: string;
|
||||
notebookUrl: string;
|
||||
container: ViewModels.Explorer;
|
||||
container?: ViewModels.Explorer;
|
||||
notebookMetadata: NotebookMetadata;
|
||||
onNotebookMetadataChange?: (newNotebookMetadata: NotebookMetadata) => Promise<void>;
|
||||
isLikedNotebook?: boolean;
|
||||
hideInputs?: boolean;
|
||||
}
|
||||
|
||||
interface NotebookViewerComponentState {
|
||||
element: JSX.Element;
|
||||
content: any;
|
||||
}
|
||||
|
||||
@ -50,7 +52,7 @@ export class NotebookViewerComponent extends React.Component<
|
||||
contentRef: createContentRef()
|
||||
});
|
||||
|
||||
this.state = { element: undefined, content: undefined };
|
||||
this.state = { content: undefined };
|
||||
}
|
||||
|
||||
private async getJsonNotebookContent(): Promise<any> {
|
||||
@ -65,24 +67,25 @@ export class NotebookViewerComponent extends React.Component<
|
||||
componentDidMount() {
|
||||
this.getJsonNotebookContent().then((jsonContent: any) => {
|
||||
this.notebookComponentBootstrapper.setContent("json", jsonContent);
|
||||
const notebookReadonlyComponent = this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer);
|
||||
this.setState({ element: notebookReadonlyComponent, content: jsonContent });
|
||||
this.setState({ content: jsonContent });
|
||||
});
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return this.state != null ? (
|
||||
return (
|
||||
<div className="notebookViewerContainer">
|
||||
<NotebookMetadataComponent
|
||||
notebookMetadata={this.props.notebookMetadata}
|
||||
notebookName={this.props.notebookName}
|
||||
container={this.props.container}
|
||||
notebookContent={this.state.content}
|
||||
onNotebookMetadataChange={this.props.onNotebookMetadataChange}
|
||||
isLikedNotebook={this.props.isLikedNotebook}
|
||||
/>
|
||||
{this.state.element}
|
||||
{this.notebookComponentBootstrapper.renderComponent(NotebookReadOnlyRenderer, {
|
||||
hideInputs: this.props.hideInputs
|
||||
})}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`NotebookMetadataComponent renders liked notebook 1`] = `
|
||||
<div
|
||||
className="notebookViewerMetadataContainer"
|
||||
>
|
||||
<h3
|
||||
className="title"
|
||||
>
|
||||
My notebook
|
||||
</h3>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
|
||||
<div
|
||||
className="notebookViewerMetadataContainer"
|
||||
>
|
||||
<h3
|
||||
className="title"
|
||||
>
|
||||
My notebook
|
||||
</h3>
|
||||
</div>
|
||||
`;
|
@ -1,27 +1,28 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
.tabSwitch {
|
||||
margin-left: @LargeSpace;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tab {
|
||||
margin-right: @MediumSpace;
|
||||
}
|
||||
|
||||
.toggleSwitch {
|
||||
.toggleSwitch();
|
||||
}
|
||||
|
||||
.selectedToggle {
|
||||
.selectedToggle();
|
||||
}
|
||||
|
||||
.unselectedToggle {
|
||||
.unselectedToggle();
|
||||
}
|
||||
}
|
||||
|
||||
.tabComponentContent {
|
||||
height: calc(100% - 20px);
|
||||
.tabComponentContainer {
|
||||
height: 100%;
|
||||
.flex-display();
|
||||
.flex-direction();
|
||||
|
||||
.tabSwitch {
|
||||
margin-left: @LargeSpace;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.tab {
|
||||
margin-right: @MediumSpace;
|
||||
}
|
||||
|
||||
.toggleSwitch {
|
||||
.toggleSwitch();
|
||||
}
|
||||
|
||||
.selectedToggle {
|
||||
.selectedToggle();
|
||||
}
|
||||
|
||||
.unselectedToggle {
|
||||
.unselectedToggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from "react";
|
||||
import { AccessibleElement } from "../../Controls/AccessibleElement/AccessibleElement";
|
||||
import "./TabComponent.less";
|
||||
|
||||
export interface TabContent {
|
||||
render: () => JSX.Element;
|
||||
@ -75,10 +76,10 @@ export class TabComponent extends React.Component<TabComponentProps> {
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className="tabComponentContainer">
|
||||
{!this.props.hideHeader && <div className="tabs tabSwitch">{this.renderTabTitles()}</div>}
|
||||
<div className={className}>{currentTabContent.render()}</div>
|
||||
</React.Fragment>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1143,7 +1143,7 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
isModal: true,
|
||||
visible: true,
|
||||
title: `Enable Azure Synapse Link on your Cosmos DB account`,
|
||||
subText: `Enable Azure Synapse Link to perform near real time analytical analytics on this account, without impacting the performance of your transactional workloads.
|
||||
subText: `Enable Azure Synapse Link to perform near real time analytical analytics on this account, without impacting the performance of your transactional workloads.
|
||||
Azure Synapse Link brings together Cosmos Db Analytical Store and Synapse Analytics`,
|
||||
primaryButtonText: "Enable Azure Synapse Link",
|
||||
secondaryButtonText: "Cancel",
|
||||
@ -3278,7 +3278,12 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
newTab.onTabClick();
|
||||
}
|
||||
|
||||
public openNotebookViewer(notebookUrl: string, notebookMetadata: DataModels.NotebookMetadata) {
|
||||
public openNotebookViewer(
|
||||
notebookUrl: string,
|
||||
notebookMetadata: DataModels.NotebookMetadata,
|
||||
onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
isLikedNotebook: boolean
|
||||
) {
|
||||
const notebookName = path.basename(notebookUrl);
|
||||
const title = notebookName;
|
||||
const hashLocation = notebookUrl;
|
||||
@ -3320,7 +3325,9 @@ export default class Explorer implements ViewModels.Explorer {
|
||||
openedTabs: this.openedTabs(),
|
||||
notebookUrl: notebookUrl,
|
||||
notebookName: notebookName,
|
||||
notebookMetadata: notebookMetadata
|
||||
notebookMetadata: notebookMetadata,
|
||||
onNotebookMetadataChange: onNotebookMetadataChange,
|
||||
isLikedNotebook: isLikedNotebook
|
||||
});
|
||||
|
||||
this.openedTabs.push(newTab);
|
||||
|
@ -9,11 +9,12 @@ import { connect } from "react-redux";
|
||||
import { Dispatch } from "redux";
|
||||
import { actions, ContentRef } from "@nteract/core";
|
||||
import loadTransform from "../NotebookComponent/loadTransform";
|
||||
import CodeMirrorEditor from "@nteract/editor";
|
||||
import CodeMirrorEditor from "@nteract/stateful-components/lib/inputs/connected-editors/codemirror";
|
||||
import "./NotebookReadOnlyRenderer.less";
|
||||
|
||||
export interface NotebookRendererProps {
|
||||
contentRef: any;
|
||||
hideInputs?: boolean;
|
||||
}
|
||||
|
||||
interface PassedEditorProps {
|
||||
@ -46,7 +47,8 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
<CodeCell id={id} contentRef={contentRef}>
|
||||
{{
|
||||
editor: {
|
||||
codemirror: (props: PassedEditorProps) => <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||
codemirror: (props: PassedEditorProps) =>
|
||||
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||
},
|
||||
prompt: ({ id, contentRef }) => <></>
|
||||
}}
|
||||
@ -63,7 +65,8 @@ class NotebookReadOnlyRenderer extends React.Component<NotebookRendererProps> {
|
||||
<RawCell id={id} contentRef={contentRef} cell_type="raw">
|
||||
{{
|
||||
editor: {
|
||||
codemirror: (props: PassedEditorProps) => <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||
codemirror: (props: PassedEditorProps) =>
|
||||
this.props.hideInputs ? <></> : <CodeMirrorEditor {...props} readOnly={"nocursor"} />
|
||||
}
|
||||
}}
|
||||
</RawCell>
|
||||
|
@ -3,7 +3,7 @@ import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import TabsBase from "./TabsBase";
|
||||
import * as React from "react";
|
||||
import { ReactAdapter } from "../../Bindings/ReactBindingHandler";
|
||||
import { GalleryViewerContainerComponent } from "../../GalleryViewer/GalleryViewerComponent";
|
||||
import { GalleryViewerContainerComponent } from "../Controls/NotebookGallery/GalleryViewerComponent";
|
||||
|
||||
/**
|
||||
* Notebook gallery tab
|
||||
|
@ -16,7 +16,9 @@ class NotebookViewerComponentAdapter implements ReactAdapter {
|
||||
private notebookUrl: string,
|
||||
private notebookName: string,
|
||||
private container: ViewModels.Explorer,
|
||||
private notebookMetadata: DataModels.NotebookMetadata
|
||||
private notebookMetadata: DataModels.NotebookMetadata,
|
||||
private onNotebookMetadataChange: (newNotebookMetadata: DataModels.NotebookMetadata) => Promise<void>,
|
||||
private isLikedNotebook: boolean
|
||||
) {}
|
||||
|
||||
public renderComponent(): JSX.Element {
|
||||
@ -26,6 +28,8 @@ class NotebookViewerComponentAdapter implements ReactAdapter {
|
||||
notebookMetadata={this.notebookMetadata}
|
||||
notebookName={this.notebookName}
|
||||
container={this.container}
|
||||
onNotebookMetadataChange={this.onNotebookMetadataChange}
|
||||
isLikedNotebook={this.isLikedNotebook}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
@ -46,7 +50,9 @@ export default class NotebookViewerTab extends TabsBase implements ViewModels.Ta
|
||||
options.notebookUrl,
|
||||
options.notebookName,
|
||||
options.container,
|
||||
options.notebookMetadata
|
||||
options.notebookMetadata,
|
||||
options.onNotebookMetadataChange,
|
||||
options.isLikedNotebook
|
||||
);
|
||||
|
||||
this.notebookViewerComponentAdapter.parameters = ko.computed<boolean>(() => {
|
||||
|
@ -1,9 +0,0 @@
|
||||
@import "../../less/Common/Constants";
|
||||
|
||||
.galleryContainer {
|
||||
padding: @DefaultSpace;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
font-family: @DataExplorerFont;
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import * as ReactDOM from "react-dom";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "./GalleryViewer.less";
|
||||
import { GalleryViewerComponent } from "./GalleryViewerComponent";
|
||||
import { CosmosClient } from "../Common/CosmosClient";
|
||||
import { GalleryViewerComponent } from "../Explorer/Controls/NotebookGallery/GalleryViewerComponent";
|
||||
import { JunoUtils } from "../Utils/JunoUtils";
|
||||
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
|
||||
|
||||
const onInit = async () => {
|
||||
initializeIcons();
|
||||
const officialSamplesData = await JunoUtils.getOfficialSampleNotebooks();
|
||||
const officialSamplesData = await JunoUtils.getOfficialSampleNotebooks(CosmosClient.authorizationToken());
|
||||
const galleryViewerComponent = new GalleryViewerComponent({
|
||||
officialSamplesData: officialSamplesData,
|
||||
likedNotebookData: undefined,
|
||||
|
@ -1,207 +0,0 @@
|
||||
/**
|
||||
* Gallery Viewer
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
import * as DataModels from "../Contracts/DataModels";
|
||||
import * as ViewModels from "../Contracts/ViewModels";
|
||||
import { GalleryCardComponent } from "./Cards/GalleryCardComponent";
|
||||
import { Stack, IStackTokens } from "office-ui-fabric-react";
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import { JunoUtils } from "../Utils/JunoUtils";
|
||||
import { CosmosClient } from "../Common/CosmosClient";
|
||||
import { config } from "../Config";
|
||||
import path from "path";
|
||||
import { SessionStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
import "./GalleryViewer.less";
|
||||
|
||||
interface GalleryCardsComponentProps {
|
||||
data: DataModels.GitHubInfoJunoResponse[];
|
||||
onClick: (url: string, notebookMetadata: DataModels.NotebookMetadata) => Promise<void>;
|
||||
}
|
||||
|
||||
class GalleryCardsComponent extends React.Component<GalleryCardsComponentProps> {
|
||||
private sectionStackTokens: IStackTokens = { childrenGap: 30 };
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Stack horizontal wrap tokens={this.sectionStackTokens}>
|
||||
{this.props.data.map((githubInfo: DataModels.GitHubInfoJunoResponse, index: any) => {
|
||||
const name = githubInfo.name;
|
||||
const url = githubInfo.downloadUrl;
|
||||
const notebookMetadata = githubInfo.metadata;
|
||||
|
||||
return (
|
||||
name !== ".gitignore" &&
|
||||
url && (
|
||||
<GalleryCardComponent
|
||||
key={url}
|
||||
name={name}
|
||||
url={url}
|
||||
notebookMetadata={notebookMetadata}
|
||||
onClick={() => this.props.onClick(url, notebookMetadata)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const TabPanel = (props: any) => (
|
||||
<Typography
|
||||
component="div"
|
||||
role="tabpanel"
|
||||
hidden={props.value !== props.index}
|
||||
id={`full-width-tabpanel-${props.index}`}
|
||||
aria-labelledby={`full-width-tab-${props.index}`}
|
||||
>
|
||||
{props.value === props.index && <Box p={2}>{props.children}</Box>}
|
||||
</Typography>
|
||||
);
|
||||
|
||||
const a11yProps = (index: number) => {
|
||||
return {
|
||||
id: `full-width-tab-${index}`,
|
||||
"aria-controls": `full-width-tabpanel-${index}`
|
||||
};
|
||||
};
|
||||
|
||||
interface FullWidthTabsProps {
|
||||
officialSamplesContent: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebooksContent: DataModels.GitHubInfoJunoResponse[];
|
||||
onClick: (url: string, notebookMetadata: DataModels.NotebookMetadata) => Promise<void>;
|
||||
}
|
||||
|
||||
const FullWidthTabs = (props: FullWidthTabsProps) => {
|
||||
const [value, setValue] = React.useState(0);
|
||||
|
||||
const handleChange = ({}, newValue: any) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar position="static" color="transparent" style={{ background: "transparent", boxShadow: "none" }}>
|
||||
<Tabs
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
aria-label="gallery tabs"
|
||||
>
|
||||
<Tab label="Official Samples" {...a11yProps(0)} />
|
||||
<Tab label="Liked Notebooks" {...a11yProps(1)} />
|
||||
</Tabs>
|
||||
</AppBar>
|
||||
<TabPanel value={value} index={0}>
|
||||
<GalleryCardsComponent data={props.officialSamplesContent} onClick={props.onClick} />
|
||||
</TabPanel>
|
||||
<TabPanel value={value} index={1}>
|
||||
<GalleryCardsComponent data={props.likedNotebooksContent} onClick={props.onClick} />
|
||||
</TabPanel>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export interface GalleryViewerContainerComponentProps {
|
||||
container: ViewModels.Explorer;
|
||||
}
|
||||
|
||||
export interface GalleryViewerContainerComponentState {
|
||||
officialSamplesData: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebooksData: DataModels.LikedNotebooksJunoResponse;
|
||||
}
|
||||
|
||||
export class GalleryViewerContainerComponent extends React.Component<
|
||||
GalleryViewerContainerComponentProps,
|
||||
GalleryViewerContainerComponentState
|
||||
> {
|
||||
constructor(props: GalleryViewerContainerComponentProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
officialSamplesData: undefined,
|
||||
likedNotebooksData: undefined
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
JunoUtils.getOfficialSampleNotebooks().then((data1: DataModels.GitHubInfoJunoResponse[]) => {
|
||||
const officialSamplesData = data1;
|
||||
|
||||
JunoUtils.getLikedNotebooks(CosmosClient.authorizationToken()).then(
|
||||
(data2: DataModels.LikedNotebooksJunoResponse) => {
|
||||
const likedNotebooksData = data2;
|
||||
|
||||
this.setState({
|
||||
officialSamplesData: officialSamplesData,
|
||||
likedNotebooksData: likedNotebooksData
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public render(): JSX.Element {
|
||||
return this.state.officialSamplesData && this.state.likedNotebooksData ? (
|
||||
<GalleryViewerComponent
|
||||
container={this.props.container}
|
||||
officialSamplesData={this.state.officialSamplesData}
|
||||
likedNotebookData={this.state.likedNotebooksData}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface GalleryViewerComponentProps {
|
||||
container: ViewModels.Explorer;
|
||||
officialSamplesData: DataModels.GitHubInfoJunoResponse[];
|
||||
likedNotebookData: DataModels.LikedNotebooksJunoResponse;
|
||||
}
|
||||
|
||||
export class GalleryViewerComponent extends React.Component<GalleryViewerComponentProps> {
|
||||
private authorizationToken = CosmosClient.authorizationToken();
|
||||
|
||||
public render(): JSX.Element {
|
||||
return this.props.container ? (
|
||||
<div className="galleryContainer">
|
||||
<FullWidthTabs
|
||||
officialSamplesContent={this.props.officialSamplesData}
|
||||
likedNotebooksContent={this.props.likedNotebookData.likedNotebooksContent}
|
||||
onClick={this.openNotebookViewer}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="galleryContainer">
|
||||
<GalleryCardsComponent data={this.props.officialSamplesData} onClick={this.openNotebookViewer} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
public getOfficialSamplesData(): DataModels.GitHubInfoJunoResponse[] {
|
||||
return this.props.officialSamplesData;
|
||||
}
|
||||
|
||||
public getLikedNotebookData(): DataModels.LikedNotebooksJunoResponse {
|
||||
return this.props.likedNotebookData;
|
||||
}
|
||||
|
||||
public openNotebookViewer = async (url: string, notebookMetadata: DataModels.NotebookMetadata) => {
|
||||
if (!this.props.container) {
|
||||
SessionStorageUtility.setEntryString(
|
||||
StorageKey.NotebookMetadata,
|
||||
notebookMetadata ? JSON.stringify(notebookMetadata) : null
|
||||
);
|
||||
SessionStorageUtility.setEntryString(StorageKey.NotebookName, path.basename(url));
|
||||
window.open(`${config.hostedExplorerURL}notebookViewer.html?notebookurl=${url}`, "_blank");
|
||||
} else {
|
||||
this.props.container.openNotebookViewer(url, notebookMetadata);
|
||||
}
|
||||
};
|
||||
}
|
@ -15,7 +15,6 @@ import "./Explorer/Menus/CommandBar/MemoryTrackerComponent.less";
|
||||
import "./Explorer/Controls/CollapsiblePanel/CollapsiblePanelComponent.less";
|
||||
import "./Explorer/Controls/DynamicList/DynamicListComponent.less";
|
||||
import "./Explorer/Controls/JsonEditor/JsonEditorComponent.less";
|
||||
import "./Explorer/Controls/Tabs/TabComponent.less";
|
||||
import "./Explorer/Graph/GraphExplorerComponent/graphExplorer.less";
|
||||
import "../less/TableStyles/queryBuilder.less";
|
||||
import "../externals/jquery.dataTables.min.css";
|
||||
|
@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import { NotebookMetadata } from "../../../Contracts/DataModels";
|
||||
import { NotebookViewerComponent } from "./NotebookViewerComponent";
|
||||
import { SessionStorageUtility, StorageKey } from "../../../Shared/StorageUtility";
|
||||
import { NotebookMetadata } from "../Contracts/DataModels";
|
||||
import { NotebookViewerComponent } from "../Explorer/Controls/NotebookViewer/NotebookViewerComponent";
|
||||
import { SessionStorageUtility, StorageKey } from "../Shared/StorageUtility";
|
||||
|
||||
const getNotebookUrl = (): string => {
|
||||
const regex: RegExp = new RegExp("[?&]notebookurl=([^&#]*)|&|#|$");
|
||||
@ -26,12 +26,14 @@ const onInit = async () => {
|
||||
SessionStorageUtility.removeEntry(StorageKey.NotebookName);
|
||||
}
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
const notebookViewerComponent = (
|
||||
<NotebookViewerComponent
|
||||
notebookMetadata={notebookMetadata}
|
||||
notebookName={notebookName}
|
||||
notebookUrl={getNotebookUrl()}
|
||||
container={null}
|
||||
hideInputs={urlParams.get("hideinputs") === "true"}
|
||||
/>
|
||||
);
|
||||
ReactDOM.render(notebookViewerComponent, document.getElementById("notebookContent"));
|
@ -8,17 +8,22 @@ export class JunoUtils {
|
||||
public static async getLikedNotebooks(authorizationToken: string): Promise<DataModels.LikedNotebooksJunoResponse> {
|
||||
//TODO: Add Get method once juno has it implemented
|
||||
return {
|
||||
likedNotebooksContent: await JunoUtils.getOfficialSampleNotebooks(),
|
||||
likedNotebooksContent: [],
|
||||
userMetadata: {
|
||||
likedNotebooks: []
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static async getOfficialSampleNotebooks(): Promise<DataModels.GitHubInfoJunoResponse[]> {
|
||||
public static async getOfficialSampleNotebooks(
|
||||
authorizationToken: string
|
||||
): Promise<DataModels.GitHubInfoJunoResponse[]> {
|
||||
try {
|
||||
const response = await window.fetch(config.JUNO_ENDPOINT + "/api/galleries/notebooks", {
|
||||
method: "GET"
|
||||
const response = await window.fetch(config.JUNO_ENDPOINT + "/api/notebooks/galleries", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
authorization: authorizationToken
|
||||
}
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error("Status code:" + response.status);
|
||||
@ -35,14 +40,16 @@ export class JunoUtils {
|
||||
): Promise<DataModels.UserMetadata> {
|
||||
return undefined;
|
||||
//TODO: add userMetadata updation code
|
||||
// TODO: Make sure to throw error if failed
|
||||
}
|
||||
|
||||
public static async updateNotebookMetadata(
|
||||
authorizationToken: string,
|
||||
userMetadata: DataModels.NotebookMetadata
|
||||
notebookMetadata: DataModels.NotebookMetadata
|
||||
): Promise<DataModels.NotebookMetadata> {
|
||||
return undefined;
|
||||
//TODO: add notebookMetadata updation code
|
||||
// TODO: Make sure to throw error if failed
|
||||
}
|
||||
|
||||
public static toPinnedRepo(item: RepoListItem): IPinnedRepo {
|
||||
|
@ -144,7 +144,7 @@ module.exports = function(env = {}, argv = {}) {
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "notebookViewer.html",
|
||||
template: "src/Explorer/Controls/NotebookViewer/notebookViewer.html",
|
||||
template: "src/NotebookViewer/notebookViewer.html",
|
||||
chunks: ["notebookViewer"]
|
||||
}),
|
||||
new HtmlWebpackPlugin({
|
||||
@ -175,7 +175,7 @@ module.exports = function(env = {}, argv = {}) {
|
||||
hostedExplorer: "./src/HostedExplorer.ts",
|
||||
heatmap: "./src/Controls/Heatmap/Heatmap.ts",
|
||||
terminal: "./src/Terminal/index.ts",
|
||||
notebookViewer: "./src/Explorer/Controls/NotebookViewer/NotebookViewer.tsx",
|
||||
notebookViewer: "./src/NotebookViewer/NotebookViewer.tsx",
|
||||
galleryViewer: "./src/GalleryViewer/GalleryViewer.tsx",
|
||||
connectToGitHub: "./src/GitHub/GitHubConnector.ts"
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user