Skip to content

Commit 78fae38

Browse files
committed
feat: Order projects and notebooks alphabetically.
1 parent 4d09262 commit 78fae38

File tree

2 files changed

+205
-1
lines changed

2 files changed

+205
-1
lines changed

src/notebooks/deepnote/deepnoteTreeDataProvider.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,28 @@ export class DeepnoteTreeDataProvider implements TreeDataProvider<DeepnoteTreeIt
140140
}
141141
}
142142

143+
// Sort projects alphabetically by name (case-insensitive)
144+
deepnoteFiles.sort((a, b) => {
145+
const labelA = typeof a.label === 'string' ? a.label : '';
146+
const labelB = typeof b.label === 'string' ? b.label : '';
147+
return labelA.toLowerCase().localeCompare(labelB.toLowerCase());
148+
});
149+
143150
return deepnoteFiles;
144151
}
145152

146153
private async getNotebooksForProject(projectItem: DeepnoteTreeItem): Promise<DeepnoteTreeItem[]> {
147154
const project = projectItem.data as DeepnoteProject;
148155
const notebooks = project.project.notebooks || [];
149156

150-
return notebooks.map((notebook: DeepnoteNotebook) => {
157+
// Sort notebooks alphabetically by name (case-insensitive)
158+
const sortedNotebooks = [...notebooks].sort((a, b) => {
159+
const nameA = a.name || '';
160+
const nameB = b.name || '';
161+
return nameA.toLowerCase().localeCompare(nameB.toLowerCase());
162+
});
163+
164+
return sortedNotebooks.map((notebook: DeepnoteNotebook) => {
151165
const context: DeepnoteTreeItemContext = {
152166
filePath: projectItem.context.filePath,
153167
projectId: projectItem.context.projectId,

src/notebooks/deepnote/deepnoteTreeDataProvider.unit.test.ts

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,194 @@ suite('DeepnoteTreeDataProvider', () => {
309309
}
310310
});
311311
});
312+
313+
suite('alphabetical sorting', () => {
314+
test('should sort projects alphabetically by name', async () => {
315+
// Note: This test verifies the concept, but actual sorting happens in getDeepnoteProjectFiles()
316+
// which scans the workspace. For now, we verify the tree items can be sorted.
317+
const mockProjects: DeepnoteProject[] = [
318+
{
319+
metadata: {
320+
createdAt: '2023-01-01T00:00:00Z',
321+
modifiedAt: '2023-01-02T00:00:00Z'
322+
},
323+
project: {
324+
id: 'project-zebra',
325+
name: 'Zebra Project',
326+
notebooks: [],
327+
settings: {}
328+
},
329+
version: '1.0'
330+
},
331+
{
332+
metadata: {
333+
createdAt: '2023-01-01T00:00:00Z',
334+
modifiedAt: '2023-01-02T00:00:00Z'
335+
},
336+
project: {
337+
id: 'project-apple',
338+
name: 'Apple Project',
339+
notebooks: [],
340+
settings: {}
341+
},
342+
version: '1.0'
343+
},
344+
{
345+
metadata: {
346+
createdAt: '2023-01-01T00:00:00Z',
347+
modifiedAt: '2023-01-02T00:00:00Z'
348+
},
349+
project: {
350+
id: 'project-middle',
351+
name: 'Middle Project',
352+
notebooks: [],
353+
settings: {}
354+
},
355+
version: '1.0'
356+
}
357+
];
358+
359+
// Create tree items
360+
const treeItems = mockProjects.map(
361+
(project) =>
362+
new DeepnoteTreeItem(
363+
DeepnoteTreeItemType.ProjectFile,
364+
{
365+
filePath: `/workspace/${project.project.name}.deepnote`,
366+
projectId: project.project.id
367+
},
368+
project,
369+
0
370+
)
371+
);
372+
373+
// Before sorting
374+
assert.strictEqual(treeItems[0].label, 'Zebra Project');
375+
376+
// Verify that sorting would work
377+
const sortedItems = [...treeItems].sort((a, b) => {
378+
const labelA = typeof a.label === 'string' ? a.label : '';
379+
const labelB = typeof b.label === 'string' ? b.label : '';
380+
return labelA.toLowerCase().localeCompare(labelB.toLowerCase());
381+
});
382+
383+
assert.strictEqual(sortedItems[0].label, 'Apple Project');
384+
assert.strictEqual(sortedItems[1].label, 'Middle Project');
385+
assert.strictEqual(sortedItems[2].label, 'Zebra Project');
386+
});
387+
388+
test('should sort notebooks alphabetically by name within a project', async () => {
389+
// Create a project with unsorted notebooks
390+
const mockProjectWithNotebooks: DeepnoteProject = {
391+
metadata: {
392+
createdAt: '2023-01-01T00:00:00Z',
393+
modifiedAt: '2023-01-02T00:00:00Z'
394+
},
395+
project: {
396+
id: 'project-123',
397+
name: 'Test Project',
398+
notebooks: [
399+
{
400+
id: 'notebook-z',
401+
name: 'Zebra Notebook',
402+
blocks: [],
403+
executionMode: 'block',
404+
isModule: false
405+
},
406+
{
407+
id: 'notebook-a',
408+
name: 'Apple Notebook',
409+
blocks: [],
410+
executionMode: 'block',
411+
isModule: false
412+
},
413+
{
414+
id: 'notebook-m',
415+
name: 'Middle Notebook',
416+
blocks: [],
417+
executionMode: 'block',
418+
isModule: false
419+
}
420+
],
421+
settings: {}
422+
},
423+
version: '1.0'
424+
};
425+
426+
const mockProjectItem = new DeepnoteTreeItem(
427+
DeepnoteTreeItemType.ProjectFile,
428+
{
429+
filePath: '/workspace/project.deepnote',
430+
projectId: 'project-123'
431+
},
432+
mockProjectWithNotebooks,
433+
1
434+
);
435+
436+
const notebookItems = await provider.getChildren(mockProjectItem);
437+
438+
// Verify notebooks are sorted alphabetically
439+
assert.strictEqual(notebookItems.length, 3, 'Should have 3 notebooks');
440+
assert.strictEqual(notebookItems[0].label, 'Apple Notebook', 'First notebook should be Apple Notebook');
441+
assert.strictEqual(notebookItems[1].label, 'Middle Notebook', 'Second notebook should be Middle Notebook');
442+
assert.strictEqual(notebookItems[2].label, 'Zebra Notebook', 'Third notebook should be Zebra Notebook');
443+
});
444+
445+
test('should sort notebooks case-insensitively', async () => {
446+
// Create a project with notebooks having different cases
447+
const mockProjectWithNotebooks: DeepnoteProject = {
448+
metadata: {
449+
createdAt: '2023-01-01T00:00:00Z',
450+
modifiedAt: '2023-01-02T00:00:00Z'
451+
},
452+
project: {
453+
id: 'project-123',
454+
name: 'Test Project',
455+
notebooks: [
456+
{
457+
id: 'notebook-z',
458+
name: 'zebra notebook',
459+
blocks: [],
460+
executionMode: 'block',
461+
isModule: false
462+
},
463+
{
464+
id: 'notebook-a',
465+
name: 'Apple Notebook',
466+
blocks: [],
467+
executionMode: 'block',
468+
isModule: false
469+
},
470+
{
471+
id: 'notebook-m',
472+
name: 'MIDDLE Notebook',
473+
blocks: [],
474+
executionMode: 'block',
475+
isModule: false
476+
}
477+
],
478+
settings: {}
479+
},
480+
version: '1.0'
481+
};
482+
483+
const mockProjectItem = new DeepnoteTreeItem(
484+
DeepnoteTreeItemType.ProjectFile,
485+
{
486+
filePath: '/workspace/project.deepnote',
487+
projectId: 'project-123'
488+
},
489+
mockProjectWithNotebooks,
490+
1
491+
);
492+
493+
const notebookItems = await provider.getChildren(mockProjectItem);
494+
495+
// Verify case-insensitive sorting
496+
assert.strictEqual(notebookItems.length, 3, 'Should have 3 notebooks');
497+
assert.strictEqual(notebookItems[0].label, 'Apple Notebook', 'First should be Apple Notebook');
498+
assert.strictEqual(notebookItems[1].label, 'MIDDLE Notebook', 'Second should be MIDDLE Notebook');
499+
assert.strictEqual(notebookItems[2].label, 'zebra notebook', 'Third should be zebra notebook');
500+
});
501+
});
312502
});

0 commit comments

Comments
 (0)