Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ALS-6928] Add details drawer to dashboard #302

Merged
merged 20 commits into from
Nov 20, 2024
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ VITE_USE_QUERY_TEMPLATE=true
VITE_API=true
VITE_DISCOVER=true
VITE_DASHBOARD=true
VITE_DASHBOARD_DRAWER=true
VITE_ENABLE_SAMPLE_ID_CHECKBOX=true

# VITE_AUTH_PROVIDER_MODULE is the prefix for any authorization providers you want to use.
Expand Down
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ VITE_DISCOVER=true
VITE_DASHBOARD=true
VITE_ENABLE_SNP_QUERY=true
VITE_ENABLE_GENE_QUERY=true
VITE_DASHBOARD_DRAWER=true
VITE_ENABLE_SAMPLE_ID_CHECKBOX=true

# VITE_AUTH_PROVIDER_MODULE is the prefix for any authorization providers you want to use.
Expand Down
76 changes: 76 additions & 0 deletions src/lib/components/datatable/DashboardDrawer.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script lang="ts">
import { getDrawerStore } from '@skeletonlabs/skeleton';
import { getDatasetDetails } from '$lib/services/dictionary';
import type { DashboardRow } from '$lib/stores/Dashboard';
import { ProgressRadial } from '@skeletonlabs/skeleton';
import ErrorAlert from '$lib/components/ErrorAlert.svelte';
const drawerStore = getDrawerStore();

const datasetId = (($drawerStore.meta.row as DashboardRow)?.dataset_id as string) || '';
const title = (($drawerStore.meta.row as DashboardRow)?.name as string) || '';
const link = (($drawerStore.meta.row as DashboardRow)?.additional_info_link as string) || '';

async function getDataset() {
const details = await getDatasetDetails(datasetId);
if (!details || Object.keys(details).length === 0) throw new Error('No details found');
if (details.datasetId) {
delete details.datasetId;
}
if (details.studyFullname) {
delete details.studyFullname;
}
return details;
}
</script>

