Skip to content

Commit 3fef5d8

Browse files
authored
Adding mgt-react (#550)
* adding mgt-react preview * updated readme * updated readme * updated to 1.3-preview.3 * published 1.3-preview.3 * fixed prettier issues * linking with lerna * fixed types and build * Fixing merge issues * fix types * Fixed dependencies * Fixed postpack * fixing versions * fixing references * Added react sample to lerna packages * updated readme * filter build scripts to scoped packages * Added react sample .env file * added mgt-react build script to root
1 parent f9a3a53 commit 3fef5d8

File tree

25 files changed

+628
-29
lines changed

25 files changed

+628
-29
lines changed

.prettierignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*-css.ts
22
*.md
3-
**/dist
3+
**/dist
4+
**/generated

artifacts/install.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ function Get-Packages {
2020
Write-Output $output
2121
}
2222

23-
$packages = Get-Packages mgt-element mgt
23+
$packages = Get-Packages mgt-element mgt mgt-react
2424

2525
npm i $packages

lerna.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"packages": ["packages/*"],
2+
"packages": ["packages/*", "samples/react-app"],
33
"npmClient": "yarn",
44
"useWorkspaces": true,
55
"version": "independent"

package.json

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,26 @@
22
"name": "root",
33
"private": true,
44
"workspaces": [
5-
"packages/*"
5+
"packages/*",
6+
"samples/react-app"
67
],
78
"scripts": {
8-
"build": "npm run prettier:check && npm run clean && lerna run build",
9+
"build": "npm run prettier:check && npm run clean && lerna run build --scope @microsoft/*",
910
"build:mgt": "cd ./packages/mgt && npm run build",
1011
"build:mgt-element": "cd ./packages/mgt-element && npm run build",
12+
"build:mgt-react": "lerna run build --scope @microsoft/mgt-react",
1113
"bundle": "cd ./packages/mgt && npm run bundle",
1214
"bootstrap": "lerna bootstrap --useWorkspaces",
13-
"clean": "lerna run --parallel --stream clean",
15+
"clean": "lerna run --parallel --stream --scope @microsoft/* clean",
1416
"link": "lerna link --force-local",
15-
"lint": "lerna run --parallel --stream lint",
16-
"pack": "shx rm -rf artifacts/*.tgz && lerna exec --stream -- npm pack",
17+
"lint": "lerna run --parallel --stream --scope @microsoft/* lint",
18+
"pack": "shx rm -rf artifacts/*.tgz && lerna exec --stream --scope @microsoft/* -- npm pack",
1719
"prepare": "lerna bootstrap --force-local",
1820
"serve": "es-dev-server --port 3000 --node-resolve --open --watch --compatibility none --app-index index.html",
1921
"serve:https": "es-dev-server --http2 --port 3000 --node-resolve --open --watch --compatibility none --app-index index.html",
2022
"start": "npm-run-all prettier:check watch:serve",
2123
"start:https": "npm-run-all prettier:check build:esm watch:serve:https",
22-
"watch": "lerna run --parallel --stream build:watch",
24+
"watch": "lerna run --parallel --stream --scope @microsoft/* build:watch ",
2325
"watch:serve": "npm-run-all --parallel watch serve",
2426
"watch:serve:https": "npm-run-all -parallel watch serve:https",
2527
"prettier:base": "prettier --parser typescript",
@@ -59,6 +61,8 @@
5961
"@babel/preset-typescript": "^7.9.0",
6062
"@types/jest": "^24.9.1",
6163
"@types/node": "12.12.22",
64+
"@types/react": "^16.9.32",
65+
"@types/react-dom": "^16.9.6",
6266
"@webcomponents/webcomponentsjs": "^2.4.3",
6367
"babel-loader": "^8.1.0",
6468
"core-js": "^3.6.4",
@@ -72,9 +76,12 @@
7276
"sass": "^1.26.3",
7377
"shx": "^0.3.2",
7478
"ts-jest": "^24.3.0",
79+
"whatwg-fetch": "^3.0.0",
80+
"fs-extra": "^9.0.0",
81+
"react": "^16.13.1",
82+
"react-dom": "^16.13.1",
7583
"typescript": "^3.7.5",
76-
"web-component-analyzer": "1.0.3",
77-
"whatwg-fetch": "^3.0.0"
84+
"web-component-analyzer": "^1.1.6"
7885
},
7986
"husky": {
8087
"hooks": {

packages/mgt-react/.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
dist
2+
node_modules
3+
temp
4+
web-components.json
5+
*.tgz

packages/mgt-react/package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "@microsoft/mgt-react",
3+
"version": "2.0.0",
4+
"description": "Microsoft Graph Toolkit React wrapper class",
5+
"author": "Microsoft",
6+
"license": "MIT",
7+
"module": "dist/es6/index.js",
8+
"main": "dist/es6/index.js",
9+
"types": "dist/es6/types.d.ts",
10+
"files": [
11+
"dist",
12+
"src"
13+
],
14+
"keywords": [
15+
"react",
16+
"microsoft graph toolkit",
17+
"web components",
18+
"mgt"
19+
],
20+
"repository": {
21+
"type": "git",
22+
"url": "https://github.com/microsoftgraph/microsoft-graph-toolkit"
23+
},
24+
"bugs": {
25+
"url": "https://github.com/microsoftgraph/microsoft-graph-toolkit/issues"
26+
},
27+
"scripts": {
28+
"build": "npm run clean && npm run generate && tsc",
29+
"clean": "node ./scripts/clean.js",
30+
"prepack": "shx rm -rf *.tgz && npm run build",
31+
"postpack": "cpx *.tgz ../../artifacts",
32+
"generate": "wca analyze ../mgt/src --format json --outFile temp/web-components.json && node ./scripts/generate.js"
33+
},
34+
"dependencies": {
35+
"@microsoft/mgt": "*",
36+
"@microsoft/microsoft-graph-types": "^1.12.0",
37+
"@microsoft/microsoft-graph-types-beta": "github:microsoftgraph/msgraph-typescript-typings#beta",
38+
"wc-react": "^0.3.1"
39+
},
40+
"publishConfig": {
41+
"access": "public"
42+
}
43+
}

packages/mgt-react/readme.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# React wrapper for Microsoft Graph Toolkit
2+
3+
[![npm](https://img.shields.io/npm/v/@microsoft/mgt-react?style=for-the-badge)](https://www.npmjs.com/package/@microsoft/mgt-react)
4+
5+
Use `mgt-react` to simplify usage of [Microsoft Graph Toolkit (mgt)](https://aka.ms/mgt) web components in React. The library wraps all mgt components and exports them as React components.
6+
7+
`mgt-react` extends [`wc-react`](https://github.com/nmetulev/wc-react) adding support for templates.
8+
9+
## Installation
10+
11+
```bash
12+
npm install @microsoft/mgt-react
13+
```
14+
15+
or
16+
17+
```bash
18+
yarn add @microsoft/mgt-react
19+
```
20+
21+
## Usage
22+
23+
Import a component at the top:
24+
25+
```tsx
26+
import { Person } from '@microsoft/mgt-react';
27+
```
28+
29+
You can now use `Person` anywhere in your JSX as a regular React component.
30+
31+
```tsx
32+
<Person personQuery="me" />
33+
```
34+
35+
### Use properties instead of attributes
36+
37+
For example, you can set the `personDetails` property to an object:
38+
39+
```jsx
40+
const App = (props) => {
41+
const personDetails = {
42+
displayName: 'Bill Gates',
43+
};
44+
45+
return <Person personDetails={personDetails}></Person>;
46+
};
47+
```
48+
49+
### Register event handlers:
50+
51+
```jsx
52+
import { PeoplePicker } from '@microsoft/mgt-react';
53+
54+
const App = (props) => {
55+
handleSelectionChanged = (e) => {
56+
this.setState({ people: e.target.selectedPeople });
57+
};
58+
59+
return <PeoplePicker selectionChanged={this.handleSelectionChanged} />;
60+
};
61+
```
62+
63+
All properties and events map exactly as they are defined on the web component - [see web component docs](https://aka.ms/mgt-docs).
64+
65+
### Templates
66+
67+
`mgt-react` allows you to leverage React for writing templates for mgt components.
68+
69+
> Note: You can learn more about [templating mgt components here](https://docs.microsoft.com/graph/toolkit/templates)
70+
71+
For example, to create a template to be used for rendering events in the `mgt-agenda` component, first define a component to be used for rendering an event:
72+
73+
```tsx
74+
import { MgtTemplateProps } from '@microsoft/mgt-react';
75+
76+
const MyEvent = (props: MgtTemplateProps) => {
77+
const { event } = props.dataContext;
78+
return <div>{event.subject}</div>;
79+
};
80+
```
81+
82+
Then use it as a child of the wrapped component and set the template prop to `event`
83+
84+
```tsx
85+
import { Agenda } from '@microsoft/mgt-react';
86+
87+
const App = (props) => {
88+
return <Agenda>
89+
<MyEvent template="event">
90+
</Agenda>
91+
}
92+
```
93+
94+
The `template` prop allows you to specify which template to overwrite. In this case, the `MyEvent` component will be repeated for every event, and the `event` object will be passed as part of the `dataContext` prop.
95+
96+
## What components can I use?
97+
98+
The library is auto generated from the Microsoft Graph Toolkit and all components are available.
99+
100+
The names of the React components are in PascalCase and do not include the `Mgt` prefix. For example, the `mgt-person` component is available as `Person`, and the `mgt-people-picker` component is available as `PeoplePicker`. See the [Microsoft Graph Toolkit documentation](https://aka.ms/mgt-docs) for a list of all components.
101+
102+
## Why
103+
104+
If you've used web components in React, you know that proper interop between web components and React components requires a bit of extra work.
105+
106+
From [https://custom-elements-everywhere.com/](https://custom-elements-everywhere.com/):
107+
108+
> React passes all data to Custom Elements in the form of HTML attributes. For primitive data this is fine, but the system breaks down when passing rich data, like objects or arrays. In these instances you end up with stringified values like some-attr="[object Object]" which can't actually be used.
109+
110+
> Because React implements its own synthetic event system, it cannot listen for DOM events coming from Custom Elements without the use of a workaround. Developers will need to reference their Custom Elements using a ref and manually attach event listeners with addEventListener. This makes working with Custom Elements cumbersome.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const fs = require('fs-extra');
2+
3+
const temp = `${__dirname}/../temp`;
4+
const dist = `${__dirname}/../dist`;
5+
6+
fs.removeSync(temp);
7+
fs.removeSync(dist);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
var fs = require('fs-extra');
2+
3+
let wc = JSON.parse(fs.readFileSync(`${__dirname}/../temp/web-components.json`));
4+
5+
const primitives = new Set(['string', 'boolean', 'number', 'any']);
6+
const mgtImports = new Set();
7+
8+
const tags = new Set([
9+
'mgt-person',
10+
'mgt-person-card',
11+
'mgt-agenda',
12+
'mgt-get',
13+
'mgt-login',
14+
'mgt-people-picker',
15+
'mgt-people',
16+
'mgt-tasks',
17+
'mgt-teams-channel-picker'
18+
]);
19+
20+
let output = '';
21+
22+
const wrappers = [];
23+
24+
for (const tag of wc.tags) {
25+
if (!tags.has(tag.name)) {
26+
continue;
27+
}
28+
29+
const className = tag.name
30+
.split('-')
31+
.slice(1)
32+
.map(t => t[0].toUpperCase() + t.substring(1))
33+
.join('');
34+
35+
wrappers.push({
36+
tag: tag.name,
37+
propsType: className + 'Props',
38+
className: className
39+
});
40+
41+
const props = {};
42+
43+
for (const prop of tag.properties) {
44+
if (prop.type) {
45+
props[prop.name] = prop.type;
46+
47+
let type = prop.type;
48+
if (type.endsWith('[]')) {
49+
type = type.substring(0, type.length - 2);
50+
} else if (type.startsWith('Array<')) {
51+
type = type.substring(6, type.length - 1);
52+
} else if (type === '*') {
53+
continue;
54+
}
55+
56+
if (type.startsWith('MicrosoftGraph.') || type.startsWith('MicrosoftGraphBeta.')) {
57+
continue;
58+
}
59+
60+
if (!primitives.has(type) && !mgtImports.has(type)) {
61+
mgtImports.add(type);
62+
}
63+
}
64+
}
65+
66+
let propsType = '';
67+
68+
for (const prop in props) {
69+
propsType += `\t${prop}?: ${props[prop]};\n`;
70+
}
71+
72+
for (const event of tag.events) {
73+
propsType += `\t${event.name}?: (e: Event) => void;\n`;
74+
}
75+
76+
output += `\nexport type ${className}Props = {\n${propsType}}\n`;
77+
}
78+
79+
for (const wrapper of wrappers) {
80+
output += `\nexport const ${wrapper.className} = wrapMgt<${wrapper.propsType}>('${wrapper.tag}');\n`;
81+
}
82+
83+
output = `import { ${Array.from(mgtImports).join(',')} } from '@microsoft/mgt';
84+
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
85+
import * as MicrosoftGraphBeta from '@microsoft/microsoft-graph-types-beta';
86+
import {wrapMgt} from '../Mgt';
87+
${output}
88+
`;
89+
90+
if (!fs.existsSync(`${__dirname}/../src/generated`)) {
91+
fs.mkdirSync(`${__dirname}/../src/generated`);
92+
}
93+
94+
fs.writeFileSync(`${__dirname}/../src/generated/react.ts`, output);

0 commit comments

Comments
 (0)