Skip to content
Open
48 changes: 48 additions & 0 deletions .github/workflows/deploy-storybook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Deploy Storybook to GitHub Pages

on:
push:
branches: ["feature/storybook-ci", "feature/aip" ]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Enable Corepack
run: corepack enable

- name: Set Yarn version to 4.9.4
run: corepack prepare [email protected] --activate

- name: Install dependencies
run: yarn install --immutable

- name: Build Storybook
run: yarn build-storybook

- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./storybook-static

deploy:
environment:
name: github-pages
url: ${{ steps.deploy.outputs.page_url }}

runs-on: ubuntu-latest
needs: build

steps:
- name: Deploy to GitHub Pages
id: deploy
uses: actions/deploy-pages@v4
21 changes: 21 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { dirname, resolve as resolvePath } from 'path';
import { fileURLToPath } from 'url';
import webpack from 'webpack';

// Needed for ESM __dirname
const __dirname = dirname(fileURLToPath(import.meta.url));

const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],

Expand All @@ -15,6 +22,7 @@ const config = {
staticDirs: ['../public'],

webpackFinal: async (config) => {
// Remove ESLint rules Storybook injects
config.module.rules = config.module.rules.filter(
(rule) => !(rule.use && rule.use.some((u) => u.loader?.includes('eslint')))
);
Expand All @@ -23,6 +31,19 @@ const config = {
(plugin) => !(plugin.constructor && plugin.constructor.name === 'ESLintWebpackPlugin')
);

// --- Polyfills for Node modules that @cosmotech/core uses ---
config.resolve.fallback = {
...config.resolve.fallback,
stream: resolvePath(__dirname, '../node_modules/stream-browserify'),
process: resolvePath(__dirname, '../node_modules/process/browser'),
};

config.plugins.push(
new webpack.DefinePlugin({
'process.env': '{}',
})
);

return config;
},
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"i18next-browser-languagedetector": "^8.2.0",
"i18next-http-backend": "^3.0.2",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.554.0",
"lucide-react": "^0.555.0",
"process": "^0.11.10",
"prop-types": "^15.8.1",
"react": "^19.1.1",
"react-countdown": "^2.3.6",
Expand Down
8 changes: 8 additions & 0 deletions public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@
}
}
},
"tabLayout": {
"statusBar": {
"prerun": {
"message": "This scenario has not been run yet.",
"tooltip": "This scenario has not been run yet."
}
}
},
"cytoviz": {
"elementDetails": "Details",
"loading": "Loading...",
Expand Down
16 changes: 12 additions & 4 deletions src/AppRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,21 @@ const AppRoutes = () => {
<>
<Route path="/" element={<Navigate to={redirectPath} replace />} />
<Route
path="/workspaces"
element={
<UserStatusGate>
<Workspaces />
<TabLayout />
</UserStatusGate>
}
/>
>
<Route
path="/workspaces"
element={
<UserStatusGate>
<Workspaces />
</UserStatusGate>
}
/>
</Route>
<Route
path=":workspaceId"
element={
Expand All @@ -39,7 +47,7 @@ const AppRoutes = () => {
<Route
element={
<UserStatusGate>
<TabLayout tabs={tabs} />
<TabLayout />
</UserStatusGate>
}
>
Expand Down
40 changes: 27 additions & 13 deletions src/components/AppBar/AppBar.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
// Copyright (c) Cosmo Tech.
// Licensed under the MIT license.
import { Bot, Languages } from 'lucide-react';
import React from 'react';
import PropTypes from 'prop-types';
import { AppBar as MuiAppBar, Toolbar } from '@mui/material';
import { HelpMenuWrapper, Logo, ThemeSwitch, UserInfoWrapper, WorkspaceInfo } from './components';
import { Button, AppBar as MuiAppBar, Toolbar } from '@mui/material';
import { StatusBar } from '../';
import { useCurrentSimulationRunner } from '../../state/runner/hooks';
import { ThemeSwitch } from './components';

export const AppBar = ({ children }) => {
const currentScenario = useCurrentSimulationRunner();

return (
<MuiAppBar
position="sticky"
sx={{
backgroundColor: (theme) => theme.palette.appbar.main,
color: (theme) => theme.palette.appbar.contrastText,
backgroundColor: (theme) => theme.palette.background.background01.main,
color: (theme) => theme.palette.neutral.neutral02.main,
boxShadow: 'none',
borderBottom: (theme) => `1px solid ${theme.palette.background.background02.main}`,
}}
>
<Toolbar variant="dense" disableGutters={true}>
<WorkspaceInfo />
<div style={{ flexGrow: 1 }}>{children}</div>
<Toolbar variant="dense" disableGutters={true} sx={{ px: 1 }}>
<div style={{ flexGrow: 1, display: 'flex', alignItems: 'center', gap: '8px' }}>{children}</div>
{currentScenario?.data?.name && (
<StatusBar status="valid" size="medium" tooltip="This scenario has not been run yet." />
)}
<Button sx={{ ml: 1 }} variant="copilot" state="enabled" startIcon={<Bot />}>
CoPilot
</Button>
<ThemeSwitch />
<HelpMenuWrapper />
<UserInfoWrapper />
<Logo />
<Button
sx={{ ml: 1, backgroundColor: (theme) => theme.palette.neutral.neutral04.main }}
variant="default"
state="enabled"
startIcon={<Languages />}
>
English
</Button>
</Toolbar>
</MuiAppBar>
);
};
AppBar.propTypes = {
/**
* React component to be implemented in dynamic part of the app bar
*/
children: PropTypes.node,
};
17 changes: 10 additions & 7 deletions src/components/AppBar/components/ThemeSwitch.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,33 @@
// Copyright (c) Cosmo Tech.
// Licensed under the MIT license.
import { Moon, Sun } from 'lucide-react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Brightness2 as Brightness2Icon, WbSunny as WbSunnyIcon } from '@mui/icons-material';
import { Fade, IconButton, Tooltip } from '@mui/material';
import { Button, Fade, Tooltip } from '@mui/material';
import { useApplicationTheme } from '../../../state/app/hooks';

export const ThemeSwitch = () => {
const { t } = useTranslation();
const { isDarkTheme, toggleTheme } = useApplicationTheme();

const { tooltipText, icon } = useMemo(
const { tooltipText } = useMemo(
() => ({
tooltipText: isDarkTheme
? t('genericcomponent.switchtheme.light', 'Switch to light')
: t('genericcomponent.switchtheme.dark', 'Switch to dark'),
icon: isDarkTheme ? <WbSunnyIcon /> : <Brightness2Icon />,
}),
[t, isDarkTheme]
);

return (
<Tooltip TransitionComponent={Fade} TransitionProps={{ timeout: 600 }} title={tooltipText}>
<IconButton sx={{ color: (theme) => theme.palette.appbar.contrastText }} onClick={toggleTheme} size="large">
{icon}
</IconButton>
<Button
sx={{ ml: 1 }}
variant="outlined"
state="enabled"
startIcon={isDarkTheme ? <Moon /> : <Sun />}
onClick={toggleTheme}
/>
</Tooltip>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const useCreateScenarioButton = ({ disabled, onScenarioCreated }) => {
const solution = useSolution();

const workspaceData = useWorkspaceData();
const workspaceId = workspaceData.id;
const workspaceId = workspaceData?.id;
const workspaceDatasets = useWorkspaceDatasets();
const usableDatasets = workspaceDatasets.filter(
(dataset) =>
Expand Down
Loading