Skip to content

Commit 9f6ecfd

Browse files
committed
Completed create app step
1 parent a57527d commit 9f6ecfd

File tree

8 files changed

+14644
-290
lines changed

8 files changed

+14644
-290
lines changed

demo/graph-tutorial/package-lock.json

Lines changed: 14279 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo/graph-tutorial/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,25 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"@fortawesome/fontawesome-free": "^5.12.1",
7+
"@microsoft/microsoft-graph-client": "^2.0.0",
68
"@testing-library/jest-dom": "^4.2.4",
79
"@testing-library/react": "^9.3.2",
810
"@testing-library/user-event": "^7.1.2",
911
"@types/jest": "^24.0.0",
1012
"@types/node": "^12.0.0",
1113
"@types/react": "^16.9.0",
1214
"@types/react-dom": "^16.9.0",
15+
"@types/react-router-dom": "^5.1.3",
16+
"@types/reactstrap": "^8.4.2",
17+
"bootstrap": "^4.4.1",
18+
"moment": "^2.24.0",
19+
"msal": "^1.2.1",
1320
"react": "^16.13.0",
1421
"react-dom": "^16.13.0",
22+
"react-router-dom": "^5.1.2",
1523
"react-scripts": "3.4.0",
24+
"reactstrap": "^8.4.1",
1625
"typescript": "~3.7.2"
1726
},
1827
"scripts": {

demo/graph-tutorial/src/App.tsx

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,63 @@
1-
import React from 'react';
2-
import logo from './logo.svg';
3-
import './App.css';
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
import React, { Component } from 'react';
4+
import { BrowserRouter as Router, Route } from 'react-router-dom';
5+
import { Container } from 'reactstrap';
6+
import NavBar from './NavBar';
7+
import ErrorMessage from './ErrorMessage';
8+
import Welcome from './Welcome';
9+
import 'bootstrap/dist/css/bootstrap.css';
410

5-
function App() {
6-
return (
7-
<div className="App">
8-
<header className="App-header">
9-
<img src={logo} className="App-logo" alt="logo" />
10-
<p>
11-
Edit <code>src/App.tsx</code> and save to reload.
12-
</p>
13-
<a
14-
className="App-link"
15-
href="https://reactjs.org"
16-
target="_blank"
17-
rel="noopener noreferrer"
18-
>
19-
Learn React
20-
</a>
21-
</header>
22-
</div>
23-
);
11+
interface AppState {
12+
error: any;
13+
isAuthenticated: boolean;
14+
user: any;
15+
}
16+
17+
class App extends Component<any, AppState> {
18+
constructor(props: any) {
19+
super(props);
20+
21+
this.state = {
22+
isAuthenticated: false,
23+
user: {},
24+
error: null
25+
};
26+
}
27+
28+
render() {
29+
let error = null;
30+
if (this.state.error) {
31+
error = <ErrorMessage message={this.state.error.message} debug={this.state.error.debug} />;
32+
}
33+
34+
return (
35+
<Router>
36+
<div>
37+
<NavBar
38+
isAuthenticated={this.state.isAuthenticated}
39+
authButtonMethod={null}
40+
user={this.state.user}/>
41+
<Container>
42+
{error}
43+
<Route exact path="/"
44+
render={(props) =>
45+
<Welcome {...props}
46+
isAuthenticated={this.state.isAuthenticated}
47+
user={this.state.user}
48+
authButtonMethod={null} />
49+
} />
50+
</Container>
51+
</div>
52+
</Router>
53+
);
54+
}
55+
56+
setErrorMessage(message: string, debug: string) {
57+
this.setState({
58+
error: {message: message, debug: debug}
59+
});
60+
}
2461
}
2562

2663
export default App;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// <ErrorMessageSnippet>
5+
import React from 'react';
6+
import { Alert } from 'reactstrap';
7+
8+
interface ErrorMessageProps {
9+
debug : string;
10+
message : string;
11+
}
12+
13+
export default class ErrorMessage extends React.Component<ErrorMessageProps> {
14+
render() {
15+
let debug = null;
16+
if (this.props.debug) {
17+
debug = <pre className="alert-pre border bg-light p-2"><code>{this.props.debug}</code></pre>;
18+
}
19+
return (
20+
<Alert color="danger">
21+
<p className="mb-3">{this.props.message}</p>
22+
{debug}
23+
</Alert>
24+
);
25+
}
26+
}
27+
// </ErrorMessageSnippet>

demo/graph-tutorial/src/NavBar.tsx

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// <NavBarSnippet>
5+
import React from 'react';
6+
import { NavLink as RouterNavLink } from 'react-router-dom';
7+
import {
8+
Button,
9+
Collapse,
10+
Container,
11+
Navbar,
12+
NavbarToggler,
13+
NavbarBrand,
14+
Nav,
15+
NavItem,
16+
NavLink,
17+
UncontrolledDropdown,
18+
DropdownToggle,
19+
DropdownMenu,
20+
DropdownItem } from 'reactstrap';
21+
import '@fortawesome/fontawesome-free/css/all.css';
22+
23+
interface NavBarProps {
24+
isAuthenticated : boolean;
25+
authButtonMethod : any;
26+
user : any;
27+
}
28+
29+
interface NavBarState {
30+
isOpen : boolean;
31+
}
32+
33+
function UserAvatar(props: any) {
34+
// If a user avatar is available, return an img tag with the pic
35+
if (props.user.avatar) {
36+
return <img
37+
src={props.user.avatar} alt="user"
38+
className="rounded-circle align-self-center mr-2"
39+
style={{width: '32px'}}></img>;
40+
}
41+
42+
// No avatar available, return a default icon
43+
return <i
44+
className="far fa-user-circle fa-lg rounded-circle align-self-center mr-2"
45+
style={{width: '32px'}}></i>;
46+
}
47+
48+
function AuthNavItem(props: NavBarProps) {
49+
// If authenticated, return a dropdown with the user's info and a
50+
// sign out button
51+
if (props.isAuthenticated) {
52+
return (
53+
<UncontrolledDropdown>
54+
<DropdownToggle nav caret>
55+
<UserAvatar user={props.user}/>
56+
</DropdownToggle>
57+
<DropdownMenu right>
58+
<h5 className="dropdown-item-text mb-0">{props.user.displayName}</h5>
59+
<p className="dropdown-item-text text-muted mb-0">{props.user.email}</p>
60+
<DropdownItem divider />
61+
<DropdownItem onClick={props.authButtonMethod}>Sign Out</DropdownItem>
62+
</DropdownMenu>
63+
</UncontrolledDropdown>
64+
);
65+
}
66+
67+
// Not authenticated, return a sign in link
68+
return (
69+
<NavItem>
70+
<Button
71+
onClick={props.authButtonMethod}
72+
className="btn-link nav-link border-0"
73+
color="link">Sign In</Button>
74+
</NavItem>
75+
);
76+
}
77+
78+
export default class NavBar extends React.Component<NavBarProps, NavBarState> {
79+
constructor(props: NavBarProps) {
80+
super(props);
81+
82+
this.toggle = this.toggle.bind(this);
83+
this.state = {
84+
isOpen: false
85+
};
86+
}
87+
88+
toggle() {
89+
this.setState({
90+
isOpen: !this.state.isOpen
91+
});
92+
}
93+
94+
render() {
95+
// Only show calendar nav item if logged in
96+
let calendarLink = null;
97+
if (this.props.isAuthenticated) {
98+
calendarLink = (
99+
<NavItem>
100+
<RouterNavLink to="/calendar" className="nav-link" exact>Calendar</RouterNavLink>
101+
</NavItem>
102+
);
103+
}
104+
105+
return (
106+
<div>
107+
<Navbar color="dark" dark expand="md" fixed="top">
108+
<Container>
109+
<NavbarBrand href="/">React Graph Tutorial</NavbarBrand>
110+
<NavbarToggler onClick={this.toggle} />
111+
<Collapse isOpen={this.state.isOpen} navbar>
112+
<Nav className="mr-auto" navbar>
113+
<NavItem>
114+
<RouterNavLink to="/" className="nav-link" exact>Home</RouterNavLink>
115+
</NavItem>
116+
{calendarLink}
117+
</Nav>
118+
<Nav className="justify-content-end" navbar>
119+
<NavItem>
120+
<NavLink href="https://developer.microsoft.com/graph/docs/concepts/overview" target="_blank">
121+
<i className="fas fa-external-link-alt mr-1"></i>
122+
Docs
123+
</NavLink>
124+
</NavItem>
125+
<AuthNavItem
126+
isAuthenticated={this.props.isAuthenticated}
127+
authButtonMethod={this.props.authButtonMethod}
128+
user={this.props.user} />
129+
</Nav>
130+
</Collapse>
131+
</Container>
132+
</Navbar>
133+
</div>
134+
);
135+
}
136+
}
137+
// </NavBarSnippet>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// <WelcomeSnippet>
5+
import React from 'react';
6+
import {
7+
Button,
8+
Jumbotron } from 'reactstrap';
9+
10+
interface WelcomeProps {
11+
isAuthenticated : boolean;
12+
authButtonMethod : any;
13+
user : any;
14+
}
15+
16+
interface WelcomeState {
17+
isOpen : boolean;
18+
}
19+
20+
function WelcomeContent(props: WelcomeProps) {
21+
// If authenticated, greet the user
22+
if (props.isAuthenticated) {
23+
return (
24+
<div>
25+
<h4>Welcome {props.user.displayName}!</h4>
26+
<p>Use the navigation bar at the top of the page to get started.</p>
27+
</div>
28+
);
29+
}
30+
31+
// Not authenticated, present a sign in button
32+
return <Button color="primary" onClick={props.authButtonMethod}>Click here to sign in</Button>;
33+
}
34+
35+
export default class Welcome extends React.Component<WelcomeProps, WelcomeState> {
36+
render() {
37+
return (
38+
<Jumbotron>
39+
<h1>React Graph Tutorial</h1>
40+
<p className="lead">
41+
This sample app shows how to use the Microsoft Graph API to access Outlook and OneDrive data from React
42+
</p>
43+
<WelcomeContent
44+
isAuthenticated={this.props.isAuthenticated}
45+
user={this.props.user}
46+
authButtonMethod={this.props.authButtonMethod} />
47+
</Jumbotron>
48+
);
49+
}
50+
}
51+
// </WelcomeSnippet>

demo/graph-tutorial/src/index.css

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
body {
2-
margin: 0;
3-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4-
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5-
sans-serif;
6-
-webkit-font-smoothing: antialiased;
7-
-moz-osx-font-smoothing: grayscale;
2+
padding-top: 4.5rem;
83
}
94

10-
code {
11-
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12-
monospace;
5+
.alert-pre {
6+
word-wrap: break-word;
7+
word-break: break-all;
8+
white-space: pre-wrap;
139
}

0 commit comments

Comments
 (0)