Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2b89570
Install react-modal
demariadaniel Jun 13, 2025
585b3b0
Most Basic Modal Setup
demariadaniel Jun 16, 2025
900f15e
First Pass CSS & Navigation; icons alphabetized
demariadaniel Jun 16, 2025
5221b33
Modal File & images
demariadaniel Jun 16, 2025
7c2446a
Improved modal/button/image styles
demariadaniel Jun 16, 2025
564281a
File Format badges & visualizer descriptions
demariadaniel Jun 16, 2025
a641811
Finalized container proportions, cursor, semantics & reorg
demariadaniel Jun 17, 2025
72bf29d
Disabled states
demariadaniel Jun 17, 2025
8babba6
Handle tables enabled/disabled w/ env
demariadaniel Jun 17, 2025
d124d8b
Fix package-lock
demariadaniel Jun 18, 2025
cd3030a
PR improvements
demariadaniel Jun 18, 2025
2569fe9
Default Iobio disabled
demariadaniel Jun 19, 2025
ca908e3
First batch code review updates
demariadaniel Jun 20, 2025
ea1d0d0
className use strings
demariadaniel Jun 20, 2025
e3efc79
Use derived state for current files
demariadaniel Jun 20, 2025
12ba4d0
Prefer css-in-js
demariadaniel Jun 20, 2025
9307856
Merge branch 'iobio' of https://github.com/overture-stack/stage into …
demariadaniel Jun 20, 2025
82f8dba
Break Modal into components
demariadaniel Jun 24, 2025
2d449e8
Handle key warning
demariadaniel Jun 24, 2025
6b0e5af
Standardize use of Visualizer, Colocate Styles
demariadaniel Jun 26, 2025
51a8cdf
Remove duplicate Modal.setAppElement
demariadaniel Jun 26, 2025
5245031
Anchor files/badges to bottom
demariadaniel Jun 26, 2025
5cc2095
Remove unused import
demariadaniel Jun 26, 2025
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
5 changes: 5 additions & 0 deletions .env.schema
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ NEXT_PUBLIC_ARRANGER_MANIFEST_COLUMNS=fieldName, "fieldName", 'fieldName'
######### DMS
NEXT_PUBLIC_SSO_PROVIDERS=

######## Visualizer Apps
NEXT_PUBLIC_JBROWSE_ENABLED=false
NEXT_PUBLIC_IOBIO_ENABLED=false
NEXT_PUBLIC_CBIOPORTAL_ENABLED=false

# ######## Optional features/functionalities
NEXT_PUBLIC_DEBUG=true

