Repository: jupyterlab/retrolab (language: python)

Issue:
Don't display .ipynb in notebook title
### Problem

Notebook files always have the .ipynb extension, so we should not display them. Classic notebook does not.

Classic Notebook:

<img width="270" alt="image" src="https://user-images.githubusercontent.com/30430/121272369-69050480-c8e3-11eb-90d6-1da190547b11.png">

Retrolab:

<img width="381" alt="image" src="https://user-images.githubusercontent.com/30430/121272385-76ba8a00-c8e3-11eb-9987-afec4569e44c.png">


### Proposed Solution

Trip `.ipynb` when displaying name of notebook



Discussion / hints:
Thanks @yuvipanda for the suggestion.

> Notebook files always have the .ipynb extension, so we should not display them. Classic notebook does not.

I think it would be fine to indeed not display the extension. And it saves some space in the top area.

One thing to keep in mind too is that RetroLab doesn't need or claim to be a strict copy of the classic notebook UI, so some differences such as this one would be fine.
> One thing to keep in mind too is that RetroLab doesn't need or claim to be a strict copy of the classic notebook UI, so some differences such as this one would be fine.

oh yeah, absolutely. Just looking for inspiration :)
Based on the discussion from other issues (for example https://github.com/jupyterlab/retrolab/issues/259) and the notebook discussion, maybe we'll want to do this to match the classic UI.

The plugin handling the title is defined here:

https://github.com/jupyterlab/retrolab/blob/45f879dc7fbedfda2ee640afe690965bdb14a5eb/packages/application-extension/src/index.ts#L381-L447

It is also activated and used for text files. Classic shows the extension for other files than notebooks:

![image](https://user-images.githubusercontent.com/591645/139862471-25eca29f-40ef-42bc-a1a0-4ee40c28c6d8.png)

So we probably need to special case `.ipynb` in the plugin, and also make sure renaming is still supported.

Marking as good first issue if someone would like to take a look, thanks!

Fix patch:
diff --git a/packages/application-extension/src/index.ts b/packages/application-extension/src/index.ts
index d85a7911..8c439e7c 100644
--- a/packages/application-extension/src/index.ts
+++ b/packages/application-extension/src/index.ts
@@ -55,6 +55,11 @@ const EDITOR_FACTORY = 'Editor';
  */
 const TREE_PATTERN = new RegExp('/(notebooks|edit)/(.*)');
 
+/**
+ * A regular expression to suppress the file extension from display for .ipynb files.
+ */
+const STRIP_IPYNB = /\.ipynb$/;
+
 /**
  * The command IDs used by the application plugin.
  */
@@ -383,7 +388,8 @@ const tabTitle: JupyterFrontEndPlugin<void> = {
           const title =
             current.sessionContext.path || current.sessionContext.name;
           const basename = PathExt.basename(title);
-          document.title = basename;
+          // Strip the ".ipynb" suffix from filenames for display in tab titles.
+          document.title = basename.replace(STRIP_IPYNB, '');
         };
         current.sessionContext.sessionChanged.connect(update);
         update();
@@ -391,7 +397,7 @@ const tabTitle: JupyterFrontEndPlugin<void> = {
       } else if (current instanceof DocumentWidget) {
         const update = () => {
           const basename = PathExt.basename(current.context.path);
-          document.title = basename;
+          document.title = basename.replace(STRIP_IPYNB, '');
         };
         current.context.pathChanged.connect(update);
         update();
@@ -435,7 +441,7 @@ const title: JupyterFrontEndPlugin<void> = {
       }
 
       const h = document.createElement('h1');
-      h.textContent = current.title.label;
+      h.textContent = current.title.label.replace(STRIP_IPYNB, '');
       widget.node.appendChild(h);
       widget.node.style.marginLeft = '10px';
       if (!docManager) {
@@ -468,7 +474,8 @@ const title: JupyterFrontEndPlugin<void> = {
 
           const newPath = current.context.path ?? result.path;
           const basename = PathExt.basename(newPath);
-          h.textContent = basename;
+
+          h.textContent = basename.replace(STRIP_IPYNB, '');
           if (!router) {
             return;
           }


Test patch:
diff --git a/ui-tests/test/notebook.spec.ts b/ui-tests/test/notebook.spec.ts
index 7028b72f..f99e4edc 100644
--- a/ui-tests/test/notebook.spec.ts
+++ b/ui-tests/test/notebook.spec.ts
@@ -34,11 +34,12 @@ test.describe('Notebook', () => {
     const notebook = `${tmpPath}/${NOTEBOOK}`;
     await page.goto(`notebooks/${notebook}`);
 
-    // Click on the title
-    await page.click('text="example.ipynb"');
+    // Click on the title (with .ipynb extension stripped)
+    await page.click('text="example"');
 
     // Rename in the input dialog
     const newName = 'test.ipynb';
+    const newNameStripped = 'test';
     await page.fill(
       `//div[normalize-space(.)='File Path${notebook}New Name']/input`,
       newName
@@ -52,6 +53,6 @@ test.describe('Notebook', () => {
 
     // Check the URL contains the new name
     const url = page.url();
-    expect(url).toContain(newName);
+    expect(url).toContain(newNameStripped);
   });
 });