Skip to content

Commit c69833f

Browse files
committed
Typescript vanilla webpack example
1 parent 5f4fbaa commit c69833f

File tree

6 files changed

+477
-2
lines changed

6 files changed

+477
-2
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
package-lock.json
3+
ci/temp
4+
bokeh-bokehjs-*.tgz

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,22 @@
1-
# bokehjs-examples
2-
Examples of integrating bokehjs with other libraries or tooling (e.g. webpack)
1+
# BokehJS Examples
2+
3+
Examples of integrating bokehjs with other libraries or tooling (e.g. webpack).
4+
5+
Initially contains TypeScripts examples, with others to follow later.
6+
7+
Cannot be built using a release NPM package of BokehJS and changes are required which will not be
8+
released until BokehJS 4.0. In the meantime simple examples work using a particular Bokeh branch
9+
which can be checked out and built in a temporary directory and the resultant NPM package copied
10+
across to the root directory of this repository as follows. Note you will need `git` and `node`
11+
installed.
12+
13+
```bash
14+
cd <directory of choice>
15+
git clone -b ianthomas23/bokehjs_examples_temp --single-branch --depth 1 git@github.com:bokeh/bokeh.git
16+
cd bokeh/bokehjs
17+
node make build
18+
npm pack
19+
```
20+
21+
This will produce the file `bokeh-bokehjs-3.7.0-dev.5.tgz` which should be copied to the root
22+
directory of the bokehjs-examples repository.

ci/prepare_playwright.sh

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Prepare for playwright tests in a single example directory.
4+
# Eventually need to look through all example directories.
5+
6+
set -eux
7+
8+
export TYPE=typescript
9+
export EXAMPLE=vanilla_webpack
10+
11+
# 1. Create and build example code in temporary directory
12+
cd $TYPE && bash ./create_$EXAMPLE.sh && cd ..
13+
14+
# 2. Create *-test directory
15+
mkdir temp/$TYPE/$EXAMPLE-test
16+
cd temp/$TYPE/$EXAMPLE-test
17+
18+
# 3. Create initial package.json
19+
npm init --yes
20+
21+
# 4. Add dev dependencies
22+
npm install --save-dev "@playwright/test"
23+
npm install --save-dev ../$EXAMPLE
24+
25+
# 5. Create playwright.config.ts
26+
cat > playwright.config.ts << EOF
27+
import { defineConfig, devices } from '@playwright/test';
28+
29+
export default defineConfig({
30+
testDir: './tests',
31+
fullyParallel: true,
32+
forbidOnly: !!process.env.CI,
33+
retries: process.env.CI ? 2 : 0,
34+
workers: process.env.CI ? 1 : undefined,
35+
reporter: 'html',
36+
use: {
37+
baseURL: 'http://localhost:4500',
38+
trace: 'on-first-retry',
39+
},
40+
projects: [
41+
{
42+
name: 'chromium',
43+
use: { ...devices['Desktop Chrome'] },
44+
}
45+
],
46+
webServer: {
47+
command: 'npm run serve',
48+
url: 'http://localhost:4500',
49+
reuseExistingServer: !process.env.CI
50+
}
51+
});
52+
EOF
53+
54+
# 4. Add test commands to package.json
55+
cat > temp.json << EOF
56+
{
57+
"scripts": {
58+
"serve": "npm explore $EXAMPLE -- npm run serve",
59+
"test": "playwright test",
60+
"test:ui": "playwright test --ui"
61+
}
62+
}
63+
EOF
64+
npm install --save-dev json-merger
65+
npx json-merger --output package.json --pretty package.json temp.json
66+
rm temp.json
67+
68+
# 5. Copy tests into temp example directory
69+
cp -r ../../../tests .
70+
71+
# Run tests
72+
npm run test

