diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9670f053..b64e7ed5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,7 +67,7 @@ jobs: node-version: "12.x" - name: Install Python if: steps.filter.outputs.extension == 'true' - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: "3.7" architecture: "x64" @@ -88,9 +88,7 @@ jobs: - name: Install the Python dependencies if: steps.filter.outputs.extension == 'true' run: | - python -m pip install --upgrade pip - python -m pip install jupyter_packaging - python -m pip install jupyterlab + python -m pip install --upgrade pip jupyter_packaging~=0.7.9 jupyterlab~=3.0 - name: Install the NPM dependencies if: steps.filter.outputs.extension == 'true' run: | @@ -112,9 +110,7 @@ jobs: run: | cd ${EXAMPLE_FOLDER} pip install -e . - jupyter labextension list 1>labextensions 2>&1 - cat labextensions - cat labextensions | grep -i "@jupyterlab-examples/*.*OK" + jupyter labextension list 2>&1 | grep -ie "@jupyterlab-examples/*.*OK" python -m jupyterlab.browser_check pip uninstall -y $(python setup.py --name) env: @@ -161,7 +157,7 @@ jobs: shell: bash - name: Install Python if: steps.filter.outputs.extension == 'true' - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: "3.7" architecture: "x64" @@ -182,9 +178,7 @@ jobs: - name: Install the Python dependencies if: steps.filter.outputs.extension == 'true' run: | - python -m pip install --upgrade pip - python -m pip install jupyter_packaging - python -m pip install jupyterlab + python -m pip install --upgrade pip jupyter_packaging~=0.7.9 jupyterlab~=3.0 - name: Install the NPM dependencies if: steps.filter.outputs.extension == 'true' run: | @@ -206,10 +200,8 @@ jobs: - name: Check extension as dev if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) run: | - jupyter server extension list 1>serverextensions 2>&1 - cat serverextensions | grep "jlab_ext_example.*OK" - jupyter labextension list 1>labextensions 2>&1 - cat labextensions | grep "@jupyterlab-examples/server-extension.*OK" + jupyter server extension list 2>&1 | grep -ie "jlab_ext_example.*OK" + jupyter labextension list 2>&1 | grep -ie "@jupyterlab-examples/server-extension.*OK" - name: Clean extension installation if: steps.filter.outputs.extension == 'true' run: | @@ -222,14 +214,11 @@ jobs: run: | cd advanced/server-extension pip install -e . - jupyter server extension enable --py jlab_ext_example - name: Check extension as dev if: steps.filter.outputs.extension == 'true' && ( startsWith(runner.os, 'Linux') || startsWith(runner.os, 'macOS') ) run: | - jupyter server extension list 1>serverextensions 2>&1 - cat serverextensions | grep "jlab_ext_example.*OK" - jupyter labextension list 1>labextensions 2>&1 - cat labextensions | grep "@jupyterlab-examples/server-extension.*OK" + jupyter server extension list 2>&1 | grep -ie "jlab_ext_example.*OK" + jupyter labextension list 2>&1 | grep -ie "@jupyterlab-examples/server-extension.*OK" python -m jupyterlab.browser_check build_all: @@ -255,7 +244,7 @@ jobs: with: node-version: "12.x" - name: Install Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: "3.7" architecture: "x64" @@ -272,11 +261,12 @@ jobs: restore-keys: | ${{ runner.os }}-pip- - name: Install the Python dependencies - run: python -m pip install jupyterlab + run: python -m pip install jupyterlab~=3.0 pytest pytest-check-links - name: Bootstrap the jlpm deps run: jlpm - name: Build all the extensions run: | + pytest --check-links jlpm build-ext jlpm lint:check jlpm install-ext diff --git a/.prettierignore b/.prettierignore index 5c6e45ce..7ad0be8a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,4 @@ node_modules **/lib **/package.json **/labextension +**/.pytest_cache diff --git a/README.md b/README.md index c6b535f1..7283232d 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ You can expect from each example: - The list of used JupyterLab API and Extension Points. - Explanations of the internal working, illustrated with code snippets. -We have structured the examples based on the [extension points](https://jupyterlab.readthedocs.io/en/stable/developer/extension_points.html). Browse the previews below or skip them and [jump directly to the sections for developers](#prerequisites). +We have structured the examples based on the [extension points](https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html). Browse the previews below or skip them and [jump directly to the sections for developers](#prerequisites). You are welcome to open any [issue](https://github.com/jupyterlab/extension-examples/issues) or [pull request](https://github.com/jupyterlab/extension-examples/pulls). @@ -255,16 +255,14 @@ jlpm clean-ext Go to the example directory you want to install, e.g. `cd ./basics/hello-world`, and run the following commands: ```bash -jlpm install -jlpm run build -jupyter labextension install . +pip install -e . +jupyter labextension develop . --overwrite ``` -Rebuild the JupyterLab application: +Rebuild the extension: ```bash jlpm run build -jupyter lab build ``` You can now start JupyterLab and check if your extension is working fine: @@ -296,11 +294,11 @@ We are using [embedme](https://github.com/zakhenry/embedme) to embed code snippe ## Install a Published Extension -Once your extension is published (outside of this scope), you can install it +Once your extension is published on [pypi.org](https://pypi.org/) (outside of this scope), you can install it with the following command: ```bash -jupyter labextension install +pip install ``` ## About JupyterLab @@ -313,9 +311,9 @@ of libraries from different languages. Complementary to these examples, you can rely on the official JupyterLab documentation. -- [Extension Developer Guide](https://jupyterlab.readthedocs.io/en/stable/developer/extension_dev.html) -- [Common Extension Points](https://jupyterlab.readthedocs.io/en/stable/developer/extension_points.html) -- [Astronomy Picture of the Day JupyterLab Extension](https://jupyterLab.readthedocs.io/en/stable/developer/extension_tutorial.html) +- [Extension Developer Guide](https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html) +- [Common Extension Points](https://jupyterlab.readthedocs.io/en/stable/extension/extension_points.html) +- [Astronomy Picture of the Day JupyterLab Extension](https://jupyterLab.readthedocs.io/en/stable/extension/extension_tutorial.html) ## Credits diff --git a/advanced/kernel-messaging/README.md b/advanced/kernel-messaging/README.md index e5950ae0..1b5c579f 100644 --- a/advanced/kernel-messaging/README.md +++ b/advanced/kernel-messaging/README.md @@ -9,7 +9,7 @@ ![Kernel Messages](preview.gif) -## Custom Kernel Interactions: Kernel Managment and Messaging +## Custom Kernel Interactions: Kernel Management and Messaging One of the main features of JupyterLab is the possibility to manage and interact with [kernels](https://jupyter-client.readthedocs.io/en/latest/kernels.html). @@ -32,7 +32,7 @@ execution result. ## Initializing and managing a Kernel Session (`panel.ts`) Jupyterlab provides a class `SessionContext` -([see the documentation](https://jupyterlab.github.io/jupyterlab/apputils/classes/sessioncontext.html)) +([see the documentation](https://jupyterlab.github.io/jupyterlab/classes/_apputils_src_index_.sessioncontext.html)) that manages a single kernel session. Here is the code to initialize such session: ```ts @@ -76,7 +76,7 @@ const manager = app.serviceManager; With these lines, you can extend the panel widget from the [signal example](../../basics/signals) to initialize a kernel. In addition, you will create a `KernelModel` class in it and overwrite the `dispose` and `onCloseRequest` methods of the `StackedPanel` -([see the documentation](https://jupyterlab.github.io/lumino/api/widgets/classes/stackedpanel.html)) +([see the documentation](https://jupyterlab.github.io/lumino/widgets/classes/stackedpanel.html)) to free the kernel session resources if the panel is closed. The whole adapted panel class looks like this: diff --git a/advanced/kernel-output/README.md b/advanced/kernel-output/README.md index e03e097b..aa800efe 100644 --- a/advanced/kernel-output/README.md +++ b/advanced/kernel-output/README.md @@ -37,11 +37,11 @@ followed by the creation of the visual element. ## Initializing a Kernel Session To interact with a kernel, you can create a `SessionContext` -object ([see the documentation](https://jupyterlab.github.io/jupyterlab/apputils/classes/sessioncontext.html)). +object ([see the documentation](https://jupyterlab.github.io/jupyterlab/classes/_apputils_src_index_.sessioncontext.html)). Here it is stored in the private `_sessionContext` variable: ```ts -// src/panel.ts#L94-L94 +// src/panel.ts#L95-L95 private _sessionContext: SessionContext; ``` @@ -50,7 +50,7 @@ A `SessionContext` handles a single kernel session. The session itself (not yet the kernel) is started with these lines: ```ts -// src/panel.ts#L44-L48 +// src/panel.ts#L45-L49 this._sessionContext = new SessionContext({ sessionManager: manager.sessions, @@ -63,7 +63,7 @@ The private session variable is exposed as read-only for other users through a getter method: ```ts -// src/panel.ts#L72-L74 +// src/panel.ts#L73-L75 get session(): ISessionContext { return this._sessionContext; @@ -75,7 +75,7 @@ with this line: ```ts -// src/panel.ts#L58-L69 +// src/panel.ts#L59-L70 void this._sessionContext .initialize() @@ -93,13 +93,13 @@ void this._sessionContext When a session has no predefined preferred kernel, a dialog will request the user to choose a kernel to start. Conveniently, this can -also be an already existing kernel, as you will see later. +also be an already existing kernel. The following two methods ensure the clean disposal of the session when you close the panel. ```ts -// src/panel.ts#L76-L79 +// src/panel.ts#L77-L80 dispose(): void { this._sessionContext.dispose(); @@ -108,7 +108,7 @@ dispose(): void { ``` ```ts -// src/panel.ts#L89-L92 +// src/panel.ts#L90-L93 protected onCloseRequest(msg: Message): void { super.onCloseRequest(msg); @@ -120,11 +120,11 @@ protected onCloseRequest(msg: Message): void { The `SimplifiedOutputArea` class is a `Widget`, as described in the [widget example](../../widget-tracker/widgets/README.md). It has the ability to display the results of a notebook cell execution. -You can instantiate it with a new `OutputAreaModel`; this is class containing +You can instantiate it with a new `OutputAreaModel`; this class is containing the data to show: ```ts -// src/panel.ts#L50-L54 +// src/panel.ts#L51-L55 this._outputareamodel = new OutputAreaModel(); this._outputarea = new SimplifiedOutputArea({ @@ -134,11 +134,11 @@ this._outputarea = new SimplifiedOutputArea({ ``` `SimplifiedOutputArea` provides a static method `execute` that sends -some code to a kernel through a `ISessionContext` ([see documentation](https://jupyterlab.github.io/jupyterlab/outputarea/classes/simplifiedoutputarea.html#execute)). And then it displays the result +some code to a kernel through a `ISessionContext` ([see documentation](https://jupyterlab.github.io/jupyterlab/classes/_outputarea_src_index_.simplifiedoutputarea.html#execute)). And then it displays the result in the specific `SimplifiedOutputArea` object you created: ```ts -// src/panel.ts#L81-L87 +// src/panel.ts#L82-L88 execute(code: string): void { SimplifiedOutputArea.execute(code, this._outputarea, this._sessionContext) @@ -158,7 +158,7 @@ To display the `SimplifiedOutputArea` Widget you need to add it to your panel with: ```ts -// src/panel.ts#L56-L56 +// src/panel.ts#L57-L57 this.addWidget(this._outputarea); ``` @@ -266,4 +266,4 @@ the variable name in the input dialog, it will be displayed in the example panel ## Where to Go Next This example makes use of input dialogs. To know which input dialogs are available -have a look at the [documentation](https://jupyterlab.readthedocs.io/en/stable/developer/ui_helpers.html). +have a look at the [documentation](https://jupyterlab.readthedocs.io/en/stable/extension/ui_helpers.html#dialogs). diff --git a/advanced/kernel-output/src/panel.ts b/advanced/kernel-output/src/panel.ts index 8494eaa1..ded4f69c 100644 --- a/advanced/kernel-output/src/panel.ts +++ b/advanced/kernel-output/src/panel.ts @@ -9,6 +9,7 @@ import { OutputAreaModel, SimplifiedOutputArea } from '@jupyterlab/outputarea'; import { IRenderMimeRegistry } from '@jupyterlab/rendermime'; import { KernelMessage, ServiceManager } from '@jupyterlab/services'; + import { ITranslator, nullTranslator, diff --git a/advanced/server-extension/README.md b/advanced/server-extension/README.md index b88414ef..cdf17043 100644 --- a/advanced/server-extension/README.md +++ b/advanced/server-extension/README.md @@ -28,12 +28,16 @@ like this (be careful to set _has\_server\_extension_ to _y_): ```bash author_name []: my_name -extension_name [myextension]: jlab_ext_example +python_name [myextension]: jlab_ext_example +extension_name [jlab_ext_example]: jlab-ext-example project_short_description [A JupyterLab extension.]: A minimal JupyterLab extension with backend and frontend parts. has_server_extension [n]: y -repository [https://github.com/my_name/myextension]: +has_binder [n]: y +repository [https://github.com/github_username/jlab_ext_example]: ``` +> The python name should not contain `-`. It is nice for user to test your extension online, so the `has_binder` was set to _yes_. + The cookiecutter creates the directory `jlab_ext_example` [or your extension name] that looks like this: @@ -41,25 +45,31 @@ that looks like this: jlab_ext_example/ │  # Generic Files │ .gitignore +│ install.json # Information retrieved by JupyterLab to help users know how to manage the extension │ LICENSE # License of your code │ README.md # Instructions to install and build │ ├───.github │ └───workflows │ build.yml +│ +├───binder +│ environment.yml +│ postBuild │   -│  # Backend (server) Files +│  # Python Package Files │ MANIFEST.in # Help Python to list your source files -│ pyproject.toml # Define dependencies for building the server package -│ setup.py # Information about the server package +│ pyproject.toml # Define dependencies for building the package +│ setup.py # Information about the package +│ +│ # Backend (server) Files +├───jupyter-config +│ jlab_ext_example.json # Server extension enabler │ ├───jlab_ext_example │ handlers.py # API handler (where things happen) │ _version.py # Server extension version │ __init__.py # Hook the extension in the server -│ -├───jupyter-config -│ jlab_ext_example.json # Server extension enabler │   │  # Frontend Files │ .eslintignore # Code linter configuration @@ -71,15 +81,17 @@ jlab_ext_example/ │   ├───src │ index.ts # Actual code of the extension -│ jlabextexample.ts # More code used by the extension +│ handler.ts # More code used by the extension │ └───style - index.css # CSS styling + base.css # CSS styling + index.css + index.js ``` There are two major parts in the extension: -- A Python package for the server extension +- A Python package for the server extension and the packaging - A NPM package for the frontend extension In this example, you will see that the template code have been extended @@ -312,20 +324,6 @@ an `IFrame` that will display static content fetched from the server extension. The server part of the extension is going to be presented in this section. -You first need to install the python source code. The following will install -the `jlab_ext_example` package in dev mode: - -```bash -pip install -e . -``` - -Then you need to enable the package at the Jupyter level -so that it becomes a server extension. - -```bash -jupyter serverextension enable --py jlab_ext_example -``` - JupyterLab server is built on top of the [Tornado](https://tornadoweb.org/en/stable/guide.html) Python package. To extend the server, your extension needs to be defined as a proper Python package with some hook functions: @@ -370,8 +368,8 @@ def load_jupyter_server_extension(lab_app): ``` -The `_jupyter_jlab_ext_example_paths` provides the Python package name -to the server. But the most important one is `load_jupyter_jlab_ext_example` +The `_jupyter_server_extension_paths` provides the Python package name +to the server. But the most important one is `load_jupyter_server_extension` that register new handlers. ```py @@ -389,7 +387,7 @@ example the url is _base_server_url_`/jlab-ext-example/hello` and the class hand host_pattern = ".*$" base_url = web_app.settings["base_url"] -# Prepend the base_url so that it works in a jupyterhub setting +# Prepend the base_url so that it works in a JupyterHub setting route_pattern = url_path_join(base_url, url_path, "hello") handlers = [(route_pattern, RouteHandler)] web_app.add_handlers(host_pattern, handlers) @@ -412,7 +410,7 @@ class RouteHandler(APIHandler): @tornado.web.authenticated def post(self): - # input_data is a dictionnary with a key "name" + # input_data is a dictionary with a key "name" input_data = self.get_json_body() data = {"greetings": "Hello {}, enjoy JupyterLab!".format(input_data["name"])} self.finish(json.dumps(data)) @@ -619,7 +617,7 @@ Basically it will build the frontend NPM package: install_npm(HERE, build_cmd="build:prod", npm=["jlpm"]), ``` -It will ensure one of the generated JS files is `lib/jlabextexample.js`: +It will ensure one of the generated files is `jlab_ext_example/labextension/package.json`: ```py # setup.py#L24-L27 @@ -704,11 +702,11 @@ And that server extension is available through `pip`: "pip" ``` -For more information on the `discovery` metadata, please refer to the [documentation](https://jupyterlab.readthedocs.io/en/stable/developer/extension_dev.html#ext-author-companion-packages). +For more information on the `discovery` metadata, please refer to the [documentation](https://jupyterlab.readthedocs.io/en/stable/extension/extension_dev.html#ext-author-companion-packages). ## Installing the Package -With the packaging described above, installing the extension is done in two commands once the package is published on pypi.org: +With the packaging described above, installing the extension is done in one command once the package is published on pypi.org: ```bash # Install the server extension and @@ -721,14 +719,11 @@ This will shunt the installation machinery described above. Therefore the comman to get you set are: ```bash -# Install server extension in editable mode +# Install package in development mode pip install -e . -# Build Typescript source -jlpm build -# Install your development version of the extension with JupyterLab -jupyter labextension develop . - -# Rebuild Typescript source after making changes -jlpm build +# Link your development version of the extension with JupyterLab +jupyter labextension develop . --overwrite +# Rebuild extension Typescript source after making changes +jlpm run build ``` diff --git a/advanced/server-extension/jlab_ext_example/handlers.py b/advanced/server-extension/jlab_ext_example/handlers.py index 7effc12b..c1a5144f 100644 --- a/advanced/server-extension/jlab_ext_example/handlers.py +++ b/advanced/server-extension/jlab_ext_example/handlers.py @@ -18,7 +18,7 @@ def get(self): @tornado.web.authenticated def post(self): - # input_data is a dictionnary with a key "name" + # input_data is a dictionary with a key "name" input_data = self.get_json_body() data = {"greetings": "Hello {}, enjoy JupyterLab!".format(input_data["name"])} self.finish(json.dumps(data)) @@ -28,12 +28,12 @@ def setup_handlers(web_app, url_path): host_pattern = ".*$" base_url = web_app.settings["base_url"] - # Prepend the base_url so that it works in a jupyterhub setting + # Prepend the base_url so that it works in a JupyterHub setting route_pattern = url_path_join(base_url, url_path, "hello") handlers = [(route_pattern, RouteHandler)] web_app.add_handlers(host_pattern, handlers) - # Prepend the base_url so that it works in a jupyterhub setting + # Prepend the base_url so that it works in a JupyterHub setting doc_url = url_path_join(base_url, url_path, "public") doc_dir = os.getenv( "JLAB_SERVER_EXAMPLE_STATIC_DIR", diff --git a/basics/datagrid/README.md b/basics/datagrid/README.md index 7d5d32ff..b9c567d5 100644 --- a/basics/datagrid/README.md +++ b/basics/datagrid/README.md @@ -7,7 +7,7 @@ JupyterLab is built on top of [Lumino](https://github.com/jupyterlab/lumino). That library defines `Widget` as the primary interface brick. -In this example [the datagrid lumino example](https://jupyterlab.github.io/lumino/datagrid/index.html) +In this example [the datagrid lumino example](https://jupyterlab.github.io/lumino/datagrid/classes/datagrid.html) is integrated into JupyterLab. First you need to import `StackedPanel`, `DataGrid` @@ -88,21 +88,27 @@ class LargeDataModel extends DataModel { The three abstract methods are `rowCount`, `columnCount` and `data`. The first two must return a number from a region argument. To know the possible values of `RowRegion` and the `ColumnRegion`, you can look at the -[Lumino code](https://github.com/jupyterlab/lumino/blob/9f5e11025b62d2c4a6fb59e2681ae1ed323dcde4/packages/datagrid/src/datamodel.ts#L112-L129): +[Lumino code](https://github.com/jupyterlab/lumino/blob/9c5f31cca3b02441850e086c1b19642a6e298493/packages/datagrid/src/datamodel.ts#L134-L155): ```ts /** * A type alias for the data model row regions. */ -type RowRegion = 'body' | 'column-header'; +export type RowRegion = 'body' | 'column-header'; + /** * A type alias for the data model column regions. */ -type ColumnRegion = 'body' | 'row-header'; +export type ColumnRegion = 'body' | 'row-header'; + /** * A type alias for the data model cell regions. */ -type CellRegion = 'body' | 'row-header' | 'column-header' | 'corner-header'; +export type CellRegion = + | 'body' + | 'row-header' + | 'column-header' + | 'corner-header'; ``` The `|` can be read as `or`. This means that the `RowRegion` type is diff --git a/basics/hello-world/README.md b/basics/hello-world/README.md index 70844c77..a149d1c3 100644 --- a/basics/hello-world/README.md +++ b/basics/hello-world/README.md @@ -22,38 +22,57 @@ like this: ```bash author_name []: tuto -extension_name [myextension]: hello-world -project_short_description [A JupyterLab extension.]: minimal lab example -repository [https://github.com/my_name/myextension]: +python_name [myextension]: hello_world +extension_name [hello_world]: hello-world +project_short_description [A JupyterLab extension.]: Minimal JupyterLab example +has_server_extension [n]: +has_binder [n]: y +repository [https://github.com/github_username/hello_world]: ``` -The cookiecutter creates the directory `hello-world` [or your extension name] +> The python name should not contain `-`. It is nice for user to test your extension online, so the `has_binder` was set to _yes_. + +The cookiecutter creates the directory `hello_world` [or your extension name] that looks like this: ```bash -hello-world/ +hello_world/ │ .eslintignore │ .eslintrc.js │ .gitignore │ .prettierignore │ .prettierrc +│ install.json │ LICENSE +│ MANIFEST.in │ package.json +│ pyproject.toml │ README.md +│ setup.py │ tsconfig.json │ ├───.github │ └───workflows │ build.yml │ +├───binder +│ environment.yml +│ postBuild +│ +├───hello_world +│ __init__.py +│ _version.py +│ ├───src │ index.ts │ └───style + base.css index.css + index.js ``` -Those files can be separated in 3 groups: +Those files can be separated in 4 groups: - Information about the extension: - `README.md` contains some instructions @@ -62,11 +81,17 @@ Those files can be separated in 3 groups: - `package.json` contains information about the extension such as dependencies - `tsconfig.json` contains information for the typescript compilation - `src/index.ts` _this contains the actual code of your extension_ - - `style/index.css` contains style elements that you can use + - `style/` folder contains style elements that you can use - Validation: - `.prettierrc` and `.prettierignore` specify the code formatter [`prettier`](https://prettier.io) configuration - `.eslintrc.js` and `.eslintignore` specify the code linter [`eslint`](https://eslint.org) configuration - `.github/workflows/build.yml` sets the continuous integration tests of the code using [GitHub Actions](https://help.github.com/en/actions) +- Packaging as a Python package: + - `setup.py` contains information about the Python package such as what to package + - `pyproject.toml` contains the dependencies to create the Python package + - `MANIFEST.in` contains list of non-Python files to include in the Python package + - `install.json` contains information retrieved by JupyterLab to help users know how to manage the package + - `hello_world/` folder contains the final code to be distributed The following sections will walk you through the extension code files. @@ -94,7 +119,7 @@ package is declared in the file `package.json`: // package.json#L46-L48 "dependencies": { - "@jupyterlab/application": "^3.0.0-rc.15" + "@jupyterlab/application": "^3.0.0" }, ``` @@ -112,13 +137,14 @@ const extension: JupyterFrontEndPlugin = { ``` ```ts -// src/index.ts#L13-L13 - -console.log('the JupyterLab main application:', app); + console.log('JupyterLab extension hello-world is activated!'); ``` ```ts -// src/index.ts#L17-L17 +// src/index.ts#L14-L17 + + } +}; export default extension; ``` @@ -132,7 +158,7 @@ A `JupyterFrontEndPlugin` contains a few attributes: function (`() => {}` notation) that takes one argument `app` of type `JupyterFrontEnd` and will be called by the main application to activate the extension. -`app` is simply the main JupyterLab application. The `activate` function acts as an entry +`app` is the main JupyterLab application. The `activate` function acts as an entry point into the extension. In this example, it calls the `console.log` function to output something into the browser developer tools console. @@ -145,31 +171,34 @@ Now that the extension code is ready, you need to install it within JupyterLab. These are the instructions on how your extension can be installed for development: -> The `jlpm` command is JupyterLab's pinned version of -> [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use -> `yarn` or `npm` in lieu of `jlpm` below. +> You will need NodeJS to build the extension package. ```bash -# Clone the repo to your local environment -# Move to hello-world directory -# Install dependencies -jlpm -# Build Typescript source -jlpm build -# Install your development version of the extension with JupyterLab -jupyter labextension install . +# Install package in development mode +pip install -e . +# Link your development version of the extension with JupyterLab +jupyter labextension develop . --overwrite +# Rebuild extension Typescript source after making changes +jlpm run build ``` -The first command installs the dependencies that are specified in -`package.json`. Among the dependencies are also all of the `JupyterLab` -components that you want to use in your project. +> The `jlpm` command is JupyterLab's pinned version of +> [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use +> `yarn` or `npm` in lieu of `jlpm` below. + +The first command installs the dependencies that are specified in the +`setup.py` file and in `package.json`. Among the dependencies are also all of the `JupyterLab` components that you want to use in your project. -The second step runs the build script. In this step, the TypeScript code gets +It then runs the build script. In that step, the TypeScript code gets converted to javascript using the compiler `tsc` and stored in a `lib` -directory. Finally, the module is installed into JupyterLab. +directory. And a condensed form of the Javascript is copied in the Python +package (in the folder `hello_world/labextension`). This is the code that +would be installed by the user in JupyterLab. + +The second command create a symbolic link to the folder `hello_world/labextension` so that extension is installed in development mode in JupyterLab. -> There is also a `jupyter labextension link ` command. It can be used to -> install packages but not consider them as extensions (more information in [that discussion](https://discourse.jupyter.org/t/about-jupyter-labextension-link-v-s-install)) +The third command allows you to update the Javascript code each time you modify your +extension code. After all of these steps are done, running `jupyter labextension list` should show something like: @@ -225,8 +254,8 @@ structure makes JupyterLab very adaptable. An overview of the classes and their attributes and methods can be found in the JupyterLab documentation. The `@jupyterlab/application` module documentation is -[here](https://jupyterlab.github.io/jupyterlab/application/index.html) -and here is the [JupyterFrontEnd class documentation](https://jupyterlab.github.io/jupyterlab/application/classes/jupyterfrontend.html). +[here](https://jupyterlab.github.io/jupyterlab/modules/_application_src_index_.html) +and here is the [JupyterFrontEnd class documentation](https://jupyterlab.github.io/jupyterlab/classes/_application_src_index_.jupyterfrontend.html). ## Where to Go Next diff --git a/basics/hello-world/package.json b/basics/hello-world/package.json index 73200c65..7a77b038 100644 --- a/basics/hello-world/package.json +++ b/basics/hello-world/package.json @@ -44,7 +44,7 @@ "watch:src": "tsc -w" }, "dependencies": { - "@jupyterlab/application": "^3.0.0-rc.15" + "@jupyterlab/application": "^3.0.0" }, "devDependencies": { "@jupyterlab/builder": "^3.0.0-rc.15", diff --git a/basics/signals/README.md b/basics/signals/README.md index 5b216e25..6f9f32fb 100644 --- a/basics/signals/README.md +++ b/basics/signals/README.md @@ -17,7 +17,7 @@ In this extension, a simple button will be added to print something to the conso JupyterLab's Lumino engine uses the `ISignal` interface and the `Signal` class that implements this interface for communication -(read more on the [documentation](https://jupyterlab.github.io/lumino/api/signaling/globals.html) page). +(read more on the [documentation](https://jupyterlab.github.io/lumino/signaling/index.html) page). The basic concept is as follows: @@ -62,7 +62,7 @@ Let's look at the implementations details. ## A simple React Button Start with a file called `src/button.tsx`. The `tsx` extension allows to use -HTML-like syntax with the tag notation `<>`to represent some visual elements +HTML-like syntax with the tag notation `<>` to represent some visual elements (note that you have to add a line: `"jsx": "react",` to the `tsconfig.json` file). This is a special syntax used by [React](https://reactjs.org/tutorial/tutorial.html). @@ -141,8 +141,9 @@ The `panel.ts` class defines an extension panel that displays the This is done in the constructor. ```ts -// src/panel.ts#L19-L30 +// src/panel.ts#L18-L30 +constructor(translator?: ITranslator) { super(); this._translator = translator || nullTranslator; this._trans = this._translator.load('jupyterlab'); @@ -188,13 +189,16 @@ In our case, that function writes `Button has been clicked ... times.` text to the browser console and in an alert when the big red button is clicked. ```ts -// src/panel.ts#L32-L36 +// src/panel.ts#L32-L39 private _logMessage(emitter: ButtonWidget, count: ICount): void { console.log('Hey, a Signal has been received from', emitter); console.log( `The big red button has been clicked ${count.clickCount} times.` ); + window.alert( + `The big red button has been clicked ${count.clickCount} times.` + ); ``` There it is. Signaling is conceptually important for building extensions. diff --git a/command-palette/README.md b/command-palette/README.md index c0f8e9bb..ae091337 100644 --- a/command-palette/README.md +++ b/command-palette/README.md @@ -23,7 +23,7 @@ import { ICommandPalette } from '@jupyterlab/apputils'; To see how you can add the command to the palette, let's have a look at `src/index.ts`. ```ts -// src/index.ts#L11-L37 +// src/index.ts#L11-L34 const extension: JupyterFrontEndPlugin = { id: 'command-palette', @@ -42,9 +42,6 @@ const extension: JupyterFrontEndPlugin = { console.log( `jlab-examples:command-palette has been called ${args['origin']}.` ); - // window.alert( - // `jlab-examples:command-palette has been called ${args['origin']}.` - // ); } }); @@ -55,7 +52,7 @@ const extension: JupyterFrontEndPlugin = { ``` The `ICommandPalette` -([documentation](https://JupyterLab.github.io/JupyterLab/interfaces/_apputils_src_commandpalette_.icommandpalette.html)) +([documentation](https://jupyterlab.github.io/jupyterlab/interfaces/_apputils_src_index_.icommandpalette.html)) is passed to the `activate` function as an argument (variable `palette`) in addition to the JupyterLab application (variable `app`). @@ -75,5 +72,5 @@ appear in the web browser console after clicking on the command in the palette. A command can be triggered by other UI elements: -- Add the command to a [menu](../../main-menu/README.md) -- Add the command to the [launcher](../../launcher/README.md) +- Add the command to a [menu](../main-menu/README.md) +- Add the command to the [launcher](../launcher/README.md) diff --git a/command-palette/src/index.ts b/command-palette/src/index.ts index 16700f29..2dfdd692 100644 --- a/command-palette/src/index.ts +++ b/command-palette/src/index.ts @@ -25,9 +25,6 @@ const extension: JupyterFrontEndPlugin = { console.log( `jlab-examples:command-palette has been called ${args['origin']}.` ); - // window.alert( - // `jlab-examples:command-palette has been called ${args['origin']}.` - // ); } }); diff --git a/commands/README.md b/commands/README.md index 5baf50e2..8a95f30c 100644 --- a/commands/README.md +++ b/commands/README.md @@ -15,7 +15,7 @@ It is quite common for extension to define one or more such a command. In this extension, you are going to add a command to the application command registry. -The registry has `CommandRegistry` type ([documentation](https://jupyterlab.github.io/lumino/api/commands/classes/commandregistry.html)). +The registry has `CommandRegistry` type ([documentation](https://jupyterlab.github.io/lumino/commands/classes/commandregistry.html)). To see how you can access the application command registry, open the file `src/index.ts`. @@ -58,7 +58,7 @@ The CommandRegistry is an attribute of the main JupyterLab application adds your own function. That method takes two arguments: the unique command id -and [options](https://jupyterlab.github.io/lumino/api/commands/interfaces/commandregistry.icommandoptions.html) for the command. +and [options](https://jupyterlab.github.io/lumino/commands/interfaces/commandregistry.icommandoptions.html) for the command. The only mandatory option is `execute`, this takes the function to be called when the command is executed. It can optionally takes arguments (arbitrarily defined diff --git a/environment.yml b/environment.yml index b80d7ad8..282f4c73 100644 --- a/environment.yml +++ b/environment.yml @@ -1,8 +1,11 @@ name: jupyterlab-extension-examples channels: -- conda-forge + - conda-forge dependencies: -- jupyterlab=3 -- nodejs -- python=3 -- yarn + - jupyter-packaging>=0.7.9,<0.8.0 + - jupyterlab=3 + - nodejs + - pytest + - pytest-check-links + - python=3 + - yarn diff --git a/launcher/README.md b/launcher/README.md index c4922135..a114aa5c 100644 --- a/launcher/README.md +++ b/launcher/README.md @@ -10,7 +10,7 @@ dependencies to JupyterLab features. > Credit: This example is copied from Jeremy Tuloup [Python file extension](https://github.com/jtpio/jupyterlab-python-file). This example allows to create an empty Python file. To do so, -your extension will use two commands defined by the [documents manager](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.ts#L47-L75) of JupyterLab: +your extension will use two commands defined by the [documents manager](https://github.com/jupyterlab/jupyterlab/blob/master/packages/docmanager-extension/src/index.ts#L53-L86) of JupyterLab: - `'docmanager:new-untitled'`: Create new untitled document - `'docmanager:open'`: Open a document @@ -107,8 +107,8 @@ if (launcher) { This example uses a _command_. This is an essential concept of JupyterLab. To know more about it have a look at the [command example](../commands/README.md). -As seen in this example too, an user can execute a command from other UI elements than the launcher. To -know more about those other possible, you could look at the following examples: +An user can execute a command from other UI elements than the launcher. To +know more about those other possibilities, you could look at the following examples: - Add the command to the [command palette](../command-palette/README.md) - Add the command to a [menu](../main-menu/README.md) diff --git a/main-menu/README.md b/main-menu/README.md index 4d56f9c5..fb84f2e6 100644 --- a/main-menu/README.md +++ b/main-menu/README.md @@ -9,7 +9,7 @@ the notion of _Commands_ as explained in the [commands example](../commands/READ One of the possibilities offered to the user to trigger that command is to call it from a menu item. -Adding new menu item works in a similar way to the [command palette](../../command-palette/README.md). +Adding new menu item works in a similar way to the [command palette](../command-palette/README.md). The `IMainMenu` interface can be requested as a new argument to the `activate` function, but first it has to be imported. And the class `Menu` to create new menu needs also to be imported but from the Lumino library: diff --git a/settings/README.md b/settings/README.md index b6ebcea9..52a2bdbc 100644 --- a/settings/README.md +++ b/settings/README.md @@ -8,7 +8,7 @@ in a JupyterLab extension. ![settings example](preview.gif) The core token required for handling the settings is -`ISettingRegistry` ([documentation](https://jupyterlab.github.io/jupyterlab/settingregistry/modules/isettingregistry.html)). To use it, +`ISettingRegistry` ([documentation](https://jupyterlab.github.io/jupyterlab/modules/_settingregistry_src_index_.isettingregistry.html)). To use it, you first need to install its npm package: ```bash @@ -266,7 +266,7 @@ setting.changed.connect(loadSetting); ``` Finally, to demonstrate the programmatic change of a setting, a command to toggle -the `flag` and `limit` settings are updated. +the `flag` and increment the `limit` settings is implemented. ```ts diff --git a/state/README.md b/state/README.md index e185dcc1..579c3f65 100644 --- a/state/README.md +++ b/state/README.md @@ -8,7 +8,7 @@ in a JupyterLab extension. ![state example](preview.gif) The core token required for handling the state database (DB) is -`IStateDB` ([documentation](https://jupyterlab.github.io/jupyterlab/statedb/interfaces/istatedb.html)). +`IStateDB` ([documentation](https://jupyterlab.github.io/jupyterlab/interfaces/_statedb_src_index_.istatedb.html)). To use it, you first need to install its npm package: @@ -156,4 +156,4 @@ will need another core token `ISettingRegistry` (see [that example](../settings/ for more information). This example makes use of a dialog to quickly request information from the user. JupyterLab -comes with a couple of helpful dialogs; see the [documentation](https://jupyterlab.readthedocs.io/en/stable/developer/ui_helpers.html#dialogs). +comes with a couple of helpful dialogs; see the [documentation](https://jupyterlab.readthedocs.io/en/stable/extension/ui_helpers.html#dialogs). diff --git a/widget-tracker/widgets/README.md b/widget-tracker/widgets/README.md index 5b20fbca..92bd058d 100644 --- a/widget-tracker/widgets/README.md +++ b/widget-tracker/widgets/README.md @@ -4,7 +4,7 @@ In this example you will learn how to add a new tab to JupyterLab. -Visible elements such as tabs and notebooks are represented by widgets in the [Lumino](http://jupyterlab.github.io/lumino/api/widgets/globals.html) +Visible elements such as tabs and notebooks are represented by widgets in the [Lumino](https://jupyterlab.github.io/lumino/widgets/index.html) library that is the basis of the JupyterLab application. It is the fundamental brick of any visual component in the JupyterLab interface. @@ -28,7 +28,7 @@ jlpm add @lumino/widgets ``` A Widget can be added to the main area through the -[JupyterLab Shell](http://jupyterlab.github.io/jupyterlab/application/classes/labshell.html). +[JupyterLab Shell](https://jupyterlab.github.io/jupyterlab/classes/_application_src_index_.labshell.html). Inside of the `activate` function, you can obtain it through the `shell` attribute of the `app` object: