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: 3 additions & 1 deletion frontend/src/__tests__/componentTests/DataLink.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ describe('Data Link dialog', () => {
});

await waitFor(() => {
expect(screen.getByText('my_zarr', { exact: false })).toBeInTheDocument();
expect(
screen.getByText('Are you sure you want to create a data link?')
).toBeInTheDocument();
});
});

Expand Down
53 changes: 44 additions & 9 deletions frontend/src/components/ui/Dialogs/DataLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

import { useState, useMemo, SetStateAction } from 'react';
import type { ReactNode, Dispatch } from 'react';
import { Button, Typography } from '@material-tailwind/react';
import { Accordion, Button, Typography } from '@material-tailwind/react';
import { HiChevronDown } from 'react-icons/hi';
import { Link } from 'react-router';

import type { ProxiedPath } from '@/contexts/ProxiedPathContext';
Expand All @@ -21,7 +22,9 @@ import type { FileSharePath } from '@/shared.types';
import type { PendingToolKey } from '@/hooks/useZarrMetadata';
import FgDialog from './FgDialog';
import TextWithFilePath from './TextWithFilePath';
import DataLinkOptions from '@/components/ui/PreferencesPage/DataLinkOptions';
import DataLinkOptions, {
SubpathModeOptions
} from '@/components/ui/PreferencesPage/DataLinkOptions';
import DeleteBtn from '@/components/ui/buttons/DeleteBtn';

