Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,99 +1,110 @@
import { LitElement, css, html } from "lit";

import { Loader } from "./Loader";
import { FullScreenToggle } from "./FullScreenToggle";
import { baseUrl } from "../server/globals";

export function getURLFromFilePath(file, projectName) {
const regexp = new RegExp(`.+(${projectName}.+)`);
return `${baseUrl}/preview/${file.match(regexp)[1]}`;
}

export class Neurosift extends LitElement {
static get styles() {
return css`
:host {
background: white;
width: 100%;
height: 100%;
display: grid;
grid-template-rows: 100%;
grid-template-columns: 100%;
position: relative;
--loader-color: hsl(200, 80%, 50%);
}

iframe,
.loader-container {
width: 100%;
height: 100%;
}

.loader-container {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
}

.fullscreen-toggle {
display: flex;
position: absolute;
top: 10px;
right: 10px;
padding: 10px;
color: white;
background-color: gainsboro;
border: 1px solid gray;
border-radius: 10px;
cursor: pointer;
}

span {
font-size: 14px;
}

small {
padding-left: 10px;
}

iframe {
border: 0;
}
`;
}

static get properties() {
return {
url: { type: String, reflect: true },
};
}

constructor({ url, fullscreen = true } = {}) {
super();
this.url = url;
this.fullscreen = fullscreen;
}

render() {
return this.url
? html` <div class="loader-container">
${new Loader({
message: `Loading Neurosift view...<br/><small>${this.url}</small>`,
})}
</div>
${this.fullscreen ? new FullScreenToggle({ target: this }) : ""}
<iframe
src="https://neurosift.app/?p=/nwb&url=${this.url}"
@load=${function () {
const loader = this.shadowRoot.querySelector(".loader-container");
if (loader) loader.remove();
}}
></iframe>`
: ``;
}
}

customElements.get("neurosift-iframe") || customElements.define("neurosift-iframe", Neurosift);
import { LitElement, css, html, CSSResult, TemplateResult } from "lit";
import { Loader } from "./Loader";
import { FullScreenToggle } from "./FullScreenToggle";
import { baseUrl } from "../server/globals";

export function getURLFromFilePath(file: string, projectName: string): string {
const regexp = new RegExp(`.+(${projectName}.+)`);
const match = file.match(regexp);
if (!match) throw new Error(`File path ${file} does not contain project name ${projectName}`);
return `${baseUrl}/preview/${match[1]}`;
}

export class Neurosift extends LitElement {
static get styles(): CSSResult {
return css`
:host {
background: white;
width: 100%;
height: 100%;
display: grid;
grid-template-rows: 100%;
grid-template-columns: 100%;
position: relative;
--loader-color: hsl(200, 80%, 50%);
}

iframe,
.loader-container {
width: 100%;
height: 100%;
}

.loader-container {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
top: 0;
left: 0;
}

.fullscreen-toggle {
display: flex;
position: absolute;
top: 10px;
right: 10px;
padding: 10px;
color: white;
background-color: gainsboro;
border: 1px solid gray;
border-radius: 10px;
cursor: pointer;
}

span {
font-size: 14px;
}

small {
padding-left: 10px;
}

iframe {
border: 0;
}
`;
}

static get properties() {
return {
url: { type: String, reflect: true },
fullscreen: { type: Boolean }
};
}

declare url?: string;
declare fullscreen: boolean;

constructor(options: { url?: string; fullscreen?: boolean } = {}) {
super();
this.url = options.url;
this.fullscreen = options.fullscreen ?? true;
}

render(): TemplateResult | string {
// Clear neurosift cross-session storage database
// see https://github.com/NeurodataWithoutBorders/nwb-guide/issues/974
if (this.url) {
indexedDB.deleteDatabase("neurosift-hdf5-cache");
}

return this.url
? html` <div class="loader-container">
${new Loader({
message: `Loading Neurosift view...<br/><small>${this.url}</small>`,
})}
</div>
${this.fullscreen ? new FullScreenToggle({ target: this }) : ""}
<iframe
src="https://neurosift.app/?p=/nwb&url=${this.url}"
@load=${function(this: Neurosift) {
const loader = this.shadowRoot?.querySelector(".loader-container");
if (loader) loader.remove();
}}
></iframe>`
: "";
}
}

customElements.get("neurosift-iframe") || customElements.define("neurosift-iframe", Neurosift);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { html } from "lit";
import { Page } from "../Page.js";
import { onThrow } from "../../../errors";
import { JSONSchemaInput } from "../../JSONSchemaInput.js";
import { Neurosift } from "../../Neurosift.js";
import { Neurosift } from "../../Neurosift";
import { baseUrl } from "../../../server/globals";

export class PreviewPage extends Page {
Expand Down
74 changes: 74 additions & 0 deletions tests/components/Neurosift.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Test that the indexed database is not cleared on initial render and cleared
// when a url is provided and the component is rendered again as in PreviewPage.js
import { describe, test, expect, beforeEach, afterEach, vi } from 'vitest';
import { Neurosift } from '../../src/electron/frontend/core/components/Neurosift';

describe('Neurosift', () => {
let originalIndexedDB: IDBFactory;
let mockDeleteDatabase: ReturnType<typeof vi.fn>;

beforeEach(() => {
// Mock indexedDB.deleteDatabase so that we can test if it is called
mockDeleteDatabase = vi.fn();
originalIndexedDB = window.indexedDB;
window.indexedDB = {
deleteDatabase: mockDeleteDatabase,
open: vi.fn(),
databases: vi.fn(),
cmp: vi.fn()
} as unknown as IDBFactory;
});

afterEach(() => {
// Restore original indexedDB
window.indexedDB = originalIndexedDB;
});

test('if neurosift storage is not cleared on initial render', async () => {
const neurosift = new Neurosift();

// Add the component to the DOM
document.body.appendChild(neurosift);

// Wait for the component to render
neurosift.requestUpdate();

// Verify deleteDatabase was not called
expect(mockDeleteDatabase).not.toHaveBeenCalled();

// Remove the component from the DOM
document.body.removeChild(neurosift);
});

test('if neurosift storage is cleared when url is provided and component is rendered again', async () => {
const neurosift = new Neurosift();

// Add the component to the DOM
document.body.appendChild(neurosift);

// Wait for the component to render
neurosift.requestUpdate();

// Set the URL to a test URL
neurosift.url = 'http://test.url/files/test.nwb';

// Wait for the component to render again
neurosift.requestUpdate();
await neurosift.updateComplete;

// Verify deleteDatabase was called
expect(mockDeleteDatabase).toHaveBeenCalledWith('neurosift-hdf5-cache');
expect(mockDeleteDatabase).toHaveBeenCalledTimes(1);

// Wait for the component to render again
neurosift.requestUpdate();
await neurosift.updateComplete;

// Verify deleteDatabase was called
expect(mockDeleteDatabase).toHaveBeenCalledWith('neurosift-hdf5-cache');
expect(mockDeleteDatabase).toHaveBeenCalledTimes(2);

// Remove the component from the DOM
document.body.removeChild(neurosift);
});
});
Loading