Skip to content

Commit f3b2b0b

Browse files
Added shout-button-message widget example. (#243)
* Initial shout button extension. * Updated workflow. * Added preview and metadata. * Removed unneeded notebooks. * Added signals note/recommendation. * Added guide link to README. * Add improved description to root/proj READMEs. * Update README/fix links. * Updated title. * Updated title. * Added a basic test. * Linting/formatting. * Formatting. * Fixed hello world config file diff in CI. * Added required yarnrc file needed by CI. * Update template * Improve widget example Add listeners * Fix CI * Update reference snapshot --------- Co-authored-by: Frédéric Collonval <[email protected]>
1 parent 834e87d commit f3b2b0b

30 files changed

+1119
-10
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ jobs:
3333
- notifications
3434
- react-widget
3535
- settings
36+
- shout-button-message
3637
- signals
3738
- state
3839
- toolbar-button

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1. [Context Menu](#context-menu)
1414
1. [Custom Log Console](#custom-log-console)
1515
1. [Datagrid](#datagrid)
16+
1. [Dual Compatibility Shout Button](#dual-compatibility-shout-button)
1617
1. [Collaborative Document](#collaborative-document)
1718
1. _[Hello World](#hello-world)_
1819
1. [Kernel Messaging](#kernel-messaging)
@@ -101,6 +102,7 @@ Start with the [Hello World](hello-world) and then jump to the topic you are int
101102
- [Context Menu](context-menu)
102103
- [Custom Log Console](custom-log-console)
103104
- [Datagrid](datagrid)
105+
- [Dual Compatibility Shout Button](shout-button-message)
104106
- [Collaborative Document](documents)
105107
- [Hello World](hello-world)
106108
- [Kernel Messaging](kernel-messaging)
@@ -179,6 +181,16 @@ Display a Datagrid as a Lumino Widget.
179181

180182
[![Datagrid](datagrid/preview.png)](datagrid)
181183

184+
### [Dual Compatibility Shout Button](shout-button-message)
185+
186+
This example shows dual compatibility: Make an extension that is compatible
187+
with both JupyterLab and Jupyter Notebook by using optional features. Adds
188+
a shout button to the right sidebar, and if running in JupyterLab, also adds
189+
a status bar widget. This example is part of the [Extension Dual Compatibility Guide](https://jupyterlab.readthedocs.io/en/latest/extension_dual_compatibility.html).
190+
Read more about this example on that page.
191+
192+
[![Dual compatibility shout button](shout-button-message/preview.jpg)](shout-button-message)
193+
182194
### [Collaborative Document](documents)
183195

184196
Create new documents and make them collaborative.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"react-widget",
3939
"server-extension",
4040
"settings",
41+
"shout-button-message",
4142
"signals",
4243
"state",
4344
"toolbar-button",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
2+
_commit: v4.2.4
3+
_src_path: https://github.com/jupyterlab/extension-template
4+
author_email: ''
5+
author_name: Project Jupyter Contributors
6+
data_format: string
7+
file_extension: ''
8+
has_binder: false
9+
has_settings: false
10+
kind: frontend
11+
labextension_name: '@jupyterlab-examples/shout-button'
12+
mimetype: ''
13+
mimetype_name: ''
14+
project_short_description: An extension that adds a button and message to the right
15+
toolbar, with optional status bar widget in JupyterLab.
16+
python_name: jupyterlab_examples_shout_button
17+
repository: https://github.com/jupyterlab/extension-examples
18+
test: true
19+
viewer_name: ''
20+

shout-button-message/.gitignore

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
*.bundle.*
2+
lib/
3+
node_modules/
4+
*.log
5+
.eslintcache
6+
.stylelintcache
7+
*.egg-info/
8+
.ipynb_checkpoints
9+
*.tsbuildinfo
10+
jupyterlab_examples_shout_button/labextension
11+
# Version file is handled by hatchling
12+
jupyterlab_examples_shout_button/_version.py
13+
14+
# Integration tests
15+
ui-tests/test-results/
16+
ui-tests/playwright-report/
17+
18+
# Created by https://www.gitignore.io/api/python
19+
# Edit at https://www.gitignore.io/?templates=python
20+
21+
### Python ###
22+
# Byte-compiled / optimized / DLL files
23+
__pycache__/
24+
*.py[cod]
25+
*$py.class
26+
27+
# C extensions
28+
*.so
29+
30+
# Distribution / packaging
31+
.Python
32+
build/
33+
develop-eggs/
34+
dist/
35+
downloads/
36+
eggs/
37+
.eggs/
38+
lib/
39+
lib64/
40+
parts/
41+
sdist/
42+
var/
43+
wheels/
44+
pip-wheel-metadata/
45+
share/python-wheels/
46+
.installed.cfg
47+
*.egg
48+
MANIFEST
49+
50+
# PyInstaller
51+
# Usually these files are written by a python script from a template
52+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
53+
*.manifest
54+
*.spec
55+
56+
# Installer logs
57+
pip-log.txt
58+
pip-delete-this-directory.txt
59+
60+
# Unit test / coverage reports
61+
htmlcov/
62+
.tox/
63+
.nox/
64+
.coverage
65+
.coverage.*
66+
.cache
67+
nosetests.xml
68+
coverage/
69+
coverage.xml
70+
*.cover
71+
.hypothesis/
72+
.pytest_cache/
73+
74+
# Translations
75+
*.mo
76+
*.pot
77+
78+
# Scrapy stuff:
79+
.scrapy
80+
81+
# Sphinx documentation
82+
docs/_build/
83+
84+
# PyBuilder
85+
target/
86+
87+
# pyenv
88+
.python-version
89+
90+
# celery beat schedule file
91+
celerybeat-schedule
92+
93+
# SageMath parsed files
94+
*.sage.py
95+
96+
# Spyder project settings
97+
.spyderproject
98+
.spyproject
99+
100+
# Rope project settings
101+
.ropeproject
102+
103+
# Mr Developer
104+
.mr.developer.cfg
105+
.project
106+
.pydevproject
107+
108+
# mkdocs documentation
109+
/site
110+
111+
# mypy
112+
.mypy_cache/
113+
.dmypy.json
114+
dmypy.json
115+
116+
# Pyre type checker
117+
.pyre/
118+
119+
# End of https://www.gitignore.io/api/python
120+
121+
# OSX files
122+
.DS_Store
123+
124+
# Yarn cache
125+
.yarn/

shout-button-message/.prettierignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
node_modules
2+
**/node_modules
3+
**/lib
4+
**/package.json
5+
!/package.json
6+
jupyterlab_examples_shout_button

shout-button-message/.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

shout-button-message/README.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Shout button (cross compatible extension)
2+
3+
This example defines an extension that adds a button in the right sidebar that
4+
if clicked will display an alert to the user and in JupyterLab will update
5+
a widget in the status bar.
6+
7+
![preview](./preview.jpg)
8+
9+
We strongly advice to look to those examples before diving into this one:
10+
11+
- [signals](../signals/): Communication between JavaScript objects.
12+
- [widgets](../widgets): The basic DOM Jupyter component
13+
14+
## Jupyter Notebook / JupyterLab compatibility
15+
16+
As Jupyter Notebook 7+ is built with components from JupyterLab, and since
17+
both use the same building blocks, that means your extension can work
18+
on both (or any other frontend built with JupyterLab components) with
19+
little or no modification depending on its design.
20+
21+
This example has a part specific to JupyterLab. This translate by having
22+
optional dependency for your extension plugin.
23+
24+
```ts
25+
// src/index.ts#L120-L120
26+
27+
optional: [IStatusBar],
28+
```
29+
30+
If your dependency is optional, the object pass to the `activate` method
31+
will be `null` if no other plugin provides it.
32+
33+
```ts
34+
// src/index.ts#L124-L124
35+
36+
activate: (app: JupyterFrontEnd, statusBar: IStatusBar | null) => {
37+
```
38+
39+
## Add the button in the sidebar
40+
41+
You can add a widget to the right sidebar through the application shell:
42+
43+
```ts
44+
// src/index.ts#L128-L131
45+
46+
const shoutWidget: ShoutWidget = new ShoutWidget();
47+
shoutWidget.id = 'JupyterShoutWidget'; // Widgets need an id
48+
49+
app.shell.add(shoutWidget, 'right');
50+
```
51+
52+
The `ShoutWidget` is a widget that contains a button that when clicked
53+
emit a signal `messageShouted` that any callback can listen to to react
54+
to it and display an alert to the user.
55+
56+
```ts
57+
// src/index.ts#L99-L103
58+
59+
shout() {
60+
this._lastShoutTime = new Date();
61+
this._messageShouted.emit(this._lastShoutTime);
62+
window.alert('Shouting at ' + this._lastShoutTime);
63+
}
64+
```
65+
66+
## Connect the button and the status bar
67+
68+
The status bar does not exist in all Jupyter applications (e.g. in
69+
Jupyter Notebook). So a good practice is to make that dependency
70+
optional and test for it to be non-null to carry related action:
71+
72+
```ts
73+
// src/index.ts#L135-L135
74+
75+
if (statusBar) {
76+
```
77+
78+
In this specific case, the action is to create a widget to add to the
79+
status bar. You can achieve that by calling the `registerStatusItem`
80+
method from the status bar object.
81+
82+
```ts
83+
// src/index.ts#L136-L138
84+
85+
const statusBarWidget = new ShoutStatusBarSummary();
86+
87+
statusBar.registerStatusItem('shoutStatusBarSummary', {
88+
```
89+
90+
If you want to react to a click on the button, you can `connect` to the
91+
widget `messageShouted` signal. In which for example, you update the
92+
text displayed in the status bar.
93+
94+
```ts
95+
// src/index.ts#L142-L144
96+
97+
// Connect to the messageShouted to be notified when a new message
98+
// is published and react to it by updating the status bar widget.
99+
shoutWidget.messageShouted.connect((widget: ShoutWidget, time: Date) => {
100+
```
101+
102+
## Where to Go Next
103+
104+
You can have more information about making extension compatible with
105+
multiple applications in the
106+
[Extension Dual Compatibility Guide](https://jupyterlab.readthedocs.io/en/latest/extension_dual_compatibility.html).

shout-button-message/install.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"packageManager": "python",
3+
"packageName": "jupyterlab_examples_shout_button",
4+
"uninstallInstructions": "Use your Python package manager (pip, conda, etc.) to uninstall the package jupyterlab_examples_shout_button"
5+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
try:
2+
from ._version import __version__
3+
except ImportError:
4+
# Fallback when using the package in dev mode without installing
5+
# in editable mode with pip. It is highly recommended to install
6+
# the package from a stable release or in editable mode: https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
7+
import warnings
8+
warnings.warn("Importing 'jupyterlab_examples_shout_button' outside a proper installation.")
9+
__version__ = "dev"
10+
11+
12+
def _jupyter_labextension_paths():
13+
return [{
14+
"src": "labextension",
15+
"dest": "@jupyterlab-examples/shout-button"
16+
}]

0 commit comments

Comments
 (0)