Skip to content

Commit 4acadc0

Browse files
committed
adding example handling
1 parent 0f538f3 commit 4acadc0

12 files changed

+1313
-264
lines changed

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<title>UI Fabric App</title>
55
<script src="//unpkg.com/react@16/umd/react.development.js"></script>
66
<script src="//unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
7+
<script src="//unpkg.com/office-ui-fabric-react/dist/office-ui-fabric-react.js"></script>
78
</head>
89
<body>
910
<div id="app"></div>

package-lock.json

+983-248
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,19 @@
2323
"webpack": "webpack"
2424
},
2525
"dependencies": {
26+
"@types/babylon": "^6.16.5",
27+
"@types/prettier": "^1.16.4",
2628
"@types/webpack-env": "^1.13.9",
2729
"@uifabric/fluent-theme": "^0.16.6",
30+
"babylon": "^6.18.0",
31+
"jscodeshift": "^0.6.4",
2832
"monaco-editor": "^0.17.0",
2933
"monaco-editor-webpack-plugin": "^1.7.0",
3034
"office-ui-fabric-react": "^6.165.0",
3135
"prettier": "^1.18.2",
3236
"react": "~16.8.0",
33-
"react-dom": "~16.8.0"
37+
"react-dom": "~16.8.0",
38+
"recast": "^0.18.1"
3439
},
3540
"devDependencies": {
3641
"@types/jest": "^24.0.0",

src/Examples/ButtonDefaultExample.tsx

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import * as React from 'react';
2+
import { css, classNamesFunction, DefaultButton, IButtonProps, IStyle, Label, PrimaryButton } from 'office-ui-fabric-react';
3+
4+
type IButtonBasicExampleStyleProps = {};
5+
6+
interface IButtonBasicExampleStyles {
7+
twoup?: IStyle;
8+
}
9+
10+
const exampleStyles: IButtonBasicExampleStyles = {
11+
twoup: [
12+
'ms-BasicButtonsTwoUp',
13+
{
14+
display: 'flex',
15+
selectors: {
16+
'& > *': {
17+
flexGrow: 1
18+
},
19+
'.ms-Label': {
20+
marginBottom: '10px'
21+
}
22+
}
23+
}
24+
]
25+
};
26+
27+
const getClassNames = classNamesFunction<IButtonBasicExampleStyleProps, IButtonBasicExampleStyles>();
28+
const classNames = getClassNames(exampleStyles, {});
29+
30+
export class ButtonDefaultExample extends React.Component<IButtonProps, {}> {
31+
public render(): JSX.Element {
32+
const { disabled, checked } = this.props;
33+
34+
return (
35+
<div className={css(classNames.twoup)}>
36+
<div>
37+
<Label>Standard</Label>
38+
<DefaultButton
39+
data-automation-id="test"
40+
allowDisabledFocus={true}
41+
disabled={disabled}
42+
checked={checked}
43+
text="Button"
44+
onClick={this._alertClicked}
45+
/>
46+
</div>
47+
<div>
48+
<Label>Primary</Label>
49+
<PrimaryButton
50+
data-automation-id="test"
51+
disabled={disabled}
52+
checked={checked}
53+
text="Button"
54+
onClick={this._alertClicked}
55+
allowDisabledFocus={true}
56+
/>
57+
</div>
58+
</div>
59+
);
60+
}
61+
62+
private _alertClicked(): void {
63+
alert('Clicked');
64+
}
65+
}
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as React from 'react';
2+
import { ColorPicker, Toggle, getColorFromString, IColor } from 'office-ui-fabric-react/lib/index';
3+
import { mergeStyleSets, HighContrastSelector } from 'office-ui-fabric-react/lib/Styling';
4+
import { updateA } from 'office-ui-fabric-react/lib/utilities/color/updateA';
5+
6+
const classNames = mergeStyleSets({
7+
wrapper: {
8+
display: 'flex'
9+
},
10+
column2: {
11+
marginLeft: 10
12+
},
13+
colorSquare: {
14+
width: 100,
15+
height: 100,
16+
margin: '16px 0',
17+
border: '1px solid #c8c6c4',
18+
selectors: {
19+
[HighContrastSelector]: {
20+
MsHighContrastAdjust: 'none'
21+
}
22+
}
23+
}
24+
});
25+
26+
export interface IBasicColorPickerExampleState {
27+
color: IColor;
28+
alphaSliderHidden: boolean;
29+
}
30+
31+
export class ColorPickerBasicExample extends React.Component<{}, IBasicColorPickerExampleState> {
32+
public state: IBasicColorPickerExampleState = {
33+
color: getColorFromString('#ffffff')!,
34+
alphaSliderHidden: false
35+
};
36+
37+
public render(): JSX.Element {
38+
const { color, alphaSliderHidden } = this.state;
39+
return (
40+
<div className={classNames.wrapper}>
41+
<ColorPicker color={color} onChange={this._updateColor} alphaSliderHidden={alphaSliderHidden} />
42+
43+
<div className={classNames.column2}>
44+
<div
45+
className={classNames.colorSquare}
46+
style={{
47+
backgroundColor: color.str
48+
}}
49+
/>
50+
<Toggle label="Hide alpha slider" onChange={this._onHideAlphaClick} checked={alphaSliderHidden} />
51+
</div>
52+
</div>
53+
);
54+
}
55+
56+
private _updateColor = (ev: React.SyntheticEvent<HTMLElement>, colorObj: IColor) => {
57+
this.setState({ color: colorObj });
58+
};
59+
60+
private _onHideAlphaClick = (ev: React.MouseEvent<HTMLElement>, checked?: boolean) => {
61+
let color = this.state.color;
62+
if (checked) {
63+
// If hiding the alpha slider, remove transparency from the color
64+
color = updateA(this.state.color, 100);
65+
}
66+
this.setState({ alphaSliderHidden: !!checked, color });
67+
};
68+
}

src/Examples/exampleTransform.tsx

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import * as babylon from 'babylon';
2+
import * as prettier from 'prettier';
3+
import * as fs from 'fs';
4+
import * as path from 'path';
5+
6+
// recast and jscodeshift have no @types :(
7+
const recast: Recast = require('recast');
8+
const jscodeshift: {
9+
withParser: (parser: string) => JSCodeShift;
10+
} = require('jscodeshift');
11+
12+
import { Recast, JSCodeShift, IASTPath, IASTSpecifier, IASTNode } from './interfaces';
13+
14+
15+
// These files are copied to lib in the pre-copy step (see config/pre-copy.json)
16+
const exampleDataFiles = {
17+
exampleData: { name: 'exampleData', contents: '' },
18+
testImages: { name: 'TestImages', contents: '' },
19+
peopleExampleData: { name: 'PeopleExampleData', contents: '' }
20+
};
21+
22+
const exampleDataRegex = new RegExp(
23+
Object.values(exampleDataFiles)
24+
// tslint:disable-next-line:typedef
25+
.map(fileInfo => `\\b${fileInfo.name}\\b`)
26+
.join('|') + '$'
27+
);
28+
29+
for (const fileInfo of Object.values(exampleDataFiles)) {
30+
// Read each file and remove all export statements (to avoid confusing the logic later that
31+
// determines which exported thing is the actual example component)
32+
let contents = fs
33+
.readFileSync(path.resolve(__dirname, '../lib', fileInfo.name + '.ts'))
34+
.toString();
35+
36+
// Strip the first few lines of comments from the files--they all have notes at the top which
37+
// don't need to be in the resulting codepen
38+
while (contents.startsWith('//')) {
39+
contents = contents.replace(/^.*?\r?\n/, '');
40+
}
41+
// Hack to quickly get rid of exports
42+
contents = contents.replace(/^export /gm, '');
43+
fileInfo.contents = contents;
44+
}
45+
46+
const j = jscodeshift.withParser('babylon');
47+
48+
const parse = (source: string) =>
49+
babylon.parse(source, {
50+
sourceType: 'module',
51+
plugins: ['jsx', 'typescript', 'classProperties', 'objectRestSpread']
52+
});
53+
54+
export function transformExample(file: string): string {
55+
let sourceStr = file;
56+
57+
// If example data files were imported, append the file contents
58+
for (const fileInfo of Object.values(exampleDataFiles)) {
59+
if (file.includes(`/${fileInfo.name}'`)) {
60+
sourceStr += `\n${fileInfo.contents}\n`;
61+
}
62+
}
63+
64+
const source = j(recast.parse(sourceStr, { parser: { parse } }));
65+
66+
// Make a list of imported identifiers, and remove all imports
67+
const identifiers: string[] = [];
68+
source.find(j.ImportDeclaration).forEach((p: IASTPath) => {
69+
const importPath = p.node.source.value;
70+
// Ignore identifiers from:
71+
// - the React import (which will be a global)
72+
// - css/scss
73+
// - example data files which will be appended if needed
74+
if (
75+
importPath !== 'react' &&
76+
!/\.s?css$/.test(importPath) &&
77+
!exampleDataRegex.test(importPath)
78+
) {
79+
p.node.specifiers.forEach((spec: IASTSpecifier) => {
80+
identifiers.push(spec.local.loc.identifierName);
81+
});
82+
}
83+
84+
// Remove the import
85+
p.prune();
86+
});
87+
88+
let exampleName;
89+
// remove exports and replace with variable or class declarations, whichever the original example used
90+
source
91+
.find(
92+
j.ExportNamedDeclaration,
93+
(node: IASTNode) =>
94+
node.declaration.type === 'VariableDeclaration' ||
95+
node.declaration.type === 'FunctionDeclaration'
96+
)
97+
.replaceWith((p: IASTPath) => {
98+
if (p.node.declaration.type === 'VariableDeclaration') {
99+
exampleName = p.node.declaration.declarations[0].id.name;
100+
}
101+
return p.node.declaration;
102+
});
103+
104+
source
105+
.find(
106+
j.ExportNamedDeclaration,
107+
(node: IASTNode) =>
108+
node.declaration.type === 'ClassDeclaration' ||
109+
node.declaration.type === 'TSInterfaceDeclaration'
110+
)
111+
.replaceWith((p: IASTPath) => {
112+
if (p.node.declaration.type === 'ClassDeclaration') {
113+
exampleName = p.node.declaration.id.name;
114+
}
115+
console.log(p.node.declaration)
116+
return p.node.declaration;
117+
});
118+
119+
let attachedWindowString = 'const {';
120+
if (identifiers.length > 0) {
121+
attachedWindowString += identifiers.join(',') + ',';
122+
}
123+
attachedWindowString += 'Fabric} = window.Fabric;\n';
124+
125+
// add imports and React render footer (with the component wrapped in a <Fabric> for styling)
126+
const sourceWithFooter = [
127+
attachedWindowString,
128+
source.toSource(),
129+
`ReactDOM.render(<Fabric><${exampleName}/></Fabric>, document.getElementById("content"));`
130+
].join('\n');
131+
132+
return sourceWithFooter;
133+
}

src/Examples/interfaces.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// tslint:disable:no-any
2+
export interface IRecastParseOptions {
3+
parser: {
4+
parse: (source: string) => any;
5+
};
6+
}
7+
export type Recast = {
8+
parse: (source: string, options: IRecastParseOptions) => any;
9+
};
10+
11+
export interface IASTSpecifier {
12+
local: { loc: { identifierName: string } };
13+
}
14+
export interface IASTDeclaration {
15+
id: { name: string };
16+
type:
17+
| 'VariableDeclaration'
18+
| 'FunctionDeclaration'
19+
| 'ClassDeclaration'
20+
| 'TSInterfaceDeclaration';
21+
declarations: IASTDeclaration[];
22+
}
23+
export interface IASTNode {
24+
source: { value: string };
25+
specifiers: IASTSpecifier[];
26+
declaration: IASTDeclaration;
27+
}
28+
export interface IASTPath {
29+
node: IASTNode;
30+
prune(): void;
31+
}
32+
export interface IASTPathSet {
33+
forEach(func: (p: IASTPath) => void): void;
34+
replaceWith(replacer: (p: IASTPath) => any): void;
35+
}
36+
export interface IAST {
37+
find: (nodeType: any, filter?: (node: IASTNode) => boolean) => IASTPathSet;
38+
toSource(): string;
39+
}
40+
41+
// tslint:disable-next-line
42+
export interface JSCodeShift {
43+
(parsed: any): IAST;
44+
readonly ImportDeclaration: any;
45+
readonly ExportNamedDeclaration: any;
46+
}

src/components/App.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { ITranspiledOutput } from '../transpiler/transpile.types';
33
import React from "react";
44
import { initializeIcons } from "office-ui-fabric-react/lib/Icons";
55
initializeIcons();
6-
6+
import { transformExample } from '../Examples/exampleTransform';
77

88
const classNames = mergeStyleSets({
99
error: {
@@ -18,8 +18,7 @@ const classNames = mergeStyleSets({
1818
interface IAppState {
1919
error?: string;
2020
editorHidden?: boolean;
21-
editor?: any;
22-
renderedCode?: any;
21+
editor?: HTMLElement;
2322
}
2423

2524
export class App extends React.Component {
@@ -76,6 +75,8 @@ export class App extends React.Component {
7675
};
7776

7877
public render() {
78+
console.log(transformExample('../Examples/ColorPicker.Basic.Example'))
79+
7980
const editor = (
8081
<Stack className={classNames.component} gap={4}>
8182
{!this.state.editorHidden && this.state.editor}

src/components/Editor.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class Editor extends React.Component<IEditorProps> {
5252

5353
private _closeEditor() {
5454
if (this.editor) {
55+
this.editor.getModel().dispose();
5556
this.editor.dispose();
5657
}
5758
}

src/transpiler/monacoTypescriptWorker.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export declare class TypeScriptWorker implements ts.LanguageServiceHost {
3737
getNavigationBarItems(fileName: string): Promise<ts.NavigationBarItem[]>;
3838
getFormattingEditsForDocument(fileName: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]>;
3939
getFormattingEditsForRange(fileName: string, start: number, end: number, options: ts.FormatCodeOptions): Promise<ts.TextChange[]>;
40-
getFormattingEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]>;
40+
getFormatEditsAfterKeystroke(fileName: string, postion: number, ch: string, options: ts.FormatCodeOptions): Promise<ts.TextChange[]>;
4141
getEmitOutput(fileName: string): Promise<ts.EmitOutput>;
4242
updateExtraLibs(extraLibs: IExtraLibs): void;
4343
}

0 commit comments

Comments
 (0)