Expand Down
18 changes: 8 additions & 10 deletions components/pages/explorer/HeaderButtons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,19 @@ export const FullScreenButton = ({

export const VisualizerButton = ({
iconColor,
isBamFileSelected,
isFileTableActive,
switchTable, // change to open modal
disabled,
openModal,
}: {
disabled: boolean;
iconColor: string;
isBamFileSelected: boolean;
isFileTableActive: boolean;
switchTable: (t: string) => void;
openModal?: () => void;
}) => {
const {
colors: { accent2, grey_1, grey_4 },
} = useTheme();
return (
<button
disabled={!isBamFileSelected && isFileTableActive}
disabled={disabled}
css={css`
${getHeaderButtonStyles({ active: false, accent: accent2 })}
:disabled {
Expand All @@ -105,7 +103,7 @@ export const VisualizerButton = ({
}
}
`}
onClick={() => switchTable(tableTypes['BAM_TABLE'])}
onClick={openModal}
Comment on lines 108 to +106

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, all of the behaviour of this button has been moved outside of this component. The button does whatever openModal specifies. Even a decision about whether it is enabled is based on props.

Perhaps its just a case where we need to rename the props to onClick instead of openModal and replace visualizerEnabled and isFileTableActive to a single disabled or enabled prop.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look into how I can organize this better. The intent was to have openModal / closeModal as fixed utilities which always do the same thing, but because they use state you can't export them that way, and have to be scoped within a component render/props pattern (which is intended to be mutable). So you get this sort of organizational conflict.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use disabled

>
<span>
<BarGraph
Expand All @@ -122,7 +120,7 @@ export const VisualizerButton = ({
);
};

export const FileButton = ({ switchTable }: { switchTable: (t: string) => void }) => {
export const FileButton = ({ setTable }: { setTable: (tableName: string) => void }) => {
const {
colors: { accent2, white },
} = useTheme();
Expand All @@ -131,7 +129,7 @@ export const FileButton = ({ switchTable }: { switchTable: (t: string) => void }
css={css`
${getHeaderButtonStyles({ active: false, accent: accent2 })}
`}
onClick={() => switchTable(tableTypes['REPO_TABLE'])}
onClick={() => setTable(tableTypes['REPO_TABLE'])}
>
<span>
<ChevronDown
Expand Down
264 changes: 264 additions & 0 deletions components/pages/explorer/Modal/components.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
/*
*
* Copyright (c) 2025 The Ontario Institute for Cancer Research. All rights reserved
*
* This program and the accompanying materials are made available under the terms of
* the GNU Affero General Public License v3.0. You should have received a copy of the
* GNU Affero General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

import { css, useTheme, Theme } from '@emotion/react';
import urlJoin from 'url-join';
import { getConfig } from '../../../../global/config';
import { BamFileExtensions, tableTypes } from '../constants';
import { FileTableData } from '../fileTypes';
import { BadgeItem, VisualizerDetailProps } from './types';

const accentBadgeStyle = ({ theme, isDisabled }: { theme: Theme; isDisabled: boolean }) => css`
${badgeStyle({ theme, isDisabled })}
background-color: ${theme.colors.accent_light};
margin-left: 5px;
${isDisabled ? `background-color: ${theme.colors.grey_5};` : ''}
`;

const badgeStyle = ({ theme, isDisabled }: { theme: Theme; isDisabled: boolean }) => css`
display: inline-flex;
border: none;
border-radius: 20px;
margin: 5px;
margin-left: 0px;
min-width: fit-content;
padding: 3px 10px;
background-color: ${theme.colors.accent};
color: ${theme.colors.white};

${isDisabled ? `background-color: ${theme.colors.grey_6};` : ''}
`;

const optionStyle = ({ theme, isEnabled }: { theme: Theme; isEnabled: boolean }) => css`
background: unset;
border: 1px solid ${theme.colors.grey_5};
border-radius: 16px;
cursor: pointer;
display: inline-flex;
flex: 1;
font-family: 'Lato', sans-serif;
margin: 0 0.5rem;
padding: 10px;
position: relative;

${isEnabled ? '' : `cursor: not-allowed;`}
`;

export const VisualizerDetail = ({ title, description, previewImage, logoImage }: VisualizerDetailProps) => {
return (
<>
<div
css={css`
max-height: 28%;
overflow-y: hidden;

img {
width: 100%;
}
`}
>
<img src={previewImage} />
</div>
<div
css={css`
text-align: left;
`}
>
<img
css={css`
height: 18px;
vertical-align: text-bottom;
width: 18px;
`}
src={logoImage}
/>
<h4
css={css`
display: inline-block;
font-size: 18px;
margin: 0.5rem;
`}
>
{title}
</h4>
</div>
<p
css={css`
font-weight: 400;
font-size: 16px;
line-height: 16px;
margin-top: 0px;
text-align: left;
height: 30%;
`}
>
{description}
</p>
</>
);
};

export const Badges = ({ isEnabled, badges }: { isEnabled: boolean; badges: BadgeItem[] }) => {
const theme = useTheme();
const badgeGroup = badges.map((badge, index) => {
const badgeCss = badge.isAccent
? accentBadgeStyle({ theme, isDisabled: !isEnabled })
: badgeStyle({ theme, isDisabled: !isEnabled });
return (
<div key={`badge-${index}`} css={badgeCss}>
{badge.label}
</div>
);
});
return <>{badgeGroup}</>;
};

Comment on lines +119 to +133

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these badges were bottom aligned before?

Screenshot 2025-06-26 at 7 15 27 AM

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, missing position: absolute, added

export const VisualizerOption = ({
badges,
details: { title, description, previewImage, logoImage },
isEnabled,
onClick,
}: {
badges: BadgeItem[];
details: VisualizerDetailProps;
isEnabled: boolean;
onClick: () => void;
}) => {
const theme = useTheme();
return (
<button css={optionStyle({ theme, isEnabled })} disabled={!isEnabled} onClick={onClick}>
<div>
<VisualizerDetail title={title} description={description} previewImage={previewImage} logoImage={logoImage} />

<div
css={css`
position: absolute;
bottom: 10px;
`}
>
<h5
css={css`
font-weight: 700;
font-size: 16px;
margin: 0.25rem 0;
text-align: left;
`}
>
Files:
</h5>
<div
css={css`
display: flex;
`}
>
<Badges isEnabled={isEnabled} badges={badges} />
</div>
</div>
</div>
</button>
);
};

/**
*
* Responsibility is rendering the options available
* ie. read configs, gather data etc then render each option with data
*
*/
export const VisualizerModal = ({
closeModal,
setTable,
currentFiles,
}: {
closeModal: () => void;
setTable: (tableType: string) => void;
currentFiles: FileTableData[];
}) => {
const {
NEXT_PUBLIC_BASE_PATH,
NEXT_PUBLIC_IOBIO_ENABLED,
NEXT_PUBLIC_JBROWSE_ENABLED,
NEXT_PUBLIC_CBIOPORTAL_ENABLED,
} = getConfig();

const isJbrowseEnabled = NEXT_PUBLIC_JBROWSE_ENABLED && currentFiles.length <= 5;
const isCBioEnabled = NEXT_PUBLIC_CBIOPORTAL_ENABLED && currentFiles.length <= 2;
const isIobioEnabled =
NEXT_PUBLIC_IOBIO_ENABLED &&
currentFiles.length === 1 &&
currentFiles[0].file_type &&
BamFileExtensions.includes(currentFiles[0].file_type);

const selectVisualizer = (tableType: string) => () => {
setTable(tableType);
closeModal();
};

return (
<>
<VisualizerOption
badges={[
{ label: '5 Max', isAccent: false },
{ label: '.VCF', isAccent: true },
{ label: '.BAM', isAccent: true },
]}
onClick={selectVisualizer(tableTypes.JBROWSE_TABLE)}
isEnabled={isJbrowseEnabled}
details={{
title: 'JBrowse',
description:
'A fully featured genome browser that is capable of visualizing diverse types of genome-located data.',
previewImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/jBrowse_Preview.png'),
logoImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/jBrowse_Logo.png'),
}}
/>
<VisualizerOption
badges={[
{ label: '1 Max', isAccent: false },
{ label: '.BAM', isAccent: true },
]}
onClick={selectVisualizer(tableTypes.BAM_TABLE)}
isEnabled={!!isIobioEnabled}
details={{
title: 'IOBIO',
description: 'Examine your sequence alignment file in seconds',
previewImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/IOBIO_Preview.png'),
logoImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/IOBIO_Logo.png'),
}}
/>
<VisualizerOption
badges={[
{ label: '2 Max', isAccent: false },
{ label: '.VCF', isAccent: true },
{ label: '.BAM', isAccent: true },
]}
details={{
title: 'cBioPortal',
description: 'Provides visualization, analysis and download of large-scale cancer genomics data sets.',
previewImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/cBioPortal_Preview.png'),
logoImage: urlJoin(NEXT_PUBLIC_BASE_PATH, '/images/cBioPortal_Logo.png'),
}}
isEnabled={isCBioEnabled}
onClick={selectVisualizer(tableTypes.CBIO_TABLE)}
/>
</>
);
};
Loading