Initial transfer from ADO (#13)

This commit is contained in:
Laurent Nguyen
2020-06-05 04:04:15 +02:00
committed by GitHub
parent ab3486bd05
commit e9d3160b57
34 changed files with 827 additions and 526 deletions

View File

@@ -0,0 +1,11 @@
.notebookViewerMetadataContainer {
margin: 0px 10px;
.title, .decoration, .persona {
display: inline-block;
}
.extras {
margin-top: 5px;
}
}

View File

@@ -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
});

View File

@@ -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}>

View File

@@ -1,11 +0,0 @@
@import "../../../../less/Common/Constants";
.notebookComponentContainer {
height: 100vh;
width: 100vw;
margin: 0px;
overflow-x: hidden;
font-family: @DataExplorerFont;
padding: 20px;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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>
) : (
<></>
);
}
}

View File

@@ -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>
`;

View File

@@ -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>