interface CommonDataLinkDialogProps {
Expand Down Expand Up @@ -152,6 +155,9 @@ export default function DataLinkDialog(props: DataLinkDialogProps) {

// Custom subpath local state (only used in this dialog, not persisted)
const [customSubpath, setCustomSubpath] = useState(folderNameOnly);
const [openAdvancedSections, setOpenAdvancedSections] = useState<string[]>(
[]
);

const customSubpathError = useMemo(
() =>
Expand Down Expand Up @@ -254,6 +260,12 @@ export default function DataLinkDialog(props: DataLinkDialogProps) {
If you share the data link with internal collaborators, they will
be able to view these data.
</Typography>
<div className="flex flex-col gap-2">
<Typography className="font-semibold text-foreground">
Don't ask me this again:
</Typography>
<DataLinkOptions checkboxesOnly hideSubpathMode />
</div>
<BtnContainer>
<CreateLinkBtn
disabled={!!customSubpathError}
Expand All @@ -262,10 +274,36 @@ export default function DataLinkDialog(props: DataLinkDialogProps) {
<CancelBtn onCancel={props.onCancel} />
</BtnContainer>
<div className="flex flex-col gap-2 mt-4">
<Typography className="font-semibold text-foreground">
Data link settings:
</Typography>
<DataLinkOptions checkboxesOnly />
<Accordion
onValueChange={
setOpenAdvancedSections as Dispatch<
SetStateAction<string | string[]>
>
}
type="multiple"
value={openAdvancedSections}
>
<Accordion.Item value="advanced">
<Accordion.Trigger className="flex w-full items-center justify-between py-2">
<div className="text-foreground font-semibold text-sm">
Advanced settings
</div>
<HiChevronDown
className={`h-4 w-4 text-foreground transition-transform ${
openAdvancedSections.includes('advanced')
? 'rotate-180'
: ''
}`}
/>
</Accordion.Trigger>
<Accordion.Content className="pt-2 pb-2 flex flex-col gap-2">
<SubpathModeOptions />
<Typography className="text-foreground text-sm font-mono break-all bg-surface/30 p-2 rounded">
{dataLinkPreview}
</Typography>
</Accordion.Content>
</Accordion.Item>
</Accordion>
{dataLinkSubpathMode === 'custom' ? (
<>
<input
Expand All @@ -281,9 +319,6 @@ export default function DataLinkDialog(props: DataLinkDialogProps) {
) : null}
</>
) : null}
<Typography className="text-foreground text-sm font-mono break-all bg-surface/30 p-2 rounded">
{dataLinkPreview}
</Typography>
<Typography className="text-xs text-foreground">
You can always modify settings on the{' '}
<Link className="text-primary underline" to="/preferences">
Expand Down
101 changes: 56 additions & 45 deletions frontend/src/components/ui/PreferencesPage/DataLinkOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import OptionsSection from '@/components/ui/PreferencesPage/OptionsSection';

type DataLinkOptionsProps = {
readonly checkboxesOnly?: boolean;
readonly hideSubpathMode?: boolean;
};

const SUBPATH_MODES = [
Expand All @@ -14,15 +15,60 @@ const SUBPATH_MODES = [
{ value: 'custom' as const, label: 'Custom' }
];

export function SubpathModeOptions({
indented = false
}: {
readonly indented?: boolean;
}) {
const { dataLinkSubpathMode, setDataLinkSubpathMode } =
usePreferencesContext();

const handleSubpathModeChange = async (
mode: 'name' | 'full_path' | 'custom'
) => {
const result = await setDataLinkSubpathMode(mode);
if (result.success) {
const label = SUBPATH_MODES.find(m => m.value === mode)?.label;
toast.success(`Link name format set to: ${label}`);
} else {
toast.error(result.error);
}
};

return (
<div className={indented ? 'pl-4' : ''}>
<Typography className={`text-foreground ${indented ? 'mt-2' : ''} mb-1`}>
Link name format:
</Typography>
{SUBPATH_MODES.map(mode => (
<div className="flex items-center gap-2" key={mode.value}>
<input
checked={dataLinkSubpathMode === mode.value}
className="icon-small accent-secondary-light dark:accent-secondary"
id={`subpath_mode_${mode.value}`}
name="subpath_mode"
onChange={() => handleSubpathModeChange(mode.value)}
type="radio"
/>
<Typography
as="label"
className="text-foreground"
htmlFor={`subpath_mode_${mode.value}`}
>
{mode.label}
</Typography>
</div>
))}
</div>
);
}

export default function DataLinkOptions({
checkboxesOnly = false
checkboxesOnly = false,
hideSubpathMode = false
}: DataLinkOptionsProps) {
const {
areDataLinksAutomatic,
toggleAutomaticDataLinks,
dataLinkSubpathMode,
setDataLinkSubpathMode
} = usePreferencesContext();
const { areDataLinksAutomatic, toggleAutomaticDataLinks } =
usePreferencesContext();

const automaticOption = {
checked: areDataLinksAutomatic,
Expand All @@ -42,51 +88,16 @@ export default function DataLinkOptions({
}
};

const handleSubpathModeChange = async (
mode: 'name' | 'full_path' | 'custom'
) => {
const result = await setDataLinkSubpathMode(mode);
if (result.success) {
const label = SUBPATH_MODES.find(m => m.value === mode)?.label;
toast.success(`Link name format set to: ${label}`);
} else {
toast.error(result.error);
}
};

return (
<>
<OptionsSection
checkboxesOnly={checkboxesOnly}
header="Data Links"
options={[automaticOption]}
/>
<div className={checkboxesOnly ? '' : 'pl-4'}>
<Typography
className={`text-foreground ${checkboxesOnly ? '' : 'mt-2'} mb-1`}
>
Link name format:
</Typography>
{SUBPATH_MODES.map(mode => (
<div className="flex items-center gap-2" key={mode.value}>
<input
checked={dataLinkSubpathMode === mode.value}
className="icon-small accent-secondary-light dark:accent-secondary"
id={`subpath_mode_${mode.value}`}
name="subpath_mode"
onChange={() => handleSubpathModeChange(mode.value)}
type="radio"
/>
<Typography
as="label"
className="text-foreground"
htmlFor={`subpath_mode_${mode.value}`}
>
{mode.label}
</Typography>
</div>
))}
</div>
{hideSubpathMode ? null : (
<SubpathModeOptions indented={!checkboxesOnly} />
)}
</>
);
}
Loading