Skip to content
Open
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
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "fileglancer",
"type": "module",
"version": "3.3.0",
"version": "3.4.0-a0",
"description": "Browse, share, and publish files on the Janelia file system",
"keywords": [
"ngff",
Expand Down
25 changes: 23 additions & 2 deletions frontend/src/__tests__/componentTests/NeuroglancerView.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,18 @@ vi.mock('@/components/ui/Views/ViewBrokenBanner', () => ({
vi.mock('@/components/ui/Views/ViewerSidebar', () => ({
default: ({
bridge,
editState
editState,
tab
}: {
tab: string;
bridge: { setState: (s: Record<string, unknown>) => void };
editState: {
markEdited: () => void;
addSources: (keys: string[]) => void;
removeSource: (key: string) => void;
};
}) => (
<aside aria-label="View data sidebar">
<aside aria-label="View data sidebar" data-tab={tab}>
<button onClick={() => editState.addSources(['plainKey'])} type="button">
fake add unsupported
</button>
Expand Down Expand Up @@ -650,6 +652,25 @@ describe('NeuroglancerView', () => {
expect(screen.queryByRole('complementary')).toBeNull();
});

it('opens the sidebar to the Sources tab from the info button', async () => {
useViewsQuery.mockReturnValue({ data: [OWNED] });
renderViewer();
const sources = screen.getByRole('button', {
name: 'View and edit data sources'
});
await userEvent.click(sources);
expect(
screen.getByRole('complementary', { name: 'View data sidebar' })
).toHaveAttribute('data-tab', 'sources');
expect(sources).toHaveAttribute('aria-pressed', 'true');
expect(screen.getByRole('button', { name: 'Add data' })).toHaveAttribute(
'aria-pressed',
'false'
);
await userEvent.click(sources);
expect(screen.queryByRole('complementary')).toBeNull();
});

it('toggling the sidebar keeps the same iframe', async () => {
useViewsQuery.mockReturnValue({ data: [OWNED] });
renderViewer();
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/__tests__/componentTests/ViewerSidebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ function setup() {
bridge={fake.bridge}
editState={editState}
onClose={vi.fn()}
onTabChange={vi.fn()}
tab="sources"
viewLayers={VIEW_LAYERS}
/>
);
Expand Down
25 changes: 20 additions & 5 deletions frontend/src/components/NeuroglancerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
HiOutlineDownload,
HiOutlineExternalLink,
HiOutlineArrowsExpand,
HiOutlineInformationCircle,
HiOutlinePlusCircle
} from 'react-icons/hi';

Expand Down Expand Up @@ -37,6 +38,7 @@ import type { RelinkTarget } from '@/components/ui/Dialogs/RelinkDialog';
import ViewBrokenBanner from '@/components/ui/Views/ViewBrokenBanner';
import ViewSaveControls from '@/components/ui/Views/ViewSaveControls';
import ViewerSidebar from '@/components/ui/Views/ViewerSidebar';
import type { SidebarTab } from '@/components/ui/Views/ViewerSidebar';

type ToolbarIconButtonProps = {
readonly label: string;
Expand Down Expand Up @@ -109,7 +111,10 @@ export default function NeuroglancerView() {
// Wait out the mount refetch: a cached state can predate a Save made just
// before leaving this page.
const [iframeSrc, setIframeSrc] = useState<string | null>(null);
const [sidebarOpen, setSidebarOpen] = useState(false);
// null when the sidebar is closed.
const [sidebarTab, setSidebarTab] = useState<SidebarTab | null>(null);
const toggleSidebar = (tab: SidebarTab) =>
setSidebarTab(current => (current === tab ? null : tab));
// While resizing, the overlay covers the iframe: over the iframe, the
// pointer's moves go to Neuroglancer and the drag stalls.
const [resizing, setResizing] = useState(false);
Expand Down Expand Up @@ -335,8 +340,16 @@ export default function NeuroglancerView() {
<ToolbarIconButton
icon={HiOutlinePlusCircle}
label="Add data"
onClick={() => setSidebarOpen(open => !open)}
pressed={sidebarOpen}
onClick={() => toggleSidebar('add')}
pressed={sidebarTab === 'add'}
/>
) : null}
{canAddData ? (
<ToolbarIconButton
icon={HiOutlineInformationCircle}
label="View and edit data sources"
onClick={() => toggleSidebar('sources')}
pressed={sidebarTab === 'sources'}
/>
) : null}
<ToolbarIconButton
Expand Down Expand Up @@ -396,7 +409,7 @@ export default function NeuroglancerView() {
src={iframeSrc ?? externalUrl}
title="Neuroglancer viewer"
/>
{canAddData && sidebarOpen ? (
{canAddData && sidebarTab ? (
<div
className={`absolute inset-0 ${resizing ? 'cursor-col-resize' : 'pointer-events-none'}`}
>
Expand All @@ -421,7 +434,9 @@ export default function NeuroglancerView() {
<ViewerSidebar
bridge={bridge}
editState={editState}
onClose={() => setSidebarOpen(false)}
onClose={() => setSidebarTab(null)}
onTabChange={setSidebarTab}
tab={sidebarTab}
viewLayers={ownedView?.layers ?? []}
/>
</Panel>
Expand Down
15 changes: 12 additions & 3 deletions frontend/src/components/ui/Views/ViewerSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ import type { ViewEditState } from '@/hooks/useViewEditState';
import type { ViewLayer } from '@/queries/viewQueries';
import type { NgState } from '@/utils/viewCheckout';

export type SidebarTab = 'add' | 'sources';

type ViewerSidebarProps = {
readonly bridge: NeuroglancerBridge;
/** The View's saved layer rows. */
readonly viewLayers: ViewLayer[];
readonly editState: ViewEditState;
readonly onClose: () => void;
readonly tab: SidebarTab;
readonly onTabChange: (tab: SidebarTab) => void;
};

/**
Expand All @@ -31,7 +35,9 @@ export default function ViewerSidebar({
bridge,
viewLayers,
editState,
onClose
onClose,
tab,
onTabChange
}: ViewerSidebarProps) {
const { markEdited, addSources, recordAdded, removeSource } = editState;
const addToView = useAddToView(bridge, {
Expand All @@ -40,7 +46,6 @@ export default function ViewerSidebar({
onAddSources: addSources,
onAdded: recordAdded
});
const [tab, setTab] = useState('add');
const sources = useViewSources(bridge, {
viewLayers,
added: editState.added,
Expand Down Expand Up @@ -139,7 +144,11 @@ export default function ViewerSidebar({
{/* Content-width triggers, as in the Properties panel: MT measures
the indicator only on window resize, so triggers that stretch
with the panel, or wrap when it narrows, would leave it behind. */}
<Tabs className="min-w-0 flex-1" onValueChange={setTab} value={tab}>
<Tabs
className="min-w-0 flex-1"
onValueChange={value => onTabChange(value as SidebarTab)}
value={tab}
>
<Tabs.List className="justify-start items-stretch shrink-0 min-w-fit w-full py-2 bg-surface dark:bg-surface-light">
<Tabs.Trigger
className="!text-foreground h-full whitespace-nowrap"
Expand Down
Loading