{#if title}
<h2 data-testid="drawer-title" class="text-2xl font-bold ml-4">{title}</h2>
{/if}
<hr class="m-4 border-t-2 border-gray-200" />
{#await getDataset()}
<div class="flex justify-center items-center h-full">
<ProgressRadial />
</div>
{:then details}
<ul data-testid="drawer-details" class="m-4 p-4">
{#each Object.entries(details) as [key, value]}
{#if value}
<li class="m-2">
<strong class="capitalize"
>{key
.replace(/([A-Z])/g, ' $1')
.toLowerCase()
.trim()}</strong
>:
{#if Array.isArray(value)}
<ul class="list-disc">
{#each value as item}
{#if item}
<li class="ml-8">{item}</li>
{/if}
{/each}
</ul>
{:else}
{value}
{/if}
</li>
{/if}
{/each}
</ul>
{#if link}
<div class="flex justify-center items-center mb-4">
<a
href={link || '#'}
on:click|stopPropagation
class="btn variant-ghost-primary hover:variant-filled-primary"
target="_blank">More Info</a
>
</div>
{/if}
{:catch}
<div class="flex justify-center items-center">
<ErrorAlert title="An Error Occured">
<p>We're having trouble fetching the dataset details right now. Please try again later.</p>
</ErrorAlert>
</div>
{/await}
10 changes: 8 additions & 2 deletions src/lib/components/datatable/DashboardLink.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
{#if consentGranted}
<i class="fa-regular fa-circle-check text-3xl text-success-500"></i>
{:else}
<a href={link} class="btn variant-ghost-primary hover:variant-filled-primary" target="_blank"
>More Info</a
<a
href={link || '#'}
title={link ? 'More Info' : 'Link not available'}
on:click|stopPropagation={(e) => !link && e.preventDefault()}
class="btn variant-ghost-primary hover:variant-filled-primary{!link
? ' opacity-50 cursor-not-allowed'
: ''}"
target="_blank">More Info</a
>
{/if}
12 changes: 10 additions & 2 deletions src/lib/components/datatable/RemoteTable.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
export let columns: Column[] = [];
export let cellOverides: Indexable = {};
export let rowClickHandler: (row: Indexable) => void = () => {};

export let isClickable: boolean = false;
let rows = handler.getRows();

onMount(() => {
Expand Down Expand Up @@ -100,7 +100,15 @@
</tr>
{:else if $rows.length > 0}
{#each $rows as row, i}
<ExpandableRow {tableName} {cellOverides} {columns} index={i} {row} {rowClickHandler} />
<ExpandableRow
{tableName}
{cellOverides}
{columns}
index={i}
{row}
{rowClickHandler}
{isClickable}
/>
{/each}
{:else}
<tr><td colspan={columns.length}>No entries found.</td></tr>
Expand Down
10 changes: 7 additions & 3 deletions src/lib/components/datatable/Row.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,23 @@
export let index: number = -2;
export let row: Indexable = {};
export let tableName: string = '';
export let isClickable: boolean = false;
export let rowClickHandler: (row: Indexable) => void = () => {};

function onClick(row: Indexable) {
setActiveRow({ row: row.conceptPath, table: tableName });
setActiveRow({ row: row.conceptPath || row.dataset_id, table: tableName });
rowClickHandler(row);
}
$: active = $activeTable === tableName && $activeRow === row?.conceptPath;
$: active =
$activeTable === tableName &&
($activeRow === row?.conceptPath || $activeRow === row.dataset_id);
</script>

<tr
id="row-{index.toString()}"
on:click|stopPropagation={() => onClick(row)}
class="cursor-pointer"
class={isClickable ? 'cursor-pointer' : ''}
tabindex={isClickable ? 0 : -1}
>
{#each columns as column, colIndex}
<td id="row-{index.toString()}-col-{colIndex.toString()}">
Expand Down
12 changes: 10 additions & 2 deletions src/lib/components/datatable/Table.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
export let stickyHeader = false;
export let showPagination = true;
export let rowClickHandler: (row: Indexable) => void = () => {};

export let isClickable: boolean = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export let data: any = []; //TODO: Fix this type

Expand Down Expand Up @@ -83,7 +83,15 @@
<tbody>
{#if $rows.length > 0}
{#each $rows as row, i}
<ExpandableRow {tableName} {cellOverides} {columns} index={i} {row} {rowClickHandler} />
<ExpandableRow
{tableName}
{cellOverides}
{columns}
index={i}
{row}
{rowClickHandler}
{isClickable}
/>
{/each}
{:else}
<tr><td colspan={columns.length}>No entries found.</td></tr>
Expand Down
1 change: 1 addition & 0 deletions src/lib/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ export const features: Indexable = {
distributionExplorer: import.meta.env?.VITE_DIST_EXPLORER === 'true',
},
dashboard: import.meta.env?.VITE_DASHBOARD === 'true',
dashboardDrawer: import.meta.env?.VITE_DASHBOARD_DRAWER === 'true',
confirmDownload: import.meta.env?.VITE_CONFIRM_DOWNLOAD === 'true',
};

Expand Down
5 changes: 5 additions & 0 deletions src/lib/services/dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { user } from '$lib/stores/User';
const dictionaryUrl = 'picsure/proxy/dictionary-api/';
const searchUrl = 'picsure/proxy/dictionary-api/concepts';
const conceptDetailUrl = 'picsure/proxy/dictionary-api/concepts/detail/';
const datasetDetailUrl = 'picsure/proxy/dictionary-api/dashboard-drawer/';

export type FacetSkeleton = {
[facetCategory: string]: string[];
Expand Down Expand Up @@ -146,3 +147,7 @@ export async function getStudiesCount(isOpenAccess = false) {
const facetsForUser = facetCat.facets.filter((facet) => facet.count > 0);
return facetsForUser.length;
}

export async function getDatasetDetails(datasetId: string) {
return api.get(`${datasetDetailUrl}${datasetId}`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
cellOverides={roleTable.overrides}
defaultRowsPerPage={10}
rowClickHandler={roleRowCLick}
isClickable={true}
/>
</div>
<div id="authorization-privilege-table" class="mb-10">
Expand All @@ -106,6 +107,7 @@
cellOverides={privilegesTable.overrides}
defaultRowsPerPage={10}
rowClickHandler={privilegeRowClick}
isClickable={true}
/>
</div>
{:catch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
defaultRowsPerPage={10}
title={connection.label}
{rowClickHandler}
isClickable={true}
/>
</div>
{/each}
Expand Down
15 changes: 14 additions & 1 deletion src/routes/(picsure)/(public)/dashboard/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { ProgressBar } from '@skeletonlabs/skeleton';

import { branding } from '$lib/configuration';
import { branding, features } from '$lib/configuration';
import Content from '$lib/components/Content.svelte';
import Datatable from '$lib/components/datatable/Table.svelte';
import DashboardLink from '$lib/components/datatable/DashboardLink.svelte';
Expand All @@ -14,6 +14,10 @@
import type { Column } from '$lib/models/Tables';
import type { DashboardRow } from '$lib/stores/Dashboard';

import { getDrawerStore } from '@skeletonlabs/skeleton';

const drawerStore = getDrawerStore();

const tableName = 'ExplorerTable';

let unsubColumns: Unsubscriber;
Expand All @@ -40,6 +44,13 @@
unsubColumns && unsubColumns();
unsubRows && unsubRows();
});

function rowClickHandler(row: DashboardRow) {
drawerStore.open({
id: 'dashboard-drawer',
meta: { row },
});
}
</script>

<svelte:head>
Expand All @@ -59,6 +70,8 @@
search={false}
showPagination={false}
stickyHeader={true}
rowClickHandler={features.dashboardDrawer ? rowClickHandler : undefined}
isClickable={features.dashboardDrawer}
/>
{/await}
</section>
Expand Down
18 changes: 16 additions & 2 deletions src/routes/(picsure)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
<script lang="ts">
import { AppShell, Modal, Toast, storePopup, type ModalComponent } from '@skeletonlabs/skeleton';
import {
AppShell,
Modal,
Toast,
Drawer,
storePopup,
type ModalComponent,
} from '@skeletonlabs/skeleton';
import { computePosition, autoUpdate, offset, shift, flip, arrow } from '@floating-ui/dom';
import Navigation from '$lib/components/Navigation.svelte';
import { onMount } from 'svelte';
Expand All @@ -8,12 +15,14 @@
import ExportStepper from '$lib/components/explorer/export/ExportStepper.svelte';
import Footer from '$lib/components/Footer.svelte';
import ModalWrapper from '$lib/components/modals/ModalWrapper.svelte';
import { getModalStore } from '@skeletonlabs/skeleton';
import { getModalStore, getDrawerStore } from '@skeletonlabs/skeleton';
import { beforeNavigate } from '$app/navigation';
import { hasInvalidFilter, hasGenomicFilter, hasUnallowedFilter } from '$lib/stores/Filter.ts';
import DashboardDrawer from '$lib/components/datatable/DashboardDrawer.svelte';
import FilterWarning from '$lib/components/modals/FilterWarning.svelte';

const modalStore = getModalStore();
const drawerStore = getDrawerStore();

// Highlight.js
import hljs from 'highlight.js/lib/core';
Expand Down Expand Up @@ -68,6 +77,11 @@

<Toast position="t" />
<Modal {...modalProps} />
<Drawer position="right" width="w-1/2" rounded="rounded-none">
{#if $drawerStore.id === 'dashboard-drawer'}
<DashboardDrawer />
{/if}
</Drawer>
<AppShell>
<svelte:fragment slot="header">
<Navigation />
Expand Down
20 changes: 18 additions & 2 deletions tests/mock-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,24 @@ export const mockDashboard: DashboardResp = {
{ label: 'Link', dataElement: 'additional_info_link' },
],
rows: [
{ name: 'A', additional_info_link: 'foo.invalid' },
{ name: 'B', additional_info_link: 'bar.invalid' },
{
name: 'A',
description: 'This is a description 1',
additional_info_link: 'foo.invalid',
dataset_id: '1',
},
{
name: 'B',
description: 'This is a description 2',
additional_info_link: 'bar.invalid',
dataset_id: '2',
},
{
name: 'C',
description: 'This is a description 3',
additional_info_link: null,
dataset_id: '3',
},
],
};

Expand Down
Loading