mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 17:30:46 +00:00
Initial transfer from ADO (#13)
This commit is contained in:
@@ -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}>
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@import "../../../../less/Common/Constants";
|
||||
|
||||
.notebookComponentContainer {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
margin: 0px;
|
||||
overflow-x: hidden;
|
||||
font-family: @DataExplorerFont;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
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";
|
||||
|
||||
const getNotebookUrl = (): string => {
|
||||
const regex: RegExp = new RegExp("[?&]notebookurl=([^&#]*)|&|#|$");
|
||||
const results: RegExpExecArray | null = regex.exec(window.location.href);
|
||||
if (!results || !results[1]) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return decodeURIComponent(results[1]);
|
||||
};
|
||||
|
||||
const onInit = async () => {
|
||||
var notebookMetadata: NotebookMetadata;
|
||||
const notebookMetadataString = SessionStorageUtility.getEntryString(StorageKey.NotebookMetadata);
|
||||
const notebookName = SessionStorageUtility.getEntryString(StorageKey.NotebookName);
|
||||
|
||||
if (notebookMetadataString == "null" || notebookMetadataString != null) {
|
||||
notebookMetadata = (await JSON.parse(notebookMetadataString)) as NotebookMetadata;
|
||||
SessionStorageUtility.removeEntry(StorageKey.NotebookMetadata);
|
||||
SessionStorageUtility.removeEntry(StorageKey.NotebookName);
|
||||
}
|
||||
|
||||
const notebookViewerComponent = (
|
||||
<NotebookViewerComponent
|
||||
notebookMetadata={notebookMetadata}
|
||||
notebookName={notebookName}
|
||||
notebookUrl={getNotebookUrl()}
|
||||
container={null}
|
||||
/>
|
||||
);
|
||||
ReactDOM.render(notebookViewerComponent, document.getElementById("notebookContent"));
|
||||
};
|
||||
|
||||
// Entry point
|
||||
window.addEventListener("load", onInit);
|
||||
@@ -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,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0" />
|
||||
|
||||
<title>Notebook Viewer</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="notebookComponentContainer" id="notebookContent"></div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user