Skip to content

Commit

Permalink
feat(components): prevalence over time: add code example and link to …
Browse files Browse the repository at this point in the history
…codepen to info box #404
  • Loading branch information
chaoran-chen authored and fengelniederhammer committed Jul 30, 2024
1 parent 2ecd695 commit 3b22850
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 77 deletions.
89 changes: 88 additions & 1 deletion components/src/preact/components/info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const Info: FunctionComponent<InfoProps> = ({ children }) => {
?
</button>
<dialog ref={dialogRef} className={'modal modal-bottom sm:modal-middle'}>
<div className='modal-box'>
<div className='modal-box sm:max-w-5xl'>
<form method='dialog'>
<button className='btn btn-sm btn-circle btn-ghost absolute right-2 top-2'></button>
</form>
Expand Down Expand Up @@ -55,4 +55,91 @@ export const InfoLink: FunctionComponent<{ href: string }> = ({ children, href }
);
};

export type InfoComponentCodeProps = {
componentName: string;
params: object;
lapisUrl: string;
};

export const InfoComponentCode: FunctionComponent<InfoComponentCodeProps> = ({ componentName, params, lapisUrl }) => {
const componentCode = componentParametersToCode(componentName, params, lapisUrl);
const codePenData = {
title: 'GenSpectrum dashboard component',
html: generateFullExampleCode(componentCode, componentName),
layout: 'left',
editors: '100',
};
return (
<>
<InfoHeadline2>Use this component yourself</InfoHeadline2>
<InfoParagraph>
This component was created using the following parameters:
<div className='p-4 border border-gray-200 rounded-lg overflow-x-auto'>
<pre>
<code>{componentCode}</code>
</pre>
</div>
</InfoParagraph>
<InfoParagraph>
You can add this component to your own website using the{' '}
<InfoLink href='https://github.com/GenSpectrum/dashboard-components'>
GenSpectrum dashboard components library
</InfoLink>{' '}
and the code from above.
</InfoParagraph>
<InfoParagraph>
<form action='https://codepen.io/pen/define' method='POST' target='_blank'>
<input
type='hidden'
name='data'
value={JSON.stringify(codePenData).replace(/"/g, '&quot;').replace(/'/g, '&apos;')}
/>

<button className='text-blue-600 hover:text-blue-800' type='submit'>
Click here to try it out on CodePen.
</button>
</form>
</InfoParagraph>
</>
);
};

export default Info;

function componentParametersToCode(componentName: string, params: object, lapisUrl: string) {
const stringifyIfNeeded = (value: unknown) => {
return typeof value === 'object' ? JSON.stringify(value) : value;
};

const attributes = indentLines(
Object.entries(params)
.map(([key, value]) => `${key}='${stringifyIfNeeded(value)}'`)
.join('\n'),
4,
);
return `<gs-app lapis="${lapisUrl}">\n <gs-${componentName}\n${attributes}\n />\n</gs-app>`;
}

function generateFullExampleCode(componentCode: string, componentName: string) {
const storyBookPath = `/docs/visualization-${componentName}--docs`;
return `<html>
<head>
<script type="module" src="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/dashboard-components.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@genspectrum/dashboard-components@latest/dist/style.css" />
</head>
<body>
<!-- Component documentation: https://genspectrum.github.io/dashboard-components/?path=${storyBookPath} -->
${indentLines(componentCode, 2)}
</body>
</html>
`;
}

function indentLines(text: string, numberSpaces: number) {
const spaces = ' '.repeat(numberSpaces);
return text
.split('\n')
.map((line) => spaces + line)
.join('\n');
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ const Template = {
height={args.height}
lapisDateField={args.lapisDateField}
pageSize={args.pageSize}
yAxisMaxConfig={args.yAxisMaxConfig}
yAxisMaxLinear={args.yAxisMaxLinear}
yAxisMaxLogarithmic={args.yAxisMaxLogarithmic}
/>
</LapisUrlContext.Provider>
),
Expand Down
96 changes: 25 additions & 71 deletions components/src/preact/prevalenceOverTime/prevalence-over-time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,22 @@ import { CsvDownloadButton } from '../components/csv-download-button';
import { ErrorBoundary } from '../components/error-boundary';
import { ErrorDisplay } from '../components/error-display';
import { Fullscreen } from '../components/fullscreen';
import Info, { InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
import Info, { InfoComponentCode, InfoHeadline1, InfoHeadline2, InfoParagraph } from '../components/info';
import { LoadingDisplay } from '../components/loading-display';
import { NoDataDisplay } from '../components/no-data-display';
import { ResizeContainer } from '../components/resize-container';
import { ScalingSelector } from '../components/scaling-selector';
import Tabs from '../components/tabs';
import { type ConfidenceIntervalMethod } from '../shared/charts/confideceInterval';
import type { YAxisMaxConfig } from '../shared/charts/getYAxisMax';
import { type AxisMax } from '../shared/charts/getYAxisMax';
import { type ScaleType } from '../shared/charts/getYAxisScale';
import { useQuery } from '../useQuery';

export type View = 'bar' | 'line' | 'bubble' | 'table';

export interface PrevalenceOverTimeProps extends PrevalenceOverTimeInnerProps {
export interface PrevalenceOverTimeProps {
width: string;
height: string;
}

export interface PrevalenceOverTimeInnerProps {
numeratorFilter: NamedLapisFilter | NamedLapisFilter[];
denominatorFilter: LapisFilter;
granularity: TemporalGranularity;
Expand All @@ -41,32 +38,25 @@ export interface PrevalenceOverTimeInnerProps {
confidenceIntervalMethods: ConfidenceIntervalMethod[];
lapisDateField: string;
pageSize: boolean | number;
yAxisMaxConfig: YAxisMaxConfig;
yAxisMaxLinear?: AxisMax;
yAxisMaxLogarithmic?: AxisMax;
}

export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = ({ width, height, ...innerProps }) => {
export const PrevalenceOverTime: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
const { width, height } = componentProps;
const size = { height, width };

return (
<ErrorBoundary size={size}>
<ResizeContainer size={size}>
<PrevalenceOverTimeInner {...innerProps} />
<PrevalenceOverTimeInner {...componentProps} />
</ResizeContainer>
</ErrorBoundary>
);
};

export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerProps> = ({
numeratorFilter,
denominatorFilter,
granularity,
smoothingWindow,
views,
confidenceIntervalMethods,
lapisDateField,
pageSize,
yAxisMaxConfig,
}) => {
export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
const { numeratorFilter, denominatorFilter, granularity, smoothingWindow, lapisDateField } = componentProps;
const lapis = useContext(LapisUrlContext);

const { data, error, isLoading } = useQuery(
Expand Down Expand Up @@ -94,42 +84,21 @@ export const PrevalenceOverTimeInner: FunctionComponent<PrevalenceOverTimeInnerP
return <NoDataDisplay />;
}

return (
<PrevalenceOverTimeTabs
views={views}
data={data}
granularity={granularity}
smoothingWindow={smoothingWindow}
confidenceIntervalMethods={confidenceIntervalMethods}
pageSize={pageSize}
yAxisMaxConfig={yAxisMaxConfig}
/>
);
return <PrevalenceOverTimeTabs data={data} {...componentProps} />;
};

type PrevalenceOverTimeTabsProps = {
views: View[];
type PrevalenceOverTimeTabsProps = PrevalenceOverTimeProps & {
data: PrevalenceOverTimeData;
granularity: TemporalGranularity;
smoothingWindow: number;
confidenceIntervalMethods: ConfidenceIntervalMethod[];
pageSize: boolean | number;
yAxisMaxConfig: YAxisMaxConfig;
};

const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({
views,
data,
granularity,
smoothingWindow,
confidenceIntervalMethods,
pageSize,
yAxisMaxConfig,
}) => {
const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = ({ data, ...componentProps }) => {
const { views, granularity, confidenceIntervalMethods, pageSize, yAxisMaxLinear, yAxisMaxLogarithmic } =
componentProps;
const [yAxisScaleType, setYAxisScaleType] = useState<ScaleType>('linear');
const [confidenceIntervalMethod, setConfidenceIntervalMethod] = useState<ConfidenceIntervalMethod>(
confidenceIntervalMethods.length > 0 ? confidenceIntervalMethods[0] : 'none',
);
const yAxisMaxConfig = { linear: yAxisMaxLinear, logarithmic: yAxisMaxLogarithmic };

const getTab = (view: View) => {
switch (view) {
Expand Down Expand Up @@ -184,43 +153,35 @@ const PrevalenceOverTimeTabs: FunctionComponent<PrevalenceOverTimeTabsProps> = (
yAxisScaleType={yAxisScaleType}
setYAxisScaleType={setYAxisScaleType}
data={data}
granularity={granularity}
smoothingWindow={smoothingWindow}
confidenceIntervalMethods={confidenceIntervalMethods}
confidenceIntervalMethod={confidenceIntervalMethod}
setConfidenceIntervalMethod={setConfidenceIntervalMethod}
views={views}
{...componentProps}
/>
);

return <Tabs tabs={tabs} toolbar={toolbar} />;
};

type ToolbarProps = {
type ToolbarProps = PrevalenceOverTimeProps & {
activeTab: string;
data: PrevalenceOverTimeData;
granularity: TemporalGranularity;
smoothingWindow: number;
yAxisScaleType: ScaleType;
setYAxisScaleType: (scaleType: ScaleType) => void;
confidenceIntervalMethods: ConfidenceIntervalMethod[];
confidenceIntervalMethod: ConfidenceIntervalMethod;
setConfidenceIntervalMethod: (confidenceIntervalMethod: ConfidenceIntervalMethod) => void;
views: View[];
};

const Toolbar: FunctionComponent<ToolbarProps> = ({
activeTab,
yAxisScaleType,
setYAxisScaleType,
confidenceIntervalMethods,
confidenceIntervalMethod,
setConfidenceIntervalMethod,
data,
granularity,
smoothingWindow,
views,
...componentProps
}) => {
const { confidenceIntervalMethods, granularity } = componentProps;
return (
<>
{activeTab !== 'Table' && (
Expand All @@ -239,23 +200,15 @@ const Toolbar: FunctionComponent<ToolbarProps> = ({
filename='prevalence_over_time.csv'
/>

<PrevalenceOverTimeInfo granularity={granularity} views={views} smoothingWindow={smoothingWindow} />
<PrevalenceOverTimeInfo {...componentProps} />
<Fullscreen />
</>
);
};

type PrevalenceOverTimeInfoProps = {
granularity: TemporalGranularity;
smoothingWindow: number;
views: View[];
};

const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeInfoProps> = ({
granularity,
smoothingWindow,
views,
}) => {
const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeProps> = (componentProps) => {
const { granularity, smoothingWindow, views } = componentProps;
const lapis = useContext(LapisUrlContext);
return (
<Info>
<InfoHeadline1>Prevalence over time</InfoHeadline1>
Expand All @@ -278,6 +231,7 @@ const PrevalenceOverTimeInfo: FunctionComponent<PrevalenceOverTimeInfoProps> = (
</InfoParagraph>
</>
)}
<InfoComponentCode componentName='prevalence-over-time' params={componentProps} lapisUrl={lapis} />
</Info>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,8 @@ export class PrevalenceOverTimeComponent extends PreactLitAdapterWithGridJsStyle
height={this.height}
lapisDateField={this.lapisDateField}
pageSize={this.pageSize}
yAxisMaxConfig={{
linear: this.yAxisMaxLinear,
logarithmic: this.yAxisMaxLogarithmic,
}}
yAxisMaxLinear={this.yAxisMaxLinear}
yAxisMaxLogarithmic={this.yAxisMaxLogarithmic}
/>
);
}
Expand Down

0 comments on commit 3b22850

Please sign in to comment.