ci/tests/example.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { test, expect } from '@playwright/test';
2+
3+
test('loads bokehjs', async ({ page }) => {
4+
const info: string[] = []
5+
page.on('console', msg => {
6+
if (msg.type() === 'info') {
7+
info.push(msg.text());
8+
}
9+
});
10+
11+
await page.goto('/');
12+
13+
const matches = info.filter(s => s.startsWith('BokehJS version:'));
14+
expect(matches.length).toBe(1);
15+
});
16+
17+
test('is interactive', async ({ page }) => {
18+
await page.goto('/');
19+
20+
for (var i = 0; i < 20; i++) {
21+
await page.locator('.bk-Button').click();
22+
}
23+
// Take screenshot
24+
25+
const boxZoom = await page.getByTitle('Box Zoom').click();
26+
const bbox = await page.locator('.bk-CartesianFrame').boundingBox();
27+
expect(bbox).not.toBeNull();
28+
29+
await page.mouse.move(bbox!.x + bbox!.width*0.2, bbox!.y + bbox!.height*0.2);
30+
await page.mouse.down();
31+
await page.mouse.move(bbox!.x + bbox!.width*0.8, bbox!.y + bbox!.height*0.8);
32+
await page.mouse.up();
33+
await page.waitForTimeout(100);
34+
// Take screenshot
35+
36+
const reset = await page.getByTitle('Reset').click();
37+
await page.waitForTimeout(100);
38+
// Take screenshot
39+
});
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#!/usr/bin/env bash
2+
3+
set -eux
4+
5+
export OUTPUT_DIRECTORY=../temp/typescript/vanilla_webpack
6+
7+
mkdir -p $OUTPUT_DIRECTORY
8+
cd $OUTPUT_DIRECTORY
9+
rm -rf *
10+
11+
# 1. Create initial package.json (npm project settings)
12+
npm init --yes
13+
14+
# 2. Install dev dependencies
15+
npm install --save-dev typescript webpack webpack-cli webpack-dev-server ts-node ts-loader
16+
17+
# 3. Create typescript configuration tsconfig.json
18+
cat > tsconfig.json << EOF
19+
{
20+
"compilerOptions": {
21+
"baseUrl": ".",
22+
"esModuleInterop": true,
23+
"moduleResolution": "node",
24+
"outDir": "./dist",
25+
"rootDir": "./src",
26+
"target": "ES2022"
27+
},
28+
"include": ["src"]
29+
}
30+
EOF
31+
32+
# 4. Create webpack configuration webpack.config.ts
33+
cat > webpack.config.ts << EOF
34+
import path from 'path';
35+
import webpack from 'webpack';
36+
import 'webpack-dev-server';
37+
38+
const config: webpack.Configuration = {
39+
entry: './src/index.ts',
40+
mode: 'development',
41+
module: {
42+
rules: [
43+
{ test: /\.ts/, use: "ts-loader", exclude: /node_modules/ }
44+
],
45+
},
46+
output: { filename: 'bundle.js' },
47+
devServer: {
48+
static: {
49+
directory: path.join(__dirname, 'assets'),
50+
},
51+
port: 4500,
52+
},
53+
};
54+
55+
export default config;
56+
EOF
57+
58+
# 5. Create HTML file
59+
mkdir assets
60+
cat > assets/index.html << EOF
61+
<!DOCTYPE html>
62+
<html>
63+
<head>
64+
<title>BokehJS example: typescript vanilla webpack</title>
65+
<script src="bundle.js"></script>
66+
</head>
67+
<body>
68+
<div id="target"></div>
69+
</body>
70+
</html>
71+
EOF
72+
73+
# 6. Create source typescript file
74+
mkdir src
75+
cat > src/index.ts << EOF
76+
console.log("Successfully loaded")
77+
EOF
78+
79+
# 7. Add build and serve commands to package.json
80+
cat > temp.json << EOF
81+
{
82+
"scripts": {
83+
"build": "webpack build",
84+
"serve": "webpack serve"
85+
}
86+
}
87+
EOF
88+
npm install --save-dev json-merger
89+
npx json-merger --output package.json --pretty package.json temp.json
90+
rm temp.json
91+
92+
# 8. Build and run basic example without any BokehJS
93+
# npm install
94+
# npm run build
95+
# npm run serve
96+
97+
# 9. Add BokehJS dependency
98+
npm install ../../../../bokeh-bokehjs-3.7.0-dev.5.tgz
99+
100+
# 10. tsconfig.json needs workaround for @bokehjs paths
101+
cat > temp.json << EOF
102+
{
103+
"compilerOptions": {
104+
"paths": {
105+
"@bokehjs/*": ["./node_modules/@bokeh/bokehjs/build/js/lib/*"]
106+
}
107+
}
108+
}
109+
EOF
110+
npx json-merger --output tsconfig.json --pretty tsconfig.json temp.json
111+
rm temp.json
112+
113+
# 11. webpack.config.ts needs workaround to resolve @bokehjs alias
114+
# TODO: use proper ts parser to parse, add new code, write back out
115+
head -13 webpack.config.ts > temp.ts
116+
cat >> temp.ts << EOF
117+
resolve: {
118+
alias: {
119+
"@bokehjs": path.resolve(__dirname, "node_modules/@bokeh/bokehjs/build/js/lib/")
120+
}
121+
},
122+
EOF
123+
tail -9 webpack.config.ts >> temp.ts
124+
mv temp.ts webpack.config.ts
125+
126+
# 12. Replace src/index.ts with code to create BokehJS plot
127+
cat > src/index.ts << EOF
128+
import { Column, ColumnDataSource, version } from "@bokehjs/bokeh";
129+
import { figure, show } from "@bokehjs/api/plotting";
130+
import { Button } from "@bokehjs/models/widgets";
131+
132+
console.info("BokehJS version:", version);
133+
134+
function create_bokehjs_plot(target_id: string) {
135+
const source = new ColumnDataSource({data: { x: [0.1, 0.9], y: [0.1, 0.9], size: [40, 10] }});
136+
137+
const plot = figure({
138+
title: "Example BokehJS plot", height: 500, width: 500,
139+
x_range: [0, 1], y_range: [0, 1], sizing_mode: "stretch_width",
140+
});
141+
142+
plot.scatter({ field: "x" }, { field: "y" }, {source, size: { field: "size" }});
143+
144+
const button = new Button({label: "Click me to add a point", button_type: "primary"});
145+
function button_callback() {
146+
const data = source.data as any;
147+
data.x.push(Math.random());
148+
data.y.push(Math.random());
149+
data.size.push(10 + Math.random()*30);
150+
source.change.emit();
151+
}
152+
button.on_click(button_callback);
153+
154+
const column = new Column({children: [plot, button], sizing_mode: "stretch_width"});
155+
show(column, target_id);
156+
}
157+
158+
create_bokehjs_plot("#target");
159+
EOF
160+
161+
# 13. Rebuild and serve
162+
npm install
163+
npm run build
164+
#npm run serve

0 commit comments

Comments
 (0)